Fatal Android 12: Exception: startForegroundService() not allowed due to mAllowStartForeground false
Asked Answered
S

8

90

I noticed one exception (Firebase Crashlytics) for Pixel 5 and Pixel 4a (both on Android 12), no other devices, happened only two times, one time for each device.

What does it mean? Android 11 and 12 have the same rules for working with foreground services, but there are no issues with Android 11. Is this a bug of Pixel?

From Firebase Crashlytics:

Fatal Exception: android.app.ForegroundServiceStartNotAllowedException
startForegroundService() not allowed due to mAllowStartForeground false: service com.*.*/.service.RecorderService

android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel (ForegroundServiceStartNotAllowedException.java:54)
androidx.core.content.ContextCompat.startForegroundService (ContextCompat.java:6)
MyAppPackageHidden.service.RecorderService$Companion.startService (RecorderService.java:2)
MyAppPackageHidden.ui.rec.RecActivity$getConnectionRecorderService$1.onServiceConnected (RecActivity.java:4)
android.app.LoadedApk$ServiceDispatcher.doConnected (LoadedApk.java:2077)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1003)

Fatal Exception: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service MyAppPackageHidden/.service.RecorderService
       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:3333)
       at android.os.Parcel.createExceptionOrNull(Parcel.java:2420)
       at android.os.Parcel.createException(Parcel.java:2409)
       at android.os.Parcel.readException(Parcel.java:2392)
       at android.os.Parcel.readException(Parcel.java:2334)
       at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:5971)
       at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1847)
       at android.app.ContextImpl.startForegroundService(ContextImpl.java:1823)
       at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:779)
       at androidx.core.content.ContextCompat$Api26Impl.startForegroundService(ContextCompat.java)
       at androidx.core.content.ContextCompat.startForegroundService(ContextCompat.java:6)
       at MyAppPackageHidden.service.RecorderService$Companion.startService(RecorderService.java:2)
       at MyAppPackageHidden.ui.rec.RecActivity$getConnectionRecorderService$1.onServiceConnected(RecActivity.java:4)
       at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:2077)
       at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:2110)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.app.ActivityThread.main(ActivityThread.java:7838)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

Caused by android.os.RemoteException: Remote stack trace:
    at com.android.server.am.ActiveServices.startServiceLocked(ActiveServices.java:691)
    at com.android.server.am.ActiveServices.startServiceLocked(ActiveServices.java:616)
    at com.android.server.am.ActivityManagerService.startService(ActivityManagerService.java:11839)
    at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2519)
    at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2498)

enter image description here

Scotney answered 20/11, 2021 at 8:36 Comment(4)
Perhaps RecActivity was in the background at the time you called startForegroundService().Ogburn
@Ogburn I guess so. Activity has bindService which takes ServiceConnection object as Callback and in onServiceConnected method I make service foreground. I guess I need to use lifecycleScope.launchWhenStarted { /* start foreground */ }Scotney
See also this related question and possible solutions regarding ForegroundServiceStartNotAllowedException hereDumpling
@Scotney How do you solve this issue. I am also facing same issue with audio_service. Can you share the solution?Royal
B
49

Apps that target Android 12 (API level 31) or higher can't start foreground services while running in the background, except for a few special cases. If an app tries to start a foreground service while the app is running in the background, and the foreground service doesn't satisfy one of the exceptional cases, the system throws a ForegroundServiceStartNotAllowedException.

Exemptions from background start restrictions

In the following situations, your app can start foreground services even while your app is running in the background:

  • Your app transitions from a user-visible state, such as an activity.
  • Your app can start an activity from the background, except for the case where the app has an activity in the back stack of an existing task.
  • Your app receives a high-priority message using Firebase Cloud Messaging.
  • The user performs an action on a UI element related to your app. For example, they might interact with a bubble, notification, widget, or activity.
  • Your app invokes an exact alarm to complete an action that the user requests.
  • Your app is the device's current input method.
  • Your app receives an event that's related to geofencing or activity recognition transition.
  • After the device reboots and receives the ACTION_BOOT_COMPLETED, ACTION_LOCKED_BOOT_COMPLETED, or ACTION_MY_PACKAGE_REPLACED intent action in a broadcast receiver.

For more info please check link1 link2

Bucci answered 11/1, 2022 at 12:43 Comment(12)
Good explanation, but what is the solution?Vano
I have foregroundServiceType="mediaPlayback" declared in my manifest, and the service is only started after clicks on the notification or play buttons in my app, so I would expect it to work. Nevertheless, I still have a couple hundred crashes every month, this is one of the most mysterious areas that there is in AndroidPatroclus
@Patroclus did you ever figure this out? I have a similar situation.Togoland
Absolutely not. This is one of the areas where Google let's us hang out to dry completelyPatroclus
I have found that if your app is attempting to start your FG service, but gets interrupted and backgrounded due to user action like answering a call (basically anything that would BG your app), you'll run into this very error. Because it was executing startForeground(notificationId, notification); inside the service, the entire process was killed.Junia
Seems this might be a bug started in AOS12 and still exists in 13. Fatal Exception BugJunia
Has anyone found a solution for this Fatal Exception: android.app.ForegroundServiceStartNotAllowedException startForegroundService() not allowed due to mAllowStartForeground false: service with foregroundServiceType="mediaPlayback"Iron
@DanDavis Then this whole restriction is a joke. Where exactly should we call startForegroundService? We can't put it at onCreate and onResume because of the Background execution limits of Android 8. Why don't they just remove this foreground service feature?Tella
"Your app transitions from a user-visible state, such as an activity". For me it was working until I started using flows. I think that was enough to break the causality. ie: I think it doesn't give you n seconds to start it. It gives you until onTaskRemoved completes and if you use something like flows it allows the method to complete before bringing to FG.Sollie
There is no solution. We should accept that unfortunately.Randle
So we can't put it in onCreate nor onPause, how about we call startForegroundService() when the user is uninstalling the app?Tella
I've observed few crash reports where the notification priority is High in RemoteMessage from FCM but still got ForegroundServiceStartNotAllowedException in Android 12,13,14 . It seems to be going against your 3rd point and android documentation:developer.android.com/develop/background-work/services/…Digestive
R
30

Previously we were using Service to run background tasks such as backup of data, setting up reminder notifications etc. And the code for invoking the service earlier will be as follows

Intent serviceIntent = new Intent ( context, BackupService.class );
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    context.startForegroundService ( serviceIntent );
} else {
    context.startService ( serviceIntent );
}

But, due to Android 12 - Foreground service launch restrictions, we will not be able to invoke Service for performing background tasks. To learn more about this restriction, please refer to Android 12 Behavior Changes.

So from now on, (i.e) from targetSdk 31 / Android 12+, Service can be invoked only when the application is in the foreground. When the application is closed or when the application went to the background, invoking Service using startForegroundService will cause ForegroundServiceStartNotAllowedException. So to perform background tasks in Android 12 and above, we need to use Worker instead of Service. To learn more about Worker, please refer to Work Requests.

So for applications targeting SDK 31 / Android 12+, the code for invoking the background task will be as follows,

Intent serviceIntent = new Intent ( context, BackupService.class );
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    OneTimeWorkRequest request = new OneTimeWorkRequest.Builder ( BackupWorker.class ).addTag ( "BACKUP_WORKER_TAG" ).build ();
    WorkManager.getInstance ( context ).enqueue ( request );
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    context.startForegroundService ( serviceIntent );
} else {
    context.startService ( serviceIntent );
}

Sample code for BackupService (Existing).

public class BackupService extends Service {

    private static final String TAG = "BackupService";

    @Nullable
    @Override
    public IBinder onBind ( Intent intent ) {
        return null;
    }

    @Override
    public int onStartCommand ( Intent intent, int flags, int startId ) {
        Log.d ( TAG, "onStartCommand" );
        startForeground ( BACKUP_SERVICE_NOTIFICATION_ID, createServiceNotification () );
        //call methods to perform background task
        return super.onStartCommand ( intent, flags, startId );
    }
}

Sample code for BackupWorker (Newly added).

public class BackupWorker extends Worker {

    private static final String TAG = "BackupWorker";

    public BackupWorker ( @NonNull Context context, @NonNull WorkerParameters workerParams ) {
        super ( context, workerParams );
    }

    @NonNull
    @Override
    public Result doWork () {
        //call methods to perform background task
        return Result.success ();
    }
}

Make sure to add the following dependencies in the module-level gradle file

implementation 'androidx.work:work-runtime:2.7.1'
implementation 'com.google.guava:guava:27.0.1-android'

I have tested the above code working with Android 5, Android 8, Android 11 & Android 12. Working as expected in my case.

Hope this solution helps someone who is targeting their application for SDK 31 / Android 12+.

Retrochoir answered 5/5, 2022 at 17:35 Comment(7)
So what if it is important for the app to have the app always running the foreground service, thus not only for a few tasks?Rigatoni
Hey @Rigatoni ! For Target SDK 31 and above, the app can use foreground service for only few tasks such as media playback, media projection, phone calls and other few. So for other tasks, we need to use any background process like a Worker. You could refer this page to find more information regarding the same :) developer.android.com/guide/components/foreground-servicesRetrochoir
Thank you, was afraid of that. I guess requiring users to disable battery optimization is my way to go then. Thanks for your quick reply!Rigatoni
I dont have any problems with this exception on Android 12 google pixel, but Samsung thats a different story.. Why?Wyman
There is no need to still use BackupService for lower Android versions. You can use a Worker there as well.Snub
What should I do with the doWork method? I mean how can I start my service from there?Bigler
This can't be an answer. Worker is a totally different thing, which is not comparable to services, Worker can replace IntentService, yes, but not a foreground service. You can't use Worker for media playback | camera (a video recording / call) | mic (audio recording) and so on, you have to use a foreground service for thisScotney
M
9

Up to Android 12 it is enough to use the android:foregroundServiceType="yourType" service attribute in the AndroidManifest to start your service from background (I assume that you need to skip this step if you don't find a suitable service type. Unfortunately, I haven't found information about this in docs). In Android 12 and above - If your task allows you to use WorkManager, then you should replace your service with Worker, e.g. to download/upload a file or for periodic synchronization. If your task is more extensive and requires the service to be constantly running in the background, e.g, playing audio, then here you still need to use the foreground service, but run it using the AlarmManager. If your service should be started immediately then you need to use exact alarm to start your service. For this purpose you must be sure user granted SCHEDULE_EXACT_ALARM permission. It is granted by the system automatically, but can be revoked by the user or by the system anytime.

Let's say we use PlaybackService to play audio. Then my solution is next:

  1. Add android:foregroundServiceType="mediaPlayback" to your AndroidManifest.xml file:
<service
    android:name=".data.playback.PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="false"
    />
  1. In a place you want to start your service:
private suspend fun startPlaybackService(state: PlaybackState) {
    withContext(Dispatchers.Main) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            val operation = PendingIntent.getForegroundService(
                context,
                REQUEST_CODE,
                Intent(context, PlaybackService::class.java),
                FLAG_UPDATE_CURRENT_COMPAT
            )
            val alarmManager = context.getSystemService<AlarmManager>()
            if (state in PlaybackStateSets.ACTIVE) {
                logi { "Starting playback service with exact alarm" }
                startPlaybackServiceWithAlarm(alarmManager, operation)
            } else {
                logi { "Cancelling exact alarm operation" }
                alarmManager.cancel(operation)
            }
        } else {
            if (state in PlaybackStateSets.ACTIVE) {
                logi { "Starting playback service" }
                ContextCompat.startForegroundService(
                    context,
                    Intent(context, PlaybackService::class.java)
                )
            }
        }
    }
}
  1. To ask your user to grant permission for starting an exact alarm you should explain user why you need this permission to be granted and to redirect user to system settings section Alarms & reminders:
fun navigateToAlarmSettings(context: Context) {
    context.startActivity(
        Intent().apply {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
            } else {
                loge { "Attempt to create Alarm settings section intent on Android sdk version < 31" }
            }
        }
    )
}
  1. When the SCHEDULE_EXACT_ALARM permission is granted to your app, the system sends it the ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED broadcast. You can implement a broadcast receiver to handle this change.

Links:

Morrow answered 3/2, 2023 at 15:56 Comment(10)
but AlarmManager sounds like a hack. You can just ask a user to disable battery optimization for your app and after that the app will be able to start a foreground service from backgroundScotney
@Scotney It's not a hack at all. This solution is recommended by google. Check the docs developer.android.com/about/versions/12/… To complete time-sensitive actions that the user requests, start foreground services within an exact alarm.Morrow
it seems if your app has something to do with alarm category appScotney
@TimurPanzhiev What will be the foregroundServiceType for the Steps Sensor?Munda
@sagar-patel if you didn't find suitable type in the foregroundServiceType list, you can just skip this step and everything will work. This step is needed only for few service types, pls check possible values here developer.android.com/guide/topics/manifest/service-element. Give a feedback if your pedometer still crashesMorrow
@TimurPanzhiev Sure. Also, I see that SCHEDULE_EXACT_ALARM is not an exact alarm. support.google.com/googleplay/android-developer/answer/… And only an exact alarm is allowed to start the foreground service from the background. developer.android.com/guide/components/…Munda
@sagar-patel SCHEDULE_EXACT_ALARM it is 100 percent exact alarm permission introduced in Android 12. This permission can be revoked by user or system anytime. But USE_EXACT_ALARM is also a permission for starting exact alarm introduced in Android 13. While both the SCHEDULE_EXACT_ALARM and the USE_EXACT_ALARM permissions signal the same capabilities, they are granted differently and support different use-cases. Check the docs developer.android.com/training/scheduling/…Morrow
@TimurPanzhiev Yeah... You're right. Thanks for correcting me. I appreciate. I just implemented the solution and moved to QA. I have already accepted your answer. I will share the conclusion here for sure.Munda
@TimurPanzhiev We are still getting the same exception in the production app even after implementing the above suggestion 🤦🏻‍♂️ It is mainly happening on OS 12 and 13.Munda
@sagar-patel I'm not aware of your implementation but probably you need to cancel pending alarm operation before start a new one, pls check updated answerMorrow
C
6

We had the same issue with our media playback apps. In our case the root cause was, that we've used stopForeground(false) every time the player has been paused and startForeground(...) when the playback has been resumed. This was necessary in API < 31, so the notification could be swiped away by the user when the playback has been paused.

Starting with Android 12 (API 31) this lead to the problem, that when the app paused playback (e.g. due to transient audiofocus loss), the following resume (e.g. on audiofocus-gain) failed with the above crash when we've tried to call startForeground(...).

Our fix was to call stopForeground(false) only when the app lost audio focus completely or has been closed by the user.

Our only change to the app is that when the playback gets paused, we now use the something like this:

if (isNotificationActive) {
    // update notification
    notificationManager.notify(...);

    // Starting with Android 12 (API 31), we cannot stop the foreground service on pause, otherwise we won't be able to resume later.
    if (Build.VERSION.SDK_INT < 31) {
        stopForeground(false);
    }
}
Chutzpah answered 31/1, 2023 at 11:42 Comment(0)
S
3

[Note: The first part is not relevant for Android 12 and later]

In my case, we use a service that does SIP communications (VoIP), and we are starting a foreground service to do time sensitive operations (such as registration) or when running SIP calls. These tasks cannot be run in a Worker.

To handle this use case, Android allows you to declare your service's "foregroundServiceType", and some types are allowed to create foreground services from the background. The other notable use case that this make sense for is media playing.

For Android 12 and later

As noted in the comments, the foregroundServiceType attribute is no longer supported in Android 12. My current solution is to catch the exception thrown from startForegroundService() (as a way of detecting that we're on Android 12) and then use an AlarmManager to start the foreground service, as explained in this answer.

Please note that AlarmManager offers several alarm modes, some will not wake up the device from "deep sleep". If you need to be able to wake the device from "deep sleep" to start the service, try setAlarmClock() or setAndAllowWhileIdle(). There are a few other options discussed in the Keep Device Awake article on Android Developers and in this answer.

Sleep answered 31/5, 2022 at 13:53 Comment(5)
I have declared my like this android:foregroundServiceType="mediaPlayback" but I'm still getting this crash. I have a media notification associated with the service. Any ideas?Togoland
After working on this a bit more, I realized that this answer is wrong - starting with Android 12, there's no foregroundServiceType that you can set that will allow you to start a foreground service, where the application would otherwise be excluded from doing so - the Android 12 heuristics for allowing foreground services are complicated but are unaffected by the use of foregroundServiceType.Sleep
My current implementation now is to try to start the foreground service using startForgroundService(), then if it fails - due to the OP error - to catch the exception thrown and use an AlarmManager for fallback, as explained here: https://mcmap.net/q/242761/-alarmmanager-for-calling-a-service-in-android-p-startforegroundserviceSleep
Hi @Guss, I have implemented the same, but the Alarm Manager doesn't fire the Pending Intent if the app is in sleep mode. While debugging it works well.Torrietorrin
@Torrietorrin the linked AlarmManager answer showed the use of set(), which is not what I actually use - I use setAlarmClock() that does wake a device from deep sleep. I've added more details in my answer.Sleep
V
2

Like told in the comments above you are not allowed to start a foreground process while the app is running in the background.

private boolean isInBackground(){
    ActivityManager.RunningAppProcessInfo myProcess = new ActivityManager.RunningAppProcessInfo();
    ActivityManager.getMyMemoryState(myProcess);
    Boolean isInBackground = myProcess.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
    Log.d("isInBackground", myProcess.processName + " " + myProcess.importance + " " + isInBackground);
    return  isInBackground;
}

Checking if the app is running in the background before startForegroundService() fixed my problem

Vellum answered 21/11, 2023 at 9:47 Comment(0)
P
0

The best solution that i have found is to start the service from background or after device reboots via AlarmManager on Android O+.

val mgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val i = Intent(context, Service::class.java)
val pi = PendingIntent.getForegroundService(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val calendar: Calendar = Calendar.getInstance()
calendar.timeInMillis = System.currentTimeMillis()
calendar.add(Calendar.SECOND, 3)
mgr.set(AlarmManager.RTC_WAKEUP, calendar.timeInMillis ,pi)

Also, in NotificationBuilder added:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    notificationBuilder.foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
}

notificationBuilder.setPriority(NotificationCompat.PRIORITY_MAX)
Preterite answered 7/12, 2022 at 21:58 Comment(4)
what the difference?Scotney
The difference is that Android seems to allow starting the service from background after device reboots via this way.Preterite
interesting, thanks, I will try it. but if it works it's still a hack and an app may be removed from Google Play :)Scotney
so it's just better to ask a user to disable battery optimization if he wants such features as automatic starting of foreground service from background on device boot/reboot.Scotney
P
0

This bug is a real headache for all Android developers. But I think I've managed to find a workaround. I start the foreground service as usual service with startService() and then internally execute startForeground() in the public void onCreate() of the Service class. It looks something like this.

AndroidManifest.xml

<service
    android:enabled="true"
    android:foregroundServiceType="location|dataSync|connectedDevice"
    android:label="@string/app_test"
    android:name=".service.TestService"/>

TestService.java

public class TestService extends Service
{
    @Override
    public void onCreate()
    {
       startForegroundService()
    }
}

private void startForegroundService()
{
    if(canStartService())
    {
        if(Build.VERSION.SDK_INT > 28)
        {
            startForeground(INFORMATION, fgNotification,
                    ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
        }
        else
        {
            startForeground(INFORMATION, fgNotification);
        }
    }
}

public static boolean canStartService() { return !isStarted && !App.isAppInBackground; }

MainActivity.java

@Override
public void onResume()
{
    super.onResume();
    
    if(TestService.canStartService())
    {
        Intent foregroundIntent = new Intent(this, TestService.class);
        Utils.startForegroundService(this, foregroundIntent);
    }
}

Utils.java

public static void startForegroundService(Context context, Intent intent)
{
   context.startService(intent);
    //ContextCompat.startForegroundService(context, srv);
}

P.S. Perhaps this is not the best solution, I understand, but it helps me to reduce the level of crashes very much.

P.P.S. Also, I had problems reproducing this error. I noticed that on Android API 34, the easiest way to reproduce it is when the application is in the foreground and the user switches to the notification bar and back

Primogenial answered 9/2 at 13:14 Comment(2)
it's default behavior to make it foreground in onCreate, so nothing special here, it's the sameScotney
What matters here is how you run the basic service. When using ContextCompat.startForegroundService, my applications were constantly crashingPrimogenial

© 2022 - 2024 — McMap. All rights reserved.