Wakelocks: Detect No-Sleep Issues in Android* Applications

Abstract

Android* applications may increase battery consumption significantly if they use wakelocks improperly. In this document, we will give some hints and tips on how to identify No Sleep bugs related to wakelock misuse.

1. Introduction
2. Wakelock
2.1. What is it?
2.2. Android User Wakelocks
2.3. Android Kernel Wakelocks
2.4. No-Sleep bugs
3. Identifying No Sleep ugs
3.1. Using adb
3.2. Using BetterBatteryStats application
4. Test case
5. Conclusion
6. Reference

1. Introduction

Limiting battery power consumption is essential for smartphones. To get the best autonomy possible, Android OS is designed to opportunistically go into sleep mode as soon as it detects no user activity on the system. Some applications require the device to stay on even if there is no user interaction for a long time. Examples of this are watching a video, listening to music, using GPS, and playing a game. Android provides such a mechanism for the OS or applications in order to keep the device awake. This mechanism is called a wakelock. For more details, please read Christopher Bird’s article: “Wakelocks for Android”.

The introduction of such a mechanism puts the responsibility of managing the component’s activities on the application developer. If used incorrectly, the application can drain the battery significantly even when the application is not running in the foreground.

2. Wakelock

2.1. What is it?

A wakelock is a software mechanism to control the power state of the host device. The OS exports explicit power management handles and APIs to specify when a particular component needs to stay on, or awake, until it is explicitly released from duty.

The wakelock mechanism is implemented in two levels: user and kernel. The figure shows the internal design of Android wakelock implementation. The user wakelock can be utilized by either high-level OS services or applications and is provided by the power management service. It allows an application to control the power state of the device. A kernel wakelock is held by the OS kernel or a driver. A user wakelock is mapped to a kernel wakelock. Any active kernel-level wakelock prevents the system from being suspended to ACPI S3 (suspended to RAM) state, which is the highest power saving state for mobile devices.

2.2. Android User Wakelocks

The Android framework exports the wakelock mechanism through the PowerManager.Wakelock class and identifies four types of user wakelocks:

Flag Value CPU Screen Keyboard

PARTIAL_WAKE_LOCK

On

Off

Off

SCREEN_DIM_WAKE_LOCK

On

Dim

Off

SCREEN_BRIGHT_WAKE_LOCK

On

Bright

Off

FULL_WAKE_LOCK

On

Bright

Bright

Note that since API level 17, FULL_WAKE_LOCK has been deprecated. Applications should use FLAG_KEEP_SCREEN_ON.

Wakelocks can be acquired to force some of the components (CPU, screen, and keyboard) to stay awake.

Use special caution with the PARTIAL_WAKE_LOCK as the CPU will continue to run regardless of any display timeouts or the state of the screen and even if the user presses the power button. This may lead to silent power drain as the phone looks like it is in standby mode (screen is off), but is actually fully awake.

In all other wakelocks, the user can still put the device to sleep using the power button. All wakelocks, except partial wakelocks, will be implicitly released after the power button is pressed.

This is how applications can hold a wakelock. It is basically an Acquire/Release mechanism. The application acquires a wakelock when it needs some components to stay on. When those are not needed anymore, the wakelock needs to be released.

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
wl.acquire();
..screen will stay on during this section..
wl.release();

2.3. Android Kernel Wakelocks

Kernel wakelocks are low-level wakelocks held by the kernel. They can be acquired/ released internally from the kernel. As such, application developers have less direct control on them, but an application’s behaviour can indirectly trigger these wakelocks and increase battery consumption inadvertently.

Here are examples of kernel wakelocks.

Wlan_rx: Held by the kernel when data is sent or received over Wi-Fi*.

PowerManagerService: Is the container for all partial wakelocks.

Sync: Held while the sync process is running.

Alarm_rtc: Handles the alarm (when an application or process checks periodically on something).

Main: Keeps the kernel awake. This is the last one to be released when the system goes to suspend mode.

2.4. No-Sleep bugs

The application has to release every wakelock it acquires at some point to allow the system to go back to a deep sleep mode. This acquire/release mechanism may lead to some bugs if the wakelock is never released. A wakelock remains held even if the application holding it is not running in the foreground anymore. A wakelock is released when it is explicitly released by a release call or if the application is actually killed (force close). Leaving some wakelocks held prevents the system from going into deep sleep mode even if there is no activity, significantly increasing the power drain and silently reducing the battery autonomy. This is called a No-Sleep bug. Due to the event-driven nature of Android, developers may not think of all the code paths that their applications had acquired wakelocks and, therefore, need to close. This type of bug is called a No-Sleep bug code path.

Another occurrence of this type of bug is when the release happens before the wakelock has been effectively acquired. This can happen in multithreaded code where the acquisition and release of the wakelock happen in different threads. This is called a No-Sleep bug race condition.

The last issue is the No-sleep dilation, where the wakelock is acquired longer than what is actually necessary.

Why are these issues important to look at? According to a study made by P. Vekris: “55% of 328 applications using wakelocks do not follow our policies for no-sleep bugs” [2012]. Some major applications have been released with No-Sleep bugs. So developers need to be aware of this specific point in order for their applications to run optimally.

3. Identifying No Sleep bugs

The no sleep bug can be tackled in two ways: either a static analysis doing a code path scanning or dynamically at run time. In this paper we will focus on run-time analysis.

This method does not guarantee you’ll find all the wakelocks bugs in an application. It will however help you spot some wakelock issues if they occur during run time. To identify a wakelock issue, you need to follow a code path where the wakelock is not released. Testing an application against no sleep bugs involves manipulating the application, trying different ways to exit it, at different places in the application to see if a wakelock persists.

In some cases, it may be necessary to prevent the system from going into deep sleep even if the application is not in the foreground anymore. An application may need to perform a task in the background. This is the case, for example, if the application has to perform a long download: a video or game dataset. The application may be put in the background by the user while it’s downloading, but the phone should stay awake until the download is completed. In that case, the application may hold a wakelock until the download is completed. Of course, you should check that the wakelock is released at some point. For example, if the phone loses network connection in the middle of a download, the phone should not remain awake if the action cannot be performed anymore.

In summary, identifying a No Sleep bug is very context dependent. There is no predefined criterion that allows someone to easily identify this issue. Only common sense can assess these types of bugs.

3.1. Using adb

The simplest tools to look at wakelocks are shell commands.

To get a full picture of the kernel wakelocks, type:

adb shell cat /proc/wakelocks

name count expire_count wake_count active_since total_time
"PowerManagerService" 1502 0 0 0 337817677431
"main" 15 0 0 0 984265842688
"alarm" 1512 0 792 0 217778251643
"radio-interface" 16 0 0 0 16676538930
"alarm_rtc" 804 4 0 0 1204136324759
"gps-lock" 1 0 0 0 10753659786
name sleep_time max_time last_change
"PowerManagerService" 95729409122 140921663667 9723417252748
"main" 0 212424732355 9498127170228
"alarm" 217617362047 357976941 9723371461242
"radio-interface" 0 1659328496 9486387144974
"alarm_rtc" 1200253446201 66082936501 9483176054624
"gps-lock" 0 10753659786 37632803440

For images using kernel 3.4 or higher, use “adb shell cat /sys/kernel/debug/wakeup_sources.” Even though it gives all the information, this format is not very user friendly. The tools we are introducing below are much handier.

A simple way to check a specific application is by using “adb shell dumpsys power.” Below is a typical output of this command. You can see in red the user wakelocks present at the time the command was issued. This command gives a snapshot of the user wakelocks present in the system.

Power Manager State:
mIsPowered=true mPowerState=3 mScreenOffTime=1618305 ms
mPartialCount=3
mWakeLockState=SCREEN_ON_BIT
mUserState=SCREEN_BRIGHT_BIT SCREEN_ON_BIT
mPowerState=SCREEN_BRIGHT_BIT SCREEN_ON_BIT
mLocks.gather=SCREEN_ON_BIT
mNextTimeout=2382037 now=2378097 3s from now
mDimScreen=true mStayOnConditions=3 mPreparingForScreenOn=false mSkippedScreenOn=false
mScreenOffReason=0 mUserState=3
mBroadcastQueue={-1,-1,-1}
mBroadcastWhy={0,0,0}
mPokey=0 mPokeAwakeonSet=false
mKeyboardVisible=false mUserActivityAllowed=true
mKeylightDelay=6000 mDimDelay=587000 mScreenOffDelay=7000
mPreventScreenOn=false mScreenBrightnessOverride=-1 mButtonBrightnessOverride=-1
mScreenOffTimeoutSetting=600000 mMaximumScreenOffTimeout=2147483647
mLastScreenOnTime=27380
mBroadcastWakeLock=UnsynchronizedWakeLock(mFlags=0x1 mCount=0 mHeld=false)
mStayOnWhilePluggedInScreenDimLock=UnsynchronizedWakeLock(mFlags=0x6 mCount=0 mHeld=true)
mStayOnWhilePluggedInPartialLock=UnsynchronizedWakeLock(mFlags=0x1 mCount=0 mHeld=true)
mPreventScreenOnPartialLock=UnsynchronizedWakeLock(mFlags=0x1 mCount=0 mHeld=false)
mProximityPartialLock=UnsynchronizedWakeLock(mFlags=0x1 mCount=0 mHeld=false)
mProximityWakeLockCount=0
mProximitySensorEnabled=false
mProximitySensorActive=false
mProximityPendingValue=-1
mLastProximityEventTime=0
mLightSensorEnabled=true mLightSensorAdjustSetting=0.0
mLightSensorValue=11.0 mLightSensorPendingValue=10.0
mHighestLightSensorValue=47 mWaitingForFirstLightSensor=false
mLightSensorPendingDecrease=false mLightSensorPendingIncrease=false
mLightSensorScreenBrightness=42 mLightSensorButtonBrightness=0 mLightSensorKeyboardBrightness=0
mUseSoftwareAutoBrightness=true
mAutoBrightessEnabled=true
creenBrightnessAnimator:
animating: start:42, end:42, duration:480, current:42
startSensorValue:47 endSensorValue:11
startTimeMillis:2361638 now:2378092
currentMask:SCREEN_BRIGHT_BIT
mLocks.size=4:
SCREEN_DIM_WAKE_LOCK          'StayOnWhilePluggedIn_Screen_Dim' activated (minState=1, uid=1000, pid=388)
PARTIAL_WAKE_LOCK             'StayOnWhilePluggedIn_Partial' activated (minState=0, uid=1000, pid=388)
PARTIAL_WAKE_LOCK             'HDA_PARTIAL_WAKE_LOCK' activated (minState=0, uid=10046, pid=4690)
PARTIAL_WAKE_LOCK             'AudioOut_2' activated (minState=0, uid=1013, pid=157)
mPokeLocks.size=0:

To identify if any wakelocks still held after an application has been put in background, you can follow this process:

1. Connect the device to USB.
2. Launch the application and play with it.
3. Press the power button to go to sleep mode or exit the application in some way.
4. Wait for about 20 sec.
5. Type the following instruction in command line:
> adb shell dumpsys power
6. Check if there are any PARTIAL_WAKE_LOCKs like this one for example:
PARTIAL_WAKE_LOCK       ‘AudioOut_2’ activated(minState=0, uid=1013, pid=157)
7.Repeat step 5 every 15 sec 3 to 5 times. If the result stays the same, there may be an issue.

3.2. Using BetterBatteryStats application

BetterBatteryStats* (https://play.google.com/store/apps/details?id=com.asksven.betterbatterystats&hl=en) is an Android app by Sven Knispel that you can buy on Google Play. It collects information that allows you to spot battery drain, especially wakelock issues.

First, by selecting the ‘Other’ entry, you can check the deep sleep and awake modes versus the total time. Ideally, most of the time, the phone should be in Deep Sleep if it is not in use.

You can also compare the Awake time vs. the Screen On time to see when there is actual activity. Under normal conditions, the Screen On time and the Awake time should be close.

You can check the battery charge evolution and the awake, Screen On, and Wi-Fi* states over time.

Then you can check the kernel wakelocks. You can check the time spent within each kernel wakelock type as well as the count. High time or high counts may be symptomatic of a problem. From this report you cannot link a hotspot to a specific application or process but you can spot a specific behaviour that may be triggered by a specific application.

In the Kernel Wakelock report, the line “PowerManagerService” aggregates the time spent in user wakelocks. If this line shows a hot spot, you can drill down by checking the Partial Wakelock report.

Most of the time, the partial wakelock refers to the application that holds it. It’s a great help to find the culprit. Though some times, some activities may be launched by other apps. For example, a game may play sound through the AudioOut channel assigned to the Android Media gallery. If not coded properly, it would be the game’s fault for not closing the sound channel. It is never the Android Gallery’s fault.

The AlarmManager may signal that wake ups were caused by alarms or that an application has made numerous alarm modifications. You may want to check the Alarm section but it will only work if you have a rooted image.

Network entry is also accessible only on rooted images. It can be useful if you suspect some high traffic on the network. The multipdp/svnet-dormancy kernel wakelock may indicate that you also have some high network usage.

4. Test case

Let’s consider a real case using a game. Start it and play for about five minutes, and then exit the game in an unusual way. In our case we forced the exit by pressing the Home button. The music stopped, and the home screen displayed. From a user standpoint, everything looks fine. After a few minutes of inactivity the screen goes dark as expected. Let’s leave the phone in this state for about half an hour and then check it with BestBatteryStats. On the “Other” screen, you can see that the phone remained awake despite the fact the screen was not on. You can also see that the battery depletion rate is at 8.5%/hr. So in this state, the phone with a full battery will last no more than 12 hrs.

Now let’s look at the Kernel wakelocks. We can see that two kernel wakelocks remain despite the fact that there is no activity. One is the PowerManagerService, which means that there is a user partial wakelock, and the other is an AudioOutLock wait lock. Let’s have a look at the Partial wakelock screen.

Looking at the partial wakelock screen, we can see that an audio channel is still open with the media gallery application. This is curious as the media gallery application was not used explicitly by the user. The game actually launched the Media Gallery for the game’s music playback. The developer has simply forgotten to close the audio channel when the application is interrupted by the home button. The developer needs to take this case into consideration and modify the application accordingly.

5. Conclusion

Wakelocks are very useful and powerful tools. But if misused, they can have a very negative effect on the device battery life, considerably impacting the user experience. It is important for developers to check during QA that their code is not causing any No Sleep bugs. They should consider using the available tools to analyse the battery impact of their application in real life usage and try to limit their impact on the customer device as much as possible.

6. Reference