Stuck partial wake locks

Partial wake locks are a mechanism in the PowerManager API that lets developers keep the CPU running after a device's display turns off (whether due to system timeout or the user pressing the power button). Your app acquires a partial wake lock by calling acquire() with the PARTIAL_WAKE_LOCK flag. A partial wake lock becomes stuck if it is held for a long time while your app is running in the background (no part of your app is visible to the user). This condition drains the device's battery because it prevents the device from entering lower power states. Partial wake locks should be used only when necessary and released as soon as no longer needed.

If your app has a stuck partial wake lock, you can use the guidance in this page to diagnose and fix the problem.

Detect the problem

You may not always know that your app's partial wake locks are stuck. If you have already published your app, Android vitals can help make you aware of the problem.

Android vitals

Android vitals can help improve your app's performance by alerting you via the Play Console when your app is exhibiting stuck partial wake locks. Android vitals reports partial wake locks as stuck when at least one, hour-long, while in the background, partial wake lock occurs in a battery session.

The definition of battery session depends on the platform version.

  • In Android 10, a battery session is the aggregation of all battery reports received within a given 24-hour period. A battery report refers to the interval between two battery charges either from below 20% to above 80% or from any charge level to 100%.
  • In Android 11, a battery session is a fixed 24-hour period.

The number of battery sessions displayed is an aggregate for all measured users of the app. For information on how Google Play collects Android vitals data, see the Play Console documentation.

Once you're aware that your app has excessive stuck partial wake locks, your next step is to address the issue.

Fix the problem

Wake locks were introduced in early versions of the Android platform, but over time, many use cases that previously required wake locks are now better served by newer APIs like WorkManager.

This section contains tips for fixing your wake locks, but in the long term, consider migrating your app to follow the recommendations in the best practices section.

Identify and fix places in your code that acquire a wake lock, such as calls to newWakeLock(int, String) or WakefulBroadcastReceiver subclasses. Here are some tips:

  • We recommend including your package, class, or method name in the wakelock tag name so that you can easily identify the location in your source where the wake lock was created. Here are some additional tips:
    • Leave out any personally identifying information (PII) in the name, such as an email address. Otherwise, the device logs _UNKNOWN instead of the wake lock name.
    • Don't get the class or method name programmatically, for example by calling getName(), because it could get obfuscated by Proguard. Instead use a hard-coded string.
    • Don't add a counter or unique identifiers to wake lock tags. The system won't be able to aggregate wake locks created by the same method because they all have unique identifiers.
  • Make sure your code releases all the wake locks that it acquires. This is more complicated than making sure that every call to acquire() has a corresponding call to release(). Here's an example of a wake lock that is not released due to an uncaught exception:

    Kotlin

    @Throws(MyException::class)
    fun doSomethingAndRelease() {
        wakeLock.apply {
            acquire()
            doSomethingThatThrows()
            release()  // does not run if an exception is thrown
        }
    }

    Java

        void doSomethingAndRelease() throws MyException {
            wakeLock.acquire();
            doSomethingThatThrows();
            wakeLock.release();  // does not run if an exception is thrown
        }

    Here's a correct version of the code:

    Kotlin

    @Throws(MyException::class)
    fun doSomethingAndRelease() {
        wakeLock.apply {
            try {
                acquire()
                doSomethingThatThrows()
            } finally {
                release()
            }
        }
    }

    Java

        void doSomethingAndRelease() throws MyException {
            try {
                wakeLock.acquire();
                doSomethingThatThrows();
            } finally {
                wakeLock.release();
            }
        }
  • Make sure that wake locks are released as soon as they are no longer needed. For example, if you are using a wake lock to allow a background task to finish, make sure that release happens when that task finishes. If a wake lock is held longer than expected without being released, this could mean that your background task is taking more time than expected.

After fixing the problem in code, verify that your app correctly releases wake locks by using the following Android tools:

  • dumpsys - a tool that provides information about the status of system services on a device. To see the status of the power service, which includes a list of wake locks, run adb shell dumpsys power.

  • Battery Historian - a tool that parses the output of an Android bug report into a visual representation of power related events.

Best practices

In general, your app should avoid partial wake locks because it is too easy to drain the user's battery. Android provides alternative APIs for almost every use-case that previously required a partial wake lock. One remaining use-case for partial wake locks is to ensure that a music app continues to play when the screen is off. If you are using wake locks to run tasks, consider the alternatives described in the background processing guide.

If you must use partial wake locks, follow these recommendations:

  • Make sure some portion of your app remains in the foreground. For example, if you need to run a service, start a foreground service instead. This visually indicates to the user that your app is still running.
  • Make sure the logic for acquiring and releasing wake locks is as simple as possible. When your wake lock logic is tied to complex state machines, timeouts, executor pools, and/or callback events, any subtle bug in that logic can cause the wake lock to be held longer than expected. These bugs are difficult to diagnose and debug.