系列目录:第一篇:电源管理架构全景图 | 第二篇:开机全链路 | 第三篇:关机/重启全链路 | 第四篇:休眠唤醒与开关机对比 |第五篇:休眠全链路| 第六篇:唤醒全链路 | 第七篇:wakelock与autosleep | 第八篇:Alarm定时唤醒 | 第九篇:libsuspend与Power HAL | 第十篇:实战调试
一、为什么要深入理解休眠流程
你可能遇到过这些问题:
- 按下电源键后屏幕黑了,但系统真的休眠了吗?为什么有时按电源键没反应?
- 应用持有 WakeLock 不释放,系统就无法休眠——这个"无法休眠"到底发生在哪一层?
- 从 Java 层的
goToSleep()到 CPU 执行 WFI 指令,中间经历了多少次状态跳转?
本篇从PowerManagerService一路下穿到内核的pm_suspend(),完整追踪一次休眠操作的调用链。不讨论原理(原理见第四篇),只追踪代码。
二、休眠触发入口
休眠有多种触发方式:超时自动休眠、电源键短按、应用主动调用。它们最终都汇聚到同一个入口:PowerManagerService.goToSleep()。
2.1 超时自动休眠
源码路径:frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
PMS 内部持有mHandler,通过sendMessageDelayed设置屏幕超时定时器。当用户操作发生时(userActivityNoUpdateLocked()调用),重置定时器;超时后触发休眠。
2.2 电源键短按
源码路径:frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
publicclassPhoneWindowManagerimplementsWindowManagerPolicy{// ...privatePowerManagermPowerManager;publicintinterceptKeyBeforeQueueing(KeyEventevent,intpolicyFlags){caseKeyEvent.KEYCODE_POWER:{// 短按 → 调用 goToSleepmPowerManager.goToSleep(eventTime,PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,0);← 触发休眠break;}}}关键设计:电源键事件在
PhoneWindowManager拦截,直接调用goToSleep(),不经过应用层。
2.3 应用调用 PowerManager.goToSleep()
源码路径:frameworks/base/core/java/android/os/PowerManager.java
publicfinalclassPowerManager{// ...privatefinalIPowerManagermService;// Binder 代理publicvoidgoToSleep(longtime){goToSleep(time,GO_TO_SLEEP_REASON_APPLICATION,0);}publicvoidgoToSleep(longtime,intreason,intflags){try{mService.goToSleep(time,reason,flags);←Binder调用到PMS}catch(RemoteExceptione){throwe.rethrowFromSystemServer();}}}关键设计:应用调用需要
DEVICE_POWER权限,通过 Binder 跨进程调用到PowerManagerService。
三、PMS.goToSleep() — 休眠的 Java 层入口
所有休眠触发方式最终都汇聚到PowerManagerService.goToSleep()。这个方法是休眠的 Java 层入口,负责加锁并调用内部实现。
源码路径:frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
publicfinalclassPowerManagerServiceextendsSystemService{// ...privatefinalObjectmLock=newObject();// PMS 内部锁@Override// Binder callpublicvoidgoToSleep(longeventTime,intreason,intflags){synchronized(mLock){goToSleepInternal(eventTime,reason,flags,Process.SYSTEM_UID);}}privatevoidgoToSleepInternal(longeventTime,intreason,intflags,intuid){synchronized(mLock){if(goToSleepNoUpdateLocked(eventTime,reason,flags,uid)){updatePowerStateLocked();← 核心状态机}}}}关键设计:核心两步——
goToSleepNoUpdateLocked()修改状态标记,updatePowerStateLocked()根据标记执行实际状态变更。
四、goToSleepNoUpdateLocked() — 状态标记修改
goToSleepNoUpdateLocked()只修改标记,不做实际操作。这是 PMS 的典型设计模式:NoUpdateLocked系列方法只负责状态计算,真正的副作用由updatePowerStateLocked()统一执行。
源码路径:frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
publicfinalclassPowerManagerServiceextendsSystemService{// ...privateintmWakefulness;// 当前唤醒状态privateintmDirty;// 状态变更标记位privateintmLastSleepReason;// 最近一次休眠原因privatebooleangoToSleepNoUpdateLocked(longeventTime,intreason,intflags,intuid){if(mWakefulness==WAKEFULNESS_AWAKE){mLastSleepReason=reason;// 记录休眠原因setWakefulnessLocked(WAKEFULNESS_DOZING);// 状态跳转:AWAKE → DOZINGmDirty|=DIRTY_WAKEFULNESS;// 通知状态机处理休眠mDirty|=DIRTY_WAKE_LOCKS;// 释放屏幕 WakeLockmDirty|=DIRTY_DISPLAY_POWER;// 关闭屏幕returntrue;}returnfalse;}}关键设计:方法名中的
NoUpdateLocked表明它只修改标记、不触发副作用——这是 PMS 的核心设计模式,所有状态变更最终由updatePowerStateLocked()统一调度。
mWakefulness的四个取值:
| 值 | 常量 | 含义 |
|---|---|---|
| 0 | WAKEFULNESS_AWAKE | 清醒(正在使用) |
| 1 | WAKEFULNESS_DREAMING | 屏保/互动屏保(Android 4.2+) |
| 2 | WAKEFULNESS_DOZING | 正在进入休眠(过渡状态) |
| 3 | WAKEFULNESS_ASLEEP | 已休眠 |
五、updatePowerStateLocked() — 状态机核心调度
updatePowerStateLocked()是 PMS 最核心的函数。每当mDirty不为零时调用,根据标记位按 Phase 顺序执行相应的状态更新。
源码路径:frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
publicfinalclassPowerManagerServiceextendsSystemService{// ...privatebooleanmSystemReady;// 系统是否就绪privateintmDirty;// 状态变更标记位privateintmWakeLockSummary;// 活跃 WakeLock 聚合privatebooleanmDisplayReady;// 显示是否就绪privatevoidupdatePowerStateLocked(){if(!mSystemReady||mDirty==0)return;// Phase 1: 更新 WakeLock 聚合状态updateWakeLockSummaryLocked();// Phase 2: 更新用户活动计数器(超时检测)updateUserActivitySummaryLocked(dirty,now);// Phase 3: 更新休眠状态updateWakefulnessLocked(dirty);// Phase 4: 更新显示状态(屏幕开关)booleandisplayBecameReady=updateDisplayPowerStateLocked(dirty);// Phase 5: 更新屏幕亮度updateScreenBrightnessLocked(dirty);// Phase 6: 管理 suspend blockerupdateSuspendBlockerLocked();}}关键设计:六个 Phase 按固定顺序执行,每个 Phase 只处理一个维度的状态更新。这种"收集标记 → 分阶段处理"的模式避免了状态变更之间的循环依赖。
当执行休眠路径时,各 Phase 行为如下:
5.1 updateWakeLockSummaryLocked() — 汇总 WakeLock
汇总当前所有活跃 WakeLock 的类型标志位。如果全部释放(mWakeLockSummary == 0),系统可以安全休眠。
| 标志位 | 对应 WakeLock 类型 |
|---|---|
WAKE_LOCK_CPU(1<<0) | PARTIAL_WAKE_LOCK |
WAKE_LOCK_SCREEN_BRIGHT(1<<1) | 亮屏锁 |
WAKE_LOCK_SCREEN_DIM(1<<2) | 暗屏锁 |
WAKE_LOCK_BUTTON_BRIGHT(1<<3) | 按键亮屏锁 |
WAKE_LOCK_PROXIMITY_SCREEN_OFF(1<<4) | 接近传感器锁 |
5.2 updateUserActivitySummaryLocked() — 超时检测
检测用户多久没有交互。如果超过mScreenOffTimeout(屏幕超时时间),标记DIRTY_WAKEFULNESS,触发状态从 AWAKE 向 ASLEEP 迁移。
5.3 updateWakefulnessLocked() — 状态跳转
如果mDirty包含DIRTY_WAKEFULNESS,调用reallyGoToSleepLocked()完成最终的状态跳转:
publicfinalclassPowerManagerServiceextendsSystemService{// ...privateintmWakefulness;// 当前唤醒状态privateintmDirty;// 状态变更标记位privatevoidreallyGoToSleepLocked(longeventTime){setWakefulnessLocked(WAKEFULNESS_ASLEEP);// DOZING → ASLEEPmDirty|=DIRTY_WAKEFULNESS|DIRTY_DISPLAY_POWER;}}关键设计:
reallyGoToSleepLocked()将状态从 DOZING 跳转到 ASLEEP,这是休眠流程中最后一次状态变更。
5.4 updateDisplayPowerStateLocked() — 关闭屏幕
调用DisplayPowerController关闭屏幕。如果屏幕已关闭,设置mDisplayReady = true,触发更深层的休眠操作。
5.5 updateSuspendBlockerLocked() — 管理 suspend blocker
管理两个关键的 suspend_blocker:
| suspend_blocker | 职责 | 释放条件 |
|---|---|---|
PowerManagerService.WakeLocks | 汇总所有部分唤醒锁 | 所有应用层 WakeLock 释放 |
PowerManagerService.Display | 控制屏幕显示 | 屏幕关闭后 |
当两个 suspend_blocker 都释放后,系统进入真正的休眠。
六、nativeSetAutoSuspend() — JNI 跨越
当 PMS 完成所有状态变更后,通过 JNI 调用 Native 层的nativeSetAutoSuspend(),开始进入底层休眠流程。
源码路径:frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
staticvoidnativeSetAutoSuspend(JNIEnv*env,jclass,jboolean enable){if(enable){autosuspend_enable();← 启用自动休眠}else{autosuspend_disable();← 禁用自动休眠}}关键设计:JNI 层只是一个简单的转发,真正的逻辑在
libsuspend库中实现。
七、libsuspend — Native 层休眠触发
libsuspend是 Android 提供的用户空间库,负责与内核的休眠机制交互。它支持两种方式:wakeup_count和autosleep。
7.1 autosuspend_enable() — 入口函数
源码路径:system/core/libsuspend/autosuspend.c
intautosuspend_enable(void){returnautosuspend_ops->enable();← 调用具体实现}autosuspend_ops是一个函数指针结构体,根据内核是否支持 autosleep 选择不同的实现。
7.2 wakeup_count 方式 — 手动管理休眠时机
源码路径:system/core/libsuspend/autosuspend_wakeup_count.c
staticintautosuspend_wakeup_count_enable(void){// 1. 读取当前 wakeup_countfd=open("/sys/power/wakeup_count",O_RDWR);read(fd,buf,sizeof(buf));// 2. 写入 wakeup_count 确认无新唤醒事件write(fd,buf,strlen(buf));// 3. 写入 "mem" 到 /sys/power/statewrite(state_fd,"mem",3);← 触发内核休眠}关键设计:
wakeup_count机制解决了关键的竞态问题——读出当前值,写入确认无新事件,再触发休眠。如果期间发生唤醒事件,wakeup_count自增,写入失败,避免"丢失"唤醒。
7.3 autosleep 方式 — 内核自动管理(推荐)
源码路径:system/core/libsuspend/autosuspend_autosleep.c
staticintautosuspend_autosleep_enable(void){write(fd,"mem",3);← 写入/sys/power/autosleep}关键设计:
autosleep方式的优势是内核自己管理休眠时机。用户空间只需写入一次"mem",内核的 autosleep 线程会在每次 wakelock 释放后自动尝试休眠,无需用户空间反复写入。
八、内核层 — pm_suspend() 完整路径
当/sys/power/state被写入"mem"后,内核执行休眠。内核的休眠流程分为五个阶段:准备、冻结、挂起设备、进入休眠、恢复。
8.1 state_store() — sysfs 回调入口
源码路径:kernel/power/main.c
staticssize_tstate_store(structkobject*kobj,structkobj_attribute*attr,constchar*buf,size_tn){suspend_state_tstate;error=pm_suspend(state);← 进入内核休眠主入口returnerror?error:n;}关键设计:
state_store()是 sysfs 的写回调,用户空间写入/sys/power/state时触发。
8.2 pm_suspend() — 休眠主入口
源码路径:kernel/power/suspend.c
intpm_suspend(suspend_state_tstate){if(state==PM_SUSPEND_ON)return-EINVAL;error=enter_state(state);← 进入具体休眠流程returnerror;}staticintenter_state(suspend_state_tstate){// 1. 同步文件系统sys_sync();// 2. 挂起前准备suspend_prepare();// 3. 挂起设备和中断suspend_devices_and_irq();// 4. 挂起系统核心syscore_suspend();// 5. 进入休眠suspend_enter(state);// 6. 恢复(被唤醒后执行)syscore_resume();return0;}关键设计:
enter_state()按顺序执行五个阶段,每个阶段都有明确的职责。被唤醒后从suspend_enter()返回,继续执行恢复流程。
8.3 freeze_processes() — 冻结所有用户进程
源码路径:kernel/power/process.c
intfreeze_processes(void){// 遍历所有用户进程for_each_process_thread(g,p){freeze_task(p);← 冻结单个进程}return0;}boolfreeze_task(structtask_struct*p){// 1. 设置 TIF_FREEZE 标志set_tsk_thread_flag(p,TIF_FREEZE);// 2. 发送伪信号唤醒进程wake_up_process(p);// 3. 进程在返回用户空间前检测 TIF_FREEZE,// 调用 try_to_freeze() 进入 TASK_UNINTERRUPTIBLE 状态}关键设计:冻结机制的精妙之处在于不是直接挂起进程,而是在进程即将返回用户空间时拦截。内核线程需要主动调用
try_to_freeze()检查标志。
8.4 suspend_enter() — 进入硬件休眠
源码路径:kernel/power/suspend.c
staticintsuspend_enter(suspend_state_tstate,bool*wakeup){// 1. 禁用中断arch_suspend_disable_irqs();// 2. 挂起系统核心syscore_suspend();// 3. 调用平台相关的休眠代码error=suspend_ops->enter(state);// → 通常执行:// ① 保存 CPU 寄存器状态// ② 配置唤醒源// ③ 执行 WFI (Wait For Interrupt) 指令// ④ CPU 进入 low-power state// 4. 被唤醒后,从这里继续执行syscore_resume();returnerror;}关键设计:
suspend_ops->enter()是平台相关的回调,由具体 SoC 实现。ARM 架构通常执行 WFI 指令让 CPU 进入低功耗状态,等待唤醒中断。
八、休眠流程完整调用链
Java层 (Framework) PowerManager.goToSleep() → PMS.goToSleepInternal() → goToSleepNoUpdateLocked() setWakefulnessLocked(WAKEFULNESS_DOZING) mDirty |= DIRTY_WAKEFULNESS | DIRTY_DISPLAY_POWER | DIRTY_WAKE_LOCKS → updatePowerStateLocked() [PMS 核心状态机] → updateWakeLockSummaryLocked() [汇总 WakeLock] → updateUserActivitySummaryLocked() [检测超时] → updateWakefulnessLocked() [状态跳转: DOZING → ASLEEP] → reallyGoToSleepLocked() → updateDisplayPowerStateLocked() [关闭屏幕] → DisplayPowerController.setState() → updateSuspendBlockerLocked() [释放 suspend_blocker] JNI层 nativeSetAutoSuspend(true) → autosuspend_enable() Native层 (libsuspend) autosuspend_enable() → wakeup_count方式: 写入 "mem" 到 /sys/power/state → autosleep方式: 写入 "mem" 到 /sys/power/autosleep Kernel层 state_store() [sysfs回调] → pm_suspend(state) → enter_state(state) → sync_filesystem() [文件系统同步] → suspend_prepare() [挂起前准备] → freeze_processes() [冻结所有用户进程] → suspend_devices_and_irq() [挂起设备、禁用IRQ] → syscore_suspend() [挂起系统核心] → suspend_enter() → suspend_ops->enter() [平台相关: WFI指令] → CPU进入deep sleep九、休眠中的关键数据流
9.1 Screen WakeLock 的释放时机
PMS 内部维护的mWakeLocks列表中有一个特殊的DisplayWakeLock(由 PMS 自己持有)。在goToSleepNoUpdateLocked()中设置DIRTY_WAKE_LOCKS标记后,updateWakeLockSummaryLocked()会将其标记为可释放,在updateSuspendBlockerLocked()中实际释放。
9.2 Notifier 的广播
源码路径:frameworks/base/services/core/java/com/android/server/power/Notifier.java
在updatePowerStateLocked()执行过程中,Notifier 向系统发送电源状态变更通知:
// 屏幕变暗mNotifier.onScreenStateChange(SCREEN_STATE_OFF);// 进入休眠mNotifier.onWakefulnessChangeStarted(WAKEFULNESS_ASLEEP);这些通知会被PhoneWindowManager、DreamManagerService等服务接收和处理。
十、小结
休眠全链路从 Java 层一气贯通到内核层,核心路径为:
- 触发:电源键/超时 → PMS.goToSleep()
- 标记:设置 mWakefulness 跳转和 mDirty 标记位
- 状态机:updatePowerStateLocked() 按 Phase 执行
- 释放 WakeLock:两个 suspend_blocker 全部释放
- Native:libsuspend 写入 /sys/power/state 或 /sys/power/autosleep
- 内核:freeze 进程 → 挂起设备 → CPU 进入 deep sleep
整个流程在 1 秒内完成,关键在于"冻结而非杀死"的设计——下一篇将分析唤醒流程,看系统如何从中断触发到点亮屏幕,以同样的速度恢复。