文章目录
由于博主不是很熟悉Java,只是大致梳理了以下流程,不涉及具体细节。浏览本文,可以大概了解Android wakelock的概念及其在Android发挥的作用、Android suspend流程。
一、Wakelock
wakelocks最初出现在Android为linux kernel打的一个补丁集,该补丁集实现了一个名称为“wakelocks”的系统调用,该系统调用允许调用者阻止系统进入低功耗模式(如idle、suspend等)。同时,该补丁集更改了Linux kernel原生的电源管理执行过程(kernel/power/main.c中的state_show和state_store),转而执行自定义的state_show、state_store。
Android wakelocks提供的功能包括:
a) /sys/power/wake_lock:一个sysfs文件,用户程序向文件写入一个字符串,即可创建一个wakelock,该字符串就是wakelock的名字。该wakelock可以阻止系统进入低功耗模式。
b) /sys/power/wake_unlock,一个sysfs文件,用户程序向文件写入相同的字符串,即可注销一个wakelock。
c) 当系统中所有的wakelock都注销后,系统可以自动进入低功耗状态。
d) 向内核其它driver也提供了wakelock的创建和注销接口,允许driver创建wakelock以阻止睡眠、注销wakelock以允许睡眠
1.1 WakeLock说明
具体请参考PowerManager,以下部分截图和简单文字介绍:
PARTIAL_WAKE_LOCK:保持CPU在运行,屏幕和键盘背光将被允许熄灭;如果用户按下Power键,屏幕会关闭,但CPU将继续运行,直到所有部分唤醒锁都已释放;
PROXIMITY_SCREEN_OFF_WAKE_LOCK:和接近传感器配合,当用户接近屏幕时黑屏,离开时亮屏(例如打电话),此API在API21后开放,以前被hide;不能与ACQUIRE_CAUSES_WAKEUP一起使用,可用isWakeLockLevelSupported(int) 用于确定设备是否支持此唤醒锁;
FULL_WAKE_LOCK:确保屏幕和键盘背光灯全亮;如果用户按下Power键, FULL_WAKE_LOCK将隐式释放,同时关闭屏幕和CPU(此API级别17中已弃用);
SCREEN_DIM_WAKE_LOCK:确保屏幕处于打开状态,键盘背光将被熄灭。如果用户按下Power键,SCREEN_DIM_WAKE_LOCK将隐式释放,同时关闭屏幕和CPU(此API级别17中已弃用);
SCREEN_BRIGHT_WAKE_LOCK:确保屏幕以全亮度开启,键盘背光将被熄灭;如果用户按下Power键,SCREEN_BRIGHT_WAKE_LOCK将隐式释放,同时关闭屏幕和CPU(此API级别15中已弃用);
ACQUIRE_CAUSES_WAKEUP:不会唤醒设备,强制亮屏,键盘灯开启。有一个例外,如果有notification弹出的话,会唤醒设备;不能与PARTIAL_WAKE_LOCK一起使用;
ON_AFTER_RELEASE:当锁被释放时,保持亮屏一段时间(如果释放时屏幕没亮,则不会亮屏)
1.2 WakeLock使用
使用public PowerManager.WakeLock newWakeLock (int levelAndFlags, String tag)申请一个wakelock
public PowerManager.WakeLock newWakeLock (int levelAndFlags, String tag)
//levelAndFlags int: Combination of wake lock level and flag values defining the requested behavior of the WakeLock.
//tag String: Your class name (or other tag) for debugging purposes.
1.3 PMS下WaeLock从上到下流程图
特别说明:
在acquireWakeLockInternal( )函数中,调用了setWakeLockDisabledStateLocked( )函数,
在PMS中,当设备进入doze时,除了白名单中的应用程序,该函数将禁用应用程序持有的所有PARTIAL_WAKE_LOCK
另外,从代码流程看,当PMS往sysfs写内容时,Android Q(包括Android R)使用HIDL重构了HAL层的接口的实现(当然,最终到底层还是使用write()函数,往节点写内容),Android P是直接调用write( )函数
1.4 wakelock sysfs节点
1.4.1 亮屏前后,wake_lock节点变化:
PowerManager.SuspendLockout:
/frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
PowerManagerService.Display :
/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
PowerManagerService( )-> synchronized( )
1.4.2 亮屏前后,wake_unlock节点变化:
1.5 wakeup_sources
一个wakeup source代表了一个具有唤醒能力的设备,也称该设备为一个wakeup source
name:该wakeup source的名称,一般为对应的device name; active_count:wakeup source
activate的次数; event_count:wakeup source上报的event个数;
wakeup_count:wakeup source终止suspend过程的次数; expire_count:wakeup source
timeout到达的次数; active_since::wakeup source从上次持锁到cat这个文件时持续处于ative状态的时间
total_time:wakeup source处于activate状态的总时间(可以反映该wakeup
source对应的设备的繁忙、耗电程度); max_time:wakeup
source持续处于activate状态的最大时间(越长越不合理); last_change:wakeup source状态变化的时间点。
prevent_suspend_time:wakeup source阻止内核autosleep触发系统休眠流程的总时间
1.6 wakelock architecture
-
wakeup events framework
core,在drivers/base/power/wakeup.c中实现,提供了wakeup events
framework的核心功能,包括:
i. 抽象wakeup source和wakeup event的概念;
ii. 向各个device driver提供wakeup source的注册、使能等接口;
iii. 向各个device driver提供wakeup event的上报、停止等接口;
iv. 向PM core(包括wakeup count、auto sleep、suspend、hibernate等模块)提供wakeup event的查询接口,以判断是否可以suspend、是否需要终止正在进行的suspend。 -
wakeup events framework
sysfs,将设备的wakeup信息,以sysfs的形式提供到用户空间,供用户空间程序查询、配置。在drivers/base/power/sysfs.c中实现。 -
wake lock/unlock,允许用户空间程序报告/停止wakeup
events。换句话说,允许用户空间的任一程序决定系统是否可以休眠。 -
wakeup count,基于wakeup events framework,解决用户空间同步的问题。
-
auto sleep,允许系统在没有活动时(即一段时间内,没有产生wakeup event),自动休眠。
Note:
在Linux kernel看来,power是系统的核心资源,不应开放给用户程序随意访问(wake lock机制违背了这个原则),而在运行时的电源管理过程中,系统何时进入低功耗状态,也不是用户空间程序能决定的。
二、Suspend
Android suspend流程中,通常使用以下两种方式:1.按power key进入休眠流程,2.settings下面的自动息屏流程。
2.1 Suspend从上到下流程图
Suspend流程基本和Wakelock流程相似,上层调用的接口大部分是相同的,
kernel休眠主要分三个主要的步骤:1.冻结用户态进程和内核态任务;2.调用注册的设备的suspend的回调函数,其调用顺序是按照驱动加载时的注册顺序;3.休眠核心设备和使CPU进入休眠态冻结进程是内核把进程列表中所有的进程的状态都设置为停止,并且保存下所有进程的上下文。
总结
当启动一个应用程序的时候,它都可以申请一个wakelock唤醒锁,每当申请成功之后都会在内核中注册一下(通知系统内核,现在已经有锁被申请),当应用程序在某种情况下释放wakelock的时候,会注销之前所申请的wakelock。特别要注意的是:只要是系统中有一个wakelock的时候,系统此时都不能进行睡眠(即kernel无法suspend)。但此时各个模块可以进行early_suspend。当系统中所有的wakelock都被释放之后,系统就会进入真正的kernel的睡眠状态。