fix(mobile): reconnect on resume instead of trusting a stale status - #3837
fix(mobile): reconnect on resume instead of trusting a stale status#3837AI-OWEN wants to merge 1 commit into
Conversation
When iOS suspends the app, the 5-second background grace timer never fires and a socket torn down by the platform or carrier NAT never delivers its onDisconnected callback. onAppResumed then sees a `connected` status, returns early, and the session sits on a dead socket -- no events arrive until some later write surfaces the error, which is why replies appear all at once after the user types something. Track whether the app actually backgrounded and only trust a `connected` status when it did not, so resuming from `inactive` (a Control Centre swipe) still keeps its live socket. After a real background stint, reconnect regardless of the reported status; reconnect is idempotent and replays missed events, so a redundant reconnect is far cheaper than a silent stall. Signed-off-by: Owen Gallagher <70806278+AI-OWEN@users.noreply.github.com>
|
this matches the laggy-replies report. can you also cover a case where resume fires while a reconnect is already in flight? |
|
Closing this as superseded by #4372 ("fix(mobile): recover stale relay sessions"), merged on 2026-08-03. That PR fixes the same bug in the same function, and does more of it. The comparison, for the record:
The one place this branch went further was calling @Chessing234 — your question about resume firing while a reconnect is already in flight is answered by that same generation counter on Nothing here is worth rebasing on top of the merged fix. |
Summary
On mobile, the relay session could come back from a real background stint believing it was still connected while sitting on a dead socket. No events arrived until some later write surfaced the error — which is why replies would appear all at once the moment the user typed something.
onAppResumed()returned early wheneverstate.statuswas alreadyconnected, on the assumption that a still-connected status meant the socket survived the 5-second background grace window:That assumption does not hold once the OS suspends the process. The grace timer never fires, and a socket torn down by the platform or by carrier NAT never delivers its
onDisconnectedcallback, so the status is stale in exactly the case where it matters.This change tracks whether the app actually backgrounded (
onAppPaused()fired) and only trusts aconnectedstatus when it did not:inactive— a Control Centre swipe or a notification banner — never callsonAppPaused(), so that path keeps its live socket and gains no reconnect churn.connected, the old socket is torn down first viareconnect()so the replacement isn't racing a half-open one.Reconnecting is idempotent and replays missed events, so an occasional redundant reconnect is far cheaper than a silent stall.
Related issue
None found — searched open issues and PRs for existing reports of stale mobile sockets on resume.
Testing
Two new tests in
mobile/test/shared/relay/relay_session_test.dart, covering both directions of the condition:reconnects on resume even when the status still claims connected— the regression itselfdoes not rebuild the socket when the app never backgrounded— guards against trading a silent stall for reconnect churn on every Control Centre swipeFull mobile suite on the rebased branch (not just the touched file):
No UI change — behaviour only.