第五板块: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 如何在 纳秒级 的精度上平衡 性能 与 功耗。全程无省电技巧、无电池优化建议,仅保留 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: 内核维护的唤醒事件计数。用户空间写入一个值,内核只有在唤醒事件不超过该值时才允许休眠。
内核接口:
bash
# 查看当前 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 代理。
java
// 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。
java
// 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 状态。
#mermaid-svg-jUGv9z9zC0iyRdfM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jUGv9z9zC0iyRdfM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jUGv9z9zC0iyRdfM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jUGv9z9zC0iyRdfM .error-icon{fill:#552222;}#mermaid-svg-jUGv9z9zC0iyRdfM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jUGv9z9zC0iyRdfM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jUGv9z9zC0iyRdfM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jUGv9z9zC0iyRdfM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jUGv9z9zC0iyRdfM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jUGv9z9zC0iyRdfM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jUGv9z9zC0iyRdfM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jUGv9z9zC0iyRdfM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jUGv9z9zC0iyRdfM .marker.cross{stroke:#333333;}#mermaid-svg-jUGv9z9zC0iyRdfM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jUGv9z9zC0iyRdfM p{margin:0;}#mermaid-svg-jUGv9z9zC0iyRdfM defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-jUGv9z9zC0iyRdfM g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-jUGv9z9zC0iyRdfM g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-jUGv9z9zC0iyRdfM g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-jUGv9z9zC0iyRdfM g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-jUGv9z9zC0iyRdfM g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-jUGv9z9zC0iyRdfM .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-jUGv9z9zC0iyRdfM .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-jUGv9z9zC0iyRdfM .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-jUGv9z9zC0iyRdfM .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-jUGv9z9zC0iyRdfM .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-jUGv9z9zC0iyRdfM .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-jUGv9z9zC0iyRdfM .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-jUGv9z9zC0iyRdfM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jUGv9z9zC0iyRdfM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jUGv9z9zC0iyRdfM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jUGv9z9zC0iyRdfM .edgeLabel .label text{fill:#333;}#mermaid-svg-jUGv9z9zC0iyRdfM .label div .edgeLabel{color:#333;}#mermaid-svg-jUGv9z9zC0iyRdfM .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-jUGv9z9zC0iyRdfM .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-jUGv9z9zC0iyRdfM .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-jUGv9z9zC0iyRdfM .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-jUGv9z9zC0iyRdfM .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-jUGv9z9zC0iyRdfM .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jUGv9z9zC0iyRdfM .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jUGv9z9zC0iyRdfM #statediagram-barbEnd{fill:#333333;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jUGv9z9zC0iyRdfM .cluster-label,#mermaid-svg-jUGv9z9zC0iyRdfM .nodeLabel{color:#131300;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-jUGv9z9zC0iyRdfM .note-edge{stroke-dasharray:5;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-note text{fill:black;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram-note .nodeLabel{color:black;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagram .edgeLabel{color:red;}#mermaid-svg-jUGv9z9zC0iyRdfM #dependencyStart,#mermaid-svg-jUGv9z9zC0iyRdfM #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-jUGv9z9zC0iyRdfM .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jUGv9z9zC0iyRdfM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 电源键 / 超时
用户交互 / 动画
用户交互
电源键 / 超时
ScreenOff
ScreenDim
ScreenOn
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 引入的革命性省电机制。
#mermaid-svg-OpmxSDCeGgOMM0Qi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OpmxSDCeGgOMM0Qi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OpmxSDCeGgOMM0Qi .error-icon{fill:#552222;}#mermaid-svg-OpmxSDCeGgOMM0Qi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OpmxSDCeGgOMM0Qi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OpmxSDCeGgOMM0Qi .marker.cross{stroke:#333333;}#mermaid-svg-OpmxSDCeGgOMM0Qi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OpmxSDCeGgOMM0Qi p{margin:0;}#mermaid-svg-OpmxSDCeGgOMM0Qi defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-OpmxSDCeGgOMM0Qi g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-OpmxSDCeGgOMM0Qi g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-OpmxSDCeGgOMM0Qi g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-OpmxSDCeGgOMM0Qi g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-OpmxSDCeGgOMM0Qi g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-OpmxSDCeGgOMM0Qi .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-OpmxSDCeGgOMM0Qi .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-OpmxSDCeGgOMM0Qi .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OpmxSDCeGgOMM0Qi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OpmxSDCeGgOMM0Qi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OpmxSDCeGgOMM0Qi .edgeLabel .label text{fill:#333;}#mermaid-svg-OpmxSDCeGgOMM0Qi .label div .edgeLabel{color:#333;}#mermaid-svg-OpmxSDCeGgOMM0Qi .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-OpmxSDCeGgOMM0Qi .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-OpmxSDCeGgOMM0Qi .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-OpmxSDCeGgOMM0Qi .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-OpmxSDCeGgOMM0Qi .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-OpmxSDCeGgOMM0Qi .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OpmxSDCeGgOMM0Qi #statediagram-barbEnd{fill:#333333;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .cluster-label,#mermaid-svg-OpmxSDCeGgOMM0Qi .nodeLabel{color:#131300;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-OpmxSDCeGgOMM0Qi .note-edge{stroke-dasharray:5;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-note text{fill:black;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram-note .nodeLabel{color:black;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagram .edgeLabel{color:red;}#mermaid-svg-OpmxSDCeGgOMM0Qi #dependencyStart,#mermaid-svg-OpmxSDCeGgOMM0Qi #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-OpmxSDCeGgOMM0Qi .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OpmxSDCeGgOMM0Qi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 屏幕亮 / 充电
屏幕关 / 拔充电线
静止一段时间
进入深度休眠
维护窗口
维护结束
屏幕亮 / 充电
ACTIVE
INACTIVE
IDLE_PENDING
IDLE
IDLE_MAINTENANCE
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。
cpp
// 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 死亡回收
java
// PowerManagerService.java
private final class WakeLock implements IBinder.DeathRecipient {
@Override
public void binderDied() {
// Binder 死亡,强制释放锁
synchronized (mLock) {
removeWakeLockLocked(mBinder, true);
}
}
}
7.3 DeviceIdleController 的维护窗口计算
java
// 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)算法