news 2026/7/28 11:48:59

Android 7系统休眠唤醒(五)休眠

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android 7系统休眠唤醒(五)休眠

系列目录:第一篇:电源管理架构全景图 | 第二篇:开机全链路 | 第三篇:关机/重启全链路 | 第四篇:休眠唤醒与开关机对比 |第五篇:休眠全链路| 第六篇:唤醒全链路 | 第七篇: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的四个取值:

常量含义
0WAKEFULNESS_AWAKE清醒(正在使用)
1WAKEFULNESS_DREAMING屏保/互动屏保(Android 4.2+)
2WAKEFULNESS_DOZING正在进入休眠(过渡状态)
3WAKEFULNESS_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_countautosleep

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);

这些通知会被PhoneWindowManagerDreamManagerService等服务接收和处理。


十、小结

休眠全链路从 Java 层一气贯通到内核层,核心路径为:

  1. 触发:电源键/超时 → PMS.goToSleep()
  2. 标记:设置 mWakefulness 跳转和 mDirty 标记位
  3. 状态机:updatePowerStateLocked() 按 Phase 执行
  4. 释放 WakeLock:两个 suspend_blocker 全部释放
  5. Native:libsuspend 写入 /sys/power/state 或 /sys/power/autosleep
  6. 内核:freeze 进程 → 挂起设备 → CPU 进入 deep sleep

整个流程在 1 秒内完成,关键在于"冻结而非杀死"的设计——下一篇将分析唤醒流程,看系统如何从中断触发到点亮屏幕,以同样的速度恢复。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/28 11:48:21

九号控制器二次开发实践:极飞A12测试与功能定制指南

这次我们来深入探讨九号控制器的二次开发&#xff0c;特别是针对极飞A12型号的测试实践。对于想要定制化智能电动车控制功能的开发者来说&#xff0c;二次开发提供了极大的灵活性&#xff0c;能够实现超出原厂功能的个性化需求。九号控制器作为智能电动车的核心控制单元&#x…

作者头像 李华
网站建设 2026/7/28 11:48:12

上门按摩平台订单越多利润越薄?破局关键在于跳出单一抽佣思维

最近在做上门按摩项目交流的圈子里&#xff0c;有一个反直觉的现象引发了广泛讨论&#xff1a;很多老板发现&#xff0c;平台每天的单量明明在涨&#xff0c;但月底一算账&#xff0c;利润却没怎么涨&#xff0c;甚至有的还在亏钱。 这其实暴露了当前行业的一个核心痛点——盈利…

作者头像 李华
网站建设 2026/7/28 11:47:49

一文搞懂C#与工控机的通信原理:Modbus/TCP协议实战教程

做工业上位机开发的朋友&#xff0c;90%都绕不开Modbus协议。 我刚入行接第一台汇川PLC的时候&#xff0c;照着文档写了一下午代码&#xff0c;要么连不上&#xff0c;要么读出来的数值全是乱码&#xff0c;蹲在车间调试到晚上十点&#xff0c;最后发现是地址偏移了1位——就这…

作者头像 李华
网站建设 2026/7/28 11:46:58

Seata分布式事务框架入门与实践指南

1. Seata 分布式事务框架快速入门第一次接触分布式事务的开发者往往会被各种专业术语和复杂配置吓退。作为阿里开源的分布式事务解决方案&#xff0c;Seata 以其简单易用的特性成为众多企业的首选。我在实际项目中多次使用 Seata 解决微服务架构下的数据一致性问题&#xff0c;…

作者头像 李华
网站建设 2026/7/28 11:45:42

直流无刷电机通用拆解教程:从工具准备到转子取出全流程详解

这次我们来看一个针对无刷风扇电机的通用拆卸教程。如果你手头有共田风扇或其他使用直流无刷电机的小家电出现故障&#xff0c;比如不转、异响或风力减弱&#xff0c;自己动手维修的第一步往往就是拆解。这个教程的核心不是高深理论&#xff0c;而是提供一套可操作、能复现的步…

作者头像 李华
网站建设 2026/7/28 11:43:12

Python游戏开发入门:Pygame界面设计核心概念与实战指南

1. 项目概述&#xff1a;为什么从Pygame界面设计开始&#xff1f;如果你刚学完Python基础语法&#xff0c;正摩拳擦掌想做个自己的小游戏&#xff0c;但面对网上那些动辄几百行的“贪吃蛇”或“飞机大战”源码&#xff0c;是不是感觉有点无从下手&#xff1f;别急&#xff0c;这…

作者头像 李华