Android push reliability — foreground service and Doze survival #59

Closed
opened 2026-08-14 10:06:52 +00:00 by nalum · 0 comments
Owner

Summary

Promote the live NotificationService.Subscribe consumer (added in #58) into a foreground service. Make Android push work when the app is closed, and survive Doze and aggressive OEM battery management.

Depends on

#58 — the self-contained streaming consumer. That issue delivers live notifications while the app is open and keeps the 15-minute poll as the background fallback. This issue replaces that fallback with a real always-on connection.

The problem

Android does not let a background app hold a socket open through Doze. A self-held push connection needs a foreground service with a persistent notification. This is the exact cost that FCM and UnifiedPush avoid by sharing one system wakeup channel across all apps. We chose the self-contained route in ADR 0028 and accept this cost.

This is where the risk sits. The work is not the happy path. It is the failure modes.

Scope

  • Run the Subscribe consumer inside a foreground service with a persistent notification.
  • Handle Doze and app-standby buckets.
  • Handle reconnect with backoff on network change, server restart, and token refresh.
  • Handle OEM battery-killers (for example Xiaomi, Huawei, Samsung). Add the standard battery-optimization exemption prompt where needed.
  • Remove InboxPollWorker once the foreground service is reliable, or keep it as a last-resort safety net. Decide during implementation.
  • Respect the member's language in the foreground-service notification text (system rule 2, appLocale()).

Open questions

  • Does the foreground-service notification need a user-facing toggle, or is it always on while signed in?
  • What is the reconnect and heartbeat policy that balances battery against latency?
  • Do we keep a low-frequency poll as a backstop for devices that kill the service anyway?

Definition of done

  • Android receives push within seconds while the app is closed, on a device left idle overnight.
  • The connection recovers from airplane-mode toggles, server restarts, and Wi-Fi-to-mobile handovers.
  • make check passes. Behavior verified on a physical device over a multi-hour idle window.
### Summary Promote the live `NotificationService.Subscribe` consumer (added in #58) into a foreground service. Make Android push work when the app is closed, and survive Doze and aggressive OEM battery management. ### Depends on #58 — the self-contained streaming consumer. That issue delivers live notifications while the app is open and keeps the 15-minute poll as the background fallback. This issue replaces that fallback with a real always-on connection. ### The problem Android does not let a background app hold a socket open through Doze. A self-held push connection needs a foreground service with a persistent notification. This is the exact cost that FCM and UnifiedPush avoid by sharing one system wakeup channel across all apps. We chose the self-contained route in ADR 0028 and accept this cost. This is where the risk sits. The work is not the happy path. It is the failure modes. ### Scope - Run the `Subscribe` consumer inside a foreground service with a persistent notification. - Handle Doze and app-standby buckets. - Handle reconnect with backoff on network change, server restart, and token refresh. - Handle OEM battery-killers (for example Xiaomi, Huawei, Samsung). Add the standard battery-optimization exemption prompt where needed. - Remove `InboxPollWorker` once the foreground service is reliable, or keep it as a last-resort safety net. Decide during implementation. - Respect the member's language in the foreground-service notification text (system rule 2, `appLocale()`). ### Open questions - Does the foreground-service notification need a user-facing toggle, or is it always on while signed in? - What is the reconnect and heartbeat policy that balances battery against latency? - Do we keep a low-frequency poll as a backstop for devices that kill the service anyway? ### Definition of done - Android receives push within seconds while the app is closed, on a device left idle overnight. - The connection recovers from airplane-mode toggles, server restarts, and Wi-Fi-to-mobile handovers. - `make check` passes. Behavior verified on a physical device over a multi-hour idle window.
nalum added reference refs/tags/v1.3.0 2026-08-14 10:08:54 +00:00
nalum added this to the (deleted) project 2026-08-14 12:20:29 +00:00
nalum closed this issue 2026-08-16 12:41:04 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eagraiclainne/app#59
No description provided.