WifiManager: guard init_wifi_state (#37413)

* failing test

* fix

* rename

* better
This commit is contained in:
Shane Smiskol
2026-02-25 19:30:02 -08:00
committed by GitHub
parent 5c630b20a9
commit 496ae85f67
2 changed files with 39 additions and 4 deletions

View File

@@ -347,6 +347,33 @@ class TestThreadRaces:
assert wm._wifi_state.ssid == "B"
assert wm._wifi_state.status == ConnectStatus.CONNECTING
def test_init_wifi_state_race_user_tap_during_dbus(self, mocker):
"""User taps B while _init_wifi_state's DBus calls are in flight.
_init_wifi_state runs from set_active(True) or worker error paths. It does
2 DBus calls (device State property + _get_active_wifi_connection) then
unconditionally writes _wifi_state. If the user taps a network during those
calls, _set_connecting("B") is overwritten with stale NM ground truth.
"""
wm = _make_wm(mocker, connections={"A": "/path/A", "B": "/path/B"})
wm._wifi_device = "/dev/wifi0"
wm._router_main = mocker.MagicMock()
state_reply = mocker.MagicMock()
state_reply.body = [('u', NMDeviceState.ACTIVATED)]
wm._router_main.send_and_get_reply.return_value = state_reply
def user_taps_b_during_dbus(*args, **kwargs):
wm._set_connecting("B")
return ("/path/A", {})
wm._get_active_wifi_connection.side_effect = user_taps_b_during_dbus
wm._init_wifi_state()
assert wm._wifi_state.ssid == "B"
assert wm._wifi_state.status == ConnectStatus.CONNECTING
# ---------------------------------------------------------------------------
# Full sequences (NM signal order from real devices)

View File

@@ -197,7 +197,7 @@ class WifiManager:
self._networks_updated: list[Callable[[list[Network]], None]] = []
self._disconnected: list[Callable[[], None]] = []
self._lock = threading.Lock()
self._scan_lock = threading.Lock()
self._scan_thread = threading.Thread(target=self._network_scanner, daemon=True)
self._state_thread = threading.Thread(target=self._monitor_state, daemon=True)
self._initialize()
@@ -227,6 +227,8 @@ class WifiManager:
cloudlog.warning("No WiFi device found")
return
epoch = self._user_epoch
dev_addr = DBusAddress(self._wifi_device, bus_name=NM, interface=NM_DEVICE_IFACE)
dev_state = self._router_main.send_and_get_reply(Properties(dev_addr).get('State')).body[0][1]
@@ -239,6 +241,10 @@ class WifiManager:
conn_path, _ = self._get_active_wifi_connection()
if conn_path:
wifi_state.ssid = next((s for s, p in self._connections.items() if p == conn_path), None)
if self._user_epoch != epoch:
return
self._wifi_state = wifi_state
if block:
@@ -281,11 +287,13 @@ class WifiManager:
@property
def connecting_to_ssid(self) -> str | None:
return self._wifi_state.ssid if self._wifi_state.status == ConnectStatus.CONNECTING else None
wifi_state = self._wifi_state
return wifi_state.ssid if wifi_state.status == ConnectStatus.CONNECTING else None
@property
def connected_ssid(self) -> str | None:
return self._wifi_state.ssid if self._wifi_state.status == ConnectStatus.CONNECTED else None
wifi_state = self._wifi_state
return wifi_state.ssid if wifi_state.status == ConnectStatus.CONNECTED else None
@property
def tethering_password(self) -> str:
@@ -822,7 +830,7 @@ class WifiManager:
return
def worker():
with self._lock:
with self._scan_lock:
if self._wifi_device is None:
cloudlog.warning("No WiFi device found")
return