3. 电源管理核心组件
Android 的电源管理并不是某一个类独立完成的,而是一组跨进程、跨层次的组件协同工作的结果。应用层看到的是 PowerManager,系统服务层真正决策的是 PowerManagerService(简称 PMS),而 WakeLock、WakeupReason、BatteryStatsService 分别承担“持有唤醒锁”“记录唤醒来源”“统计功耗账本”的职责。理解这五个组件的分工与数据流,是排查待机耗电、异常唤醒、休眠失败等问题的前提。
一、五个组件的职责边界
先建立一张“谁负责什么”的全局视图,避免把应用 API 与系统服务混为一谈。
| 组件 | 所在层次 | 核心职责 | 典型调用/输出 |
|---|---|---|---|
| PowerManager | 应用框架 API | 向应用暴露唤醒、休眠、WakeLock 申请等接口 | newWakeLock()、goToSleep()、isInteractive() |
| PowerManagerService | SystemServer 系统服务 | 汇总所有 WakeLock、屏幕状态、用户活动,决策系统电源状态 | 更新 mWakefulness、触发 suspend/resume |
| WakeLock | 应用/系统持有的锁对象 | 阻止系统进入休眠或阻止屏幕熄灭 | acquire()、release()、setReferenceCounted() |
| WakeupReason | 内核到框架的唤醒来源 | 记录“是谁把系统从休眠中唤醒” | wakeup_reason、irq、alarm、gpio |
| BatteryStatsService | SystemServer 统计服务 | 记录各进程、各子系统的耗电与唤醒时长 | dumpsys batterystats、WakeLock 统计 |
可以看到,PowerManager 是“门面”,PowerManagerService 是“大脑”,WakeLock 是“开关”,WakeupReason 是“证据”,BatteryStatsService 是“账本”。
二、协作关系与数据流
下面这张图展示了从应用申请 WakeLock 到系统决策休眠、再到唤醒原因回传与统计记录的完整链路。
这条链路的关键在于:WakeLock 只表达“我需要保持唤醒”,并不直接决定系统是否休眠;真正做决策的是 PMS。PMS 会综合屏幕状态、用户活动、WakeLock 计数、Alarm 等因素,计算出一个“允许休眠”的条件,再通过 PowerManagerService.nativeSetAutoSuspend() 通知内核。
三、典型场景:一次息屏待机发生了什么
- 用户按下电源键,屏幕熄灭,PMS 将
mWakefulness从AWAKE切换为ASLEEP。 - PMS 检查当前是否存在未释放的 WakeLock。若存在
PARTIAL_WAKE_LOCK,则暂不允许 suspend。 - 当所有 WakeLock 释放且无其他唤醒源后,PMS 调用内核接口进入 suspend。
- 若此时有 RTC Alarm 到期或 GPIO 中断触发,内核 resume 并记录
wakeup_reason。 - PMS 读取唤醒原因,BatteryStatsService 记录本次唤醒的进程、时长与来源。
可以用以下命令快速查看当前状态与唤醒原因:
# 查看电源状态与 WakeLock 汇总
adb shell dumpsys power | grep -E "mWakefulness|Wake Locks|mHolding"
# 查看内核唤醒原因
adb shell cat /sys/kernel/debug/wakeup_sources
adb shell cat /sys/kernel/debug/suspend_stats
# 查看电池统计中的唤醒与耗电
adb shell dumpsys batterystats --charged | grep -A 20 "Wake lock"
四、误区与复盘
误区一:认为 acquire() 后系统一定不休眠。实际上只有 PARTIAL_WAKE_LOCK 才会阻止 CPU 休眠;SCREEN_DIM_WAKE_LOCK 等只影响屏幕,且在新版本中已废弃。
误区二:把 WakeupReason 当成 WakeLock。WakeupReason 是“事后记录”,描述谁把系统唤醒;WakeLock 是“事前声明”,描述谁要求保持唤醒。两者方向相反。
误区三:只看 BatteryStats 就下结论。BatteryStats 是统计结果,必须结合 dumpsys power 的 WakeLock 持有栈和 wakeup_sources 的原始计数,才能定位到具体进程与中断源。
排查待机耗电的推荐顺序:先看 dumpsys power 是否有异常 WakeLock 长期持有,再看 wakeup_sources 是否有高频中断,最后用 dumpsys batterystats 关联到具体 UID 与进程。三者交叉验证,才能避免误判。
总结来说,PowerManager 提供接口,PowerManagerService 做全局决策,WakeLock 表达持有意愿,WakeupReason 记录唤醒来源,BatteryStatsService 完成功耗归因。它们构成了一条从应用到内核再回到统计的闭环,任何一环的异常都会在待机功耗上被放大。