Skip to content

[DRO-2646] Prevent keep-awake foreground-service crash - #160

Merged
RasulOs merged 1 commit into
mainfrom
fix/dro-2646-keep-awake-lifecycle
Aug 6, 2026
Merged

[DRO-2646] Prevent keep-awake foreground-service crash#160
RasulOs merged 1 commit into
mainfrom
fix/dro-2646-keep-awake-lifecycle

Conversation

@RasulOs

@RasulOs RasulOs commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • promote KeepAliveService immediately in onCreate(), before publishing the live instance or registering listeners
  • make shutdown service-owned and start-ID-aware so a rapid disable cannot cancel a newer foreground-service start
  • add a mirrored 50-cycle instrumentation regression with a 15-second watchdog, final-state checks, sustained re-enable, and foreground-notification assertions

Root cause

A valid rapid sequence—

screen/keepAwake/set(enabled=true)
screen/keepAwake/status
screen/keepAwake/set(enabled=false)

—could stop a service created by startForegroundService() before it called startForeground(), producing ForegroundServiceDidNotStartInTimeException and killing Portal. There was a second ordering hazard when a live service received a newer queued start: a plain stopSelf() or external stopService() could consume that newer obligation.

The service now promotes first, records the latest delivered startId, posts stop decisions to its main thread, rechecks the persisted target state, and calls stopSelfResult(latestStartId). A pending service creation is allowed to reach onCreate(), promote, and then safely self-stop if the saved state is disabled.

Compatibility

  • preserves ACTION_STOP, disabled-state shutdown, wake-lock recovery, controller behavior, and enabled START_STICKY semantics
  • no RPC, response-schema, ContentProvider, daemon, permission, manifest, or version changes
  • test notification assertions remain unconditional on Android 8 and are permission-aware on API 33+
  • exact mirrored production logic with the internal companion: mobilerun-portal-internal#80

Validation

Host (OpenJDK 17.0.20)

  • focused keep-awake/API/provider tests: 137 passed
  • full debug unit suite: 566 tests, with only the documented pre-existing SwitchTintResourceTest.trackTintSeparatesCheckedAndUncheckedStates failure
  • lintDebug: passed
  • assembleDebug + instrumentation APK assembly: passed
  • git diff --check: passed

Android matrix

Device Instrumentation Local transport stress Headless Reverse WS Mobilerun UI + notification Stop
Android 8 / API 26 8/8 25 sync + 25 pipelined; pass n/a pass pass
Android 15 / API 35, 16 KB 8/8 25 sync + 25 pipelined; pass 10 + 10; pass pass pass
Android 16 / API 36 8/8 25 sync + 25 pipelined; pass 10 + 10; pass pass pass

Across all devices:

  • 15-second watchdog completed with unchanged Portal PID
  • original authenticated WebSocket stayed responsive and accessibility state remained available
  • sustained enable produced enabled=true/serviceActive=true, service, notification ID 2004, and keep_alive_steady wake lock
  • disable removed the service, notification, and wake lock
  • HTTP authentication passed and keep-awake mutation retained its existing WebSocket-only rejection
  • sanitized logcat contained no foreground-service timeout, missing-promotion, RemoteServiceException, crash, ANR, process restart, or accessibility failure

Cloud tasks and the unrelated insufficient-balance condition were intentionally excluded.

Internal companion: mobilerun-portal-internal#80.

Related to DRO-2646.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Build Ready

APK for PR #160 is available for testing.

Download APK from workflow run

Artifacts expire after 30 days.

@RasulOs
RasulOs merged commit 7c6ca81 into main Aug 6, 2026
5 of 6 checks passed
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