Skip to content

fix(tracker): harden foreground-service startup and handle FGS timeouts#221

Draft
adsamcik wants to merge 1 commit into
dev/v10from
fix/fgs-lifecycle-robustness
Draft

fix(tracker): harden foreground-service startup and handle FGS timeouts#221
adsamcik wants to merge 1 commit into
dev/v10from
fix/fgs-lifecycle-robustness

Conversation

@adsamcik

Copy link
Copy Markdown
Owner

Summary

Hardens foreground-service startup and adds Android 15+ FGS-timeout handling for TrackerService and ActivityWatcherService. Part of the tracking-architecture review follow-ups (FGS robustness / platform compliance).

On the current dev/v10 code, TrackerService always calls startForeground(..., FOREGROUND_SERVICE_TYPE_LOCATION) with no permission gating and no exception handling. That means:

  • if the location permission isn't held, the location-typed FGS start throws SecurityException on Android 14+ (uncaught crash), and
  • a blocked background start throws ForegroundServiceStartNotAllowedException (Android 12+) — also uncaught.

Neither service implements Service.onTimeout(), so a platform FGS time-out would surface as a fatal RemoteServiceException.

Changes

Robust FGS startup (TrackerService)

  • ensureForegroundStarted() now returns Boolean and selects the FGS type by held permission: LOCATION when a location permission is granted, else SPECIAL_USE. This lets Wi-Fi/cell/activity-only ("ambient") sessions run when location permission is absent — and is also more accurate per Play policy, since a no-GPS session shouldn't declare itself a location FGS.
  • startForeground is wrapped (tryStartForeground) catching SecurityException and IllegalStateException (the supertype of ForegroundServiceStartNotAllowedException, avoiding an API-31 type reference). On total failure it posts a user-visible, dismissible notification ("Tracking couldn't start — open the app to resume") and stops cleanly instead of crashing or silently no-oping.
  • onCreate / onStartCommand react to the boolean (stop cleanly on failure).

Manifest

  • TrackerService now declares android:foregroundServiceType="location|specialUse" plus PROPERTY_SPECIAL_USE_FGS_SUBTYPE="SignalTracking" so the special-use fallback is legal. FOREGROUND_SERVICE_SPECIAL_USE was already declared and ActivityWatcherService already uses specialUse.

onTimeout() (Android 15+, API 35)

  • Implemented in both TrackerService and ActivityWatcherService: report + stopSelf(startId). The normal onDestroy cleanup still flushes the durable signal buffer; auto-tracking is re-triggered by ActivityWatcherService.

ActivityWatcherService

  • startForegroundCompat returns Boolean + try/catch; stops cleanly on failure. Removed an unused android.util.Log import.

Verification

  • ./gradlew :tracker:engine:compileDebugKotlinBUILD SUCCESSFUL.
  • Unit tests not run locally (the engine's JUnit5 + Robolectric tests fail locally with "No instrumentation registered"); CI will run them.

Reviewer decision point

Adding specialUse to TrackerService's declared types broadens the FGS-type surface. It's consistent with the existing ActivityWatcherService special-use usage and the app's already-declared FOREGROUND_SERVICE_SPECIAL_USE permission, and it fixes a latent policy mismatch (ambient sessions previously declared location). Flagging it explicitly in case you'd prefer to keep this PR purely defensive (drop the type fallback + manifest change) and handle ambient-FGS-type separately.

Notes

Draft — opened for review per the "extensive review before merge" workflow.

- Select FGS type by held permission (LOCATION when granted, SPECIAL_USE
  fallback) so Wi-Fi/cell/activity-only sessions can run without location
  permission; declare specialUse + subtype for TrackerService in the manifest.
- Catch SecurityException / ForegroundServiceStartNotAllowedException around
  startForeground; on failure post a user-visible notification and stop
  cleanly instead of crashing or silently no-oping.
- Implement Service.onTimeout() (Android 15+) in TrackerService and
  ActivityWatcherService to stop gracefully and avoid fatal RemoteServiceException.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant