transistorsoft / react-native-background-geolocation

Sophisticated, battery-conscious background-geolocation with motion-detection
http://shop.transistorsoft.com/pages/react-native-background-geolocation
MIT License
2.63k stars 425 forks source link

Fatal exception: android.app.ForegroundServiceStartNotAllowedException #1452

Closed jonb-berry closed 2 years ago

jonb-berry commented 2 years ago

Hello, we are getting an increased number of Crashlytics issues reported on the Android 12 devices (mainly Samsung Galaxy) connected to the usage of this library. It occurs when the device comes from the background to the foreground.

Your Environment

Expected Behavior

The application shouldn't crash when coming to foreground

Actual Behavior

The application crashes when coming to foreground

Steps to Reproduce

  1. Use an app in the foreground
  2. Put the app to the background
  3. Use the app in the foreground again

Context

  useEffect(() => {
    if (prevAppStateStatus?.match(/inactive|background/) && appStateStatus === 'active') {
      // App has come to the foreground
      BackgroundGeolocation.getCurrentPosition({
        timeout: 15, // 15 second timeout to fetch location
        maximumAge: 10000, // Accept the last-known-location if not older than 10000 ms.
        desiredAccuracy: 10, // Try to fetch a location with an accuracy of `10` meters.
        samples: 1, // How many location samples to attempt.
        persist: false,
      }).then(...).catch(...);
    }
  }, [prevAppStateStatus, appStateStatus]);

Debug logs

Logs ``` ForegroundServiceStartNotAllowedException startForegroundService() not allowed due to mAllowStartForeground false: service [bundle ID]/com.transistorsoft.locationmanager.service.LocationRequestService 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 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:8633) 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) 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:14013) at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2943) at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3034) ```
mikehardy commented 2 years ago

Strange, this is a geolocation package using motion activity and geofencing, which is supposed to allow this even if you use targetSdkVersion 31 (where the restrictions start to apply)

https://developer.android.com/guide/components/foreground-services#background-start-restriction-exemptions

I bet this goes away (temporariliy) if you downgrade targetSdkVersion to 30, but that has a known clash in the react-native ecosystem with the very popular react-native-bootsplash for instance which requires a targetSdkVersion of 31 for it's v3 (I have bootsplash pinned to v2 for that reason at the moment)

https://developer.android.com/about/versions/12/behavior-changes-12#foreground-service-launch-restrictions - @christocracy this looks a real thing, possibly a structural change here. I know you comb those change lists though, I'm sure you are aware, maybe the module already handles it and it's an integration thing? Or maybe best advice is targetSdkVersion 30 for a bit while you work on something?

Notifee is stuck on targetSdkVersion 30 as well (at least as our current best advice) since 31 brought some serious changes in notifications as well :weary:

christocracy commented 2 years ago

Yes, I'm aware of these changes. I was fighting with these changes last summer when I first began working with Android 12. This is why the plugin now forces its foreground-services to be always on as soon as .start() is called, rather than activating only when the device is detected to be moving.

which is supposed to allow this even if you use targetSdkVersion 31 (where the restrictions start to apply)

That's what I'd hoped but Android 12 no longer allows PendingIntent.getForegroundService on the MotionActivity API to launch a foreground-service in the background. This is a real shame. It only allows PendingIntent.getBroadcast to launch a foreground-service, which won't work when an app is terminated.

@jonb-berry In the case of .getCurrentPosition(), you are probably executing that while the plugin is disabled (state.enabled === false) and your app is in the background. Currently, any of the plugin's requests for a location always attempt to launch a foreground-service. If one's app is in the background, Android 12 now forbids a foreground-service to be launched.

There probably needs to be some re-engineering within the plugin to handle the case of requesting .getCurrentPosition() while your app is in the background where the plugin's state.enabled === false.

PaulOskarSoe commented 2 years ago

I am facing the same issue. I am checking that plugin is enabled before requesting .getCurrentPosition() . All the crashes are occurring with Android 12. My plugin version is 4.4.6.

android.app.ForegroundServiceStartNotAllowedException: 
  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 android.content.ContextWrapper.startForegroundService (ContextWrapper.java:796)
  at com.transistorsoft.locationmanager.service.AbstractService$a.run (Unknown Source: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:8633)
  at java.lang.reflect.Method.invoke (Native Method)
  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: 
  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:14013)
  at android.app.IActivityManager$Stub.onTransact (IActivityManager.java:2943)
  at com.android.server.am.ActivityManagerService.onTransact (ActivityManagerService.java:3034)
christocracy commented 2 years ago

Duplicate #1444

I have found a solution to calling .start() and .getCurrentPosition() when the plugin is State.enabled == false and in the background.

It will be merged here for RN soon.