IdleService runs continuously as a FOREGROUND_SERVICE_TYPE_DATA_SYNC
foreground service (push is on by default). With targetSdk 37, Android 14+'s
dataSync FGS runtime cap (~6h per rolling 24h) calls Service.onTimeout(...)
and then force-stops the service — throwing a system FGS-timeout exception —
if it doesn't stop itself. IdleService overrode onStartCommand/onDestroy/onBind
but not onTimeout, so after ~6 cumulative hours push silently died and the app
hit the exception; on API 35+ the budget is cumulative and a restart can't
recover it until the next 24h window.
Override both onTimeout(startId) (deprecated, API 34) and
onTimeout(startId, fgsType) (API 35+); both route to a clean shutdown that
re-asserts the already-scheduled 15-minute periodic sync, swaps the persistent
notification to a degraded "paused" text and DETACHes it so it survives, then
stopForeground(DETACH) + stopSelf so we never leave a dataSync FGS running past
its cap (the exact condition the platform kills on). This mirrors the existing
low-battery PushMode.POLLING fallback.
The push-status text choice is pulled into a pure PushStatusNotification.statusTextRes
seam and unit-tested on the JVM; the built notification's new timed-out text is
covered by PushStatusNotificationInstrumentedTest.
Closes#302
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Coverage lane 4 (#249) flagged reporting/push classes as unreachable by tests.
Add minimal, behaviour-preserving seams and the tests they unblock (issue #257):
- ReportUploadScheduler: inject Provider<WorkManager> (mirroring SyncScheduler)
instead of calling the WorkManager.getInstance static that MockK can't stub on
the abstract WorkManager (AbstractMethodError). New ReportUploadSchedulerTest
pins the per-report unique-work name + REPLACE policy.
- ReportUploadWorker: take the ingest endpoint via a new @DebugReportEndpoint
qualifier (provided from BuildConfig.DEBUG_REPORT_ENDPOINT in ReportingModule)
rather than reading the BuildConfig static inline. New ReportUploadWorkerHttpTest
drives the transmit path against an in-process JDK HttpServer on loopback and
covers 2xx success + delete, 4xx failure, 5xx retry/attempt-cap, and network
error. Production value is unchanged (empty by default).
- IdleService: extract the foreground-notification channel + push-mode-to-text
logic into PushStatusNotification. New PushStatusNotificationInstrumentedTest
asserts channel importance and the IDLE/POLLING notification text with a real
application Context (never a mocked Context).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>