En Android 9 y versiones posteriores, la plataforma puede supervisar las apps en busca de comportamientos que afecten negativamente la duración de batería de los dispositivos. La plataforma usa y evalúa las reglas de configuración para proporcionar un flujo de UX que les brinda a los usuarios la opción de restringir las apps que infrinjan las reglas.
En Android 8.0 y versiones anteriores, había restricciones a través de funciones como Doze, la suspensión de apps, los límites en segundo plano y los límites de ubicación en segundo plano. Sin embargo, algunas apps siguieron mostrando comportamientos inadecuados, algunos de los cuales se describen en Android vitals. Android 9 introdujo una infraestructura del SO que puede detectar y restringir apps según reglas de configuración que se pueden actualizar con el tiempo.
Restricciones en segundo plano
Los usuarios pueden restringir apps, o el sistema puede sugerir apps que detecta que afectan de manera negativa el estado del dispositivo.
Apps restringidas:
- El usuario puede iniciarlo.
- No se pueden ejecutar trabajos ni alarmas, ni usar la red en segundo plano.
- No se pueden ejecutar servicios en primer plano.
- El usuario puede cambiarla a una app sin restricciones.
Los implementadores de dispositivos pueden agregar restricciones adicionales a las apps para lo siguiente:
- Restringe el reinicio automático de la app.
- Restringir la vinculación de los servicios (muy riesgoso)
Se espera que las apps restringidas en segundo plano no consuman recursos del dispositivo, como la memoria, la CPU y la batería. Las apps con restricciones en segundo plano no deben afectar el estado del dispositivo cuando el usuario no las usa de forma activa. Sin embargo, se espera que las mismas apps funcionen por completo cuando el usuario las inicie.
Usa implementaciones personalizadas
Los implementadores de dispositivos pueden seguir usando sus métodos personalizados para aplicar restricciones en las apps.
Cómo integrar restricciones de apps
En las siguientes secciones, se describe cómo definir e integrar las restricciones de apps en tu dispositivo. Si usas métodos de restricción de apps de Android 8.x o versiones anteriores, revisa las siguientes secciones en detalle para ver los cambios en Android 9 y versiones posteriores.
Establece la marca de AppOpsManager
Cuando se restringe una app, configura la marca adecuada en AppOpsManager
. Ejemplo de fragmento de código de packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryUtils.java
:
public void setForceAppStandby(int uid, String packageName, int mode) { final boolean isPreOApp = isPreOApp(packageName); if (isPreOApp) { // Control whether app could run in the background if it is pre O app mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName, mode); } // Control whether app could run jobs in the background mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode); }
Asegúrate de que isBackgroundRestricted devuelva un valor verdadero.
Cuando se restringe una app, asegúrate de que ActivityManager.isBackgroundRestricted()
muestre true
.
Registra el motivo de la restricción
Cuando se restrinja una app, registra los motivos de la restricción. Un fragmento de código de ejemplo de registro desde packages/apps/Settings/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java
:
mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName,AppOpsManager.MODE_IGNORED); if (CollectionUtils.isEmpty(appInfo.anomalyTypes)) { // Only log context if there is no anomaly type mMetricsFeatureProvider.action(mContext, MetricsProto.MetricsEvent.ACTION_TIP_RESTRICT_APP, packageName, Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT,metricsKey)); } else { // Log ALL the anomaly types for (int type : appInfo.anomalyTypes) { mMetricsFeatureProvider.action(mContext, MetricsProto.MetricsEvent.ACTION_TIP_RESTRICT_APP, packageName, Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, metricsKey), Pair.create(MetricsProto.MetricsEvent.FIELD_ANOMALY_TYPE, type)); }
Reemplaza type
por el valor de AnomalyType
.
Los implementadores de dispositivos pueden usar las constantes definidas en src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java
:
public @interface AnomalyType { // This represents an error condition in the anomaly detection. int NULL = -1; // The anomaly type does not match any other defined type. int UNKNOWN_REASON = 0; // The application held a partial (screen off) wake lock for a period of time that // exceeded the threshold with the screen off when not charging. int EXCESSIVE_WAKELOCK_ALL_SCREEN_OFF = 1; // The application exceeded the maximum number of wakeups while in the background // when not charging. int EXCESSIVE_WAKEUPS_IN_BACKGROUND = 2; // The application did unoptimized Bluetooth scans too frequently when not charging. int EXCESSIVE_UNOPTIMIZED_BLE_SCAN = 3; // The application ran in the background for a period of time that exceeded the // threshold. int EXCESSIVE_BACKGROUND_SERVICE = 4; // The application exceeded the maximum number of wifi scans when not charging. int EXCESSIVE_WIFI_SCAN = 5; // The application exceed the maximum number of flash writes int EXCESSIVE_FLASH_WRITES = 6; // The application used more than the maximum memory, while not spending any time // in the foreground. int EXCESSIVE_MEMORY_IN_BACKGROUND = 7; // The application exceeded the maximum percentage of frames with a render rate of // greater than 700ms. int EXCESSIVE_DAVEY_RATE = 8; // The application exceeded the maximum percentage of frames with a render rate // greater than 16ms. int EXCESSIVE_JANKY_FRAMES = 9; // The application exceeded the maximum cold start time - the app has not been // launched since last system start, died or was killed. int SLOW_COLD_START_TIME = 10; // The application exceeded the maximum hot start time - the app and activity are // already in memory. int SLOW_HOT_START_TIME = 11; // The application exceeded the maximum warm start time - the app was already in // memory but the activity wasn't created yet or was removed from memory. int SLOW_WARM_START_TIME = 12; // The application exceeded the maximum number of syncs while in the background. int EXCESSIVE_BACKGROUND_SYNCS = 13; // The application exceeded the maximum number of gps scans while in the background. int EXCESSIVE_GPS_SCANS_IN_BACKGROUND = 14; // The application scheduled more than the maximum number of jobs while not charging. int EXCESSIVE_JOB_SCHEDULING = 15; // The application exceeded the maximum amount of mobile network traffic while in // the background. int EXCESSIVE_MOBILE_NETWORK_IN_BACKGROUND = 16; // The application held the WiFi lock for more than the maximum amount of time while // not charging. int EXCESSIVE_WIFI_LOCK_TIME = 17; // The application scheduled a job that ran longer than the maximum amount of time. int JOB_TIMED_OUT = 18; // The application did an unoptimized Bluetooth scan that exceeded the maximum // time while in the background. int LONG_UNOPTIMIZED_BLE_SCAN = 19; // The application exceeded the maximum ANR rate while in the background. int BACKGROUND_ANR = 20; // The application exceeded the maximum crash rate while in the background. int BACKGROUND_CRASH_RATE = 21; // The application exceeded the maximum ANR-looping rate. int EXCESSIVE_ANR_LOOPING = 22; // The application exceeded the maximum ANR rate. int EXCESSIVE_ANRS = 23; // The application exceeded the maximum crash rate. int EXCESSIVE_CRASH_RATE = 24; // The application exceeded the maximum crash-looping rate. int EXCESSIVE_CRASH_LOOPING = 25; // The application crashed because no more file descriptors were available. int NUMBER_OF_OPEN_FILES = 26; }
Cuando el usuario o el sistema quita las restricciones de una app, debes registrar los motivos para quitarlas. Un ejemplo de fragmento de código de registro desde packages/apps/Settings/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java
:
public void handlePositiveAction(int metricsKey) { final AppInfo appInfo = mUnRestrictAppTip.getUnrestrictAppInfo(); // Clear force app standby, then app can run in the background mBatteryUtils.setForceAppStandby(appInfo.uid, appInfo.packageName, AppOpsManager.MODE_ALLOWED); mMetricsFeatureProvider.action(mContext, MetricsProto.MetricsEvent.ACTION_TIP_UNRESTRICT_APP, appInfo.packageName, Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, metricsKey)); }
Restricciones de la app de prueba
Para probar el comportamiento de las restricciones de apps en Android 9 y versiones posteriores, usa uno de los siguientes comandos:
- Cómo restringir una app:
appops set package-name RUN_ANY_IN_BACKGROUND ignore
- Quita una app de la restricción y restablece el comportamiento predeterminado:
appops set package-name RUN_ANY_IN_BACKGROUND allow
- Cómo hacer que una app en segundo plano pase a inactivo de inmediato:
am make-uid-idle [--user user-id | all | current] package-name
- Agrega un paquete a
tempwhitelist
por un período breve:cmd deviceidle tempwhitelist [-u user] [-d duration] [package package-name]
- Agrega o quita un paquete de la lista blanca de usuarios:
cmd deviceidle whitelist [+/-]package-name
- Verifica el estado interno de
jobscheduler
y el administrador de alarmas:dumpsys jobscheduler
dumpsys alarm
App Standby
App Standby extiende la duración de la batería aplazando la actividad de red en segundo plano y las tareas de las apps que el usuario no está usando de forma activa.
Ciclo de vida de App Standby
La plataforma detecta las apps inactivas y las coloca en modo en espera hasta que el usuario comienza a interactuar con ellas de forma activa.
Durante la fase de detección, la plataforma detecta que una app está inactiva cuando el dispositivo no se está cargando y el usuario no la inició de forma directa o indirecta durante una cantidad específica de tiempo de reloj y una cantidad específica de tiempo de pantalla activa. (Los inicios indirectos ocurren cuando una app en primer plano accede a un servicio en una segunda app).
Durante el modo inactivo de la app, la plataforma evita que las apps accedan a la red más de una vez al día, lo que aplaza las sincronizaciones de la app y otras tareas.
La plataforma sale del modo de espera de la app en los siguientes casos:
- La app se vuelve activa.
- El dispositivo está conectado y se está cargando.
Las apps activas no se ven afectadas por el modo de suspensión. Una app está activa cuando tiene lo siguiente:
- Un proceso que se encuentra actualmente en primer plano (ya sea como una actividad o un servicio en primer plano, o que usa otra actividad o servicio en primer plano), como un objeto de escucha de notificaciones, servicios de accesibilidad, fondo de pantalla en vivo, etcétera
- Una notificación que ve el usuario, como en la pantalla de bloqueo o en la bandeja de notificaciones
- El usuario la inició de forma explícita
Una app está inactiva si no se produjo ninguna de las actividades anteriores durante un período.
Cómo probar el modo de espera de la app
Puedes probar el modo de suspensión de la app de forma manual con los siguientes comandos de adb
:
adb shell dumpsys battery unplug
adb shell am set-idle package-name true
adb shell am set-idle package-name false
adb shell am get-idle package-name