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 到系统决策休眠、再到唤醒原因回传与统计记录的完整链路。

电源管理核心组件协作链路 应用进程 PowerManager WakeLock.acquire() SystemServer PowerManagerService WakeLock 汇总与电源决策 BatteryStatsService 耗电与唤醒统计 dumpsys batterystats Linux Kernel suspend / resume wakeup_reason 记录 硬件与中断源 RTC / GPIO / 电源键 / 传感器 Binder 调用 统计上报 决策休眠 进入 suspend 中断唤醒 WakeupReason WakeLock 阻止休眠 → PMS 汇总决策 → 内核 suspend → 中断唤醒 → WakeupReason 回传 → BatteryStatsService 记账 任一环节异常都会表现为待机耗电偏高或无法进入休眠

这条链路的关键在于:WakeLock 只表达“我需要保持唤醒”,并不直接决定系统是否休眠;真正做决策的是 PMS。PMS 会综合屏幕状态、用户活动、WakeLock 计数、Alarm 等因素,计算出一个“允许休眠”的条件,再通过 PowerManagerService.nativeSetAutoSuspend() 通知内核。

三、典型场景:一次息屏待机发生了什么

  1. 用户按下电源键,屏幕熄灭,PMS 将 mWakefulness 从 AWAKE 切换为 ASLEEP。
  2. PMS 检查当前是否存在未释放的 WakeLock。若存在 PARTIAL_WAKE_LOCK,则暂不允许 suspend。
  3. 当所有 WakeLock 释放且无其他唤醒源后,PMS 调用内核接口进入 suspend。
  4. 若此时有 RTC Alarm 到期或 GPIO 中断触发,内核 resume 并记录 wakeup_reason。
  5. 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 完成功耗归因。它们构成了一条从应用到内核再回到统计的闭环,任何一环的异常都会在待机功耗上被放大。