| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 | 
							- package settings
 
- import (
 
- 	"context"
 
- 	"strings"
 
- 	"time"
 
- 	"github.com/sagernet/sing-box/adapter"
 
- 	"github.com/godbus/dbus/v5"
 
- )
 
- type connmanMonitor struct {
 
- 	conn       *dbus.Conn
 
- 	callback   func(adapter.WIFIState)
 
- 	cancel     context.CancelFunc
 
- 	signalChan chan *dbus.Signal
 
- }
 
- func newConnManMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
 
- 	conn, err := dbus.ConnectSystemBus()
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	cmObj := conn.Object("net.connman", "/")
 
- 	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
 
- 	defer cancel()
 
- 	call := cmObj.CallWithContext(ctx, "net.connman.Manager.GetServices", 0)
 
- 	if call.Err != nil {
 
- 		conn.Close()
 
- 		return nil, call.Err
 
- 	}
 
- 	return &connmanMonitor{conn: conn, callback: callback}, nil
 
- }
 
- func (m *connmanMonitor) ReadWIFIState() adapter.WIFIState {
 
- 	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
 
- 	defer cancel()
 
- 	cmObj := m.conn.Object("net.connman", "/")
 
- 	var services []interface{}
 
- 	err := cmObj.CallWithContext(ctx, "net.connman.Manager.GetServices", 0).Store(&services)
 
- 	if err != nil {
 
- 		return adapter.WIFIState{}
 
- 	}
 
- 	for _, service := range services {
 
- 		servicePair, ok := service.([]interface{})
 
- 		if !ok || len(servicePair) != 2 {
 
- 			continue
 
- 		}
 
- 		serviceProps, ok := servicePair[1].(map[string]dbus.Variant)
 
- 		if !ok {
 
- 			continue
 
- 		}
 
- 		typeVariant, hasType := serviceProps["Type"]
 
- 		if !hasType {
 
- 			continue
 
- 		}
 
- 		serviceType, ok := typeVariant.Value().(string)
 
- 		if !ok || serviceType != "wifi" {
 
- 			continue
 
- 		}
 
- 		stateVariant, hasState := serviceProps["State"]
 
- 		if !hasState {
 
- 			continue
 
- 		}
 
- 		state, ok := stateVariant.Value().(string)
 
- 		if !ok || (state != "online" && state != "ready") {
 
- 			continue
 
- 		}
 
- 		nameVariant, hasName := serviceProps["Name"]
 
- 		if !hasName {
 
- 			continue
 
- 		}
 
- 		ssid, ok := nameVariant.Value().(string)
 
- 		if !ok || ssid == "" {
 
- 			continue
 
- 		}
 
- 		bssidVariant, hasBSSID := serviceProps["BSSID"]
 
- 		if !hasBSSID {
 
- 			return adapter.WIFIState{SSID: ssid}
 
- 		}
 
- 		bssid, ok := bssidVariant.Value().(string)
 
- 		if !ok {
 
- 			return adapter.WIFIState{SSID: ssid}
 
- 		}
 
- 		return adapter.WIFIState{
 
- 			SSID:  ssid,
 
- 			BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
 
- 		}
 
- 	}
 
- 	return adapter.WIFIState{}
 
- }
 
- func (m *connmanMonitor) Start() error {
 
- 	if m.callback == nil {
 
- 		return nil
 
- 	}
 
- 	ctx, cancel := context.WithCancel(context.Background())
 
- 	m.cancel = cancel
 
- 	m.signalChan = make(chan *dbus.Signal, 10)
 
- 	m.conn.Signal(m.signalChan)
 
- 	err := m.conn.AddMatchSignal(
 
- 		dbus.WithMatchInterface("net.connman.Service"),
 
- 		dbus.WithMatchSender("net.connman"),
 
- 	)
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	state := m.ReadWIFIState()
 
- 	go m.monitorSignals(ctx, m.signalChan, state)
 
- 	m.callback(state)
 
- 	return nil
 
- }
 
- func (m *connmanMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
 
- 	for {
 
- 		select {
 
- 		case <-ctx.Done():
 
- 			return
 
- 		case signal, ok := <-signalChan:
 
- 			if !ok {
 
- 				return
 
- 			}
 
- 			if signal.Name == "PropertyChanged" {
 
- 				state := m.ReadWIFIState()
 
- 				if state != lastState {
 
- 					lastState = state
 
- 					m.callback(state)
 
- 				}
 
- 			}
 
- 		}
 
- 	}
 
- }
 
- func (m *connmanMonitor) Close() error {
 
- 	if m.cancel != nil {
 
- 		m.cancel()
 
- 	}
 
- 	if m.signalChan != nil {
 
- 		m.conn.RemoveSignal(m.signalChan)
 
- 		close(m.signalChan)
 
- 	}
 
- 	if m.conn != nil {
 
- 		return m.conn.Close()
 
- 	}
 
- 	return nil
 
- }
 
 
  |