Android 7系统休眠唤醒(六)唤醒全链路—Kernel Resume到屏幕点亮
系列目录第一篇电源管理架构全景图 | 第二篇开机全链路 | 第三篇关机/重启全链路 | 第四篇休眠唤醒与开关机对比 | 第五篇休眠全链路 |第六篇唤醒全链路| 第七篇wakelock与autosleep | 第八篇Alarm定时唤醒 | 第九篇libsuspend与Power HAL | 第十篇实战调试一、为什么要深入理解唤醒流程你可能遇到过这些问题按电源键后屏幕亮了但从按键到亮屏经历了多少层调用为什么有时按了没反应闹钟能准时唤醒系统这个准时是如何保证的内核休眠时谁在计时唤醒后应用恢复运行ART 虚拟机和 Activity 栈是如何原地复活的上一篇追踪了从 Java 层到内核 CPU 进入 deep sleep 的完整路径。现在系统处于静止状态——仅 RAM 自刷新供电若干唤醒源处于监听状态。本篇追踪从唤醒信号触发到用户看到亮屏的完整恢复过程。二、唤醒源全景 — 谁能唤醒系统唤醒是被动的——系统不会主动醒来必须由外部事件触发。Android 支持的主要唤醒源唤醒源类型具体来源硬件层面GPIO 按键电源键、音量键、Home 键GPIO 边沿触发中断RTC Alarm闹钟、定时任务、Doze 维护窗口RTC 定时器中断Modem 事件来电、收到短信、网络切换Modem 的 IPC 中断USB 事件USB 插拔、充电器插入USB PHY 中断其他外设耳机插拔、SD 卡插拔等对应硬件的 IRQ2.1 为什么这些中断能唤醒 CPU在休眠前suspend_enter()阶段内核通过enable_irq_wake()将这些中断源标记为唤醒源源码路径kernel/irq/manage.cintenable_irq_wake(unsignedintirq){// 设置 IRQ 的 wakeup 标志// 即使 CPU 处于 deep sleep该 IRQ 仍能触发// 中断控制器在检测到唤醒源边沿时向 CPU 发送唤醒信号}关键设计enable_irq_wake()标记的唤醒源在 CPU 执行 WFI 指令后仍然有效中断控制器持续监听这些 IRQ 的边沿信号。三、Kernel 层唤醒 — 从中断到 thaw 进程当唤醒源触发时CPU 从中断唤醒内核开始执行恢复流程。恢复顺序与休眠顺序严格相反。3.1 CPU 被中断唤醒当唤醒源触发时唤醒源如电源键按下 → GPIO 边沿检测 → 中断控制器 → 向 CPU 发送唤醒信号 → CPU 退出 WFI / low-power state → 跳转到唤醒入口代码 → resume 路径唤醒入口是平台相关的汇编代码负责恢复 CPU 寄存器状态、MMU、缓存等然后跳转到 C 语言的 resume 函数。3.2 suspend_enter() 的返回源码路径kernel/power/suspend.c在第五篇中suspend_enter()调用suspend_ops-enter()后 CPU 进入休眠。现在被中断唤醒代码从suspend_ops-enter()的下一行继续执行staticintsuspend_enter(suspend_state_tstate,bool*wakeup){// ... 进入休眠前的准备工作 ...errorsuspend_ops-enter(state);// ⬆ CPU在这里进入休眠// ⬇ CPU被唤醒后从这里继续执行// 检查唤醒原因*wakeuptrue;// 恢复系统核心syscore_resume();return0;}关键设计suspend_ops-enter()是一个阻塞调用——CPU 休眠时执行流暂停在此被唤醒后从这里继续执行。3.3 enter_state() 的恢复阶段源码路径kernel/power/suspend.csuspend_enter()返回后enter_state()按休眠时的逆序执行恢复staticintenter_state(suspend_state_tstate){// 休眠路径 suspend_prepare();suspend_devices_and_irq();syscore_suspend();suspend_enter(state,wakeup);// ⬆ 休眠在此, ⬇ 唤醒后继续// 唤醒路径 // 1. 恢复系统核心non-boot CPU、系统时钟、中断控制器等syscore_resume();// 2. 恢复设备驱动解除 IRQ 禁用恢复设备状态suspend_devices_and_irq_resume();// 3. 解冻所有用户进程thaw_processes();// 4. 完成唤醒suspend_finish();return0;}关键设计恢复顺序与挂起顺序严格相反——先挂起的后恢复后挂起的先恢复。这确保了设备依赖关系的正确性。3.4 syscore_resume() — 恢复系统核心源码路径kernel/power/syscore.c唤醒第一步恢复系统关键基础设施恢复 non-boot CPUSMP 系统中休眠时关闭了除 CPU0 以外的所有核恢复系统时钟源timekeeping_resume()恢复中断控制器的非唤醒 IRQ恢复控制台console_resume()3.5 suspend_devices_and_irq_resume() — 恢复设备源码路径kernel/power/suspend.c按休眠时的逆序逐一恢复设备voidsuspend_devices_and_irq_resume(void){// 1. 解除 IRQ 的禁用状态// 2. early resume总线控制器先恢复// dpm_resume_start(PMSG_RESUME)// 3. device resume设备按挂载顺序的逆序恢复// dpm_resume(PMSG_RESUME)// 4. 恢复平台固件// 5. 恢复控制台}关键设计恢复顺序与挂起顺序严格相反。例如USB 设备先于 USB 控制器挂起那么 USB 控制器必须先于 USB 设备恢复。3.6 thaw_processes() — 解冻所有进程源码路径kernel/power/process.cvoidthaw_processes(void){structtask_struct*g,*p;// 遍历所有进程for_each_process_thread(g,p){__thaw_task(p);}}staticvoid__thaw_task(structtask_struct*p){// 清除 TIF_FREEZE 标志// 将进程从 TASK_UNINTERRUPTIBLE 唤醒到 TASK_RUNNINGwake_up_process(p);}关键设计解冻后所有用户进程恢复运行——但这对它们来说是透明的它们感觉不到自己被冻结过。ART/Dalvik 虚拟机也在此刻恢复堆内存完整可用。四、Native 层 — libsuspend 的唤醒检测内核完成恢复后用户空间的 libsuspend 检测到系统已唤醒通知 Java 层开始亮屏流程。4.1 autosuspend 模式的唤醒处理源码路径system/core/libsuspend/autosuspend.c在 wakeup_count 模式下写入/sys/power/state的write()系统调用在系统被唤醒后返回。libsuspend 的 autosuspend 线程检测到返回staticvoid*autosuspend_thread_func(void*arg){while(1){// 写入 mem 到 /sys/power/state// 此调用在内核唤醒后返回retwrite(state_fd,mem,3);// 唤醒后通知上层// 调用 autosuspend 的回调}}关键设计write()是一个阻塞调用——系统休眠时执行流暂停在此被唤醒后返回。这是用户空间感知唤醒的关键机制。在 autosleep 模式下内核 autosleep 线程在唤醒后自动重新评估是否应该休眠。如果还有 active wakelock内核等待 wakelock 释放如果没有立即再次休眠。4.2 JNI 回调唤醒后libsuspend 通过 JNI 通知 Java 层源码路径frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp// 当检测到系统唤醒时nativeSetAutoSuspend(env,clazz,JNI_FALSE);// → autosuspend_disable()关键设计JNI 层只是一个简单的转发真正的逻辑在libsuspend库中实现。五、Java 层 — PMS.wakeUp() 的状态机Native 层通知唤醒后Java 层的PowerManagerService开始执行唤醒流程。与休眠流程对称wakeUpNoUpdateLocked()修改标记updatePowerStateLocked()执行操作。5.1 wakeUp() 入口源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.javapublicfinalclassPowerManagerServiceextendsSystemService{// ...privatefinalObjectmLocknewObject();// PMS 内部锁Override// Binder callpublicvoidwakeUp(longeventTime,StringopPackageName){synchronized(mLock){wakeUpInternal(eventTime,opPackageName,Process.SYSTEM_UID);}}privatevoidwakeUpInternal(longeventTime,StringopPackageName,intuid){synchronized(mLock){if(wakeUpNoUpdateLocked(eventTime,opPackageName,uid)){updatePowerStateLocked();← 核心状态机}}}}关键设计与休眠流程完全对称——wakeUpNoUpdateLocked()只修改标记updatePowerStateLocked()根据标记执行实际状态变更。5.2 wakeUpNoUpdateLocked()源码路径frameworks/base/services/core/java/com/android/server/power/PowerManagerService.javapublicfinalclassPowerManagerServiceextendsSystemService{// ...privateintmWakefulness;// 当前唤醒状态privateintmDirty;// 状态变更标记位privateStringmLastWakeReason;// 最近一次唤醒原因privatebooleanwakeUpNoUpdateLocked(longeventTime,StringopPackageName,intuid){if(mWakefulnessWAKEFULNESS_ASLEEP||mWakefulnessWAKEFULNESS_DREAMING||mWakefulnessWAKEFULNESS_DOZING){setWakefulnessLocked(WAKEFULNESS_AWAKE);// 状态跳转回 AWAKEmLastWakeReasonopPackageName;// 记录唤醒来源mDirty|DIRTY_WAKEFULNESS;// 通知状态机处理唤醒mDirty|DIRTY_WAKE_LOCKS;// 重新获取屏幕 WakeLockmDirty|DIRTY_DISPLAY_POWER;// 准备点亮屏幕userActivityNoUpdateLocked(eventTime,...);// 重置超时计时器returntrue;}returnfalse;}}关键设计wakeUpNoUpdateLocked()将状态从 ASLEEP/DOZING/DREAMING 跳转回 AWAKE并设置DIRTY标记通知状态机执行后续操作。5.3 updatePowerStateLocked() — 唤醒的 Phase 处理与休眠使用相同的updatePowerStateLocked()但mDirty标记导致不同的分支Phase 1: updateWakeLockSummaryLocked()唤醒后PMS 重新获取SCREEN_BRIGHT_WAKE_LOCK确保屏幕点亮期间系统不休眠回去。Phase 2: updateUserActivitySummaryLocked()重置用户活动计时器根据mScreenOffTimeout设置下一次超时休眠的时间。Phase 3: updateWakefulnessLocked()状态已从 ASLEEP 跳转为 AWAKE此阶段无需额外操作。Phase 4: updateDisplayPowerStateLocked()这是唤醒中最关键的一步——点亮屏幕。5.4 DisplayPowerController — 屏幕点亮时序源码路径frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.javapublicfinalclassDisplayPowerController{// ...privateDisplayPowerStatemDisplayPowerState;// 显示状态管理privatevoidupdatePowerState(){// 1. 决定目标状态ON / OFF / DOZEinttargetPowerManager.SCREEN_STATE_ON;// 2. 发送请求给 DisplayPowerStatemDisplayPowerState.setScreenState(target);// 3. 设置亮度可通过渐变动画// animateScreenBrightness(brightness, rampRate);// 4. 通知 DisplayManagermCallbacks.onDisplayStateChange(target);}}关键设计DisplayPowerController负责协调屏幕状态变更通过DisplayPowerState向下传递到 SurfaceFlinger → Hardware Composer → 显示驱动。屏幕点亮的硬件路径DisplayPowerController → DisplayManagerService → SurfaceFlinger (native) → Hardware Composer (HWC) → 显示驱动 → 实际物理屏幕通电、显示5.5 updateSuspendBlockerLocked()唤醒阶段PMS 重新获取两个关键 suspend_blockersuspend_blocker职责获取时机PowerManagerService.Display确保屏幕点亮期间不休眠屏幕点亮前PowerManagerService.WakeLocks汇总所有活跃应用 WakeLock应用持有 WakeLock 时六、唤醒后的收尾 — 锁屏与按键处理屏幕点亮后系统还需要处理一些收尾工作分发按键事件、显示锁屏界面、发送唤醒广播。6.1 按键事件分发唤醒由按键触发时如电源键PhoneWindowManager在休眠前拦截了按键事件。唤醒后PhoneWindowManager需要处理该事件——通常是显示锁屏界面。源码路径frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.javapublicclassPhoneWindowManagerimplementsWindowManagerPolicy{// ...privatebooleanisScreenOn;// 屏幕是否点亮publicintinterceptKeyBeforeQueueing(KeyEventevent,intpolicyFlags){caseKeyEvent.KEYCODE_POWER:{if(isScreenOn){// 屏幕亮着 → 正常处理可能是锁屏}else{// 屏幕灭着 → 仅唤醒不向上传递按键事件result|ACTION_PASS_TO_USER;}}}}关键设计PhoneWindowManager在按键事件分发链的最前端负责拦截电源键等系统按键决定是唤醒屏幕还是传递给应用。6.2 Keyguard锁屏显示源码路径frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java唤醒后KeyguardViewMediator判断是否需要显示锁屏如果没有安全锁PIN/图案/密码直接显示滑动解锁如果有安全锁显示对应解锁界面如果设置了 Smart Lock信任设备/地点自动解锁6.3 Notifier 的唤醒广播源码路径frameworks/base/services/core/java/com/android/server/power/Notifier.javapublicclassNotifier{// ...privatefinalWindowManagerPolicymPolicy;// 窗口管理策略// 屏幕亮起mNotifier.onScreenStateChange(SCREEN_STATE_ON);// 设备交互启动mNotifier.onWakefulnessChangeStarted(WAKEFULNESS_AWAKE);}关键设计Notifier负责向系统其他组件广播电源状态变更确保所有服务同步感知唤醒事件。七、唤醒流程完整调用链唤醒源触发 │ GPIO按键 / RTC Alarm / Modem事件 / USB事件 ▼ 中断控制器 → CPU退出WFI → 跳转到唤醒入口 │ ▼ Kernel层 (逆序恢复) syscore_resume() [恢复non-boot CPU、时钟、中断控制器] → suspend_devices_and_irq_resume() [设备early resume → device resume] → thaw_processes() [解冻所有用户进程] → suspend_finish() [完成唤醒] │ ▼ Native层 write(/sys/power/state, mem) 返回 [wakeup_count模式] 或 autosleep线程检测到唤醒 [autosleep模式] → autosuspend_disable() │ ▼ JNI层 nativeSetAutoSuspend(false) → Java层回调 │ ▼ Java层 (Framework) PMS.wakeUp() → wakeUpInternal() → wakeUpNoUpdateLocked() setWakefulnessLocked(WAKEFULNESS_AWAKE) mDirty | DIRTY_WAKEFULNESS | DIRTY_DISPLAY_POWER | DIRTY_WAKE_LOCKS → updatePowerStateLocked() [PMS核心状态机] → updateWakeLockSummaryLocked() [重新获取WakeLock] → updateUserActivitySummaryLocked() [重置超时计时器] → updateWakefulnessLocked() [状态确认] → updateDisplayPowerStateLocked() [点亮屏幕] → DisplayPowerController.setState() → SurfaceFlinger → HWC → 显示驱动 → 物理亮屏 → updateScreenBrightnessLocked() [恢复亮度] → updateSuspendBlockerLocked() [重获suspend_blocker] │ ▼ PhoneWindowManager → 分发按键 / 显示锁屏 KeyguardViewMediator → 显示解锁界面 Notifier → 广播 SCREEN_ON / WAKEFULNESS_AWAKE七、唤醒 vs 开机的核心区别将唤醒流程与第二篇的开机流程对比一目了然开机阶段唤醒中对应的操作BootROM → BootLoader → Kernel 启动❌ 跳过内核一直保持运行init → 守护进程启动❌ 跳过进程仅解冻Zygote 预加载类❌ 跳过ART 堆内存保留SystemServer 三阶段启动服务❌ 跳过服务仅恢复调度Launcher 冷启动❌ 跳过Activity 栈保留—✅ 从中断唤醒 CPU—✅ 恢复非 boot CPU—✅ 恢复设备驱动—✅ 解冻进程—✅ 点亮屏幕唤醒跳过了开机的所有初始化工作只做了三件事恢复 CPU → 恢复设备 → 解冻进程。这就是为什么唤醒能在亚秒级完成。八、小结唤醒全链路的核心路径唤醒源触发→ 中断控制器唤醒 CPU内核恢复syscore → devices → IRQ逆序恢复thaw_processes()解冻而非重启所有进程libsuspend 检测write() 返回通知 Java 层PMS.wakeUp()状态机从 ASLEEP → AWAKE亮屏DisplayPowerController → HWC → 物理点屏收尾锁屏显示、按键处理、通知广播整个过程在 1 秒内完成。与关机→开机的 30-60 秒相比唤醒的速度优势来自状态保持的核心设计——所有数据都在 RAM 中原地等待恢复仅是解冻。