Skip to content

[Windows]: Report captive portals and address changes to the shared engine - #230

Open
lavan8t wants to merge 1 commit into
mainfrom
feat/windows-network-signals
Open

lavan8t wants to merge 1 commit into
mainfrom
feat/windows-network-signals

Conversation

@lavan8t

@lavan8t lavan8t commented Nov 11, 2026

Copy link
Copy Markdown
Member

Replaces #197 — that PR added a second Windows-only connectivity state machine next to LatchEngine. This one keeps the two pieces that were worth keeping (NCSI's connectivity level, event-driven address-change detection) and feeds them to the shared engine.

What changed

Windows tells the engine when a captive portal appears (WindowsWifiPlatform)

  • The Wi-Fi query now also asks WinRT for the connectivity level of the internet profile (NetworkInformation.GetInternetConnectionProfile()): None, LocalAccess, ConstrainedInternetAccess, InternetAccess.
  • Only a WLAN profile counts. On a docked laptop the internet profile is Ethernet, and its level says nothing about the Wi-Fi link, so it is dropped rather than read as the Wi-Fi's own.
  • When Windows moves the level into ConstrainedInternetAccess, the platform emits the new WifiEvent.Changed. The engine handles it exactly as it handles WifiEvent.Available: the same probe, the same campus-SSID and Pronto gate, the same autoLogin setting. Nothing about the credential path changes.
  • A level that flaps is damped — one report per 30 s.

The Wi-Fi loop wakes on the OS instead of only on its timer (WindowsIpChangeNotifier)

  • iphlpapi's NotifyAddrChange is registered asynchronously with an OVERLAPPED of our own, so it can be given back with CancelIPChangeNotify when the app stops. A blocking call cannot be cancelled and keeps triggering checks after stop().
  • The handle Windows hands back is left alone — the documentation says not to close it, because Windows shares it.
  • Off Windows the notifier does nothing and the poll loop carries on exactly as before.

The shared engine (LatchEngine)

  • One new case in the Wi-Fi event handling: WifiEvent.Changed → re-check. No new states, no second coordinator, no second login path.

What is deliberately not here

Verification

  • ./gradlew :core:desktopTest — green, including the new tests
  • ./gradlew :desktop:compileKotlinDesktop :cli:build :cli:test :desktop:smoke — green
  • ./gradlew assembleDebug testDebugUnitTest :app:lintDebug — green

Needs a run on Windows

The native path and the WinRT query only exist on Windows, and CI here is Linux, so this still needs a run on a real Windows machine: the NCSI level, the NotifyAddrChange wake-up, and the Changed event reaching the engine.

Windows knows two things the engine cannot work out for itself: NCSI's
connectivity level for the current network, and the moment an address or a
route moves. Both are platform signals, so they belong in the platform.

- The Wi-Fi query asks WinRT for the connectivity level of the internet
  profile, and only a WLAN profile counts: on a docked machine the internet
  profile is Ethernet, and its level says nothing about the Wi-Fi.
- A move into ConstrainedInternetAccess is reported as the new
  WifiEvent.Changed, which the engine handles exactly as it handles
  WifiEvent.Available: the same probe, the same campus-SSID and Pronto
  gate, the same auto-login setting.
- The Wi-Fi loop wakes on iphlpapi's NotifyAddrChange instead of only on
  its timer. The notification is asynchronous and cancellable, so nothing
  keeps firing after the app stops listening.

This replaces #197, which added a second connectivity state machine next to
the engine.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant