From 539a6e417192fd5b678769e175e837bab5643ded Mon Sep 17 00:00:00 2001 From: Constantin Leue <77660791+Consti3D@users.noreply.github.com> Date: Thu, 6 Aug 2026 22:24:03 +0200 Subject: [PATCH] Drop SSID display from settings, connection pill shows generic "Connected" Real hardware testing (Pixel 10a) showed NetworkCapabilities.getTransportInfo() consistently returns a redacted WifiInfo (SSID "", masked BSSID) even for the app's own self-requested WifiNetworkSpecifier network, disproving this codebase's prior assumption of a self-request exemption from ACCESS_FINE_LOCATION for that API path (confirmed via native diagnostic logging cross-checked against `adb shell dumpsys wifi`, which does show and correctly attribute the true SSID at the OS level). Rather than add a location permission with a runtime prompt purely for this cosmetic display, the settings pill now just shows "Connected". Documented as decision 4.24. --- DMC_Architektur_und_Design.md | 1 + .../com/dmc/dmc_app/MlrsNetworkPlugin.kt | 22 ++++++++++++++----- .../providers/wifi_connection_provider.dart | 9 -------- .../ui/screens/settings/settings_screen.dart | 22 +++++++++---------- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/DMC_Architektur_und_Design.md b/DMC_Architektur_und_Design.md index 706b515..51099a4 100644 --- a/DMC_Architektur_und_Design.md +++ b/DMC_Architektur_und_Design.md @@ -164,6 +164,7 @@ Enthält u. a.: | 4.21 | **Protokoll-Parsing in eigenem Isolate (Flutter-Ziel)** | Verhindert UI-Ruckeln bei hoher Telemetrie-Rate; Isolates kommunizieren nur über Message-Passing, kein Shared Memory | | 4.22 | **Mission-Domain-Layer + Protokoll-Adapter als eigenständiges reines Dart-Package (Flutter-Ziel)** | Isoliert unit-testbar ohne Emulator; wiederverwendbar für z. B. CLI-Tools oder Backend-Simulatoren | | 4.23 | **`go_router` für Navigation (Flutter-Ziel)** | Typisierte Übergänge, passend zum expliziten Mode-Wechsel-Trigger der State-Machine | +| 4.24 | **SSID-Anzeige in den Settings aufgegeben, Verbindungspille zeigt nur generisches "Connected"** | Auf echter Hardware (Pixel-Gerät, Android 16) liefert `NetworkCapabilities.getTransportInfo()` konsequent ein redigiertes `WifiInfo` (SSID ``, BSSID maskiert) — bestätigt per natives `Log.d` in `MlrsNetworkPlugin.onAvailable()`/`onCapabilitiesChanged()`, gegengeprüft mit `adb shell dumpsys wifi` (Shell-Kontext kennt und ordnet die echte SSID korrekt der App zu, das App-API liefert sie aber nicht). Die im Code angenommene "Ausnahme für selbst angefragte `WifiNetworkSpecifier`-Netze ohne `ACCESS_FINE_LOCATION`" gilt offenbar nur für `WifiManager.getConnectionInfo()`, nicht für diesen `NetworkCapabilities`-Weg über `ConnectivityManager.NetworkCallback`. Statt `ACCESS_FINE_LOCATION` (inkl. Laufzeit-Prompt) nur für diese kosmetische Anzeige hinzuzufügen, hat der Nutzer sich bewusst dagegen entschieden — die zugrundeliegende SSID-Merk-Logik (`UdpTransport.connectedSsid`/`_rememberSsid`, `preferredSsid` für stilles Reconnect) bleibt bestehen, liefert im Regelfall aber `null`/keinen Effekt | --- diff --git a/app/android/app/src/main/kotlin/com/dmc/dmc_app/MlrsNetworkPlugin.kt b/app/android/app/src/main/kotlin/com/dmc/dmc_app/MlrsNetworkPlugin.kt index 37e12d9..21d4fbc 100644 --- a/app/android/app/src/main/kotlin/com/dmc/dmc_app/MlrsNetworkPlugin.kt +++ b/app/android/app/src/main/kotlin/com/dmc/dmc_app/MlrsNetworkPlugin.kt @@ -171,7 +171,8 @@ class MlrsNetworkPlugin( val linkProperties = connectivityManager.getLinkProperties(network) val gateway = gatewayAddressOf(linkProperties) val broadcast = broadcastAddressOf(linkProperties) - val ssid = ssidOf(connectivityManager.getNetworkCapabilities(network)) + val capabilities = connectivityManager.getNetworkCapabilities(network) + val ssid = ssidOf(capabilities) if (ssid != null) reportedSsid = ssid eventSink?.success( mapOf( @@ -260,10 +261,21 @@ class MlrsNetworkPlugin( } /** - * Ermittelt die tatsaechliche SSID des verbundenen Netzes. Erfordert - * normalerweise ACCESS_FINE_LOCATION, ausser fuer die anfragende App - * selbst bei einem ueber WifiNetworkSpecifier bewilligten Netz - genau - * unser Fall, siehe `WifiInfo.getSSID()`-Dokumentation. + * Ermittelt die tatsaechliche SSID des verbundenen Netzes - liefert auf + * echter Hardware (getestet: Pixel-Geraet, Android 16) zuverlaessig + * `null`, da `NetworkCapabilities.getTransportInfo()` ohne + * ACCESS_FINE_LOCATION konsequent ein redigiertes `WifiInfo` liefert + * (SSID ``, BSSID maskiert), auch fuer ein selbst per + * WifiNetworkSpecifier angefragtes/bewilligtes Netz. Mit + * `dumpsys wifi` (Shell-Kontext, volle Rechte) bestaetigt: das System + * kennt die echte SSID und ordnet sie korrekt dieser App zu + * (`Requesting package name`) - die dokumentierte Ausnahme "kein + * ACCESS_FINE_LOCATION noetig fuer selbst angefragte Netze" gilt in der + * Praxis offenbar nur fuer `WifiManager.getConnectionInfo()`, nicht fuer + * diesen `NetworkCapabilities`-Weg ueber `ConnectivityManager. + * NetworkCallback`. ACCESS_FINE_LOCATION bewusst NICHT hinzugefuegt nur + * fuer diese kosmetische Anzeige (Nutzerentscheidung) - der Aufrufer + * (UdpTransport) behandelt ein dauerhaftes `null` hier als Normalfall. */ private fun ssidOf(capabilities: NetworkCapabilities?): String? { val wifiInfo = capabilities?.transportInfo as? WifiInfo ?: return null diff --git a/app/lib/ui/providers/wifi_connection_provider.dart b/app/lib/ui/providers/wifi_connection_provider.dart index 315b11c..54f14bb 100644 --- a/app/lib/ui/providers/wifi_connection_provider.dart +++ b/app/lib/ui/providers/wifi_connection_provider.dart @@ -35,15 +35,6 @@ final wifiLinkErrorReasonProvider = Provider((ref) { return ref.watch(wifiTransportProvider).lastErrorReason; }); -/// Reaktive tatsaechliche SSID des verbundenen Netzes (Doku: "connected -/// pille soll den WiFi-Namen anzeigen") - eigener Stream statt Ableitung aus -/// [wifiLinkStateProvider], weil Android die SSID fuer WifiNetworkSpecifier- -/// Netze oft erst nachtraeglich liefert (siehe UdpTransport.connectedSsidChanges/ -/// MlrsNetworkPlugin.kt), also ohne begleitenden LinkState-Wechsel. -final wifiConnectedSsidProvider = StreamProvider((ref) { - final transport = ref.watch(wifiTransportProvider); - return transport.connectedSsidChanges; -}); /// Zeigt an, ob gerade UDP-Pakete ankommen ("receiving"/"not receiving"), /// unabhaengig vom (binaeren) Verbindungszustand - der Socket kann offen diff --git a/app/lib/ui/screens/settings/settings_screen.dart b/app/lib/ui/screens/settings/settings_screen.dart index eb1e684..0a743ac 100644 --- a/app/lib/ui/screens/settings/settings_screen.dart +++ b/app/lib/ui/screens/settings/settings_screen.dart @@ -136,7 +136,6 @@ class _WifiPanel extends ConsumerWidget { final connectionSettings = ref.watch(connectionSettingsProvider).value; final log = ref.watch(wifiConnectionLogProvider); final packetCount = ref.watch(wifiPacketCountProvider); - final connectedSsid = ref.watch(wifiConnectedSsidProvider).value; // telemetryProvider loest selbst keinen Verbindungsversuch aus, solange // WLAN/MSP noch nicht ueber den "Connect"-Knopf verbunden ist (siehe @@ -175,7 +174,14 @@ class _WifiPanel extends ConsumerWidget { const Spacer(), _StreamActivityPill(isReceiving: isReceiving, packetCount: packetCount), const SizedBox(width: 6), - _StatePill(state: linkState, connectedLabel: connectedSsid ?? 'unknown'), + // Zeigt bewusst nur "Connected" statt "Connected to $ssid" (Doku + // Abschnitt 4: "SSID-Anzeige in den Settings aufgegeben") - + // NetworkCapabilities.getTransportInfo() liefert auf echter + // Hardware konsequent nur die redigierte SSID/BSSID + // (""), siehe MlrsNetworkPlugin.ssidOf()-Doku fuer + // Details. Bewusst keine ACCESS_FINE_LOCATION-Berechtigung nur + // fuer diese kosmetische Anzeige hinzugefuegt. + _StatePill(state: linkState), ], ), if (errorReason != null) ...[ @@ -385,22 +391,14 @@ class _StreamActivityPill extends StatelessWidget { } class _StatePill extends StatelessWidget { - const _StatePill({required this.state, this.connectedLabel}); + const _StatePill({required this.state}); final LinkState? state; - /// WLAN-Netzname, falls bekannt - erscheint im verbundenen Zustand als - /// "Connected to {name}" statt des generischen "Connected" (Doku: "die - /// connected pille soll den WiFi-Namen anzeigen"). - final String? connectedLabel; - @override Widget build(BuildContext context) { final (label, color) = switch (state) { - LinkState.connected => ( - connectedLabel != null ? 'Connected to $connectedLabel' : 'Connected', - DmcColors.liveGreen, - ), + LinkState.connected => ('Connected', DmcColors.liveGreen), LinkState.connecting => ('Connecting…', DmcColors.pafAmber), LinkState.error => ('Error', DmcColors.warnRed), LinkState.disconnected || null => ('Disconnected', Colors.grey),