第五板块:Android 系统服务与电源管理 | 第十七篇:Power Manager Service 与 WakeLock 机制
所属板块:第五板块 — Android 系统服务与电源管理
前置知识:第十六篇中的输入系统、System Server 架构、Binder IPC、Linux 内核基础、Wakelock 基础概念
本篇定位:这是 Android 设备续航能力的守护神。我们将以手术刀级的精度,深入 Linux 内核电源管理、Power Manager Service (PMS) 的锁仲裁算法、WakeLock 的引用计数与 Binder 死亡回收、Doze 模式的时空置换算法。本篇将包含内核 sysfs 接口、native 层调用、System Server 的源码级逻辑,揭示 Android 如何在 纳秒级 的精度上平衡 性能 与 功耗。
1. 核心结论先行(Thesis Statement)
Android 的电源管理是一个基于时间的强制休眠系统。
- Power Manager Service (PMS) 的本质:中央仲裁器。它运行在 System Server 中,维护着 SuspendBlocker(内核级锁)和 WakeLock(应用级锁)的双层计数模型。只有当所有计数归零,且屏幕熄灭后,才会触发内核休眠。
- WakeLock 的本质:带作用域的引用计数。它不是一把“锁”,而是一个计数器。同一个 Binder 代理可以申请多次,释放时需对应次数。如果持有者进程死亡,Binder 死亡通知会强制清零。
- 休眠(Suspend)的本质:冻结用户空间进程,并让 CPU 进入 C-State(空闲状态)。此时 CPU 几乎不耗电,只有 RAM 保持自刷新。
- 唤醒(Wakeup)的本质:中断(IRQ)。硬件中断(如电源键、RTC Alarm)会打破 CPU 的空闲状态,PMS 重新计算锁状态。
2. Linux 内核电源管理框架(Kernel Layer)
2.1 内核的休眠状态机
Linux 内核定义了多种休眠状态,Android 在此基础上进行了定制。
| 状态 | 学术定义 | 硬件表现 |
|---|---|---|
| Running | 正常运行 | CPU 全速,设备供电。 |
| Freeze | 浅度休眠 | 冻结用户进程,CPU 可能仍运行。 |
| Standby | 待机 | CPU 暂停,RAM 自刷新,大部分设备断电。 |
| Mem (Suspend-to-RAM) | Android 主要休眠模式 | CPU 断电,RAM 自刷新,上下文保存在 RAM。 |
| Disk (Hibernate) | 休眠到磁盘 | 上下文写入磁盘,RAM 断电(Android 极少使用)。 |
2.2 Wakeup Source 机制
内核使用 Wakeup Source 来阻止系统休眠。
学术定义:
- Wakeup Source: 一个内核对象,表示一个可以唤醒系统的源。可以是 IRQ、Timer、或者用户空间持有的锁。
- wakeup_count: 内核维护的唤醒事件计数。用户空间写入一个值,内核只有在唤醒事件不超过该值时才允许休眠。
内核接口:
# 查看当前 Wakeup Source
cat /sys/kernel/debug/wakeup_sources
# 查看休眠状态
cat /sys/power/state
# 输出: freeze standby mem disk
# 触发休眠
echo mem > /sys/power/state
2.3 suspend 流程(内核侧)
当 PMS 写入 mem 到 /sys/power/state 时,内核执行:
- 冻结进程(Freezer):向所有用户空间进程发送
SIGSTOP,冻结它们。 - 设备挂起(Device Suspend):调用所有设备驱动的
suspend()回调,关闭屏幕、背光、传感器、网络。 - 平台挂起(Platform Suspend):SoC 特定的挂起代码,保存 CPU 上下文,关闭 CPU 时钟。
- 进入 C-State:CPU 进入低功耗状态,等待中断唤醒。
3. Power Manager Service 的锁仲裁模型
3.1 双层锁架构
PMS 使用两层锁来与内核交互:
| 锁类型 | 层级 | 作用 | 对应内核机制 |
|---|---|---|---|
| WakeLock | 应用层 | 应用申请,防止系统休眠 | 映射到 SuspendBlocker |
| SuspendBlocker | 系统层 | PMS 内部使用,直接控制内核 | wakeup source / sysfs |
3.2 WakeLock 的引用计数与 Binder 死亡回收
PMS 内部维护一个 HashMap<IBinder, WakeLock>,其中 IBinder 是申请者的 Binder 代理。
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
final class WakeLock {
final IBinder mBinder; // 申请者的 Binder 标识
final int mFlags; // WakeLock 类型 (PARTIAL, SCREEN_BRIGHT 等)
int mCount; // 引用计数 (可重入)
boolean mEnabled; // 是否激活
String mTag; // 调试标签
WorkSource mWorkSource; // 工作源 (用于统计)
}
关键逻辑:
- 申请(acquire):如果
mBinder不存在,创建新 WakeLock,mCount = 1;如果存在,mCount++。 - 释放(release):
mCount--。当mCount == 0时,从mWakeLocks中移除。 - 死亡回收(Binder Death Recipient):PMS 为每个
mBinder注册死亡通知。如果应用进程崩溃,Binder 死亡,PMS 强制将该mBinder对应的所有 WakeLock 清零。
3.3 SuspendBlocker 的持有
PMS 根据 WakeLock 的状态,决定是否持有 SuspendBlocker。
// PowerManagerService.java
void updatePowerStateLocked() {
// 计算是否需要保持唤醒
boolean needWakeLock = (mWakeLockSummary & WAKE_LOCK_CPU) != 0;
boolean screenOn = (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0;
if (needWakeLock || screenOn) {
// 持有 SuspendBlocker,阻止内核休眠
mSuspendBlocker.acquire();
} else {
// 释放 SuspendBlocker,允许内核休眠
mSuspendBlocker.release();
}
}
4. 屏幕与 CPU 的协同
4.1 屏幕状态机
屏幕状态独立于 CPU 状态。
stateDiagram-v2
[*] --> ScreenOff: 电源键 / 超时
ScreenOff --> ScreenDim: 用户交互 / 动画
ScreenDim --> ScreenOn: 用户交互
ScreenOn --> ScreenOff: 电源键 / 超时
4.2 屏幕与 WakeLock 的交互
| 场景 | WakeLock 状态 | 屏幕状态 | CPU 状态 |
|---|---|---|---|
| 玩游戏 | PARTIAL_WAKE_LOCK | ON | 全速 |
| 听音乐 | PARTIAL_WAKE_LOCK | OFF | 全速 |
| 看视频 | SCREEN_BRIGHT_WAKE_LOCK | ON | 全速 |
| 待机 | 无 WakeLock | OFF | 休眠 |
学术定义:
- PARTIAL_WAKE_LOCK:仅保持 CPU 运行,屏幕可关。这是后台任务唯一合法的 WakeLock。
- SCREEN_BRIGHT_WAKE_LOCK:保持屏幕亮。Android 已弃用,改用 Window Flag
FLAG_KEEP_SCREEN_ON,因为 Window 销毁时系统能自动释放。
5. Doze 模式的时空置换算法
5.1 DeviceIdleController 的状态机
Doze 是 Android 6.0 引入的革命性省电机制。
stateDiagram-v2
[*] --> ACTIVE: 屏幕亮 / 充电
ACTIVE --> INACTIVE: 屏幕关 / 拔充电线
INACTIVE --> IDLE_PENDING: 静止一段时间
IDLE_PENDING --> IDLE: 进入深度休眠
IDLE --> IDLE_MAINTENANCE: 维护窗口
IDLE_MAINTENANCE --> IDLE: 维护结束
IDLE --> ACTIVE: 屏幕亮 / 充电
5.2 维护窗口(Maintenance Window)算法
Doze 不是一直睡,而是周期性地醒过来处理任务。
学术定义:
- 维护窗口间隔:随着时间推移,间隔越来越长(指数退避)。
- 网络访问限制:在 IDLE 状态下,应用无法访问网络。
- Alarm 延迟:
AlarmManager的非唤醒 Alarm 被延迟到维护窗口执行。
5.3 应用待机桶(App Standby Buckets)
Android 9.0 引入,根据应用使用频率限制后台任务。
| 桶 (Bucket) | 限制程度 | 定义 |
|---|---|---|
| ACTIVE | 无限制 | 用户正在交互。 |
| WORKING_SET | 轻微限制 | 经常使用。 |
| FREQUENT | 较强限制 | 偶尔使用。 |
| RARE | 强限制 | 很少使用。 |
| NEVER | 完全限制 | 从未使用或禁止后台运行。 |
算法:PMS 根据应用的最近使用时间、启动次数、与前台应用的交互,动态计算应用所属的桶,并限制其 JobScheduler 和 Alarm 的频率。
6. AlarmManager 的唤醒机制
6.1 Alarm 的类型与内核接口
AlarmManager 是唤醒系统的主要手段,它使用内核的 alarmtimer 驱动。
| Alarm 类型 | 是否唤醒 CPU | 时间基准 | 适用场景 |
|---|---|---|---|
| RTC_WAKEUP | 是 | 绝对时间 (1970-01-01) | 闹钟、日历提醒。 |
| RTC | 否 | 绝对时间 | 定时记录日志。 |
| ELAPSED_REALTIME_WAKEUP | 是 | 相对时间 (开机至今) | 后台下载重试。 |
| ELAPSED_REALTIME | 否 | 相对时间 | 定时刷新 UI。 |
6.2 Alarm 的批处理(Batch Processing)
为了减少唤醒次数,PMS 将多个 Alarm 合并。
学术定义:
- Batch Window:PMS 维护一个时间窗口(如 5 分钟)。在这个窗口内的 Alarm 会被合并,在同一时刻触发。
- 对齐(Alignment):将非紧急的 Alarm 对齐到最近的 Batch Window,减少 CPU 唤醒次数。
7. 关键源码深度解析
7.1 PowerManagerService 的休眠决策(Native 层)
PMS 通过 JNI 调用 native 方法,最终写入 sysfs。
// frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
static void nativeSetInteractive(JNIEnv* env, jclass clazz, jboolean enable) {
// 调用内核接口
setInteractive(enable);
}
static void nativeSetAutoSuspend(JNIEnv* env, jclass clazz, jboolean enable) {
// 写入 /sys/power/state
if (enable) {
writeSysfs("/sys/power/state", "mem");
} else {
writeSysfs("/sys/power/state", "on");
}
}
7.2 WakeLock 的 Binder 死亡回收
// PowerManagerService.java
private final class WakeLock implements IBinder.DeathRecipient {
@Override
public void binderDied() {
// Binder 死亡,强制释放锁
synchronized (mLock) {
removeWakeLockLocked(mBinder, true);
}
}
}
7.3 DeviceIdleController 的维护窗口计算
// DeviceIdleController.java
void stepIdleStateLocked(String reason) {
long now = SystemClock.elapsedRealtime();
long nextAlarmTime = now + mConstants.IDLE_TIMEOUT;
// 指数退避
if (mState == STATE_IDLE) {
nextAlarmTime = now + mConstants.IDLE_TIMEOUT_FACTOR * mConstants.IDLE_TIMEOUT;
}
// 设置 Alarm
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextAlarmTime, mMaintenanceAlarmListener);
}
8. 电源管理的常见误区与坑(学术级)
8.1 WakeLock 泄漏的灾难性后果
| 错误做法 | 学术后果 |
|---|---|
| 在 Service 的 onCreate 中申请,在 onDestroy 中释放 | Service 被系统杀死时,onDestroy 不一定调用,导致锁泄漏。 |
| 在 BroadcastReceiver 的 onReceive 中申请,异步释放 | onReceive 执行完即结束,异步任务未完成,锁泄漏。 |
| 使用 PARTIAL_WAKE_LOCK 播放音乐,但忘记释放 | 即使按电源键,CPU 仍运行,电池几小时内耗尽。 |
8.2 过度唤醒的代价
| 错误做法 | 学术后果 |
|---|---|
| 每秒钟一个 RTC_WAKEUP Alarm | 屏幕无法熄灭,CPU 每秒唤醒一次,电池疯狂消耗。 |
| 在 Doze 模式下申请 PARTIAL_WAKE_LOCK | 系统频繁唤醒,Doze 失去意义,手机发热。 |
9. 本篇总结(Knowledge Closure)
| 关键点 | 纯学术定义 |
|---|---|
| PMS 的本质 | 中央仲裁器,基于双层锁(WakeLock/SuspendBlocker)计数决定系统休眠。 |
| WakeLock 的本质 | 带作用域的引用计数,由 Binder 死亡通知保障释放。 |
| 内核休眠 | 冻结进程 -> 挂起设备 -> CPU 进入 C-State。 |
| Doze 算法 | 时空置换,通过维护窗口周期性唤醒,平衡省电与可用性。 |
| Alarm 批处理 | 将多个 Alarm 合并,减少 CPU 唤醒次数,提升续航。 |
下一篇预告:第五板块:Android 系统服务与电源管理 | 第十八篇:Battery Service 与 电量统计(Fuel Gauge)算法