-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from matubu/feat/connectivity-driver
feat: netmanager
- Loading branch information
Showing
4 changed files
with
312 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package netmanager | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
type ConnectivityState int | ||
|
||
const ( | ||
ConnectivityStateUnknown ConnectivityState = iota | ||
ConnectivityStateOff | ||
ConnectivityStateOn | ||
) | ||
|
||
func (cs ConnectivityState) String() string { | ||
switch cs { | ||
case ConnectivityStateUnknown: | ||
return "unknown" | ||
case ConnectivityStateOff: | ||
return "off" | ||
case ConnectivityStateOn: | ||
return "on" | ||
default: | ||
return "error" | ||
} | ||
} | ||
|
||
type ConnectivityNetType int | ||
|
||
const ( | ||
ConnectivityNetUnknown ConnectivityNetType = iota | ||
ConnectivityNetNone | ||
ConnectivityNetWifi | ||
ConnectivityNetEthernet | ||
ConnectivityNetCellular | ||
) | ||
|
||
func (cnt ConnectivityNetType) String() string { | ||
switch cnt { | ||
case ConnectivityNetUnknown: | ||
return "unknown" | ||
case ConnectivityNetNone: | ||
return "none" | ||
case ConnectivityNetWifi: | ||
return "wifi" | ||
case ConnectivityNetEthernet: | ||
return "ethernet" | ||
case ConnectivityNetCellular: | ||
return "cellular" | ||
default: | ||
return "error" | ||
} | ||
} | ||
|
||
type ConnectivityCellularType int | ||
|
||
const ( | ||
ConnectivityCellularUnknown ConnectivityCellularType = iota | ||
ConnectivityCellularNone | ||
ConnectivityCellular2G | ||
ConnectivityCellular3G | ||
ConnectivityCellular4G | ||
ConnectivityCellular5G | ||
) | ||
|
||
func (cct ConnectivityCellularType) String() string { | ||
switch cct { | ||
case ConnectivityCellularUnknown: | ||
return "unknown" | ||
case ConnectivityCellularNone: | ||
return "none" | ||
case ConnectivityCellular2G: | ||
return "2G" | ||
case ConnectivityCellular3G: | ||
return "3G" | ||
case ConnectivityCellular4G: | ||
return "4G" | ||
case ConnectivityCellular5G: | ||
return "5G" | ||
default: | ||
return "error" | ||
} | ||
} | ||
|
||
type ConnectivityInfo struct { | ||
// False when the device is not connected to a network. | ||
State ConnectivityState | ||
|
||
// True when the device is connected to a metered network. | ||
Metering ConnectivityState | ||
|
||
// True when the device is connected to a bluetooth network. | ||
Bluetooth ConnectivityState | ||
|
||
// The type of the network the device is connected to: wifi/ethernet/cellular. | ||
NetType ConnectivityNetType | ||
|
||
// If the device is connected to a cellular network: | ||
// The type of the cellular network the device is connected to: 2G/3G/4G/5G. | ||
CellularType ConnectivityCellularType | ||
} | ||
|
||
func (ci ConnectivityInfo) String() string { | ||
return fmt.Sprint("ConnectivityInfo{ ", | ||
"State: ", ci.State.String(), ", ", | ||
"Metering: ", ci.Metering.String(), ", ", | ||
"Bluetooth: ", ci.Bluetooth.String(), ", ", | ||
"NetType: ", ci.NetType.String(), ", ", | ||
"CellularType: ", ci.CellularType.String(), | ||
" }") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package netmanager | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
|
||
"berty.tech/weshnet/internal/notify" | ||
) | ||
|
||
type NetManager struct { | ||
currentState ConnectivityInfo | ||
|
||
locker *sync.RWMutex | ||
notify *notify.Notify | ||
} | ||
|
||
type EventType uint | ||
|
||
const ( | ||
ConnectivityStateChanged EventType = 1 << iota | ||
ConnectivityMeteringChanged | ||
ConnectivityBluetoothChanged | ||
ConnectivityNetTypeChanged | ||
ConnectivityCellularTypeChanged | ||
|
||
ConnectivityChanged = 0 | | ||
ConnectivityStateChanged | | ||
ConnectivityMeteringChanged | | ||
ConnectivityBluetoothChanged | | ||
ConnectivityNetTypeChanged | | ||
ConnectivityCellularTypeChanged | ||
) | ||
|
||
func (t EventType) Has(other EventType) bool { | ||
return (t & other) == other | ||
} | ||
|
||
func NewNetManager(initialState ConnectivityInfo) *NetManager { | ||
var locker sync.RWMutex | ||
return &NetManager{ | ||
currentState: initialState, | ||
locker: &locker, | ||
notify: notify.New(&locker), | ||
} | ||
} | ||
|
||
// UpdateState update the current state of the Manager | ||
func (m *NetManager) UpdateState(state ConnectivityInfo) { | ||
m.locker.Lock() | ||
if m.currentState != state { | ||
m.currentState = state | ||
m.notify.Broadcast() | ||
} | ||
m.locker.Unlock() | ||
} | ||
|
||
// WaitForStateChange waits until the currentState changes from sourceState or ctx expires. | ||
// The eventType argument allow you to filter out the event you want to wait for. | ||
// A true value is returned in former case and false in latter. | ||
// The EventType is also returned to know which events has been triggered. | ||
func (m *NetManager) WaitForStateChange(ctx context.Context, sourceState *ConnectivityInfo, eventType EventType) (bool, EventType) { | ||
m.locker.Lock() | ||
|
||
var currentEventType EventType | ||
ok := true | ||
|
||
for ok { | ||
currentEventType = 0 | ||
|
||
if sourceState.State != m.currentState.State { | ||
currentEventType |= ConnectivityStateChanged | ||
} | ||
if sourceState.Metering != m.currentState.Metering { | ||
currentEventType |= ConnectivityMeteringChanged | ||
} | ||
if sourceState.Bluetooth != m.currentState.Bluetooth { | ||
currentEventType |= ConnectivityBluetoothChanged | ||
} | ||
if sourceState.NetType != m.currentState.NetType { | ||
currentEventType |= ConnectivityNetTypeChanged | ||
} | ||
if sourceState.CellularType != m.currentState.CellularType { | ||
currentEventType |= ConnectivityCellularTypeChanged | ||
} | ||
|
||
if (eventType & currentEventType) != 0 { | ||
break | ||
} | ||
// wait until state has been changed or context has been cancel | ||
ok = m.notify.Wait(ctx) | ||
} | ||
|
||
m.locker.Unlock() | ||
return ok, currentEventType | ||
} | ||
|
||
// GetCurrentState return the current state of the Manager | ||
func (m *NetManager) GetCurrentState() (state ConnectivityInfo) { | ||
m.locker.RLock() | ||
state = m.currentState | ||
m.locker.RUnlock() | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package netmanager | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestNewNetManager(t *testing.T) { | ||
initial := ConnectivityInfo{ | ||
State: ConnectivityStateOn, | ||
NetType: ConnectivityNetWifi, | ||
Bluetooth: ConnectivityStateOn, | ||
} | ||
|
||
netmanager := NewNetManager(initial) | ||
|
||
require.Equal(t, initial, netmanager.GetCurrentState()) | ||
initial.State = ConnectivityStateOff | ||
require.NotEqual(t, initial, netmanager.GetCurrentState()) | ||
} | ||
|
||
func TestNetManagerSingleUpdate(t *testing.T) { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
a := ConnectivityInfo{ | ||
State: ConnectivityStateOn, | ||
} | ||
state := ConnectivityInfo{} | ||
|
||
netmanager := NewNetManager(state) | ||
|
||
netmanager.UpdateState(a) | ||
require.Equal(t, a, netmanager.GetCurrentState()) | ||
|
||
ok, eventType := netmanager.WaitForStateChange(ctx, &state, ConnectivityChanged) | ||
|
||
require.Equal(t, a, netmanager.GetCurrentState()) | ||
require.True(t, ok) | ||
require.Equal(t, ConnectivityStateChanged, eventType) | ||
} | ||
|
||
func TestNetManagerDoubleUpdate(t *testing.T) { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
a := ConnectivityInfo{ | ||
State: ConnectivityStateOn, | ||
} | ||
b := ConnectivityInfo{ | ||
State: ConnectivityStateOff, | ||
} | ||
state := ConnectivityInfo{} | ||
|
||
netmanager := NewNetManager(state) | ||
|
||
netmanager.UpdateState(a) | ||
require.Equal(t, a, netmanager.GetCurrentState()) | ||
netmanager.UpdateState(b) | ||
require.Equal(t, b, netmanager.GetCurrentState()) | ||
|
||
ok, eventType := netmanager.WaitForStateChange(ctx, &state, ConnectivityChanged) | ||
|
||
require.Equal(t, b, netmanager.GetCurrentState()) | ||
require.True(t, ok) | ||
require.Equal(t, ConnectivityStateChanged, eventType) | ||
} | ||
|
||
func TestNetManagerFilterUpdate(t *testing.T) { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
a := ConnectivityInfo{ | ||
State: ConnectivityStateOff, | ||
} | ||
b := ConnectivityInfo{ | ||
State: ConnectivityStateOn, | ||
NetType: ConnectivityNetCellular, | ||
CellularType: ConnectivityCellular3G, | ||
} | ||
state := ConnectivityInfo{} | ||
|
||
netmanager := NewNetManager(state) | ||
|
||
netmanager.UpdateState(a) | ||
require.Equal(t, a, netmanager.GetCurrentState()) | ||
netmanager.UpdateState(b) | ||
require.Equal(t, b, netmanager.GetCurrentState()) | ||
|
||
ok, eventType := netmanager.WaitForStateChange(ctx, &state, ConnectivityCellularTypeChanged) | ||
|
||
require.Equal(t, b, netmanager.GetCurrentState()) | ||
require.True(t, ok) | ||
require.Equal(t, ConnectivityStateChanged | ConnectivityNetTypeChanged | ConnectivityCellularTypeChanged, eventType) | ||
} |