react-native-background-geolocation: [Android 12] Fatal Exception: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false

Hello, we are getting a lot of Crashlytics issues reported for our app on the android 12 devices. we do use background geolocation to monitor geofences only.

Your Environment

  • Platform: Android
  • OS version: 12
  • Device manufacturer / model: Galaxy Note10+, Galaxy S21 Ultra 5G, Pixel 6 Pro, Pixel 4, Pixel 4a
  • React Native version (react-native -v): 0.64.2
  • Plugin config
const config = {
      enableHeadless: true,
      geofenceModeHighAccuracy: true,
      desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
      distanceFilter: 10,
      geofenceInitialTriggerEntry: false,
      disableMotionActivityUpdates: true,
      stopOnTerminate: false,
      url,
    };

    await BackgroundGeolocation.ready(config);

    BackgroundGeolocation.startGeofences();

Expected Behavior

App should never crash while attempting to start foreground service on android and geofences should work fine for all OS versions and devices.

Actual Behavior

App is crashing when plugin tries to start foreground service on android 12 devices.

Context

Crash Report: Fatal Exception: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.transistorsoft.locationmanager.service.TrackingService at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54) at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50) at android.os.Parcel.readParcelable(Parcel.java:3345) at android.os.Parcel.createExceptionOrNull(Parcel.java:2432) at android.os.Parcel.createException(Parcel.java:2421) at android.os.Parcel.readException(Parcel.java:2404) at android.os.Parcel.readException(Parcel.java:2346) at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:6914) at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1926) at android.app.ContextImpl.startForegroundService(ContextImpl.java:1892) at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:796) at com.transistorsoft.locationmanager.service.AbstractService$a.run(:10) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8641) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1133)

Caused by android.os.RemoteException: Remote stack trace: at com.android.server.am.ActiveServices.startServiceLocked(ActiveServices.java:771) at com.android.server.am.ActiveServices.startServiceLocked(ActiveServices.java:679) at com.android.server.am.ActivityManagerService.startService(ActivityManagerService.java:13987) at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2943) at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3030)

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 1
  • Comments: 36 (22 by maintainers)

Most upvoted comments

@ZerozAndOnes release-4.8.0 branch in the private repo implements a much more generalized solution for the .getCurrentPosition() and .start() problem in the background on Android 12.

The SDK no longer needs to be concerned specifically with any particular method — it now simply does a catch (ForegroundServiceStartNotAllowedException e) { ... } and does the AlarmManager oneShot event in the exception-handler, for every potential use-case.

4.8.0 — Unreleased

  • [Android] Android 12: Guard Context.startForegroundService with try / catch: the plugin will now catch exception ForegroundServiceStartNotAllowedException and automatically retry with an AlarmManager oneShot event.
  • [Android] Android 12: Refactor foreground-service management for Android 12: A way has been found to restore the traditional behaviour of foreground-services, allowing them to stop when no longer required (eg: where the plugin is in the stationary state).
  • [Android] Refactor application life-cycle management. Remove deprecated permission android.permission.GET_TASKS traditionally used for detecting when the app has been terminated. The new life-cycle mgmt system can detect Android headless-mode in a much more elegant manner.
  • [Android] Better handling for WhenInUse behaviour: The plugin will not allow .changePace(true) to be executed when the app is in the background (since Android forbids location-services to initiated in the background with WhenInUse).
  • [Android] Refactor useSignificantChangesOnly behaviour. Will use a default motionTriggerDelay with minimum 60000ms, minimum distanceFilter: 250 and enforced stopTimeout: 20.
  • [iOS] iOS 15 has finally implemented Mock Location Detection. location.mock will now be present for iOS when the location is mocked, just like Android.

This issue hasn’t come up in our latest release, we can close this issue and the following ones in the private repo:

@ZerozAndOnes I’ve made another push to branch release-4.7.2

yarn add https://github.com/transistorsoft/react-native-background-geolocation.git#release-4.7.2

@ZerozAndOnes I’ve reproduced this. I added a little bit of code to immediately launch a foreground-service when .start() is called but it seems under these conditions that OS will only allow the foreground-service to be launched specifically due to a location API request.

I can work around this.

4.7.2 branch is not yet released. It’s not tagged yet.

I believe that somehow your code is going this while plugin is in background and disabled:

BackgroundGeolocation.start();
.
. a few milliseconds later:
.
BackgroundGeolocation.start();

I will guard against this.

I can see it in your logs:

TSLocationManager: [c.t.l.s.TSScheduleManager oneShot] 
      ⏰ Scheduled OneShot: START in 1ms (jobID: 79219778).  <--- GOOD, ONESHOT fired.
TSLocationManager: [c.t.locationmanager.util.c g] 
      ℹ️  LocationAuthorization: Permission granted
TSLocationManager: - Enable: false → true, trackingMode: 1.  // <--------- HERE is .start() begin called a 2nd time

The getCurrentPosition method was correctly using forceAlarmManager: true. When initially developing this with my test case, I initially implemented with start method. It was only after moving on to getCurrentPosition that I discovered that the OS provides a little leeway on background time (about 10s) to launch FG services. After I increasing the Timer to 15 seconds, the exception was raised again and I remembered to use forceAlarmManager: true on the ONESHOT.

I just didn’t circle back to .start().

You will know you have 4.7.2 installed when you see this build number in the logs:

TSLocationManager: ╔═════════════════════════════════════════════
TSLocationManager: ║ TSLocationManager version: 3.1.44 (400)
TSLocationManager: ╠═════════════════════════════════════════════

Ok, turns out it was just a blunder on my part. The SDK has a general purpose “one-shot” timer mechanism built into it which uses JobScheduler by default. I neglected to pass the 3rd optional param forceAlarmManager: true.

Screen Shot 2022-05-27 at 9 32 42 AM

Only AlarmManager-based events are granted carte blanche by the OS to launch foreground-services.

@ZerozAndOnes, @ozberkctn Please test branch #release-4.7.2 just pushed.

yarn add https://github.com/transistorsoft/react-native-background-geolocation.git#release-4.7.2