fix(android): handle ForegroundServiceStartNotAllowedException on system-restart#538
Open
vairagadeharshwardhan wants to merge 1 commit into
Conversation
…tem-restart
On Android 12+ (API 31+), when the OS recreates BackgroundService after
killing it for memory pressure while the host app is in the background,
the unconditional ServiceCompat.startForeground() call in
updateNotificationInfo() throws ForegroundServiceStartNotAllowedException,
which is uncaught and crashes the host app.
Real-world stack (Crashlytics, Android 14, multiple OEMs):
java.lang.RuntimeException: Unable to create service ... BackgroundService:
android.app.ForegroundServiceStartNotAllowedException: Service.startForeground()
not allowed due to mAllowStartForeground false
at id.flutter.flutter_background_service.BackgroundService
.updateNotificationInfo(BackgroundService.java:173)
at id.flutter.flutter_background_service.BackgroundService
.onCreate(BackgroundService.java:101)
The existing catch around ServiceCompat.startForeground only handles
SecurityException; ForegroundServiceStartNotAllowedException is an
IllegalStateException subclass and slips past.
Setting AndroidConfiguration.isForegroundMode=false in Dart does not
prevent this crash: that flag only affects how Dart starts the service,
not what the OS does when it restarts the service on its own.
Fix: extend the catch to also handle IllegalStateException (the parent
of ForegroundServiceStartNotAllowedException). When caught, log a
warning and stopSelf() so the service exits cleanly. The host app can
restart the service the next time it returns to a state where FGS
starts are permitted.
Behavior preserved on API < 31 - IllegalStateException is essentially
never thrown by startForeground on those versions.
No public API change. No new permissions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On Android 12+ (API 31+), when
BackgroundServiceis recreated by the OS while the host app is in the background, the unconditionalServiceCompat.startForeground()call inupdateNotificationInfo()can throwForegroundServiceStartNotAllowedException. This is uncaught and crashes the host app.Real-world stack from production (Firebase Crashlytics, Android 14, multiple OEMs):
The existing
try { ... } catch (SecurityException e)block aroundServiceCompat.startForeground(added previously to handle missing-permission cases) only catchesSecurityException.ForegroundServiceStartNotAllowedExceptionis anIllegalStateExceptionsubclass introduced in API 31 and is not caught by the current code.Setting
AndroidConfiguration.isForegroundMode = falsefrom Dart does not prevent this — that flag only controls how the host app callsstartServicevsstartForegroundService. It does nothing about the OS-driven service recreation path, whereonCreaterunsupdateNotificationInforegardless.Fix
Extend the existing catch in
updateNotificationInfo()to also handleIllegalStateException— the API-level-stable parent class ofForegroundServiceStartNotAllowedException. When caught, log a warning at the same level as the existingSecurityExceptionbranch, then callstopSelf()so the service exits cleanly instead of crashing the host. The host app can restart the service the next time it returns to a state that permits FGS starts.IllegalStateExceptionis essentially never thrown bystartForeground, so behavior on older Androids is unchanged.catch (SecurityException e)immediately above: log + bail.Validation
Production evidence: the crash signature above comes from a live Flutter app using this plugin. It reproduces frequently enough to be a top-N Crashlytics issue for that app (Android 12 / 13 / 14, mixed OEMs — Pixel, Xiaomi, Samsung).
Code-level reasoning:
ForegroundServiceStartNotAllowedExceptionis documented (https://developer.android.com/reference/android/app/ForegroundServiceStartNotAllowedException) as anIllegalStateExceptionsubclass thrown byService.startForegroundon API 31+. The catch block in this PR catches the documented parent type at the exact call site shown in the production stack.On-device synthetic repro: I attempted a synthetic repro by adding a temporary
throw new IllegalStateException(...)immediately beforeServiceCompat.startForegroundand observing the catch block log +stopSelf()path. On the test device available to me (Xiaomi / MIUI, Android 15) the OS's own process-management aggressively brought the service down before either the synthetic throw or the originalstartForegroundcould run, masking the validation. I did not have a non-OEM-customised Android 12+ device available, so this PR is submitted on the strength of:SecurityExceptioncatch the maintainer already added at the same call site.Happy to add further repro evidence in whichever form you'd prefer if reviewing.
Notes
pubspec.yaml.