blob: 7d4e8711199c187f7a5205494243b7f2091cf39a [file] [log] [blame]
<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>