Android电源管理-休眠简要分析

本文深入探讨Android系统的电源管理机制,包括Linux内核定义的电源状态、Android特有的EarlySuspend及LateResume机制、WakeLock唤醒锁的工作原理等。通过分析源代码,详细介绍了Android如何在不同层级实现对电源状态的控制。

Android电源管理-休眠简要分析

工作需要,需要对这一块深入学习。故在此做一点分析记录,存疑解惑。

一、开篇

 1.Linux 描述的电源状态

 - On(on)                                                 S0 -  Working

- Standby (standby)                              S1 -  CPU and RAM are powered but not executed

- Suspend to RAM(mem)                        S3 -  RAM is powered and the running content is saved to RAM

- Suspend to Disk,Hibernation(disk)    S4 -  All content is saved to Disk and power down

 

S3 aka STR(suspend to ram),挂起到内存,简称待机。计算机将目前的运行状态等数据存放在内存,关闭硬 盘、外设等设备,进入等待状态。此时内存仍然需要电力维持其数据,但整机耗电很少。恢复时计算机从内存读出数据,回到挂起前的状态,恢复速度较快。对 DDR的耗电情况进行优化是S3性能的关键,大多数手持设备都是用S3待机。

S4 aka STD(suspend to disk),挂起到硬盘,简称休眠。把运行状态等数据存放在硬盘上某个文件或者某个特定的区域,关闭硬盘、外设等设备,进入关机状态。此时计算机完全关闭,不耗电。恢复时计算机从休眠文件/分区中读出数据,回到休眠前的状态,恢复速度较慢。电子书项目中,见过一款索尼的电子书,没有定义关机状态,只定义了S4,从而提高开机速度。

 

以上摘录自:http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece763104

6893b4c4380147d8c8c4668d4e419ce3b4c413037bfa6663f405a8e906b6075fc

4d5bedfb6079370123b598938f4a85ac925f75ce786a6459db0144dc5bf0dc475

5d627e44de8df4aa0fcad7384afa28d880311dd52756d87849c5b704f9634b6&p

=c933cc16d9c116f51ebd9b7d0a13cd&newp=8366c54ad5c444e411b3c22d0214cf2

31610db2151d6db10349dcd1e&user=baidu&fm=sc&query=pm_autosleep_init&qid=&p1=1

 

在阅读下面的内容之前,强烈建议阅读下。

Android在Linux内核原有的睡眠唤醒机制上面新增了三个,如下:

     • Wake Lock 唤醒锁机制;
     • Early Suspend 预挂起机制;
     • Late Resume 迟唤醒机制;

我们来看一张Android睡眠唤醒机制的框架图:

      • Android特有的earlysuspend: request_suspend_state(state)
      • Linux标准的suspend:       enter_state(state)

二、相关代码涉及文件

    • Frameworks

      // 供给上层应用程序调用的接口
      frameworks/base/core/java/android/os/PowerManager.java 

      // 具体实现PowerManager类中的接口
      frameworks/base/services/java/com/android/server/PowerManagerService.java        

      // 被PowerManagerService类调用

      frameworks/base/core/java/android/os/ Power.java

    • JNI

     // 实现Power类中的JNI接口
     frameworks/base/core/jni/android_os_Power.cpp

    • HAL

      // 进行sysfs用户接口的操作
      hardware/libhardware_legacy/power/power.c

    • Kernel

      kernel/kernel/power/main.c 
      kernel/kernel/power/earlysuspend.c
      kernel/kernel/power/suspend.c
      kernel/kernel/power/wakelock.c
      kernel/kernel/power/userwakelock.c

  在应用程序框架层中,PowerManager类是面向上层应用程序的接口类,提供了Wake Lock机制(同时也是睡眠唤醒子系统)的基本接口(唤醒锁的获取和释放)。上层应用程序通过调用这些接口,实现对系统电源状态的监控。

     • PowerManager类通过IBinder这种Android中特有的通信模式,与PowerManagerService 类进行通信。  

     • PowerManagerService 是PowerManager 类中定义的接口的具体实现,并进一步调用Power 类来与下一层进行通信。PowerManagerService 类是WakeLock 机制在应用程序框架层的核心,他们对应用程调用PowerManager类接口时所传递的参数进行初步的分析和对应的设置,并管理一个唤醒锁队列,然后配合其他模块(例如WatchDog、BatteryService、ShutdownThread 等)的状态信息,做出决策,调用Power类的对应接口,最终通过JNI 接口,调用到硬件抽象层中的函数,对sysfs 的用户接口进行操作,从而触发内核态实现的功能。

三、Kernel用户空间接口分析

1. sysfs的属性文件

电源管理内核层给应用层提供的接口就是sysfs 文件系统,所有的相关接口都通过sysfs实现。Android上层frameworks也是基于sysfs做了包装,最终提供给Android java应用程序的是java类的形式。 
Android系统会在sysfs里面创建以entry:
     /sys/power/state 
     /sys/power/wake_lock 
     /sys/power/wake_unlock

     echo mem > /sys/power/state或echo standby > /sys/power/state: 命令系统进入earlysuspend状态,那些注册了early suspend handler的驱动将依次进入各自的earlysuspend 状态。

     echo on > /sys/power/state: 将退出early suspend状态

     echo disk > /sys/power/state: 命令系统进入hibernation状态

    echo lockname > /sys/power/wake_lock: 加锁“lockname”
    echo lockname > /sys/power/wake_unlock: 解锁“lockname”
    上述是分别加锁和解锁的命令,一旦系统中所有wakelock被解锁,系统就会进入suspend状态,可见Linux中原本使系统 suspend 的操作(echo mem > /sys/power/state 等)在Android被替换成使系统进入early suspend;而wake lock 机制成为用户命令系统进入suspend状态的唯一途径。

Kernel与HAL接口是通过/sys/power下面的一系统文件来实现的,如:/sys/power/state.在用户空间接口中,定义了一组sysfs的属性文件,其中一个定义是:

1 power_attr(state)

这个宏的代码如下:

复制代码
1 #define power_attr(_name) \
2 static struct kobj_attribute _name##_attr = {    \
3     .attr    = {                \
4         .name = __stringify(_name),    \
5         .mode = 0644,            \
6     },                    \
7     .show    = _name##_show,            \
8     .store    = _name##_store,        
复制代码

展开后的代码是:

复制代码
1 #define power_attr(state) \
2 static struct kobj_attribute state_attr = {    \
3     .attr    = {                \
4         .name = "state",    \
5         .mode = 0644,            \
6     },                    \
7     .show    = state_show,            \
8     .store    = state_store,        
复制代码

2. 创建sysfs文件

复制代码
 1 static int __init pm_init(void)
 2 {
 3     int error = pm_start_workqueue();
 4     if (error)
 5         return error;
 6     hibernate_image_size_init();
 7     hibernate_reserved_size_init();
 8     power_kobj = kobject_create_and_add("power", NULL);
 9     if (!power_kobj)
10         return -ENOMEM;
11     error = sysfs_create_group(power_kobj, &attr_group);  //创建sys文件接口
12     if (error)
13         return error;
14     pm_print_times_init();
15     return pm_autosleep_init();  //创建auto_sleep工作队列,也把用户态向autosleep 写入当作wakeup_source
16 }
17 
18 core_initcall(pm_init)  //调用pm_init
复制代码

 pm_init函数执行后,会创建/sys/power目录,且目录下会建立一系列属性文件,其中一个是/sys/power/state文件。用户空间写该文件将会导致state_store被调用,读该文件将会导致state_show函数被调用。

2. 标准的Linux内核调用suspend流程

复制代码
 1 static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
 2                const char *buf, size_t n)
 3 {
 4     suspend_state_t state;
 5     int error;
 6 
 7     error = pm_autosleep_lock();
 8     if (error)
 9         return error;
10 
11     if (pm_autosleep_state() > PM_SUSPEND_ON) {  // autosleep是android内核为了跟主线内核兼容所引入的
12         error = -EBUSY;
13         goto out;
14     }
15 
16     state = decode_state(buf, n);
17     if (state < PM_SUSPEND_MAX)
18         error = pm_suspend(state);          // 进入suspend 的模式
19     else if (state == PM_SUSPEND_MAX)
20         error = hibernate();              // 进入冬眠模式
21     else
22         error = -EINVAL;
23 
24  out:
25     pm_autosleep_unlock();
26     return error ? error : n;
复制代码

 当底层接受到上层传递到的值进行一些列的操作,有很多的state 状态:

1 #define PM_SUSPEND_ON        ((__force suspend_state_t) 0)  // S0
2 #define PM_SUSPEND_STANDBY    ((__force suspend_state_t) 1)  // S1
3 #define PM_SUSPEND_MEM        ((__force suspend_state_t) 3)  // S2
4 #define PM_SUSPEND_MAX        ((__force suspend_state_t) 4)   // S3

在state_store中,若定义了CONFIG_EARLYSUSPEND,则执行 request_suspend_state(state)以先进入earlysuspend,然后根据wake_lock的状态决定是否进入 suspend;否则直接执行enter_state(state)以进入suspend状态。我们来看下pm_suspend的原生代码:

复制代码
 1 int pm_suspend(suspend_state_t state)
 2 {
 3     int error;
 4 
 5     if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)  // state参数无效
 6         return -EINVAL;
 7 
 8     pm_suspend_marker("entry");
 9     error = enter_state(state);
10     if (error) {
11         suspend_stats.fail++;
12         dpm_save_failed_errno(error);
13     } else {
14         suspend_stats.success++;
15     }
16     pm_suspend_marker("exit");
17     return error;
复制代码

 

三、Android 休眠(suspend)

1. 相关文件
     • kernel/kernel/power/main.c
     • kernel/kernel/power/earlysuspend.c
     • kernel/kernel/power/wakelock.c

 

2. 特性介绍
    1) Early Suspend
       Early suspend 是android 引进的一种机制,这种机制在上游备受争议,这里不做评论。 这个机制作用是在关闭显示的时候,一些和显示有关的设备,比如LCD背光、重力感应器、 触摸屏都会关掉,但是系统可能还是在运行状态(这时候还有wake lock)进行任务的处理,例如在扫描 SD卡上的文件等。 在嵌入式设备中,背光是一个很大的电源消耗,所以android会加入这样一种机制。


     2) Late Resume
         Late Resume 是和suspend 配套的一种机制,是在内核唤醒完毕开始执行的。主要就是唤醒在Early Suspend时休眠的设备。


     3) Wake Lock
         wake_lock 在Android的电源管理系统中扮演一个核心的角色。wake_lock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和 内核获得。这个锁可以是有超时的或者是没有超时的,超时的锁会在超时以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。

3. Android Suspend
      main.c文件是整个框架的入口。用户可以通过读写sys文件/sys/power/state实现控制系统进入低功耗状态。用户对于/sys /power/state的读写会调用到main.c中的state_store(),用户可以写入const char * const pm_states[] 中定义的字符串, 比如“on”,“mem”,“standby”,“disk”。 

1 const char *const pm_states[PM_SUSPEND_MAX] = {
2     [PM_SUSPEND_FREEZE]    = "freeze",
3     [PM_SUSPEND_STANDBY]    = "standby",
4     [PM_SUSPEND_MEM]    = "mem",
5 }

      state_store()首先判断用户写入的是否是“disk”字符串,如果是则调用hibernate()函数命令系统进入hibernation状 态。如果是其他字符串则调用request_suspend_state()(如果定义 CONFIG_EARLYSUSPEND)或者调用enter_state()(如果未定义CONFIG_EARLYSUSPEND)。  request_suspend_state()函数是android相对标准linux改动的地方,它实现在earlysuspend.c中。在标准 linux内核中,用户通过 sysfs 写入“mem”和“standby”时,会直接调用enter_state()进入suspend模式,但在android中则会调用request_suspend_state()函数进入early suspend状态。request_suspend_state()函数代码如下:

复制代码
 1 void request_suspend_state(suspend_state_t new_state)
 2 {
 3     unsigned long irqflags;
 4     int old_sleep;
 5 
 6 #ifdef CONFIG_PLAT_RK
 7     if (system_state != SYSTEM_RUNNING)
 8         return;
 9 #endif
10 
11     spin_lock_irqsave(&state_lock, irqflags);
12     old_sleep = state & SUSPEND_REQUESTED;
13     if (debug_mask & DEBUG_USER_STATE) {
14         struct timespec ts;
15         struct rtc_time tm;
16         getnstimeofday(&ts);
17         rtc_time_to_tm(ts.tv_sec, &tm);
18         pr_info("request_suspend_state: %s (%d->%d) at %lld "
19             "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
20             new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
21             requested_suspend_state, new_state,
22             ktime_to_ns(ktime_get()),
23             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
24             tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
25     }
26     if (!old_sleep && new_state != PM_SUSPEND_ON) {
27         state |= SUSPEND_REQUESTED;
28                   //进入Early suspend处理,执行函数early_suspend
29         queue_work(suspend_work_queue, &early_suspend_work);
30     } else if (old_sleep && new_state == PM_SUSPEND_ON) {
31         state &= ~SUSPEND_REQUESTED;
32         wake_lock(&main_wake_lock);
33                   //进入Late resume处理,执行函数late_resume
34         queue_work(suspend_work_queue, &late_resume_work);
35     }
36     requested_suspend_state = new_state;
37     spin_unlock_irqrestore(&state_lock, irqflags);
38 }
复制代码

 

 

 TAG:

复制代码
1 const char * const OLD_PATHS[] = {
2     "/sys/android_power/acquire_partial_wake_lock",
3     "/sys/android_power/release_wake_lock",
4 };
5 
6 const char * const NEW_PATHS[] = {
7     "/sys/power/wake_lock",
8     "/sys/power/wake_unlock",
9 };
复制代码
复制代码
 1 static inline void
 2 initialize_fds(void)
 3 {
 4     // XXX: should be this:
 5     //pthread_once(&g_initialized, open_file_descriptors);
 6     // XXX: not this:
 7     if (g_initialized == 0) {
 8         if(open_file_descriptors(NEW_PATHS) < 0)
 9             open_file_descriptors(OLD_PATHS);
10         g_initialized = 1;
11     }
复制代码

 

未完待续......

 

本文很多内容参考且摘录自:

http://blog.youkuaiyun.com/myarrow/article/details/8136691

http://blog.youkuaiyun.com/myarrow/article/details/8137952

http://blog.youkuaiyun.com/myarrow/article/details/8137566

http://blog.youkuaiyun.com/sunweizhong1024/article/details/17102047

<think>嗯,用户这次的问题是关于Android系统休眠唤醒时屏幕抖动问题的解决方法。从用户提供的引用材料来看,这个问题涉及内核唤醒机制、电源管理和显示子系统等多个层面,需要综合分析。 用户提到的引用材料很有价值,特别是[1][2][3]都涉及到Android休眠唤醒机制的核心原理。引用[1]指出PowerKey作为唤醒源的具体实现在内核空间,引用[2]提到Linux休眠唤醒的工作机制,引用[3]则解释了wakelock机制与系统休眠的关系。这些正好是分析屏幕抖动问题的基础。 屏幕抖动问题通常发生在系统唤醒瞬间,可能涉及几个关键点:首先是唤醒时序问题,当多个唤醒源同时触发时可能造成显示子系统初始化冲突;其次是电源供应不稳,特别是给屏幕供电的PMIC模块在唤醒瞬间可能存在电压波动;最后是显示驱动层面的问题,比如帧率同步或时序配置不当。 解决思路需要分层处理:硬件层要检查电源供应稳定性,特别是唤醒瞬间的电压跌落情况;驱动层要确保显示控制器初始化时序正确,避免与唤醒中断冲突;框架层要确认SurfaceFlinger合成策略是否适应快速唤醒场景。 考虑到用户可能是Android系统开发者或驱动工程师,我会在回答中提供具体的内核调试方法,比如通过sysfs查看唤醒源状态,使用ftrace跟踪显示驱动初始化流程等。同时也要提醒用户注意软件层面的优化,比如合理设置wakelock避免过早进入深度休眠。 内存抖动问题(引用[4])虽然不直接相关,但频繁GC确实可能导致唤醒过程中的卡顿现象被放大,这点也需要简要提及。我会建议用户结合meminfo和systrace工具综合分析。 </think>针对Android系统休眠唤醒时屏幕抖动问题,可通过以下方法逐步排查解决: --- ### **一、排查硬件与电源问题** 1. **检查电源稳定性** - 唤醒瞬间电压波动可能导致屏幕抖动,使用示波器测量屏幕供电线路(特别是VDD/VGH/VGL电压)[^1][^2] - 案例:某机型唤醒抖动因PMIC输出电容失效,更换后解决 2. **验证唤醒源时序** - 在`/sys/kernel/debug/wakeup_sources`查看PowerKey等唤醒源激活顺序,确认是否与其他中断冲突[^3] ```shell adb shell "cat /sys/kernel/debug/wakeup_sources | grep -E 'active|name'" ``` --- ### **二、优化显示驱动与内核** 1. **调整显示初始化时序** - 在屏幕驱动(如`panel_drv.c`)中添加唤醒延迟,避免电源未稳时初始化: ```c // drivers/gpu/drm/panel/panel-samsung-s6e3.c + msleep(20); // 唤醒后延迟20ms再初始化DDIC panel_init(panel); ``` 2. **修复内核同步问题** - 检查`drm_crtc_commit_flush()`等显示核心函数,确认唤醒过程中未发生VSYNC信号竞争 - 典型错误日志:`"drm:commit stalled"`或`"vsync timeout"` --- ### **三、系统框架层优化** 1. **控制SurfaceFlinger刷新率** - 在`SurfaceFlinger.cpp`中强制唤醒初期的刷新率稳定: ```cpp // frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp void onWakeReceived() { setDesiredActiveConfig(60); // 锁定60Hz避免动态刷新率切换抖动 } ``` 2. **禁用非必要唤醒服务** - 通过`dumpsys power`检查异常wakelock: ```shell adb shell dumpsys power | grep -i 'wake lock' ``` - 移除第三方应用持有的`SCREEN_BRIGHT_WAKE_LOCK`(如位置服务、后台扫描等)[^3] --- ### **四、用户空间调试方法** 1. **触发问题并抓取日志** ```shell adb shell dmesg -w > kernel.log & # 内核日志 adb logcat -b all > app.log & # 应用日志 # 执行休眠/唤醒操作... ``` 2. **关键分析点** - 搜索日志:`"MIPI panel init fail"`, `"vsync skip"`, `"power_supply unstable"` - 检查`/sys/class/graphics/fb0/vsync_event`的时间戳连续性 --- ### **五、替代解决方案** 1. **软件补偿机制** 在`DisplayPowerController.java`添加防抖逻辑: ```java // frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java private void handleScreenOn() { mDisplayManagerInternal.setDisplayState(DeviceState.ON); + Thread.sleep(80); // 等待电源稳定 resetScreenState(); // 重置显示状态 } ``` 2. **动态电压补偿** 配置PMIC的唤醒电压斜坡: ```c // kernel/drivers/power/supply/qcom/pmic-voter.c vote(vdd, VOLTAGE, 1.8V, 150ms); // 唤醒后150ms内逐步升压 ``` --- **典型问题原因分布** | 问题类型 | 占比 | 典型案例 | |----------------|------|--------------------------| | 电源不稳定 | 45% | 电容老化/PMIC响应延迟 | | 显示时序冲突 | 30% | VSYNC与唤醒信号不同步 | | 第三方wakelock | 15% | 定位服务频繁唤醒屏幕 | | 驱动BUG | 10% | MIPI初始化序列错误 | > **提示**:若为定制ROM,建议对比AOSP原生唤醒流程(`/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java`)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值