feat(mobile): keep Android connections alive in the background - #5179
feat(mobile): keep Android connections alive in the background#5179snipemanmike wants to merge 7 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Needs human review This PR introduces a substantial new Android feature (background connection support) with a new native module, foreground service, multiple new permissions, and complex background task/connection management infrastructure. Additionally, there is an unresolved review comment identifying a potential race condition in the retained-thread clearing logic. You can customize Macroscope's approvability policy. Learn more. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 81c65f7. Configure here.
| // same deleted ref was saved while this clear was pending, clear it once | ||
| // more now so the final persistence operation cannot restore it. | ||
| if (started && refsEqual(retainedThread, ref)) { | ||
| clearRetainedIfCurrent(ref); |
There was a problem hiding this comment.
Stopped root skips pending re-clear
Medium Severity
After a pending retained-thread clear finishes, the follow-up clear only runs when started is still true. If the background root stops in between (for example disabling background connection) while navigation re-published the same deleted thread, the second clear is skipped and a queued save can persist that thread again.
Reviewed by Cursor Bugbot for commit 81c65f7. Configure here.


What Changed
Android currently suspends the JavaScript connection runtime when T3 Code is backgrounded or the phone is locked. Reopening the app can therefore require a full reconnect and synchronization before current thread state appears.
This PR adds an Android-only, opt-in background connection mode that:
starting/runningthreadsThis does not change the WebSocket protocol, synchronization reducer, server contracts, or server behavior.
The recovery work in #5154 improves what happens after Android has suspended the client. This PR addresses the preceding problem: while the setting is enabled, it prevents Android suspension from stopping the connection runtime in the first place. The approaches remain compatible.
Why
Mobile is commonly used to monitor or continue work running on another machine through a direct connection, Tailscale, or T3 Connect. Keeping the existing runtime alive means events can arrive while the app is backgrounded, outgoing messages continue draining, and reopening can render current state immediately rather than beginning a reconnect cycle.
The feature defaults off. Android force-stop remains an unavoidable boundary, and users must launch the app once afterward.
Verification
UI Changes
Before:
After:
Interaction video (sanitized emulator)
Checklist
This is one focused Android runtime concern, but it is not a small diff; the native lifecycle, shared runtime ownership, relay authentication, resume semantics, tests, and documentation must land together to avoid partial behavior.
Implemented with GPT-5.6-sol through the Codex harness. Reviewed with Claude Fable through the Claude Code CLI.
Note
High Risk
Touches native foreground-service lifecycle, shared connection supervisors, and Clerk/relay session ownership across UI and headless contexts; mis-coordination could leak connections, double-send outbox messages, or use stale relay credentials.
Overview
Adds an opt-in Android-only “Keep connected in background” mode so the existing mobile connection runtime can keep running when the app is locked or backgrounded, instead of suspending and forcing a full reconnect on return.
The work introduces the
t3-background-connectionExpo module: aremoteMessagingforeground service, boot/package-replace recovery, bounded restart backoff, optional battery-optimization prompts, and a React Native Headless JS task registered at startup. The headless path reusesappAtomRegistry—one supervisor stack per environment—not a second WebSocket protocol.Shared ownership is the main architectural change on the JS side: reference-counted leases mount the background connection root (catalog, shells, last-opened plus
starting/runningthread detail),acquireThreadOutboxDrain(single outbox dispatcher for UI and headless), andmanagedRelaySessionOwnershipplusbackgroundManagedRelayAuthso T3 Connect tokens can bootstrap cold while UI Clerk ownership still wins when visible.Foreground resume behavior changes when native reports a healthy background service and runtime: a new
application-active-preservedwakeup probes the current session instead of blindly reconnecting, with immediate replacement only if the probe fails. The active thread is persisted as a retained target; settings and cache/thread lifecycle paths clear it when environments or threads are removed.User-facing surface: Settings → Background connection (Android), silent ongoing notification while enabled, and updated user/internal docs on limits (force-stop, battery cost).
Reviewed by Cursor Bugbot for commit 81c65f7. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add Android background connection service to keep relay connections alive when the app is backgrounded
HeadlessJsTaskServicewith a persistent notification and a Wi-Fi high-performance lock to maintain relay connections while the app is in the background.acquireThreadOutboxDrainlease so it can be shared between UI and headless task contexts without double-sending.managedRelaySessionOwnershipandbackgroundManagedRelayAuthto mediate UI vs. background relay session ownership, with UI taking precedence and background bootstrapping via Clerk with exponential backoff retry.application-active-preservedwakeup in the connection supervisor: when both native protection signals are healthy at foreground, the supervisor probes the existing connection and only reconnects immediately on probe failure, skipping normal backoff.BackgroundConnectionSettingsSectionincluding battery optimization exemption prompting.Macroscope summarized 81c65f7.