On our application there's a service that is normally started during Application.OnCreate
(directly calling context.startService
) and also later on via AlarmManager
(refactor is in progress to migrate some of its work to JobScheduler).
Our application also have a BroadcastReceiver
that gets launched with its direct intent.
Given the new limitations in Android Oreo (https://developer.android.com/about/versions/oreo/android-8.0-changes.html) we're having an issue as follows:
- app/process is in background/dead
- BroadcastReceiver gets fired by the OS
- Application.onCreate() executes before the BroadcastReceiver
- Application.onCreate() code tries to run the Service
this leads to crash with "IllegalStateException: Not allowed to start service Intent".
I'm aware of the new recommended ways of launching a Service as answered by CommonsWare here https://stackoverflow.com/a/44505719/906362, but for this specific case, I simply want to have if(process in foreground) { startService }
. I'm currently using the following method and it seems to work:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static boolean isProcessInForeground_V21(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.AppTask> tasks = am.getAppTasks();
return tasks.size() > 0;
}
But I can't find the exact checks Android Oreo is doing (I got as far as here https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java on the startServiceCommon
method, but from there requireForeground
flag seems to go to some native implementation)
So my question:
For the specific purpose of Android Oreo new limitations, how to check if my process is foreground before calling startService
?
To continue your investigation: (TL;DR: see between horizontal lines at the bottom)
Disclaimer, I don't know too much about Android internals, I just like digging in the source code.
or searching for text in Project and Libraries.

Note: you can also navigate the code in Android Studio if you jump to file instead of class:
IActivityManager
is defined by AIDL, that's why there are no sources for it: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/IActivityManager.aidl#145Based on how AIDL needs to be implemented I found that
ActivityManagerService extends IActivityManager.Stub
(God bless Google indexing).Note I also found this, which might be an interesting read if you're really interested how things work internally. https://programmer.group/android-9.0-source-app-startup-process.html
ActivityManagerService
sources reveal that in OreostartService
is forwarded toActiveServices
which is located in the same package.Assuming we're looking for an exception like this:
we have to continue down the rabbit hole:
requireForeground
gets assigned tofgRequired
parameter and the message is here. The condition to allow this depends on the start mode returned byActivityManagerService.getAppStartModeLocked(packageTargetSdk = 26 or greater, disabledOnly = false, forcedStandby = false)
.There are 4 start modes:
!=
)return null
)Ephemeral apps will immediately return
APP_START_MODE_DISABLED
, but assuming this is a normal app, we end up inappServicesRestrictedInBackgroundLocked
. Note: this is where some of the whitelist mentioned in https://stackoverflow.com/a/46445436/253468 is decided. Since all branches but last returnAPP_START_MODE_NORMAL
, this redirects toappRestrictedInBackgroundLocked
where we find our most likely suspect:So the reason for denial is simply targeting O. I think the final answer to your question of how the OS decides if your app is foreground or background is this condition in
getAppStartModeLocked
My guess is that a missing record means it's not running (but then how is it starting a service?!), and
idle
means it's backgrounded. Notice that in my exception message theUidRecord
is saying that it's idle and has been backgrounded for 3m52s.I peeked into your
getAppTasks
and it's based onTaskRecord.effectiveUid
, so I'm guessing that's quite close to listingUidRecord
s for your app.Not sure if this helps, but I'll post it anyway, so if anyone wants to investigate more, they have more info.