安卓电源状态
首先安卓将电源分为以下几种情况:
1.sleep:休眠
从代码看,安卓将系统的休眠原因分为以下几种:
goToSleepNoUpdateLocked
GO_TO_SLEEP_REASON_DEVICE_ADMIN//Going to sleep due to device administration policy
GO_TO_SLEEP_REASON_TIMEOUT //Going to sleep due to screen timeout
GO_TO_SLEEP_REASON_LID_SWITCH //Going to sleep due to lid switch 滑盖
GO_TO_SLEEP_REASON_POWER_BUTTON //Going to sleep due to power button
GO_TO_SLEEP_REASON_SLEEP_BUTTON //Going to sleep due to sleep button 头显的远近
GO_TO_SLEEP_REASON_HDMI //Going to sleep due to HDMI standby
GO_TO_SLEEP_REASON_ACCESSIBILITY//Going to sleep by an accessibility service request
如果不是以上几种情况,则默认为应用导致的休眠:
GO_TO_SLEEP_REASON_APPLICATION//Going to sleep by application request
2.nap:进入屏保
默认屏幕超时后进入休眠,如果希望进入的是屏保模式
则需要在overlay/frameworks/base/core/res/res/values/config.xml中将屏保模式打开
<bool name="config_dreamsActivatedOnSleepByDefault">true</bool>
3.wakeup:唤醒
wakeUpNoUpdateLocked
从以下状态中唤醒,reason不固定:
WAKEFULNESS_ASLEEP:
WAKEFULNESS_DREAMING:
WAKEFULNESS_DOZING:
4.shutdown:关机
5.reboot:重启
在此前的内核篇,我们讲到了用户层会去轮询查看是否有唤醒事件需要处理,如果没有则echo mem > /sys/power/state,从而进入内核的休眠唤醒流程;所以在本篇我们主要将这两个流程是怎么衔接上的~
由于通过调用api进行休眠唤醒的流程更为简单,所以我们看下老化apk是如何进行调用休眠唤醒的api,然后进行老化休眠唤醒实验的;
老化休眠唤醒代码demo
1 | 休眠动作: |
从调用流程可以看到就是不断的调用goToSleep,然后设置一个闹钟,当闹钟来临的时候唤醒系统;
framework相关代码
1 | core/java/android/os/PowerManager.java |
sleep流程
当屏幕超时,按power键休眠,和应用主动调goToSleep;无论是哪种方式,如果系统启动没有异常,都会走到PowerManagerService的核心函数,updatePowerStateLocked;按照安卓注释,将updatePowerStateLocked 分为五个阶段,我们也按照五个阶段对该函数的功能进行讲解;
Phase 0: Basic state updates
1.USB插播亮屏入口点
//如果需要插拔事件不影响到屏幕的亮灭,在overlay/frameworks/base/core/res/res/values/config.xml下
//修改<bool name="config_unplugTurnsOnScreen">true</bool>从字面意思貌似是拔出的时候不亮屏;
//mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean(
com.android.internal.R.bool.config_unplugTurnsOnScreen);//从这里就能完整看到这个配置的用途,插拔都会影响到屏幕
2.更新低电量模式
当电池电量状态发生变化的时候,才能调到该方法
updateIsPoweredLocked(mDirty);
判断系统是否在setting中设置了充电时保持屏幕亮屏
updateStayOnLocked(mDirty);
updateScreenBrightnessBoostLocked(mDirty);
Phase 1: Update wakefulness
wakefulness,安卓将休眠唤醒的电源状态分为四个:
类型 | 状态介绍 |
---|---|
WAKEFULNESS_ASLEEP | 设备处于sleep状态,只能被wakeUp函数唤醒,屏幕会被关闭,设备先进入doze状态 |
WAKEFULNESS_AWAKE | 设备处于awake状态,可通过goToSleep的接口将设备置于休眠状态,当用户超时时间到了设备将开始dreaming或者进入休眠流程 |
WAKEFULNESS_DREAMING | 设备处于dreaming状态,会被wakeUp唤醒,当dreaming时间结束或者 |
WAKEFULNESS_DOZING | 打盹 |
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
//此循环只会循环两次,然后就退出;
for (;;) {
updateWakeLockSummaryLocked(dirtyPhase1);
getWakeLockSummaryFlags(wakeLock);
adjustWakeLockSummaryLocked(mWakeLockSummary);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
1.updateWakeLockSummaryLocked(dirtyPhase1); ;
简单先介绍安卓的锁:
类型 | 属性 | 功能介绍 |
---|---|---|
PARTIAL_WAKE_LOCK | WAKE_LOCK_CPU | 保持CPU运行,屏幕和键盘灯允许关闭。用户按power键之后,屏幕和键盘灯会关闭,CPU keep on,直到所有该类型所被释放 |
FULL_WAKE_LOCK | WAKE_LOCK_SCREEN_BRIGHT WAKE_LOCK_BUTTON_BRIGH | 保证屏幕和键盘灯亮(at full brightness)。用户按power键之后,CPU和屏幕键盘灯都会被关闭 |
SCREEN_BRIGHT_WAKE_LOCK | WAKE_LOCK_SCREEN_BRIGHT | 保证屏幕亮(full brightness),键盘灯允许关闭。用户按power键之后,CPU和屏幕都会被关闭 |
SCREEN_DIM_WAKE_LOCK | WAKE_LOCK_SCREEN_BRIGHT | 保证屏幕亮(full brightness),键盘灯允许关闭。用户按power键之后,CPU和屏幕都会被关闭 |
PROXIMITY_SCREEN_OFF | PROXIMITY_SCREEN_OFF | pSensor导致的灭屏情况下系统不会进入休眠,正常情况下不影响系统休眠 |
DOZE_WAKE_LOCK | WAKE_LOCK_DOZE | 使屏幕进入low power状态,允许cpu挂起。只有在电源管理进入doze模式时生效 |
DRAW_WAKE_LOCK | WAKE_LOCK_DRAW | 保持设备awake状态已完成绘制事件,只在doze模式下生效 |
该函数的主要作用为,遍历申请的所有锁,根据当前mWakefulness的状态(asleep,dozing,wake,dream),过滤不需要的锁,如DOZE模式下过滤WAKE_LOCK_SCREEN_BRIGHT,WAKE_LOCK_SCREEN_DIM,WAKE_LOCK_BUTTON_BRIGHT,而ASLEEP模式下在基础上过滤WAKE_LOCK_PROXIMITY_SCREEN_OFF,在Dream模式下会添加WAKE_LOCK_CPU.
2.updateUserActivitySummaryLocked(now, dirtyPhase1);
mUserActivitySummary的种类如下:
类型 | 类型介绍 |
---|---|
USER_ACTIVITY_SCREEN_BRIGHT | 点亮屏幕 |
USER_ACTIVITY_SCREEN_DIM | 屏幕变暗 |
USER_ACTIVITY_SCREEN_DREAM | 屏保模式 |
这里有三个时间参数:
sleepTimeout:设备完全休眠的时间,该值可以理解为保持唤醒或屏保的最大值或者上限,并且该值要大
Settings.System.SCREEN_OFF_TIMEOUT,默认为-1;表示禁用此功能项;
screenOffTimeoutSetting:表示设备在一段不活动进入睡眠或者屏保的时间,也称为用户超时时间,但屏幕不一定关闭,可能进入屏保
screenDimDuration 亮屏后不操作,多久变暗;
根据这三个时间参数计算nextTimeout的时间:
scheduleUserInactivityTimeout(nextTimeout);
故该函数的主要作用为,更新用户活动时间,当设备和用户有交互的时,都会根据当前时间和休眠时长,dim时长,所处状态而计算下次休眠的时间,从而完成活动超时时的操作,如由亮屏进入Dim的时长,Dim到灭屏的时长,亮屏到屏保的时长;
3.updateWakefulnessLocked
if (shouldNapAtBedTimeLocked()) {
changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
-->mSandmanSummoned = true;
setWakefulnessLocked(WAKEFULNESS_DREAMING, 0);
-->mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
} else {
changed = goToSleepNoUpdateLocked(time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
--> mSandmanSummoned = true;
setWakefulnessLocked(WAKEFULNESS_DOZING, reason);
-->mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
}
该函数的主要作用在与更新电源状态;
Phase2:Lock profiles that became inactive/not kept awake.
updateProfilesLocked(now); //估计是cts的时候使用,这个不再赘述
Phase 3: Update display power state.
updateDisplayPowerStateLocked(dirtyPhase2);
-->mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
-->requestPowerState
-->return mDisplayPowerController.requestPowerState(request,waitForNegativeProximity);
-->requestPowerState
-->sendUpdatePowerStateLocked
-->Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
mHandler.sendMessage(msg);
handleMessage(Message msg)
-->updatePowerState();
-->animateScreenStateChange
-->setScreenState
-->mPowerState.setScreenState(state);(DPC)
-->scheduleScreenUpdate-->postScreenUpdateThreadSafe-->setState->mLock.notifyAll
-->mBlanker.requestDisplayState(DPS)
-->callbacks.onDisplayStateChange(state) -->根据亮灭屏走PowerManagerService enable/disable autosuspend
-->requestGlobalDisplayStateInternal(state, brightness=0) -->mTempDisplayStateWorkQueue.get(i).run()(DMS)
-->SurfaceControl.setDisplayPowerMode(LDA)
-->blockScreenOn 在亮屏的流程里面监听窗口是否绘制完成,
是的话重新发起updatePowerState,设置mColorFadeLevel为1;此时才会去设置背光,这时候对于用户来说屏幕才是最终的亮了
否的话此时的背光值一直会为0;
-->mWindowManagerPolicy.screenTurningOff(mPendingScreenOffUnblocker)
在灭屏的同时还发起了activity的pause流程
-->updateScreenOffSleepToken
-->acquireSleepToken
-->updateSleepIfNeededLocked
-->goingToSleepLocked
-->checkReadyForSleepLocked
-->putStacksToSleepLocked
-->goToSleepIfPossible
-->startPausingLocked
-->schedulePauseAcvity --> sendMessage
-->handleMessage -->handlePauseActivity-->performPauseActivity-->performPauseActivityIfNeed
-->callActivityOnPause -->activity.performPause--> onPause
唤醒的时候应用resume的流程
ActivityStackSupervisor.java
SleepTokenImpl.release
-->removeSleepTokenLocked
-->updateSleepIfNeededLocked
-->applySleepTokensLocked
-->resumeFocusedStackTopActivityLockedi
-->resumeTopActivityUncheckedLocked
-->resumeTopActivityInnerLocked
函数作用:决策屏幕的量灭状态
如果当前是WAKEFULNESS_ASLEEP状态,直接设置屏幕为POLICY_OFF
如果包含以下一种状态,就设置屏幕为POLICY_BRIGHT
1.mWakeLockSummary 如果有 WAKE_LOCK_SCREEN_BRIGHT类型的wakeLock
2.mUserActivitySummary 屏幕状态为USER_ACTIVITY_SCREEN_BRIGHT
3.当前系统未启动完成
4.当前处于最大屏幕亮度
可以看出屏幕的状态和前面设置的wakeLock,stayon,userActivity,screenBrightness等有关
Phase 4: Update dream state (depends on display ready signal).
updateDreamLocked(dirtyPhase2, displayBecameReady);
-->scheduleSandmanLocked();
-->if (!mSandmanScheduled) {
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
}
函数作用:决定是否进入屏保状态
Phase 5: Send notifications, if needed.
finishWakefulnessChangeIfNeededLocked();
函数作用:发出wakefulness发生改变的通知
Phase 6: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
updateSuspendBlockerLocked();
-->needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);//根据是否有CPU的wakelock,来决定cpu是否保持唤醒
-->needDisplaySuspendBlockerLocked//根据前面屏幕的状态,屏幕是否需要亮屏,来决定是否需要持持有屏幕的锁
函数作用:进行锁的申请和释放
分析完整个函数的调用流程,依然找不到怎么跟内核休眠的流程衔接起来;那么安卓是什么是否才会去调用内核休眠流程对接起来的?原来安卓在上面的函数调到灭屏的接口时候,才会去使能autosuspend的流程;想想也没有什么毛病,毕竟对于用户来说灭屏时标识着系统要走到休眠的标志;
代码流程如下:
onDisplayStateChange
-->if (state == Display.STATE_OFF) {
if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
setHalInteractiveModeLocked(true);
}
}
nativeSetAutoSuspend(enable);
enabelAutosuspend
suspendControl->enableAutoSuspend 使能autoSuspend进程,进程会一直检测系统是否无锁
gSuspendBlocker->release释放锁
对应的灭屏disable的流程
disableAutoSuspend
suspendHal->acquireWakeLock(WakeLockType:PARTIAL,"PowerManager.SuspendLockout");所以一旦亮屏我们都会在内核的wake_lock节点看到这个锁,当然等到安卓把这个锁放在上层申请
的话就看不到了
为了呼应上次内核篇上讲的安卓等待的时机,我们在这里把读写wakeup count内核的的相关操作也po出来;
1 | ReadFdToString(wakeup_count_fd, &wakeup_count) |
总之,当安卓灭屏后,才会使能autosuspend不断轮询是否有锁也即唤醒事件,没有才会下发mem到写到内核节点state中;
安卓按键亮灭屏唤醒流程
1 | 亮屏: |
安卓的持锁流程
1 | PhoneWindowManager mPowerKeyWakeLock.acquire |
autospend 流程
androidR上,谷歌说要弃用/sys/power/wake_lock节点;安卓此前申请锁的流程在hardware/libhardware_legacy/power.cpp;原理就是去往wake_lock节点写值;
现在的话想将这个流程放在system/hardware/interfaces/suspend中进行实现;
在这里只能说谷歌终于想通了要把这套放在用户层,毕竟曾经为了wake_lock,一直在争吵,最后还是被谷歌给干进内核,现在的话又抽出来放在用户层里面;具体细节可以参照这篇文章Linux电源管理
谷歌列举这样的好处是:
- 减少对内核的依赖
- 在没有debugfs的时候用户层仍有能力去debug和log suspend blocks
- 当进程消亡的时候可以进行锁通过binder的管理进行释放 dangling wake locks
通过mSuspendCounter去统计当前持锁的数量,当mSuspendCounter为0的时候,说明上层持锁为0
1 |
|
其他
如何设置安卓休眠超时时间?
settings get system screen_off_timeout
settings put system screen_off_timeout 2147483647 //设置永不休眠
设置屏保命令
settings put secure screensaver_activate_on_sleep 1 打开屏保
设置插入适配器后不休眠
settings put global stay_on_while_plugged_in 1
安卓系统不休眠debug
查看上层有没有释放锁
dumpsys power | grep -Hrsn 'LOCK'
PARTIAL_WAKE_LOCK 'messaging_service_start_wakelock'
查看active_since这一项,看哪个锁不为0
cat /sys/kernel/debug/wakeup_sources
上层传向底层的锁
cat /sys/power/wake_lock
当这几步查找后都没有对应的锁时,可以通过/sys/power/pm_test结点,操作设备进入对应的休眠阶段,详见linux休眠篇章
进入深度休眠后被唤醒问题debug
一般而言,平台会提供深度休眠后如何查看系统唤醒源,如全志平台的查看方式为
一:休眠的时候不关闭终端
echo N > /sys/module/printk/parameters/console_suspend
二:唤醒后查看对应的唤醒源
查看唤醒时候打印:
platform wakeup, standby wakesource is:0x10000
在allwinner对应的pm.h里面就可以看到对应的唤醒源编号,如linux4.9的在driver/soc/sunxi/pm.h
其他版本可以直接搜索关键字找到对应的唤醒源编号;
#define CPUS_WAKEUP_DESCEND (1<<16)
而CPUS的GPIO一般是WIFI中断管脚在连接,故此时可以断开wifi连接查看系统唤醒是否依然异常
另外如果当log出现为闹钟唤醒:
platform wakeup, standby wakesource is:0x100000
查看对应唤醒源编号
#define CPUS_WAKEUP_ALM0 (1<<20)
这种情况下我们一般需要通过去查看到底是哪个应用去唤醒系统的:这个时候我们可以借助这个应用 BetterBatteryStats2.0
1.下载地址:
http://cn.apkhere.com/app/com.asksven.betterbatterystats
安装该软件:
2.adb install com.asksven.betterbatterystats_2.2.2.0_paid-www.apkhere.com.apk
3.获取权限:
adb -d shell pm grant com.asksven.betterbatterystats android.permission.BATTERY_STATS
4.若依然不可以:
setenforce 0:取消selinux
getenforce 查看是否为安全模式 Permissive 则是非安全;
观察各项参数:
rtc闹钟设置与查看
venus-a3:/ # cat /proc/driver/rtc
rtc_time : 05:44:49
rtc_date : 2019-04-26
alrm_time : 00:00:00
alrm_date : 1970-01-01
alarm_IRQ : no
alrm_pending : no
update IRQ enabled : no
periodic IRQ enabled : no
periodic IRQ frequency : 1
max user IRQ frequency : 64
24hr : yes
设置当前时间+100000后唤醒
venus-a3:/ # echo +100000 > /sys/devices/platform/soc/rtc/rtc/rtc0/wakealarm
venus-a3:/ # cat /proc/driver/rtc
rtc_time : 05:47:15
rtc_date : 2019-04-26
alrm_time : 09:33:52
alrm_date : 2019-04-27
alarm_IRQ : yes
alrm_pending : no
update IRQ enabled : no
periodic IRQ enabled : no
periodic IRQ frequency : 1
max user IRQ frequency : 64
24hr : yes
echo 0取消
查看闹钟应用设置闹钟:
venus-a3:/ # settings list system | grep next
next_alarm_formatted=Wed 11:50 AM
怎么捕捉休眠唤醒的systrace
休眠前
atrace –async_start -b 8192 -o /data/trace2.dat gfx view wm am power
唤醒后
atrace –async_stop
参考资料
Android7.0 PowerManagerService 之亮灭屏(二) PMS 电源状态管理updatePowerStateLocked()
Android7.0 PowerManagerService(3)核心函数updatePowerStateLocked的主要流程
Android7.0 PowerManagerService亮灭屏分析(一)
锁屏分析/)