Fatal Android 12: Excepción: startForegroundService() no permitido debido a mAllowStartForeground false

15 minutos de lectura

avatar de usuario de user924
usuario924

Noté una excepción (Firebase Crashlytics) para Pixel 5 y Pixel 4a (ambos en Android 12), ningún otro dispositivo, sucedió solo dos veces, una vez para cada dispositivo.

¿Qué significa? Android 11 y 12 tienen las mismas reglas para trabajar con servicios en primer plano, pero no hay problemas con Android 11. ¿Es un error de Pixel?

Desde 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)

ingrese la descripción de la imagen aquí

  • Tal vez RecActivity estaba en segundo plano en el momento en que llamaste startForegroundService().

    – CommonsWare

    20 de noviembre de 2021 a las 13:11

  • @CommonsWare Supongo que sí. La actividad tiene bindService el cual toma ServiceConnection objeto como devolución de llamada y en onServiceConnected método pongo el servicio en primer plano. Supongo que necesito usar lifecycleScope.launchWhenStarted { /* start foreground */ }

    – usuario924

    20 de noviembre de 2021 a las 23:22


  • Consulte también esta pregunta relacionada y las posibles soluciones con respecto a ForegroundServiceStartNotAllowedException aquí

    – Jadent

    16 de diciembre de 2021 a las 11:59

  • @ user924 ¿Cómo resuelve este problema? También estoy enfrentando el mismo problema con audio_service. ¿Puedes compartir la solución?

    – Alex Aung

    11 de julio de 2022 a las 18:57

Las aplicaciones destinadas a Android 12 (nivel de API 31) o superior no pueden iniciar servicios en primer plano mientras se ejecutan en segundo plano, excepto en algunos casos especiales. Si una aplicación intenta iniciar un servicio en primer plano mientras se ejecuta en segundo plano y el servicio en primer plano no satisface uno de los casos excepcionales, el sistema genera una excepción ForegroundServiceStartNotAllowedException.

Exenciones de las restricciones de inicio en segundo plano

En las siguientes situaciones, su aplicación puede iniciar servicios en primer plano incluso mientras se ejecuta en segundo plano:

  • Su aplicación pasa de un estado visible al usuario, como una actividad.
  • Su aplicación puede iniciar una actividad desde el fondo, excepto en el caso de que la aplicación tenga una actividad en la pila de actividades de una tarea existente.
  • Su aplicación recibe un mensaje de alta prioridad mediante Firebase Cloud Messaging.
  • El usuario realiza una acción en un elemento de la interfaz de usuario relacionado con su aplicación. Por ejemplo, pueden interactuar con una burbuja, notificación, widget o actividad.
  • Su aplicación invoca una alarma exacta para completar una acción que solicita el usuario.
  • Su aplicación es el método de entrada actual del dispositivo.
  • Su aplicación recibe un evento relacionado con la transición del reconocimiento de actividad o geoperimetraje.
  • Después de que el dispositivo se reinicia y recibe la acción de intención ACTION_BOOT_COMPLETED, ACTION_LOCKED_BOOT_COMPLETED o ACTION_MY_PACKAGE_REPLACED en un receptor de transmisión.

Para obtener más información, consulte enlace1 enlace2

  • Buena explicación, pero ¿cuál es la solución?

    – Shailendra Madda

    14/04/2022 a las 17:23

  • Tengo foregroundServiceType="mediaPlayback" declarado en mi manifiesto, y el servicio solo se inicia después de hacer clic en la notificación o en los botones de reproducción en mi aplicación, por lo que espero que funcione. Sin embargo, todavía tengo un par de cientos de fallas cada mes, esta es una de las áreas más misteriosas que hay en Android.

    – avalancha

    4 de agosto de 2022 a las 8:02


  • @avalancha, ¿alguna vez te diste cuenta de esto? Tengo una situación similar.

    – casolorz

    8 oct 2022 a las 14:24

  • Absolutamente no. Esta es una de las áreas donde Google nos deja pasar el rato para secarnos por completo.

    – avalancha

    15 oct 2022 a las 16:12

  • Descubrí que si su aplicación intenta iniciar su servicio FG, pero se interrumpe y se pone en segundo plano debido a la acción del usuario, como responder una llamada (básicamente cualquier cosa que haga BG su aplicación), se encontrará con este mismo error. porque estaba ejecutando startForeground(notificationId, notification); dentro del servicio, se eliminó todo el proceso.

    – Dan Davis

    13 de noviembre de 2022 a las 17:11

Anteriormente estábamos usando Service para ejecutar tareas en segundo plano, como la copia de seguridad de datos, la configuración de notificaciones de recordatorio, etc. Y el código para invocar el servicio antes será el siguiente

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

Pero, debido a Android 12 - Foreground service launch restrictions, no podremos invocar el Servicio para realizar tareas en segundo plano. Para obtener más información sobre esta restricción, consulte Cambios de comportamiento de Android 12.

Entonces, de ahora en adelante, (es decir) desde targetSdk 31 / Android 12+, Service solo se puede invocar cuando la aplicación está en primer plano. Cuando se cierra la aplicación o cuando la aplicación pasa a segundo plano, invocando Service usando startForegroundService causará ForegroundServiceStartNotAllowedException. Entonces, para realizar tareas en segundo plano en Android 12 y superior, necesitamos usar Worker en lugar de Service. Para aprender más sobre Workerpor favor refiérase a Solicitudes de trabajo.

Entonces, para aplicaciones destinadas a SDK 31/Android 12+, el código para invocar la tarea en segundo plano será el siguiente:

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 );
}

Código de muestra para BackupService (Existente).

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 );
    }
}

Código de muestra para BackupWorker (Agregado recientemente).

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 ();
    }
}

Asegúrese de agregar las siguientes dependencias en el nivel de módulo gradle archivo

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

He probado el código anterior trabajando con Android 5, Android 8, Android 11 y Android 12. Funciona como se esperaba en mi caso.

Espero que esta solución ayude a alguien que tiene como objetivo su aplicación para SDK 31/Android 12+.

  • Entonces, ¿qué sucede si es importante que la aplicación siempre ejecute el servicio en primer plano, por lo tanto, no solo para algunas tareas?

    – Lars

    31 de mayo de 2022 a las 14:37


  • ¡Hola @Lars! Para Target SDK 31 y superior, la aplicación puede usar el servicio de primer plano solo para algunas tareas, como la reproducción de medios, la proyección de medios, las llamadas telefónicas y otras pocas. Entonces, para otras tareas, necesitamos usar cualquier proceso en segundo plano como un Worker. Puede consultar esta página para encontrar más información sobre el mismo 🙂 desarrollador.android.com/guide/components/foreground-services

    – niranj1997

    1 de junio de 2022 a las 5:45


  • Gracias, tenía miedo de eso. Supongo que exigir a los usuarios que deshabiliten la optimización de la batería es mi camino a seguir entonces. ¡Gracias por su respuesta rápida!

    – Lars

    1 de junio de 2022 a las 13:19

  • No tengo ningún problema con esta excepción en Android 12 Google Pixel, pero Samsung es una historia diferente. ¿Por qué?

    – Cangrejo kebab

    8 oct 2022 a las 10:55

  • No es necesario seguir usando BackupService para versiones inferiores de Android. También puedes usar un Worker allí.

    – Malachiasz

    22 de noviembre de 2022 a las 8:20

Avatar de usuario de Guss
Guss

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

En mi caso, usamos un servicio que realiza comunicaciones SIP (VoIP) y estamos iniciando un servicio en primer plano para realizar operaciones sensibles al tiempo (como el registro) o cuando se ejecutan llamadas SIP. Estas tareas no se pueden ejecutar en un Worker.

Para manejar este caso de uso, Android le permite declarar su servicio “foregroundServiceType“, y algunos tipos pueden crear servicios en primer plano desde el fondo. El otro caso de uso notable para el que esto tiene sentido es la reproducción multimedia.

Para Android 12 y posteriores

Como se indica en los comentarios, el foregroundServiceType El atributo ya no es compatible con Android 12. Mi solución actual es capturar la excepción lanzada desde startForegroundService() (como una forma de detectar que estamos en Android 12) y luego usar un AlarmManager para iniciar el servicio de primer plano, como se explica en esta respuesta.

Tenga en cuenta que AlarmManager ofrece varios modos de alarma, algunos no despertarán el dispositivo del “sueño profundo”. Si necesita poder activar el dispositivo desde el “sueño profundo” para iniciar el servicio, intente setAlarmClock() o setAndAllowWhileIdle(). Hay algunas otras opciones discutidas en el Mantenga el dispositivo activo artículo sobre desarrolladores de Android y en esta respuesta.

  • He declarado mi así android:foregroundServiceType="mediaPlayback" pero sigo teniendo este bloqueo. Tengo una notificación de medios asociada con el servicio. ¿Algunas ideas?

    – casolorz

    8 oct 2022 a las 14:30

  • Después de trabajar un poco más en esto, me di cuenta de que esta respuesta es incorrecta: a partir de Android 12, no hay foregroundServiceType que puede configurar que le permitirán iniciar un servicio en primer plano, donde la aplicación de lo contrario estaría excluida de hacerlo: las heurísticas de Android 12 para permitir servicios en primer plano son complicadas pero no se ven afectadas por el uso de foregroundServiceType.

    – Guss

    8 oct 2022 a las 16:04


  • Mi implementación actual ahora es intentar iniciar el servicio de primer plano usando startForgroundService()luego, si falla, debido al error OP, para capturar la excepción lanzada y usar un AlarmManager para respaldo, como se explica aquí: stackoverflow.com/a/53759060/53538

    – Guss

    8 oct 2022 a las 16:05

  • Hola, @Guss, implementé lo mismo, pero el administrador de alarmas no activa el intento pendiente si la aplicación está en modo de suspensión. Mientras que la depuración funciona bien.

    – AndroidStud

    6 de febrero a las 2:01

  • @androidStud el vinculado AlarmManager respuesta mostró el uso de set()que no es lo que realmente uso, uso setAlarmClock() que despierta un dispositivo del sueño profundo. He agregado más detalles en mi respuesta.

    – Guss

    6 de febrero a las 9:04

Avatar de usuario de Timur Panzhiev
Timur Panzhiev

Hasta Android 12 es suficiente usar el android:foregroundServiceType=”suTipo” atributo de servicio en el AndroidManifest para iniciar su servicio desde el fondo (supongo que debe omitir este paso si no encuentra un tipo de servicio adecuado. Desafortunadamente, no he encontrado información sobre esto en los documentos). En Android 12 y superior: si su tarea le permite usar WorkManagerentonces debe reemplazar su servicio con Worker, por ejemplo, para descargar/cargar un archivo o para una sincronización periódica. Si su tarea es más extensa y requiere que el servicio se ejecute constantemente en segundo plano, por ejemplo, reproducir audio, entonces todavía necesita usar el servicio en primer plano, pero ejecútelo usando el AlarmManager. Si su servicio debe iniciarse de inmediato, debe usar la alarma exacta para iniciar su servicio. Para ello debe estar seguro de que el usuario ha concedido SCHEDULE_EXACT_ALARM permiso. Es otorgado por el sistema automáticamente, pero puede ser revocado por el usuario o por el sistema en cualquier momento.

digamos que usamos PlaybackService para reproducir audio. Entonces mi solución es la siguiente:

  1. Agregar android:foregroundServiceType="mediaPlayback" para usted AndroidManifest.xml archivo:
<service
    android:name=".data.playback.PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="false"
    />
  1. En un lugar donde desea iniciar su servicio:
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. Para pedirle a su usuario que otorgue permiso para iniciar una alarma exacta, debe explicar al usuario por qué necesita que se le otorgue este permiso y redirigir al usuario a la sección de configuración del sistema 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. Cuando el SCHEDULE_EXACT_ALARM se otorga permiso a su aplicación, el sistema le envía el ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED transmisión. Puede implementar un receptor de transmisión para manejar este cambio.

Enlaces:

La mejor solución que he encontrado es iniciar el servicio desde el fondo o después de que el dispositivo se reinicie a través de AlarmManager en 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)

Además, en NotificationBuilder agregó:

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

notificationBuilder.setPriority(NotificationCompat.PRIORITY_MAX)

  • ¿cuál es la diferencia?

    – usuario924

    8 dic 2022 a las 10:36

  • La diferencia es que Android parece permitir iniciar el servicio desde el fondo después de que el dispositivo se reinicia de esta manera.

    – EQUIPO EAK

    8 dic 2022 a las 18:16

  • interesante, gracias, lo intentare. pero si funciona, sigue siendo un truco y es posible que se elimine una aplicación de Google Play 🙂

    – usuario924

    3 de febrero a las 17:44


  • por lo tanto, es mejor pedirle a un usuario que deshabilite la optimización de la batería si desea funciones tales como el inicio automático del servicio en primer plano desde el fondo en el arranque/reinicio del dispositivo.

    – usuario924

    3 de febrero a las 17:45


Avatar de usuario de Shailendra Madda
Shailendra Madda

Para solucionar este problema en Android 12 o superior, actualice la lógica de su aplicación:

Si descubre que su aplicación inicia servicios en primer plano mientras se ejecuta en segundo plano, actualice la lógica de su aplicación para usar administrador de trabajo. Para ver un ejemplo de cómo actualizar su aplicación, mire a través de la WorkManagerMuestra en GitHub.

  • ¿cuál es la diferencia?

    – usuario924

    8 dic 2022 a las 10:36

  • La diferencia es que Android parece permitir iniciar el servicio desde el fondo después de que el dispositivo se reinicia de esta manera.

    – EQUIPO EAK

    8 dic 2022 a las 18:16

  • interesante, gracias, lo intentare. pero si funciona, sigue siendo un truco y es posible que se elimine una aplicación de Google Play 🙂

    – usuario924

    3 de febrero a las 17:44


  • por lo tanto, es mejor pedirle a un usuario que deshabilite la optimización de la batería si desea funciones tales como el inicio automático del servicio en primer plano desde el fondo en el arranque/reinicio del dispositivo.

    – usuario924

    3 de febrero a las 17:45


avatar de usuario de th3hamm0r
th3hamm0r

Tuvimos el mismo problema con nuestras aplicaciones de reproducción multimedia. En nuestro caso, la causa raíz fue que hemos usado stopForeground(false) cada vez que el reproductor ha sido pausado y startForeground(...) cuando se haya reanudado la reproducción. Esto era necesario en API

Comenzando con Android 12 (API 31), esto condujo al problema de que cuando la aplicación detuvo la reproducción (por ejemplo, debido a una pérdida transitoria del enfoque de audio), la siguiente reanudación (por ejemplo, en la ganancia de enfoque de audio) falló con el bloqueo anterior cuando intentamos llamar startForeground(...).

Nuestra solución fue llamar stopForeground(false) solo cuando la aplicación perdió el foco de audio por completo o el usuario la cerró.

Nuestro único cambio en la aplicación es que cuando la reproducción se detiene, ahora usamos algo como esto:

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);
    }
}

¿Ha sido útil esta solución?