Reputation: 12283
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)
Upvotes: 100
Views: 98819
Reputation: 2425
All answers here are not relevant to my case. I don't start the service from background. I start it on app launch from the activity. I don't know why I get this exception. I will try to do something like this as DeepSeek recommended to check if app is in foreground before starting the service. I have no idea how it's possible that my app is in background while the service is starting if I start the service on oncreate.
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
intent?.let {
when (it.action) {
"ACTION_EXIT" -> handleExit()
}
}
// Check if the app is in the foreground
if (isAppInForeground()) {
if (!isForegroundServiceStarted && checkPermissions()) {
startForeground(1, createNotification(createNotificationText()))
isForegroundServiceStarted = true
} else if (!checkPermissions()) {
// Request permissions or handle denial
stopSelf()
}
} else {
// Handle the case when the app is in the background
// You can use WorkManager or JobScheduler for background tasks
stopSelf()
}
return START_STICKY
}
private fun isAppInForeground(): Boolean {
val appProcessInfo = ActivityManager.RunningAppProcessInfo()
ActivityManager.getMyMemoryState(appProcessInfo)
return appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
}
Upvotes: 0
Reputation: 41
Solution: Disabling Battery Optimization Restriction To resolve the issue of apps being interrupted when running in the background, I created a native module to request users to disable battery optimization for the app.
https://github.com/doublesymmetry/react-native-track-player/issues/1666#issuecomment-2354671144
Upvotes: 1
Reputation: 11
I'm just a beginner, I don't know how it works. If you want to solve this error right away, just add these lines in the Android Manifest.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<service android:name=".RunningService" //change it to your name
android:foregroundServiceType="specialUse"/>
Upvotes: 1
Reputation: 127
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
Upvotes: 0
Reputation: 21
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
Upvotes: 2
Reputation: 760
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:
android:foregroundServiceType="mediaPlayback"
to your AndroidManifest.xml
file:<service
android:name=".data.playback.PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="false"
/>
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)
)
}
}
}
}
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" }
}
}
)
}
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:
Upvotes: 10
Reputation: 32374
[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.
Upvotes: 3
Reputation: 676
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);
}
}
Upvotes: 7
Reputation: 7552
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)
Upvotes: 0
Reputation: 872
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+.
Upvotes: 33
Reputation: 2309
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:
For more info please check link1 link2
Upvotes: 53