02 October 2025
Written by Alice Yuan, Senior Developer Relations Engineer
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.
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.
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.
Before we go over how to debug excessive wake lock usage, ensure you’re following wake lock best practices.
Consider these four critical questions.Before considering acquiring a manual partial wake lock, follow this decision-making flowchart:
Flowchart to decide when to manually acquire a wake lock
Does the screen need to stay on?
Yes: Reference the Keep Screen On documentation instead
Is the application running a foreground service?
No: You don't need to manually acquire a wake lock.
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.
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.
If you've answered all these questions and determined no alternative exists, you should proceed with manually acquiring a wake lock.
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.
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() } } }
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:
WorkManager: Increase the periodic interval in PeriodicWorkRequests.
SensorManager: Leverage batching by specifying maxReportLatencyMs when registering the listener.
Fused Location Provider:
Reduce location retrieval frequency by using getLastLocation for the most recent cached location.
Use setPriority(PRIORITY_PASSIVE) for a less battery-intensive update method.
Also, you can leverage the location batching mechanism by setting a minimum update interval with setMinUpdateIntervalMillis.
You can view more details in the wake lock best practices documentation.
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.
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 getStopReasonFor 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) } }
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:
Utilizing the system trace command line tool
Using the Android Studio CPU Profiler
Using the Perfetto UI
Recording a trace manually on the device directly from the developer options.
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 analysisSystem 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”.
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
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.
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.