Fix event-listener gap in UdpTransport.connect() dropping late network events

Between cancelling the initial "available" subscription and attaching
the network-loss listener, no listener was attached to the broadcast
event stream for a brief window. Events landing in that gap (notably
a late SSID update, which real hardware without STA concurrency often
fires very close after "available") were silently lost since broadcast
streams don't buffer for late subscribers. Now uses one continuous
subscription for the whole connection lifetime instead.
This commit is contained in:
Constantin Leue
2026-08-06 22:23:44 +02:00
parent 7ff88c5435
commit 221c0552d5
2 changed files with 64 additions and 11 deletions
+28 -11
View File
@@ -129,8 +129,28 @@ class UdpTransport implements LinkTransport {
final ssidPrefix = await _getSsidPrefix();
final rememberedSsid = await _getRememberedSsid();
final firstEvent = Completer<MlrsNetworkEvent>();
final requestSub = _networkController.events.listen((event) {
if (!firstEvent.isCompleted) firstEvent.complete(event);
// EIN durchgehendes Abonnement ab hier, statt vorher ein temporaeres
// request-Abo zu registrieren, es nach dem ersten Ereignis wieder
// abzumelden und danach durch ein zweites (fuer Netzverlust) zu
// ersetzen: zwischen `requestSub.cancel()` und dem Aufsetzen des
// zweiten Listeners lag eine (kurze, aber reale) Luecke ganz ohne
// Listener auf dem Broadcast-EventChannel-Stream - Ereignisse, die
// genau in diesem Fenster ankamen, gingen unwiderruflich verloren
// (Broadcast-Streams puffern nicht fuer spaeter hinzukommende
// Listener). Betroffen war insbesondere das nachtraeglich eintreffende
// SSID-Ereignis (`onCapabilitiesChanged`, siehe
// MlrsNetworkSsidUpdated-Doku): auf echter Hardware (Pixel-Geraet ohne
// STA-Concurrency) trifft es haeufig so kurz nach dem "available"-
// Ereignis ein, dass es fast immer in genau diese Luecke fiel - die
// Verbindungsprotokoll-Pille zeigte dauerhaft "Connected to unknown",
// obwohl `dumpsys wifi` auf dem Geraet die korrekte SSID zeigte.
_networkLossSub = _networkController.events.listen((event) {
if (!firstEvent.isCompleted &&
(event is MlrsNetworkAvailable || event is MlrsNetworkUnavailable)) {
firstEvent.complete(event);
return;
}
_onNetworkEvent(event);
});
try {
@@ -139,7 +159,6 @@ class UdpTransport implements LinkTransport {
preferredSsid: rememberedSsid,
);
final event = await firstEvent.future;
await requestSub.cancel();
if (event is! MlrsNetworkAvailable) {
_fail(
@@ -150,10 +169,6 @@ class UdpTransport implements LinkTransport {
}
final available = event;
// Ab hier auf Netzverlust reagieren - auch waehrend des Socket-Aufbaus
// unten, nicht erst danach (Doku Abschnitt 8).
_networkLossSub = _networkController.events.listen(_onNetworkEvent);
_connectedSsid = available.ssid;
_ssidController.add(_connectedSsid);
if (available.ssid != null) {
@@ -219,13 +234,13 @@ class UdpTransport implements LinkTransport {
_setState(LinkState.connected);
} on MissingPluginException {
await requestSub.cancel();
await _networkLossSub?.cancel();
_networkLossSub = null;
_fail(
LinkErrorReason.unknown,
'mLRS network channel unavailable on this platform.',
);
} on PlatformException catch (e) {
await requestSub.cancel();
await _networkLossSub?.cancel();
_networkLossSub = null;
await _networkController.releaseNetwork();
@@ -263,8 +278,10 @@ class UdpTransport implements LinkTransport {
'The mLRS WiFi network was lost.',
);
case MlrsNetworkAvailable() || MlrsNetworkUnavailable():
// Wird hier nicht erwartet - diese Ereignisse werden nur waehrend
// des initialen connect() ueber firstEvent ausgewertet (siehe oben).
// Werden bereits im Listener in connect() ueber firstEvent
// abgefangen, bevor sie hierher durchgereicht werden (siehe dort) -
// hier nur zur Vollstaendigkeit des switch, kein weiteres Handling
// noetig.
break;
}
}