Appearance
11 电源体系:wakelock、Doze 与 suspend 的三道闸门
本页导读:Android 省电的本质是「让 CPU 和屏幕尽可能睡下去」,而 framework 的全部电源设计就是三道闸门的管理学:wakelock 决定「谁说不许睡」、PowerManagerService 决定「屏幕和系统的清醒状态」、Doze/待机策略决定「App 什么时候被断网断闹钟」。本页把 PowerManagerService、wakelock 家族、Doze 分级、内核 autosuspend 串成一条链,并给出「谁偷电」的取证方法。基线 Android 16(API 36)。
读完本页你要能回答:屏灭一晚掉电 10% 该先查哪三段读数?Doze 之后应用到底还剩什么权利?framework 和内核各握着哪张否决票?
一、清醒状态机:PowerManagerService
PowerManagerService(PMS,frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java)维护系统电源状态机(状态名以 AOSP 为准,大致为):
| 状态 | 含义 | 屏幕 | CPU |
|---|---|---|---|
| ON | 正常亮屏 | 亮 | 正常 |
| DIM | 超时前调暗 | 暗 | 正常 |
| DREAM | Daydream/屏保运行 | 亮(低亮) | 正常 |
| DOZE / DOZE_SUSPEND | 环境显示(AOD)/ AOD 且允许挂起 | 部分像素 | 低 |
| OFF | 屏灭 | 灭 | 可 suspend |
两个设计要点:
- 状态 = f(亮度请求, wakelock, 用户活动):任何组件都不直接「关灯」,而是提交投票,PMS 仲裁——这保证了策略集中、账目可审计;
- Android 13 起多显示(折叠/外接)由 PowerGroup 分组仲裁(
power/包内 PowerGroup 类,每屏独立状态机,主组联动从组,以 AOSP 为准)。
二、wakelock 家族:谁说不许睡
| 类型 | 作用 | 现状 |
|---|---|---|
| PARTIAL_WAKE_LOCK | 保 CPU 运行,不管屏 | 唯一仍推荐给系统级场景的锁 |
| SCREEN_BRIGHT / SCREEN_DIM / FULL | 亮屏类 | 早废弃(用 window flag/直接亮屏 API) |
| PROXIMITY 类 | 通话距感 | 废弃(HAL 层策略接管) |
| DOZE / DOZE_SUSPEND | AOD 场景专用 | 系统内部(AOD 服务) |
| NOTIFICATION / FOUL_WAKE_LOCK 等 flag | 行为修饰(超时、强制唤醒) | 部分随版本退役,以 AOSP 为准 |
三层事实要钉牢:
- wakelock 拿的是
WAKE_LOCK权限下的引用计数;dumpsys power的 wakelock 段列出当前持有者与统计(含 unattended); - 应用侧 wakelock 在 Doze 面前无效:进入 Doze 后闹钟/网络/job 被折叠,partial lock 也不让 CPU 常醒(非白名单);
- framework 内部还有一层 SuspendBlocker(native 挂起票,对应内核 wakelock):system_server/SF 在关键路径短暂持票防「睡在半路」,普通应用接触不到——
dumpsys power的 suspend blockers 段可见; - 排查入口:
dumpsys power顶部即列出活动 wakelock 与最近释放者统计;Android 13+ 提供「禁用指定应用 wakelock」的调试开关(PowerManager 侧接口名以 AOSP 为准),验证「这货是不是元凶」最快; - wakelock 计数≠耗电:10s 内高频取放(如传感器轮询)比单次长锁更耗电,看 batterystats 的 count 与 actual time 两列要一起看。
应用侧 API 骨架(类型常量见 frameworks/base/core/java/android/os/PowerManager.java):
java
// 示意:唯一仍常用的 PARTIAL 锁正确用法骨架
PowerManager pm = context.getSystemService(PowerManager.class);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Tag:WhatFor");
wl.setReferenceCounted(true); // 默认即引用计数
wl.acquire(timeoutMillis); // 永远带超时兜底
// ... 必须成对 release;tag 会出现在 dumpsys 里,写清楚用途是职业操守三、亮灭屏的完整链路
text
电源键/活动事件 → PowerManager(use-case 投票)
→ PMS 状态迁移(wakeUp/goToSleep 语义)
→ DisplayManager 亮度/模式(联动第 10 页刷新率降档)
→ 屏 OFF 后:所有 PARTIAL/DOZE 锁释放 → 用户态可睡
→ 内核 autosuspend:延迟窗口(默认量级数百 ms,以配置为准)无新唤醒 → suspend屏灭 ≠ 立刻 suspend:中间隔着「用户态 wakelock 清零 + autosuspend 延迟」两小节——「灭屏后待机一晚上掉电 10%」的凶手九成在第一小节(某个 App 灭屏后仍持锁)。
四、Doze:应用侧的断舍离
DeviceIdleController(frameworks/base/services/core/java/com/android/server/deviceidle/DeviceIdleController.java,目录以 AOSP 当前为准)实现 Android 6 引入的 Doze,状态机(名称以 AOSP 为准,语义稳定):
- ACTIVE:在用;INACTIVE:静止但轻约束;IDLE 家族:深度限制(Doze)、维护窗口周期性放行;
- 触发前提:设备静止+灭屏(屏幕关闭、非充电或低电),时间常数以设备配置(
config_系列 overlay)为准; - IDLE 的限制内容:网络访问被挂(netd 按 uid 置 idle 状态,第 03 页网络底座)、Wake Lock 被忽略、闹钟折叠到维护窗口、sync/job 推迟;
- Light Doze(轻度档)先收紧网络与 sync,Full Doze 上全量限制——两档在 Android 7 定型;
- 维护窗口不是固定闹钟:进 IDLE 前会有一次短窗,IDLE 内按指数退避排长窗;「Doze 后任务偶尔集中跑一波」正是维护窗的体感;
- 亮屏/移动/插电即退出 Doze 回到 ACTIVE,所有被折叠的闹钟/作业在出口处统一结算。
- 白名单(电池优化豁免
ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS与 OEM/运营商预置白名单)是唯一的合法旁路,OEM 省电激进程度差异也主要体现在 Doze 参数上。
调试三板斧(需 root/eng 或白名单身份):cmd deviceidle force-idle 强制进 Doze 复现;dumpsys deviceidle whitelist +<pkg> 临时加白;dumpsys deviceidle 直接看状态机与剩余时间常数。
应用侧的「正统姿势」变迁:wakelock 后台干活 → JobScheduler/FirebaseJobDispatcher(Doze 自动调度窗口)→ WorkManager(Android 架构组件推荐)——链的终点是「你描述需求,系统挑时间」。
五、应用待机分桶与功耗配额
Android 9 起 UsageStats 驱动 App Standby Buckets(RESTRICTED/ACTIVE/WORKING_SET/FREQUENT/RARE/NEVER,常量名以 UsageStatsManager 为准):按使用频度给 App 定档,后台执行/网络/Job 配额随之收紧;Android 12+ 耗电大户(高功耗后台行为)还会被系统弹窗建议限制。
- 查看:
dumpsys usagestats的分桶与dumpsys deviceidle; - 省电与稳定性的接口在这里第一次「双向」:framework 省电配额反过来成为第三方 App 后台存活率的主要变量,国内 OEM 的「自启动/后台管理」都是这套机制上的加严策略;
- 配额的具体形态:低桶的 App 后台网络被限时、Job 频次受限、高省电模式(PowerSave 开启)还会再压一层临时配额;
- 观察面:
dumpsys usagestats看桶位变迁,dumpsys battery power save(子命令随版本,以设备帮助为准)看省电模式开关对系统的降级动作。
六、亮度、dim 与灭屏通道
亮屏段的电量大头是屏本身,framework 的控制面:
- 亮度链路:自动亮度(ALS 传感器 + 厂商算法)、热限亮(ThermalManager 压制上限)、低亮度省电策略,最终都落到 DisplayManager→显示 HAL(第 10/12 页);
- DIM 态是超时前的「礼貌劝退」:PMS 先进 DIM 再 OFF,动画期间用户一动即回 ON;
- 应用「挽留」屏的合法姿势只有
FLAG_KEEP_SCREEN_ON(第 07 页转交 PMS)与导航/通话等场景的系统级 use-case,而不是自己 new 一个亮屏 wakelock; - 灭屏超时是用户设置值(Settings screen_off_timeout),OEM 可改默认;「调试时总是很快灭屏」改这个值而不是在代码里抢锁。
七、CPU 调速器、cluster 与电量归因
framework 不直接管频率(那是内核 schedutil/walt 与厂商 EAS 的事),但留了三个抓手:
PowerManager的 SCHED_BOOST hint 类通路(历史 API 多已废弃,现代走 PowerHint HAL(AIDLandroid.hardware.power,第 12 页)由厂商实现语义);- ThermalManager(
services/core/java/com/android/server/power/thermal/ThermalManager.java)聚合热缓解等级(THERMAL_STATUS_*),应用可用getCurrentThermalStatus/注册监听主动降载; - 电量归因:BatteryStatsService(core 阶段启动,第 03 页)按 uid/组件记账(wakelock、cpu、sensor、camera、kernel wakelock……),
dumpsys batterystats+ bugreport 的 Battery Usage 段即「谁偷电」的最终答案。
八、内核侧:autosuspend 与 last wake
- 所有用户态票清零后,内核走 autosuspend(现代走 delay 机制,
/sys/power/...节点名以内核版本为准)进入 suspend(suspend-to-idle/deep,厂商配置); - 唤醒源统计:
/sys/kernel/debug/wakeup_sources、/proc/last_kmsg/pstore、/sys/power/suspend_stats——「睡不下去」先看哪个 wakeup source 活跃; - SELinux 与 debugfs 限制下很多节点普通 shell 不可读,量产机常需 eng 版或 OEM dump 工具(以设备策略为准);
- 厂商深睡策略(DoD/智能待机)都在这一层做手脚,framework 的 Doze 只是第一道闸门。
九、实战:抓一个偷电贼
bash
adb shell dumpsys batterystats -u0 --checkin > bs.txt # 取原始账本
# 关注段:
# "Wake lock" —— App wakelock 累计时长/次数 Top
# "Kernel wakelock"—— 内核态票据(驱动嫌疑)
# "Network" / "Jobs" / "Syncs"
adb shell dumpsys deviceidle # Doze 状态机与白名单
adb shell dumpsys power # wakelock 现值 + suspend blocker
adb shell dumpsys usagestats -b # 分桶结果判读三步:先看 screen on time vs on time 确认问题在灭屏段还是亮屏段;灭屏段查 wakelock Top 与 kernel wakelock;亮屏段查刷新率/亮度与 CPU 频率曲线(perfdump/厂商工具)。
补充两个趁手工具:dumpsys batterystats -bh(battery history 逐事件流,含 wakelock/unplug/plug,子命令以版本为准)适合「昨晚到底发生了什么」类问题;bugreport 自带的 Battery Usage 汇总段则适合交给不写代码的同事看。
十、省电对外 API 一览(framework 的接口面)
PowerManager:newWakeLock(受限使用)、isInteractive、getThermalHeadroom(Android 13+)、user activity(Android 15+ 的getUserActivityEvent类接口,命名以 AOSP 为准);BatteryManager/BatteryEx(Android 15+ Mainline 化)、EnergyStatsManager(Android 12 引入、16 起稳定 AIDL 接口):App 查自身能耗;- 亮度/刷新率偏好走
WindowManager.LayoutParams与显示投票 API(第 10 页); - 省电策略「被管」侧:JobScheduler/AlarmManager/WorkManager 的 deadline、exact 权限(Android 12+ exact alarm 需
SCHEDULE_EXACT_ALARM特殊权限)都是电源策略在应用 API 上的投影。
十一、判例清单
| 症状 | 高概率机制 | 取证 |
|---|---|---|
| 一晚待机掉电两位数 | 灭屏后 App wakelock 或驱动 wakeup | batterystats wakelock 段 + wakeup_sources |
| 息屏播放音乐中断 | 未持 PARTIAL 锁的「伪后台播放」 | dumpsys media.session/audio 与 wakelock |
| 闹钟不准时 | Doze 折叠非 exact 闹钟 | deviceidle 状态 + alarm 账本 |
| Job 迟迟不跑 | 分桶档位低/约束未满足 | dumpsys jobscheduler 与 usagestats 桶 |
| 一插电就醒 | charger 唤醒源正常,但唤醒后 App 借机干活 | batterystats 充电段事件流 |
| AOD 耗电异常 | 厂商 AOD 实现未走 DOZE_SUSPEND | dumpsys power 状态分布 |
十二、自测清单
- 能画出「屏灭→suspend」的完整闸门序列;
- 能区分 wakelock、SuspendBlocker、kernel wakeup 三层票据;
- 能说出 Doze 两档各限制什么、维护窗口放行什么;
- 能用 batterystats 三段式在 5 分钟内指认偷电大户;
- 能解释为什么 Doze 白名单申请在商店合规与用户口碑上都是高成本操作。
十三、版本注记
- Android 6(API 23):Doze 与电池优化白名单登场。
- Android 7(API 24):Light Doze、implicit broadcast 大削减(省电组合拳)。
- Android 9(API 28):App Standby Buckets;
SuspendControlServiceHAL 化(AOSP 统一 suspend 入口)。 - Android 11(API 30):
PowerManager热缓解 API 扩展;isLightDevice轻量设备策略。 - Android 12(API 31):EnergyStats 登场;exact alarm 收紧;AdaptiveConnectivity 类省电。
- Android 13(API 33):PowerGroup 多屏电源仲裁;AOD/DOZE 族语义细化。
- Android 15~16(API 35~36):BatteryEx/能耗稳定接口、user activity 类新面;Doze 骨架十年未动,变的是配额与白名单治理。
本页小结
本页小结:三道闸门记清楚:wakelock 是「否决票」,PMS 状态机是「仲裁庭」,Doze/桶是「配额局」;屏灭之后还要过 suspend blocker 清零与内核 autosuspend 才真睡。查偷电永远
dumpsys batterystats三段式(App wakelock/kernel wakelock/屏幕段),写后台任务永远优先 Job/Work 而不是「持锁硬扛」。
最后连一次网:wakelock 的引用计数走的是 system_server 单点(第 03 页),Doze 的静止判定依赖传感器(第 09 页),刷新率降档归图形(第 10 页),HAL 语义归 Treble(第 12 页)——电源从来不是一个模块,是全系统的一张网。
参考
- 省电官方指南:developer.android.com/topic/performance/power-features
- DeviceIdleController 源码:cs.android.com/android/platform/frameworks/base/+/main/services/core/java/com/android/server/deviceidle/DeviceIdleController.java
- PowerManagerService 源码:cs.android.com/android/platform/frameworks/base/+/main/services/core/java/com/android/server/power/PowerManagerService.java
- Doze 与 App Standby:developer.android.com/training/monitoring/manage-device-usage