Skip to content

Commit

Permalink
Merge pull request #16 from matubu/feat/connectivity-driver
Browse files Browse the repository at this point in the history
feat: netmanager
  • Loading branch information
gfanton authored Mar 2, 2023
2 parents 3dd9c04 + 295f8d0 commit b9644b2
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 1 deletion.
2 changes: 1 addition & 1 deletion pkg/logutil/logger_native_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func NativeLog(logLevel zapcore.Level, namespace string, message string) {
level = C.INFO
}

log := C.os_log_create(C.CString(namespace), C.CString(""))
log := C.os_log_create(C.CString(namespace), C.CString(namespace))

C.os_log_wrapper(level, log, C.CString(fmt.Sprintf("[%s] %s", logLevel.CapitalString(), message)))
}
111 changes: 111 additions & 0 deletions pkg/netmanager/connectivity.go
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(),
" }")
}
103 changes: 103 additions & 0 deletions pkg/netmanager/netmanager.go
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
}
97 changes: 97 additions & 0 deletions pkg/netmanager/netmanager_test.go
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)
}

0 comments on commit b9644b2

Please sign in to comment.