Android Developers Blog
The latest Android and Google Play news for app and game developers.
🔍
Platform Android Studio Google Play Jetpack Kotlin Docs News

02 October 2025

Optimize your app battery using Android vitals wake lock metric


Link copied to clipboard

Written by Alice Yuan, Senior Developer Relations Engineer


Most of the content of this post is also available in video format, go give it a watch!

Battery life is a crucial aspect of user experience and wake locks play a major role. Are you using them excessively? In this blog post we’ll explore what wake locks are, what are some best practices for using them and how you can better understand your own app’s behavior with the Play Console metric.

Excessive partial wake lock usage in Android Vitals

The Play Console now monitors battery drain, with a focus on excessive partial wake lock usage, as a key performance indicator.

This feature elevates the importance of battery efficiency alongside existing core metric stability indicators: excessive user-perceived crashes and ANRs. Currently, an app exceeding the threshold will not be less discoverable on Google Play.

The excessive wake lock warning in the Android vitals overview.

For mobile devices, the Android vitals metric applies to non-exempted wake locks acquired while the screen is off and the app is in the background or running a foreground service. Android vitals considers partial wake lock usage excessive if:
  • Wake locks are held for at least two hours within a 24-hour period.

  • It affects more than 5% of your app's sessions, averaged over 28 days.

Wake locks created by audio, location, and JobScheduler user initiated APIs are exempted from the wake lock calculation.

Understanding wake locks

A wake lock is a mechanism that allows an app to keep a device's CPU running even when the user isn't actively interacting with it. 

A partial wake lock keeps the CPU running even if the screen is off, preventing the CPU from entering a low-power "suspend" state. A full wake lock keeps both the screen and the CPU running.

There are 2 methods partial wake locks are acquired:

  • The app manually acquires and releases the wake lock using PowerManager APIs for a specific use case, often this is acquired in conjunction with a Foreground Service - a platform lifecycle API intended for user-perceptible operation.

  • Alternatively, the wake lock is acquired by another API, and attributed to the app due to usage of the API, more on this in the best practices section.

While wake locks are necessary for tasks like completing a user-initiated download of a large file, their excessive or improper use can lead to significant battery drain. We've seen cases where apps hold wake locks for hours or fail to release them properly, leading to user complaints about significant battery drain even when they're not interacting with the app.

Best Practices for Wake Lock Usage

Before we go over how to debug excessive wake lock usage, ensure you’re following wake lock best practices. 

Consider these four critical questions.

1. Have you considered alternative wake lock options?

Before considering acquiring a manual partial wake lock, follow this decision-making flowchart:

Flowchart to decide when to manually acquire a wake lock

  1. Does the screen need to stay on?

  2. Is the application running a foreground service? 

    • No: You don't need to manually acquire a wake lock.

  3. Is it detrimental to the user experience if the device suspends? 

    • No: For instance, updating a notification after the device wakes up doesn't require a wake lock. 

    • Yes: If it’s critical to prevent the device from suspending, like ongoing communication with an external device, proceed.

  4. Is there already an API keeping the device awake on your behalf?

    • You can leverage the documentation Identify wake locks created by other APIs to identify scenarios where wake locks created by other APIs to identify scenarios where wake locks are created by other APIs such as LocationManager.

    • If no APIs exist, proceed to the final question.

  5. If you've answered all these questions and determined no alternative exists, you should proceed with manually acquiring a wake lock.

2. Are you naming the wake lock correctly?

When manually acquiring wake locks, proper naming is important for debugging:

  • Leave out any Personally Identifiable Information (PII) in the name like email addresses. If PII is detected, the wake lock is logged as _UNKNOWN, hindering debugging.

  • Don't name your wake lock programmatically using class or method names, as these can be obfuscated by tools like Proguard. Instead, use a hard-coded string.

  • Do not add counters or unique identifiers to wake lock tags. The same tag should be used every time the wake lock runs to allow the system to aggregate usage by name, making abnormal behavior easier to detect.

3. Is the acquired wake lock always released?

If you're acquiring a wake lock manually, ensure the wake lock release always executes. Failing to release a wake lock can cause significant battery drain. 

For example, if an uncaught exception is thrown during processingWork(), the release() call might never happen. Instead, you can use a try-finally block to guarantee the wake lock is released, even if an exception occurs.

Additionally, you can add a timeout to the wake lock to ensure it releases after a specific period, preventing it from being held indefinitely.

fun processingWork() {
    wakeLock.apply {
        try {
            acquire(60 * 10 * 1000) // timeout after 10 minutes
            doTheWork()
        } finally {
            release()
        }
    }
}

4. Can you reduce the wake-up frequency?

For periodic data requests, reducing how often your app wakes up the device is key to battery optimization. Some examples of reducing wake-up frequency include:

You can view more details in the wake lock best practices documentation.


Debugging excessive wake lock usage


Even with the best intentions, excessive wake lock usage can occur. If your app is flagged in the Play Console, here's how to debug it:

Initial identification with Play Console

The Android vitals excessive partial wake lock dashboard provides breakdowns of non-exempted wake lock names associated with your app, showing affected sessions and durations. Reminder to use the documentation to help you identify if the wake lock name is app-held or held by another API.

The Android vitals excessive partial wake lock dashboard scrolled down to the breakdowns section to view excessive wake lock tags.

Debugging excessive wake locks held by workers/jobs


You can identify worker-held wake locks with this wake lock name:

*job*/<package_name>/androidx.work.impl.background.systemjob.SystemJobService

The full list of variations of worker-held wake lock names is available in documentation. To debug these wake locks, you can use Background Task Inspector to debug locally, or leverage getStopReason to debug issues in the field. 


Android Studio Background Task Inspector



Screen capture of the Background Task Inspector, where it has been able to identify a worker “WeatherSyncWorker” that has frequently retried and failed.

For local debugging of WorkManager issues, use this tool on an emulator or connected device (API level 26+). It shows a list of workers and their statuses (finished, executing, enqueued), allowing you to inspect details and understand worker chains. 

For instance, it can reveal if a worker is frequently failing or retrying due to hitting system limitations. 

See Background Task Inspector documentation for more details.

WorkManager getStopReason

For in-field debugging of workers with excessive wake locks, use WorkInfo.getStopReason() on WorkManager 2.9.0+ or for JobScheduler, JobParameters.getStopReason() available on SDK 31+. 

This API helps log the reason why a worker stopped (e.g., STOP_REASON_TIMEOUT, STOP_REASON_QUOTA), pinpointing issues like frequent timeouts due to exhausting runtime duration.

backgroundScope.launch {
    WorkManager.getInstance(context)
        .getWorkInfoByIdFlow(workRequest.id)
        .collect { workInfo ->
            logStopReason(workRequest.id, workInfo?.stopReason)
        }
}


Debugging other types of excessive wake locks

For more complex scenarios involving manually held wake locks or APIs holding the wake lock, we recommend you use system trace collection to debug.

System trace collection

A system trace is a powerful debugging tool that captures a detailed record of system activity over a period, providing insights into CPU state, thread activity, network activity, and battery-related metrics like job duration and wake lock usage.

You can capture a system trace using several methods: 

Enable "power:PowerManagement" Atrace category in the Perfetto UI under the Android apps & svcs tab. 

Regardless of the chosen method, it's crucial to ensure that you are collecting the "power:PowerManagement" Atrace category to enable viewing of device state tracks. 

Perfetto UI inspection and SQL analysis

System traces can be opened and inspected in the Perfetto UI. When you open the trace, you will see a visualization of various processes on a timeline. The tracks we will be focused on in this guide are the ones under “Device State”.



Pin the tracks under “Device State” such as "Top app", "Screen state", "Long Wake locks", and “Jobs” tracks to visually identify long-running wake lock slices.

Each block lists the name of the event, when the event started, and when it ended. In Perfetto, this is called a slice.

For scalable analysis of multiple traces, you can use Perfetto's SQL analysis. A SQL query can find all wake locks sorted by duration, helping identify the top contributors to excessive usage.

Here’s an example query summing all the wake lock tags that occurred in the system trace, ordered by total duration:

SELECT slice.name as name, track.name as track_name,
SUM(dur / 100000) as total_dur_ms
FROM slice
JOIN track ON slice.track_id = track.id
WHERE track.name = 'WakeLocks'
GROUP BY slice.name, track.name
ORDER BY total_dur_ms DESC

Use ProfilingManager for in-field trace collection

For hard-to-reproduce issues, ProfilingManager (added in SDK 35) is a programmatic API that allows developers to collect system traces in the field with start and end triggers. It offers more control over the start and end trigger points for profile collection and enforces system-level rate limiting to prevent device performance impact. 

Check out the ProfilingManager documentation for further steps on how to implement in field system trace collection which include how to programmatically capture a trace, analyze profiling data, and use local debug commands.

The system traces collected using ProfilingManager will look similar to the ones collected manually, but system processes and other app processes are redacted from the trace.

Conclusion

The excessive partial wake lock metric in Android vitals is only a small part of our ongoing commitment to supporting developers in reducing battery drain and improving app quality. 

By understanding and properly implementing wake locks, you can significantly optimize your app's battery performance. Leveraging alternative APIs, adhering to wake lock best practices, and using powerful debugging tools such as Background Task Inspector, system traces and ProfilingManager are key to ensuring your app's success on Google Play.