應用程式電源管理

在 Android 9 以上版本中,這個平台可監控應用程式的行為, 會對裝置的電池續航力造成負面影響平台會使用和 評估設定規則,提供使用者體驗流程,讓使用者選擇 限制違規應用程式

在 Android 8.0 以下版本中,我們發現某些功能會受到限制 例如打盹、應用程式待命、背景限制和背景位置資訊等功能 不過,有些應用程式還是持續出現不良行為, 請參閱 Android Vitals。 Android 9 導入了可偵測及限制的 OS 基礎架構 並定義適當的設定規則,且這些規則可以隨著時間更新。

背景限制

使用者可以限制應用程式,系統也可能會建議該應用程式 會對裝置的健康狀態有負面影響。

受限制的應用程式:

  • 仍可由使用者啟動。
  • 無法執行工作/鬧鐘,也無法在背景使用網路。
  • 無法執行前景服務。
  • 使用者可以變更為不受限制的應用程式。

裝置實作者可以為應用程式新增額外限制,以達成下列目的:

  • 限制應用程式不會自行重新啟動。
  • 限制服務無法繫結 (高風險)。

在背景中受限制的應用程式不會耗用任何裝置資源,例如 包括記憶體、CPU 和電池在下列情況中,背景受限制的應用程式不應影響裝置健康狀態 使用者未主動使用這些應用程式不過,這個應用程式 正常運作。

使用自訂導入方式

裝置實作者可以繼續使用自訂方法,為應用程式套用限制。

整合應用程式限制

以下各節將說明如何定義及整合應用程式 你的裝置受到限制如果您使用應用程式限制方法 (適用於 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