| <html devsite> |
| <head> |
| <title>Application Power Management</title> |
| <meta name="project_path" value="/_project.yaml" /> |
| <meta name="book_path" value="/_book.yaml" /> |
| </head> |
| <body> |
| <!-- |
| Copyright 2018 The Android Open Source Project |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <p> |
| In Android 9 and later, the platform can monitor apps for behavior that |
| negatively affects the battery life of devices. The platform uses and |
| evaluates setup rules to provide a UX flow that gives users the option to |
| restrict apps that violate the rules. |
| </p> |
| |
| <p> |
| In Android 8.0 and earlier, there were restrictions via features |
| such as Doze, App Standby, background limits, and background location |
| limits. However, some apps continued to exhibit bad behaviors, some of |
| which are described in <a |
| href="https://developer.android.com/topic/performance/vitals/" class="external">Android vitals</a>. |
| Android 9 introduces an OS infrastructure that can detect and restrict |
| apps based on setup rules that can be updated over time. |
| </p> |
| |
| <h2 id="app-restrictions">Background Restrictions</h2> |
| |
| <p> |
| Users can choose to restrict apps, or the system may suggest apps that it |
| detects are negatively impacting the health of the device. |
| </p> |
| |
| <p> |
| Restricted apps: |
| </p> |
| |
| <ul> |
| <li>Can still be launched by the user.</li> |
| <li>Cannot run jobs/alarms or use network in the background.</li> |
| <li>Cannot run foreground services.</li> |
| <li>Can be changed to an unrestricted app by the user.</li> |
| </ul> |
| |
| <p> |
| Device implementers can add additional restrictions to apps to: |
| </p> |
| |
| <ul> |
| <li>Restrict the app from self restarts.</li> |
| <li>Restrict services from being bound (highly risky).</li> |
| </ul> |
| |
| <p> |
| Restricted apps are not expected to consume any device resources, such as |
| memory, CPU, and battery, when they are in the background. Background |
| restricted apps should not impact the device health when the user is not |
| actively using those apps. However, the same apps are expected to be |
| fully functional when the user launches the apps. |
| </p> |
| |
| |
| <h3 id="using-customg-restrictions">Using custom implementations</h3> |
| |
| <p> |
| Device implementers can continue to use their custom methods to apply |
| restrictions on the apps. |
| </p> |
| |
| <aside class="caution"><strong>Caution:</strong> Future releases may break |
| device implementers' customizations. We recommend adopting the Android |
| 9 App Restrictions architecture in AOSP. |
| </aside> |
| |
| <h3 id="integrating-app-restrictions">Integrating App Restrictions</h3> |
| |
| <p> |
| The following sections outline how to define and integrate app |
| restrictions on your device. If you are using app restriction methods |
| from Android 8.x or earlier, review the following sections closely for |
| changes in Android 9. |
| </p> |
| |
| <h4 id="set-appopsmanager-flag">Set the AppOpsManager flag</h4> |
| |
| <p> |
| When an app is restricted, set the appropriate flag in |
| <code>AppOpsManager</code>. An example code snippet from |
| <code>packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryUtils.java</code>: |
| </p> |
| |
| <pre class="prettyprint"> 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); |
| } |
| </pre> |
| |
| <h4 id="ensure-isbackgroundrestricted-returns-true"> |
| Ensure <code>isBackgroundRestricted</code> returns <code>true</code> |
| </h4> |
| |
| <p> |
| When an app is restricted, ensure that |
| <code>ActivityManager.isBackgroundRestricted()</code> returns |
| <code>true</code>. |
| </p> |
| |
| <h4>Log the reason for restriction</h4> |
| |
| <p> |
| When an app is restricted, log the reasons for the restriction. An |
| example code snippet of logging from |
| <code>packages/apps/Settings/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java</code>: |
| </p> |
| |
| |
| |
| <pre class="prettyprint">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)); |
| } |
| </pre> |
| |
| <p> |
| The <code>type</code> should be replaced with the value from |
| <code>AnomalyType</code>. |
| </p> |
| |
| <p> |
| Device implementers can use the constants defined in |
| <code>src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java</code>: |
| </p> |
| |
| <pre class="prettyprint">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; |
| } |
| </pre> |
| |
| <p> |
| When the user or the system removes an app's restrictions, you must |
| log the reasons for removing the restrictions. An example code |
| snippet of logging from |
| <code>packages/apps/Settings/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java</code>: |
| </p> |
| |
| <pre class="prettyprint"> 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)); |
| } |
| </pre> |
| |
| <h3 id="testing-app-restrictions">Testing App Restrictions</h3> |
| |
| <p> |
| To test the behavior of App Restrictions in Android 9.x and later, |
| use one of the following commands: |
| </p> |
| |
| <ul> |
| <li>To put an app into App Restriction: |
| <pre class="devsite-terminal devsite-click-to-copy">appops set <var>package-name</var> RUN_ANY_IN_BACKGROUND ignore</pre> |
| </li> |
| <li>To take it out and restore the default behaviour: |
| <pre class="devsite-terminal devsite-click-to-copy">appops set <var>package-name</var> RUN_ANY_IN_BACKGROUND allow</pre> |
| </li> |
| <li>Make an app in the background go idle immediately: |
| <pre class="devsite-terminal devsite-click-to-copy">am make-uid-idle [--user <var>user-id</var> | all | current] <var>package-name</var></pre> |
| </li> |
| <li>Add a package to the <code>tempwhitelist</code> for a short duration: |
| <pre class="devsite-terminal devsite-click-to-copy">cmd deviceidle tempwhitelist [-u <var>user</var>] [-d <var>duration</var>] [package <var>package-name</var>]</pre> |
| </li> |
| <li>Add/Remove a package from the user whitelist: |
| <pre class="devsite-terminal devsite-click-to-copy">cmd deviceidle whitelist [+/-]<var>package-name</var></pre> |
| </li> |
| <li>To check internal state of jobscheduler and alarm manager: |
| <pre class="devsite-click-to-copy"><code class="devsite-terminal">dumpsys jobscheduler</code> |
| <code class="devsite-terminal">dumpsys alarm</code></pre> |
| </li> |
| </ul> |
| |
| |
| <h2 id="app-standby">App Standby</h2> |
| |
| <p> |
| App Standby extends battery life by deferring background network |
| activity and jobs for applications the user is not actively using. |
| </p> |
| |
| <h3 id="app-standby-life">App Standby lifecycle</h3> |
| |
| <p> |
| The platform detects inactive applications and places them in App |
| Standby until the user begins actively engaging with the application. |
| </p> |
| |
| <table> |
| <tbody> |
| <tr> |
| <th width=46%>Detection</th> |
| <th width=23%>During App Standby</th> |
| <th width=31%>Exit</th> |
| </tr> |
| <tr> |
| <td> |
| <p> |
| The platform detects an application is inactive when the device is not |
| charging <strong>and</strong> the user has not launched the application |
| directly or indirectly for a specific amount of clock time as well as a |
| specific amount of screen-on time. (Indirect launches occur when a |
| foreground app accesses a service in a second app.) |
| </p> |
| </td> |
| <td> |
| <p> |
| The platform prevents applications from accessing the network more than |
| once a day, deferring application syncs and other jobs. |
| </p> |
| </td> |
| <td> |
| <p>The platform exits the app from App Standby when:</p> |
| <ul> |
| <li>Application becomes active.</li> |
| <li>Device is plugged in and charging.</li> |
| </ul> |
| </td> |
| </tr> |
| </tbody> |
| </table> |
| |
| <p>Active applications are unaffected by App Standby. An application is active |
| when it has:</p> |
| <ul> |
| <li>A process currently in the foreground (either as an activity or |
| foreground service, or in use by another activity or foreground service), |
| such as notification listener, accessibility services, live wallpaper, etc. |
| </li> |
| <li>A notification viewed by the user, such as in the lock screen or |
| notification tray. |
| </li> |
| <li>Explicitly been launched by the user.</li> |
| </ul> |
| |
| <p>An application is inactive if none of the above activities has occurred for |
| a period of time. |
| </p> |
| |
| <h3 id=testing_app_standby>Testing App Standby</h3> |
| <p>You can manually test App Standby using the following <code>adb</code> |
| commands:</p> |
| |
| <pre class="devsite-click-to-copy"> |
| <code class="devsite-terminal">adb shell dumpsys battery unplug</code> |
| <code class="devsite-terminal">adb shell am set-idle <var>package-name</var> true</code> |
| <code class="devsite-terminal">adb shell am set-idle <var>package-name</var> false</code> |
| <code class="devsite-terminal">adb shell am get-idle <var>package-name</var></code> |
| </pre> |
| |
| </body> |
| </html> |