Управление питанием приложения

В Android 9 и более поздних версиях платформа может отслеживать поведение приложений, которое отрицательно влияет на время автономной работы устройств. Платформа использует и оценивает правила настройки, чтобы обеспечить поток UX, который дает пользователям возможность ограничивать приложения, нарушающие правила.

В Android 8.0 и более ранних версиях существовали ограничения, связанные с такими функциями, как Doze, режим ожидания приложения, фоновые ограничения и ограничения местоположения в фоновом режиме. Однако некоторые приложения продолжали демонстрировать плохое поведение, некоторые из которых описаны в Android Vitals . В Android 9 появилась инфраструктура ОС, которая может обнаруживать и ограничивать приложения на основе правил настройки, которые могут обновляться с течением времени.

Фоновые ограничения

Пользователи могут ограничивать приложения, или система может предлагать приложения, которые, по ее мнению, негативно влияют на работоспособность устройства.

Ограниченные приложения:

  • Пользователь все еще может запустить его.
  • Невозможно запускать задания/сигналы тревоги или использовать сеть в фоновом режиме.
  • Не могу запустить службы переднего плана.
  • Пользователь может изменить приложение на неограниченное.

Разработчики устройств могут добавлять к приложениям дополнительные ограничения, чтобы:

  • Запретить самозапуску приложения.
  • Ограничить привязку сервисов (очень рискованно).

Приложения с ограниченным доступом в фоновом режиме не должны потреблять какие-либо ресурсы устройства, такие как память, процессор и аккумулятор. Приложения с фоновым ограничением не должны влиять на работоспособность устройства, если пользователь не использует эти приложения активно. Однако ожидается, что те же приложения будут полностью функциональными, когда пользователь их запускает.

Используйте собственные реализации

Разработчики устройств могут продолжать использовать свои собственные методы для применения ограничений к приложениям.

Интегрируйте ограничения приложений

В следующих разделах описано, как определить и интегрировать ограничения приложений на вашем устройстве. Если вы используете методы ограничения приложений из Android 8.x или более ранней версии, внимательно просмотрите следующие разделы на предмет изменений в Android 9 и более поздних версиях.

Установите флаг AppOpsManager

Если приложение ограничено, установите соответствующий флаг в AppOpsManager . Пример фрагмента кода из 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);
    }

Убедитесь, что isBackgroundRestricted возвращает true

Если приложение ограничено, убедитесь, что ActivityManager.isBackgroundRestricted() возвращает true .

Запишите причину ограничения

Если приложение ограничено, запишите причины ограничения. Пример фрагмента кода для ведения журнала из 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));
  }

Замените type значением из AnomalyType .

Разработчики устройств могут использовать константы, определенные в 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;
    }

Когда пользователь или система снимает ограничения приложения, вы должны зарегистрировать причины снятия ограничений. Пример фрагмента кода для ведения журнала из 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));
    }

Ограничения тестового приложения

Чтобы проверить поведение ограничений приложений в Android 9 и более поздних версиях, используйте одну из следующих команд:

  • Поставьте приложение на ограничение:
    appops set package-name RUN_ANY_IN_BACKGROUND ignore
  • Снимите ограничения для приложения и восстановите поведение по умолчанию:
    appops set package-name RUN_ANY_IN_BACKGROUND allow
  • Заставьте приложение в фоновом режиме немедленно перейти в режим ожидания:
    am make-uid-idle [--user user-id | all | current] package-name
  • Добавьте пакет в tempwhitelist на короткое время:
    cmd deviceidle tempwhitelist [-u user] [-d duration] [package package-name]
  • Добавить/удалить пакет из белого списка пользователей:
    cmd deviceidle whitelist [+/-]package-name
  • Проверьте внутреннее состояние jobscheduler и менеджера тревог:
    dumpsys jobscheduler
    dumpsys alarm

Приложение в режиме ожидания

Режим ожидания приложений продлевает срок службы батареи, откладывая фоновую сетевую активность и задания для приложений, которые пользователь не использует активно.

Жизненный цикл ожидания приложения

Платформа обнаруживает неактивные приложения и переводит их в режим ожидания до тех пор, пока пользователь не начнет активно с ними работать.

На этапе обнаружения платформа обнаруживает, что приложение неактивно, когда устройство не заряжается , и пользователь не запускал приложение прямо или косвенно в течение определенного времени, а также определенного времени включения экрана. . (Непрямые запуски происходят, когда приложение переднего плана обращается к службе во втором приложении.)

В режиме ожидания приложения платформа запрещает приложениям доступ к сети более одного раза в день, откладывая синхронизацию приложений и другие задания.

Платформа выходит из режима ожидания, когда:

  • Приложение станет активным.
  • Устройство подключено и заряжается.

Активные приложения не зависят от режима ожидания. Приложение активно , если оно имеет:

  • Процесс, который в данный момент находится на переднем плане (либо как действие, либо служба переднего плана, либо используется другим действием или службой переднего плана), например прослушиватель уведомлений, службы специальных возможностей, живые обои и т. д.
  • Уведомление, просматриваемое пользователем, например на экране блокировки или в области уведомлений.
  • Был явно запущен пользователем

Приложение неактивно , если в течение определенного периода времени не происходило ни одного из вышеперечисленных действий.

Тестирование приложения в режиме ожидания

Вы можете вручную протестировать режим ожидания приложения, используя следующие команды 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