Battery performance in Android M

Exploring the new Android M battery performance features.

batteries

It has been a long held personal belief that most battery drain issues on smartphone devices are due to applications that are improperly tuned. I work very closely with mobile developers to help optimize mobile apps for speed and battery life with AT&T’s own Application Resource Optimizer. I am also in the process of finishing up a book on High Performance Android Apps that will be published later this summer. So I am always excited to see mobile application performance hit the center stage.

Last month, Google held its annual Google I/O conference, where they announce new products, tools and features. This year, with the release of the Android M developer preview, performance of mobile devices/battery life and app performance were on the center stage (and unveiled at the keynote!). Lets look at the new features and tools available to users and developers to make Android’s battery life better.

  1. Doze: Announced during the I/O keynote, Doze is a major OS push to curb app activity when the device is asleep. In current devices (Lollipop and older), when the device is asleep, any app can wake up the device to update so that the information in the app remains fresh. The new Doze framework uses the accelerometer and activity state to find when the device is not in use, and can go into a deep sleep (Doze). In this mode, app updates will be suspended until the device wakes ups. This means that Network activity, wakelock, alarms and JobScheduler jobs are not run until the device wakes up.

In my experience, applications that frequently update in the background cause significant battery drain, and Google has discovered the same. In the keynote at I/O, the reported that the battery in Nexus 9 tablets running Doze lasted 2x longer in standby mode.

Since this feature only works on M preview devices that are not charging, you must update a device to M. Then – using ADB commands, stop the device from charging:

adb shell dumpsys battery unplug

The Doze framework has several states:
ACTIVE: Screen is on
INACTIVE: Screen is off, but device is awake
IDLE_PENDING: “nodding off” into Doze
IDLE: device is asleep
IDLE_MAINTENANCE: A short window for all queued alarms and updates to occur.

To jump in between these states manually, you can use:

adb shell dumpsys deviceidle step

How long does it take for the device to get in to this mode?

adb shell dumpsys deviceidle

This gives us lots of information about Doze. On my Nexus 6 (with the screen on), I get the following results:

Whitelist system apps:
com.android.providers.downloads
com.android.vending
com.google.android.gms
Whitelist user apps:
com.facebook.katana
Whitelist app uids:
UID=10012: true
UID=10016: true
UID=10026: true
UID=10100: true
mSigMotionSensor={Sensor name="Invensense Significant Motion Detector", vendor="Invensense Inc.", version=1, type=17, maxRange=1.0, resolution=1.0, power=0.3, minDelay=-1}
mCurDisplay=Display id 0: DisplayInfo{"Built-in Screen", uniqueId "local:0", app 1440 x 2392, real 1440 x 2560, largest app 2413 x 2308, smallest app 1440 x 1356, mode 1, defaultMode 1, modes [{id=1, width=1440, height=2560, fps=60.0}], rotation 0, density 560 (494.27 x 492.606) dpi, layerStack 0, appVsyncOff 7500000, presDeadline 12666667, type BUILT_IN, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}, DisplayMetrics{density=3.5, width=1440, height=2392, scaledDensity=3.5, xdpi=494.27, ydpi=492.606}, isValid=true
mIdleDisabled=false
mScreenOn=true
mCharging=false
mSigMotionActive=false
mState=ACTIVE
mInactiveTimeout=+30m0s0ms 

The first section reports the applications that are whitelisted from Doze – these apps are not restricted in their alarms, etc. Note that I have manually whitelisted Facebook from Doze (I did this in the settings menu – > Battery – Facebook, and turned “Ignore Optimizations” to On.) Next we see sensor and screen information. These will be used to pull the device out of Doze. The screen is currently on, and thus is considered ACTIVE. The Inactive Timeout is 30 minutes. When I turn off the screen, this output changes:

mState=INACTIVE
mInactiveTimeout=+30m0s0ms
mNextAlarmTime=+28m26s193ms

With the screen off, the device is considered INACTIVE, and getting ready to go into its deep sleep. The Timeout is still 30 minutes, and the timer shows we are about a minute and a half into the process. Stepping manually to IDLE_PENDING:

mState=IDLE_PENDING
mInactiveTimeout=+30m0s0ms
mNextAlarmTime=+24m34s189ms
mNextIdlePendingDelay=+5m0s0ms
mNextIdleDelay=+60m0s0ms

After 30 minutes in IDLE_PENDING, the device will go into IDLE – so a total of 60 minutes to reach Doze mode. Entering Doze mode:

mState=IDLE
mInactiveTimeout=+30m0s0ms
mNextAlarmTime=+59m42s953ms
mNextIdlePendingDelay=+5m0s0ms
mNextIdleDelay=+2h0m0s0ms

The next alarm is in 60 minutes, and the device will wakeup and allow all the queued messages and alarms fire. The mNextIdleDelay shows the next wakeup in Doze mode will be 2 hours. Cycling the Doze states shows the wakeups follow a fallback schedule: 1 hour, 2 hours, 4 hours and 6 hours. The longest update wait will be 6 hours – telling us that all applications will update every 6 hours (4x per day) even when in a long term Doze.

So how does your application behave when the device wakes up from its “Doze”?

You can test your app using:

adb shell am set-idle <packageName> true

to place your device in an artificial sleep. You can wakeup with the same command (but using false) to see your apps processes awake and fire off all the requests that might be pending.

Doze is a great feature to slow battery drain when your phone or tablet is off.

  1. App standby: In the current Android system, any application has access to the radio and other features of the phone, even in the background. This means that applications downloaded (and long since forgotten on the phone) could be accessing the radio and transmitting data several times a day. As you might imagine Doze will help prevent these apps from running, but they will still run processes and update when your screen is on. The new App standby feature will determine apps that have not run in the foreground “recently” (over a period of days), and if they are not used, place them on app standby. This will limit the ability of these applications to access the internet or run any processes until the phone is plugged in. By limiting access to these apps when running on battery, power will undoubtedly be saved, allowing for longer runtime on battery.

How does App Standby work? Well, by typing: adb shell dumpsys usagestats

We get a great deal of information about app usage over the last day/week/month and year. Of course, the information on my phone is from 5/29 (the day I upgraded) to 6/1 (as I write this), so all of the tables after one day are identical:

user=0
In-memory daily stats
timeRange="6/1/2015, 5:00 - 11:16 PM"
   packages
     package=com.google.android.googlequicksearchbox totalTime="00:36" lastTime="6/1/2015, 10:56 PM" inactiveTime="01:05"
     package=com.android.providers.calendar totalTime="00:00" lastTime="6/1/2015, 10:20 PM" inactiveTime="02:26"
     package=com.android.providers.media totalTime="00:00" lastTime="6/1/2015, 10:20 PM" inactiveTime="02:20"
     package=com.android.providers.downloads totalTime="00:00" lastTime="6/1/2015, 10:20 PM" inactiveTime="02:20"
     package=com.android.defcontainer totalTime="00:00" lastTime="6/1/2015, 11:10 PM" inactiveTime="00:00"
     package=android totalTime="00:00" lastTime="6/1/2015, 10:32 PM" inactiveTime="01:44"
     package=com.urbandroid.inline totalTime="00:00" lastTime="6/1/2015, 11:14 PM" inactiveTime="00:00"
     package=com.google.android.gm totalTime="00:00" lastTime="6/1/2015, 9:01 PM" inactiveTime="06:36"
<snip>
In-memory weekly stats
timeRange="5/29/2015, 2:33 PM - 6/1/2015, 11:16 PM"
   packages
     package=com.amazon.mShop.android.shopping totalTime="01:04" lastTime="5/29/2015, 3:24 PM" inactiveTime="3:55:51"
     package=com.google.android.youtube totalTime="55:51" lastTime="5/31/2015, 2:04 AM" inactiveTime="1:09:00"
     package=com.android.providers.telephony totalTime="00:00" lastTime="5/29/2015, 2:56 PM" inactiveTime="5:37:10"
     package=com.android.sdm.plugins.connmo totalTime="00:00" lastTime="5/29/2015, 3:13 PM" inactiveTime="5:46:30"
     package=com.google.android.googlequicksearchbox totalTime="33:12" lastTime="6/1/2015, 10:56 PM" inactiveTime="01:05"
     package=com.android.providers.calendar totalTime="00:00" lastTime="6/1/2015, 10:20 PM" inactiveTime="02:26"
     package=com.android.providers.media totalTime="00:00" lastTime="6/1/2015, 10:20 PM" inactiveTime="02:20"
     package=com.google.android.apps.docs.editors.docs totalTime="00:00" lastTime="5/29/2015, 3:13 PM" inactiveTime="5:46:30"
     package=com.google.android.onetimeinitializer totalTime="00:00" lastTime="5/29/2015, 3:13 PM" inactiveTime="5:46:30"

There is an algorithm in the OS to determine if the last time used and inactive timers warrant turning the app to an inactive state. If you are curious what apps are in the inactive state, a new Developer Options menu called “inactive apps” will tell you the current state. It also allows you top toggle your apps state for testing.

android_m_1

  1. Application performance. Have you found that your device was losing battery faster than normal — but you were not sure what was causing the battery drain? In settings — Battery, each application has much more detail on the amount of battery drain and what the app is doing to contribute to battery drain:

android_m_2 android_m_3

CPU, (and CPU in the background) are major drain of battery. As are the number of connections and how long the mobile radio is active. In the examples of the Google and Facebook apps, we can see that the mobile radio was on for 17-22 minutes over a period of about 28 hours — battery stats are recorded from that last 100% battery charge (~ 10 AM, and this was taken at ~2:30 PM the next day (as seen in the chart at the top).

There is also a menu that allows you to “ignore optimizations” This will turn off Doze and app standby for your mobile app. Using this you could test your application’s performance head to head with and without the optimizations to see how much battery is saved (and how stale the content in your app becomes.)

  1. Last (but certainly not least!) is the GCM Network Manager. This is a cool improvement because it does not require the M preview – it will run on any device back to 2.3!

    The Lollipop release last year featured the JobScheduler API. For devices on Android 5.0 and higher, the JobScheduler API abstracts all device alarms and wakelocks away from the app to the OS. This allows the OS to aggregate these alarms and wakeups with other apps, thus reducing the number of wakeups and alarms — and saving the battery. This is great, but again, will only work on Lollipop and above (which is currently ~12.4% of devices.) To get the same performance in your network connections, Google Play Services announced that version 7.5 will add a GCM Network Manager. GCMNetworkManager uses a similar framework to JobScheduler (and JobScheduler is the underlying framework in 5.0+) but also works on any device with Google Play Services (supported back to Android 2,3 and covering >99% of Android devices!) It has similar behaviors and states to JobScheduler, and so allows you limit connections to when the phone is plugged in, or only when the connection is Wi-Fi for large updates.

Google has shown its commitment to application performance, and these tools will go a long way to help developers build better apps, but also gives the device a lot more control to limit poor behavior of applications to save battery.


Editor’s note: learn how to squeeze every bit of performance out of your Android app with High Performance Android Apps by Doug Sillar.

This post is part of our ongoing exploration into end-to-end optimization. It originally appeared on the AT&T Developer Program blog.

Public domain battery image via Pixabay.

tags: , , , , ,