Android 9.0: Not allowed to start service: app is in background.. after onResume()

AndroidAndroid ServiceAndroid 9.0-Pie

Android Problem Overview


I've got a music player which attempts to start a Service in onResume() of an Activity. I've removed a few lines for clarity, but the code is effectively:

@Override
protected void onResume() {
    super.onResume();
    
    startService(new Intent(this, MusicService.class));
}

According to the crash logs, this is throwing an Exception on some devices running Android P:

Caused by java.lang.IllegalStateException: Not allowed to start service Intent { cmp=another.music.player/com.simplecity.amp_library.playback.MusicService }: app is in background uid UidRecord{6a4a9c6 u0a143 TPSL bg:+3m25s199ms idle change:cached procs:1 seq(1283,1283,1283)}
       at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1577)
       at android.app.ContextImpl.startService(ContextImpl.java:1532)
       at android.content.ContextWrapper.startService(ContextWrapper.java:664)
       at android.content.ContextWrapper.startService(ContextWrapper.java:664)
       at com.simplecity.amp_library.utils.MusicServiceConnectionUtils.bindToService(SourceFile:36)
       at com.simplecity.amp_library.ui.activities.BaseActivity.bindService(SourceFile:129)
       at com.simplecity.amp_library.ui.activities.BaseActivity.onResume(SourceFile:96)

How is it possible that my app is in the background, immediately after onResume() (and super.onResume()) is called?

This doesn't make any sense to me. Could this be a platform bug? All 3500+ users affected by this crash are on Android P.

Android Solutions


Solution 1 - Android

There is a workaround from Google:

> The issue has been addressed in future Android release. > > There is a workaround to avoid application crash. Applications can get > the process state in Activity.onResume() by calling > ActivityManager.getRunningAppProcesses() and avoid starting Service if > the importance level is lower than > ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND. If the > device hasn’t fully awake, activities would be paused immediately and > eventually be resumed again after its fully awake.

So I think it should like that:

// hack for https://issuetracker.google.com/issues/113122354
    List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
    if (runningAppProcesses != null) {
        int importance = runningAppProcesses.get(0).importance;
        // higher importance has lower number (?)
        if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
            URLPlayerService.startActionBroadcastServiceData(PlayerActivity.this);
    }

I have used handler as a workaround and it works pretty good but not 100%:

// hack for https://issuetracker.google.com/issues/113122354
   handler.postDelayed(() -> URLPlayerService.startService(PlayerActivity.this),200);

Solution 2 - Android

UPDATE: This is working for us in Prod, but it's not 100%. I have received one crash report over the past month and a half when there would have been well over a hundred otherwise. Until this is properly fixed, this seems like our best option for now. Maybe if I raised the time beyond 300 that one crash would never have happened?

We're testing this out right now which so far seems to be working. Will update as we see more results

class ResumingServiceManager(val lifecycle: Lifecycle) : LifecycleObserver {

    init {
        lifecycle.addObserver(this)
    }

    val disposable: CompositeDisposable = CompositeDisposable()

    fun startService(context: Context, intent: Intent) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            context.startService(intent)
        } else {
            Single.just(true)
                    .delaySubscription(300, TimeUnit.MILLISECONDS)
                    .subscribeOn(AndroidSchedulers.mainThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeBy(
                            onSuccess = {
                                context.startService(intent)
                            }

                    ).addTo(disposable)
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stopped() {
        disposable.clear()
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destroy() {
        lifecycle.removeObserver(this)
    }
}

In onCreate() initialize it and then anytime you want to start a service in onResume just call resumingServiceManager.startService(this, intent)

It's lifecycle aware so it will clear the disposable if it pauses cancelling the onSuccess from triggering when it might be on the way to the background with an immediate open/close.

Solution 3 - Android

This has been marked as 'fixed' in the Android Issue Tracker:

Presumably the fix will be released in one of the Android Q releases.

According to the Googler who closed the issue,

>There is a workaround to avoid application crash. Applications can get the process state in Activity.onResume() by calling ActivityManager.getRunningAppProcesses() and avoid starting Service if the importance level is lower than ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND. If the device hasn’t fully awake, activities would be paused immediately and eventually be resumed again after its fully awake.

Solution 4 - Android

our team faced with the same problem. My calendar shows 5 of April, 2019, but problem still reproduce on my Samsung Galaxy S9+, android 9.0 (One UI)

We start service in onResume and unbind onPause.

How to reproduce

Just lock your device, when activity with this logic is on the screen, and don't touch 10-15 minutes. After unlock screen, app will crash.

How to fix

We found solution that actually work. Since android 8+, start your service in android.os.Handler.post(...)

Example (Kotlin):

override fun onResume() {
    super.onResume()
    Handler().post {
        val serviceIntent = Intent(activity, SomeService::class.java)
        activity?.startService(serviceIntent)
        activity?.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)
    }
}

Good luck!

Solution 5 - Android

I found a way to reproduce the problem.

Tested on Android Emulator with Android 10, 11, 12 DP2:

  • Launch the app which starts a service in Activity.onResume()
  • Press the Home button to change activity's state to stopped
  • Wait until the system destroys the service (logcat log will appear: "ActivityManager: Stopping service due to app idle")
  • Open Android settings -> System -> Gestures -> System navigation
  • Change the system navigation option to a different one
  • The application will crash

I also created a new issue in the Android Issue Tracker with these steps: https://issuetracker.google.com/issues/183716536

Solution 6 - Android

It seems that the error message is ambiguous, and what I've found is that the last part of the message is referring to the application where the service resides, and NOT the application that is attempting to start the service and bind to it.

The error I get if the service's application is not foregrounded (even though the service itself is a background service):

W/System.err: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.mycompany.myserviceapp/.MyService }: app is in background uid null
        at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505)
        at android.app.ContextImpl.startService(ContextImpl.java:1461)
        at android.content.ContextWrapper.startService(ContextWrapper.java:644)

Now, if I run the application that has the service so it's in the foreground immediately prior to launching the "client" app that will attempt to start the service, everything works fine for the service start.

If the service is a "background service" and the app for the service is not in the foreground, the service may be terminated - so getting the service started isn't really enough if you need it to stay around.

This seems to be a result of the changes from Android 7 to Android 8 where in Oreo they began restricting things a bit..

It's all explained here in the Android docs on the Background Execution Limits of Oreo

The recommendation in the docs is to migrate your logic to scheduled jobs, OR make the service a foreground service - where there will be a visual indication that the service is running.

Solution 7 - Android

Maybe android.arch.lifecycle could be used as a workaround for this Android 9 bug?

public class MyActivity extends Activity implements LifecycleObserver {

    protected void onResume() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
                startService(intent);
            } else {
                ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
            }
        } else {
            startService(intent);
        }
    }
 

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    void onEnterForeground() {
        startService(intent);
        ProcessLifecycleOwner.get().getLifecycle().removeObserver(this);
    }
}

[I've found it here.][1]

[1]: https://github.com/mapbox/mapbox-events-android/pull/157/commits/afb487fe2433368abc7ec9c0d4be7248c03adf6f "Found here"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTim MalseedView Question on Stackoverflow
Solution 1 - AndroidMateusz KaflowskiView Answer on Stackoverflow
Solution 2 - AndroidBen987654View Answer on Stackoverflow
Solution 3 - AndroidTim MalseedView Answer on Stackoverflow
Solution 4 - AndroidРома БогданView Answer on Stackoverflow
Solution 5 - AndroidWaskyView Answer on Stackoverflow
Solution 6 - Androidm0blView Answer on Stackoverflow
Solution 7 - AndroidMarcinLeView Answer on Stackoverflow