一、电源管理基础知识
1.1电源管理的几种状态
Android kernel源码中,定义了三种电源状态,在kernel/power/suspend.c中:
对应的宏定义/include/linux/suspend.h
1.2 电源管理状态的介绍:
PM_SUSPEND_ON
设备处于正常工作状态
PM_SUSPEND_STANDBY
设备处于省电状态,但还可以接收某些事件
PM_SUSPEND_MEM
设备进入睡眠状态,保存系统运行的上下文至内存后挂起系统,只有特定外部中断才可以唤醒设备
PM_SUSPEND_TO_IDLE
设备进入空闲状态,冻结用户空间并将外围设备调至低耗电模式,强制将 CPU 进入idle
ADB 查看支持的电源模式
cat /sys/power/state
1.3 Idle State
Android的Idle状态分为二类:CPU Idle和Device Idle
CPU Idle
每一个 CPU 核心都会有一个 idle 进程,idle 进程是当系统没有调度 CPU 资源的时候,会进入 idle 进程,而 idle 进程的作用就是不使用 CPU,以此达到省电的目的。
有关CPU idle的内容可以查看公众号之前的文章《Linux Cpuidle介绍》
Device Idle
Device Idle属于android Doze模式中的概念,即指手机屏幕熄屏、不充电、静置不动
在 Doze 模式下,按照google的官方说法,Wakelocks,网络访问,jobshedule,闹钟,GPS/WiFi扫描都会停止。
系统会定期退出 Doze 一小段时间,让应用程序完成其延迟的活动。在此维护窗口期间,系统运行所有挂起的同步、作业和警报,并允许应用程序访问网络。
二、Android电源管理框架
Android系统的电源管理框架分成五个部分:应用层,框架层,Native层,HAL层和内核层。电源管理架构图例如下:
应用接口层: PowerManager.java 负责向应用程序提供一系列接口,例如wakelock的申请与释放,进而让系统休眠或唤醒
框架层: PowerManagerService.java PowerManagerServic是android电源管理的核心服务,向上提供应用程序接口.向下通过hal层和kernel层来控制待机状态和系统硬件设备状态
HAL层: power.c 接收上层参数,通过写节点与kernel层通信
内核层: Kernel/Power 实现系统电源管理框架机制,为设备电源管理提供基础框架
三、WakeLock
Android中wakelock是一种锁的机制,用于阻止系统进入睡眠状态,只要有任意应用持有wakelock,那么系统就无法进入睡眠状态。
newWakeLock(int flags, String tag)
申请wakelock时有一个关键的参数flags,它有如下几种情况:
PARTIAL_WAKE_LOCK: Screen off, keyboard light off
SCREEN_DIM_WAKE_LOCK: Screen dim, keyboard light off
SCREEN_BRIGHT_WAKE_LOCK: Screen bright, keyboard light off
FULL_WAKE_LOCK: Screen bright, keyboard bright
上面4种是互斥的,即只能指定其中之一,但可以与下面两种flag不是互斥的:
ACQUIRE_CAUSES_WAKEUP:一旦有请求锁时强制打开Screen和keyboard light
ON_AFTER_RELEASE:在释放锁时reset activity timer
如果系统申请了PARTIAL_WAKE_LOCK,那么即使按power键,系统也不会进sleep,如music播放时.如果申请了其它的wakelocks,按power键,系统还是会进sleep
wakelock有加锁和解锁两种状态:
一种是永久性锁住,这种锁除非后续放开,否则不会解锁;
另一种是超时锁,这种锁会锁定系统一段时间后会自动解锁。
电源锁的两种类型:
(1)WAKE_LOCK_SUSPEND:阻止系统进入睡眠,属于永久性锁,超时锁为WAKE_LOCK_AUTO_EXPIRE
(2)WAKE_LOCK_IDLE:阻止持有该锁的系统进入idle状态
Android中使用两条链表分别保存处于active状态的suspend lock和idle lock和保存处于inactive状态的wakelock。
系统执行加放锁有两种机制,第一种是不计数锁,另一种是计数锁。可以通过PowerManager.WakeLock.setReferenceCounted(boolean value) 来指定,默认为计数机制。这两种机制的区别在于,前者无论 acquire() 了多少次,只要通过一次 release()即可直接解锁。而后者正真解锁是在( --count == 0 )的时候,同样当 (count == 0) 的时候才会去申请加锁,其他情况 isHeld 状态是不会改变的。所以 wakeLock 的计数机制并不是正真意义上的对每次请求进行申请/释放每一把锁,它只是对同一把锁被申请/释放的次数进行了统计后才去执行操作。
3.1wakelock在framework层
内核启动完成后,电源管理系统会在文件系统中建立两个节点:
/sys/power/wake_lock
/sys/power/wake_unlock
应用程序可以通过/sys/power/wake_lock申请一个WAKE_LOCK_SUSPEND 类型的锁,通过/sys/power/wake_unlock则可以释放一个锁。内核在进入suspend之前如果检测到某个锁没有释放,则会放弃本次的suspend过程,直到这个锁释放为止.Android持有电源锁后可以让持锁的进程持续执行,即使进入了睡眠模式。
如果应用崩掉或退出,系统会自动释放他们获取的所有电源锁;如果是在服务中获取的,当服务崩掉或注销时也会自动释放;
Framework层有关电源锁的内容是通过PowerManagerService类来实现,这个类是用来管理所有应用程序申请的wakelock,比如音视频播放器,camera等申请的wakelock都是通过这个类来管理的。如:
static final String PARTIAL_NAME = "PowerManagerService"
PARTIAL_NAME作为参数传递到底层去。
ADB 调试命令
echo lockname > /sys/power/wake_lock
加锁“lockname”
echo lockname > /sys/power/wake_unlock
解锁“lockname”
四、Earlysuspend和Lateresume
Early Suspend和Late Resume是Android在标准Linux的基础上增加的一项特性。当用户空间申请进入suspend时,会先进入early suspend状态.外设驱动程序可以注册early suspend回调函数,当进入early suspend时,内核会逐一地调用这些回调函数。例如在进入early suspend后,回调函数会通过屏幕驱动把屏幕和背光都关闭, 但此时系统依然在正常运行。进入early suspend状态以后,一旦所有wakelock被释放,系统马上会进入真正的suspend流程.
Android 4.4起,也就是引入ART的版本,摒弃了early suspend机制,改用了fb event通知机制,后续Android版本只有suspend、resume以及runtime suspend、runtime resume。
结语
本文讲述了Android电源管理模块的主要内容,旨在让读者对于Android电源状态及wakelock有一个初步的认识,方便以后深入介绍内核wakelock的实现和Android的待机唤醒流程.
引文:
[1]https://developer.android.google.cn/training/monitoring-device-state/doze-standby?hl=en
[2] 深入理解LINUX内核(第三版)(美)博韦,西斯特 著,陈莉君,冯锐,牛欣源 译