diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 15aeb94ed..3ce5c839e 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -158,15 +158,26 @@ class WifiManager: self._disconnected: list[Callable[[], None]] = [] self._lock = threading.Lock() - self._scan_thread = threading.Thread(target=self._network_scanner, daemon=True) - self._scan_thread.start() - self._state_thread = threading.Thread(target=self._monitor_state, daemon=True) - self._state_thread.start() - + self._initialize() atexit.register(self.stop) + def _initialize(self): + def worker(): + self._wait_for_wifi_device() + + self._scan_thread.start() + self._state_thread.start() + + if self._tethering_ssid not in self._get_connections(): + self._add_tethering_connection() + + self._tethering_password = self._get_tethering_password() + cloudlog.debug("WifiManager initialized") + + threading.Thread(target=worker, daemon=True).start() + def set_callbacks(self, need_auth: Callable[[str], None] | None = None, activated: Callable[[], None] | None = None, forgotten: Callable[[], None] | None = None, @@ -213,18 +224,11 @@ class WifiManager: self._last_network_update = 0.0 def _monitor_state(self): - # TODO: make an initialize function to only wait in one place - device_path = self._wait_for_wifi_device() - if device_path is None: - return - - self._tethering_password = self._get_tethering_password() - rule = MatchRule( type="signal", interface=NM_DEVICE_IFACE, member="StateChanged", - path=device_path, + path=self._wifi_device, ) # Filter for StateChanged signal @@ -261,8 +265,6 @@ class WifiManager: self._enqueue_callbacks(self._forgotten) def _network_scanner(self): - self._wait_for_wifi_device() - while not self._exit: if self._active: if time.monotonic() - self._last_network_update > SCAN_PERIOD_SECONDS: @@ -273,15 +275,12 @@ class WifiManager: self._last_network_update = time.monotonic() time.sleep(1 / 2.) - def _wait_for_wifi_device(self) -> str | None: - with self._lock: - device_path: str | None = None - while not self._exit: - device_path = self._get_wifi_device() - if device_path is not None: - break - time.sleep(1) - return device_path + def _wait_for_wifi_device(self): + while not self._exit: + device_path = self._get_wifi_device() + if device_path is not None: + break + time.sleep(1) def _get_wifi_device(self) -> str | None: if self._wifi_device is not None: @@ -304,15 +303,12 @@ class WifiManager: conns: dict[str, str] = {} for conn_path in known_connections: - conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_CONNECTION_IFACE) - reply = self._router_main.send_and_get_reply(new_method_call(conn_addr, "GetSettings")) + settings = self._get_connection_settings(conn_path) - # ignore connections removed during iteration (need auth, etc.) - if reply.header.message_type == MessageType.error: - cloudlog.warning(f"Failed to get connection properties for {conn_path}") + if len(settings) == 0: + cloudlog.warning(f'Failed to get connection settings for {conn_path}') continue - settings = reply.body[0] if "802-11-wireless" in settings: ssid = settings['802-11-wireless']['ssid'][1].decode("utf-8", "replace") if ssid != "": @@ -322,7 +318,6 @@ class WifiManager: def _get_active_connections(self): return self._router_main.send_and_get_reply(Properties(self._nm).get('ActiveConnections')).body[0][1] - # TODO: use this def _get_connection_settings(self, conn_path: str) -> dict: conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_CONNECTION_IFACE) reply = self._router_main.send_and_get_reply(new_method_call(conn_addr, 'GetSettings')) @@ -331,6 +326,43 @@ class WifiManager: return {} return dict(reply.body[0]) + def _add_tethering_connection(self): + connection = { + 'connection': { + 'type': ('s', '802-11-wireless'), + 'uuid': ('s', str(uuid.uuid4())), + 'id': ('s', 'Hotspot'), + 'autoconnect-retries': ('i', 0), + 'interface-name': ('s', 'wlan0'), + 'autoconnect': ('b', False), + }, + '802-11-wireless': { + 'band': ('s', 'bg'), + 'mode': ('s', 'ap'), + 'ssid': ('ay', self._tethering_ssid.encode("utf-8")), + }, + '802-11-wireless-security': { + 'group': ('as', ['ccmp']), + 'key-mgmt': ('s', 'wpa-psk'), + 'pairwise': ('as', ['ccmp']), + 'proto': ('as', ['rsn']), + 'psk': ('s', DEFAULT_TETHERING_PASSWORD), + }, + 'ipv4': { + 'method': ('s', 'shared'), + 'address-data': ('aa{sv}', [[ + ('address', ('s', TETHERING_IP_ADDRESS)), + ('prefix', ('u', 24)), + ]]), + 'gateway': ('s', TETHERING_IP_ADDRESS), + 'never-default': ('b', True), + }, + 'ipv6': {'method': ('s', 'ignore')}, + } + + settings_addr = DBusAddress(NM_SETTINGS_PATH, bus_name=NM, interface=NM_SETTINGS_IFACE) + self._router_main.send_and_get_reply(new_method_call(settings_addr, 'AddConnection', 'a{sa{sv}}', (connection,))) + def connect_to_network(self, ssid: str, password: str, hidden: bool = False): def worker(): # Clear all connections that may already exist to the network we are connecting to @@ -503,7 +535,6 @@ class WifiManager: self._current_network_metered = MeteredType.YES elif metered_prop == MeteredType.NO: self._current_network_metered = MeteredType.NO - print('current_network_metered', self._current_network_metered) return def set_current_network_metered(self, metered: MeteredType):