Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程
2017-04-15 17:07
459 查看
前面的博客中,我们已经分析过,当Android中的进程要使用电量时,需要向PMS申请WakeLock;当进程完成工作后,需要释放对应的WakeLock。
PMS收到申请和释放WakeLock的请求后,均需要调用updatePowerStateLocked来更新电源的状态,该函数是PMS的核心方法。
接下来,我们就结合代码,看一下updatePowerStateLocked的工作流程。
在PMS中有个很重要的变量mDirty,该变量按位存储PMS中的各种变化状态。
例如,之前介绍PMS的acquire WakeLock流程时,就进行了以下操作:
每当PMS检测到一些重要事件发生时,就会更新mDirty的相应的位。
从updatePowerStateLocked的代码可以看出,它将根据mDirty中的信息,来更新手机中的电源状态。
根据Android源码中的注释,可以看出updatePowerStateLocked的工作主要分为几个步骤,接下来我们一个一个步骤的来进行分析。
一、更新基本状态信息
1、updateIsPoweredLocked
我们先来看看updateIsPoweredLocked函数:
从以上代码可以看出updateIsPoweredLocked主要用于:
更新PMS中的一些变量,包括记录终端是否在充电、充电的类型、电池的电量及电池电量是否处于低电状态;
当电源的充电状态,或者充电类型发生变化,判断出现插拔充电器等操作时,是否需要点亮或熄灭屏幕;
当电源充电状态发生变化,或者终端是否处于低电量的标志发生变化的时候,终端调用updateLowPowerModeLocked()更新低电模式相关的操作。
我们跟进一下updateLowPowerModeLocked函数:
从上面的代码可以看出updateLowPowerModeLocked函数,
首先判断手机是否在充电,如果手机在充电,退出LowPowerMode模式,同时更新数据库;
当手机的低电量模式发生了变化,就发送广播进行通知,并回调关于监听该模式变化的观察者的接口。
例如:UI对应的APK收到低电量省电模式的广播,就会弹出低电量省电模式的提醒界面。
可以看出这一部分除了更新PMS中的一些变量外,关注的重点还是集中在:
充电状态是否改变;
充电状态的改变,将引出对充电器插拔是否需要亮屏的考虑;
同样,充电状态的改变,将引出对终端的低电模式是否发生改变的考虑。
从这个角度来看,updateIsPoweredLocked函数的命名是实至名归的。
2、updateStayOnLocked
现在我们看看基本状态更新第二部分的updateStayOnLocked函数:
这一部分的代码功能比较单一,主要用于更新变量mStayOn的值。
如果mStayOn如果为true,则屏幕保持长亮的状态。
3、updateScreenBrightnessBoostLocked
Android手机定义了一个最大屏幕亮度,用户可以手动或者让终端自动确定最大的屏幕亮度。
updateScreenBrightnessBoostLocked函数主要用于:更新终端可处于最大屏幕亮度的时间。
为了比较好的理解updateScreenBrightnessBoostLocked函数,
我们可以先分析一下与之相关的,PMS提供的对外的接口boostScreenBrightness。
该方法的作用是让屏幕在一段时间内保持最大的亮度,使屏幕在强光下有更好的可读性。
从上面的代码可以看出,该函数:
首先,使用mLastScreenBrightnessBoostTime变量记录了终端处于最大屏幕亮度的起始时间;
然后,将最大屏幕亮度的标志位置为true,并修改mDirty标志位,以表示最大屏幕亮度的状态发生了变化;
最后,调用updatePowerStateLocked方法更新电源状态信息。
我们已经知道,updatePowerStateLocked将会调用到updateScreenBrightnessBoostLocked。
接下来,我们看看updateScreenBrightnessBoostLocked对应的代码:
至此,PMS第一阶段更新基本状态信息的流程结束。
二、更新wakelock和用户活动
1、updateWakeLockSummaryLocked
updateWakeLockSummaryLocked函数根据PMS当前持有的所有WakeLock,得到当前终端整体的信息,保存到mWakeLockSummary变量中。
结合每个WakeLock level的注释信息,以上代码还是比较好理解的。
这里唯一需要说明的是,Android定义一个mWakeLockSummary变量的原因是:
PMS将WakeLock定义为不同进程的请求信息,这些请求信息对CPU、屏幕和键盘有不同的需求。
对于每一种资源而言,只要有一个申请满足获取条件,PMS就需要为终端分配该申请对应的资源。
例如:假设PMS有20个WakeLock,只有1个申请亮屏,另外19个只申请CPU唤醒,PMS仍然需要保持终端亮屏。
因此,mWakeLockSummary就提供了一种整合多个WakeLock请求的功能,方便PMS进行集中的控制。
2、updateUserActivitySummaryLocked
updateUserActivitySummaryLocked主要根据用户最后的活动来决定当前屏幕的状态。
从上面的代码可以看出,在该函数中用mUserActivitySummary变量存储当前屏幕的状态。
一共有3中基本状态:
* USER_ACTIVITY_SCREEN_BRIGHT 点亮屏幕
* USER_ACTIVITY_SCREEN_DIM 屏幕变暗
* USER_ACTIVITY_SCREEN_DREAM 屏保状态
从代码可以看出,屏幕变化和userActivity活动有关,它根据最后的userActivity活动的时间决定点亮屏幕、调暗屏幕或熄灭屏幕。
之前的很多方法中都会调用userActivityNoUpdateLocked方法。该方法将触发一次用户活动,以更新用户活动的时间,这样屏幕变暗和熄灭时间就会重新进行计算。
这也就是为什么用户一直操作手机,屏幕不会熄灭或者变暗的原因。
![](http://img.blog.csdn.net/20161019170347709)
大图地址
整体来讲,个人感觉这个函数的代码写的还是挺绕的,因此还是作一个图记录一下。
大家有兴趣可以看一下。
3、updateWakefulnessLocked
从之前的代码可以看出,updateWakefulnessLocked将决定第二阶段的电源状态更新是否结束。
我们看一下updateWakefulnessLocked函数:
从上面的代码可以看出,如果终端可以一直保持唤醒状态,或一开始就是非唤醒态,
那么mWakefulness不会发生改变,第二阶段的for循环将会break;
如果终端要从唤醒态变为非唤醒态,那么for循环将再运行一次,即重新计算一次mWakeLockSummary和mUserActivitySummary。
这么做的原因是:updateWakeLockSummaryLocked和updateUserActivitySummaryLocked函数的一些计算,与终端是否处于唤醒状态,即mWakefulness的值有关。
由于这两个函数并不会修改mWakefulness,因此在这一次运行时,updateWakefulnessLocked将返回false,即第二阶段结束。
因此,我们可以得出结论:更新电源状态的第二阶段,正常情况下最多运行两次。
在第二阶段的最后,我们看一下isItBedTimeYetLocked函数:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
参考原生代码的注释,这一部分代码还是比较好理解的。
三、更新display power state
第三阶段将负责更新屏幕的显示状态。
我们跟进一下DisplayPowerController的requestPowerState函数:
根据requestPowerState的代码,我们知道:
当PMS传入一个新的mDisplayPowerRequest时,requestPowerState应该返回为false;当DisplayPowerController按照mDisplayPowerRequest修改完屏幕状态,再次进入回到updateDisplayPowerStateLocked函数,调用requestPowerState时才会返回true。
整体的代码流程大概可以抽象成下图:
![](http://img.blog.csdn.net/20161020173414430)
这一阶段的代码,我们只是分析了整个过程的冰山一角,并没有分析更新屏幕状态的实际操作。
但从现有的代码可以看出,PMS的作用仅仅是维护终端电源相关状态,实际的工作还是通过类似发送Request的方式,让其它的服务协助完成。
例如:在整个阶段,PMS根据之前得到信息,构造出DisplayPowerRequest,然后发送给DisplayPowerController进行实际的处理。
当DisplayPowerController完成实际的工作(部分工作还依赖于PhoneWindowManager)后,再通知PMS进行复查。
因此PMS的定位,确实可以用一个”Manager”来形容;
负责整个终端信息的搜集和维护,然后将相应的工作指派给具体的“员工”执行;
“员工”执行完毕后,向”Manager”汇报;
“Manager”检查工作的完成情况后,然后做出下一步的指示。
四、更新dream state
updateDreamLocked函数主要用于更新屏保状态,当设备进入或者退出屏保的时候都会触发这个方法:
handleSandman函数比较复杂,主要用于决定设备是否应该停留在dreaming或dozing状态。
我们分段介绍该函数的功能。
1、决定是否可以进入屏保状态
这段代码主要用于确定,设备是否可以看是dreaming。
除去前置条件的限制外,此处的结果主要由canDreamLocked和canDozeLocked决定。
我们分别看看这两个函数:
从上面的代码可以看出,dreaming除了对终端当前的状态、配置项有关外,在非唤醒状态下还与当前的电池电量有关系。
canDozeLocked函数相对简单:
2、在必要时,进入屏保状态
决定了是否可以进入屏保状态后,这一部分就开始进行实际的工作。
mDreamManager为DreamManagerService的Binder代理。
我们重点看看DreamManagerService的startDream函数,stopDream的工作内容与startDream相反,不做细致分析:
从这部分代码我们知道了,所谓的屏保其实就是拉起一个特殊的服务。
3、更新屏保状态
以上是PMS更新屏保状态的基本流程,整体来看相当的繁琐。
我们还是用一个图来整体整理一下:
![](http://img.blog.csdn.net/20161020220536778)
大图链接
这部分代码最后太乱,每次更新状态后都会重新调用updatePowerStateLocked,然后再次进入到handleSandman函数中。
这种反复地递归调用,比较难以把控。
五、更新suspend blocker
updateSuspendBlockerLocked函数主要根据之前流程的执行结果,持有或者释放CPU和屏幕的锁。
我们一起来看看对应的函数:
从上面的代码可以看出PMS是非常依赖于native层的,真实的持锁、释放锁、设置交互状态等工作,均是移交到native层进行操作。
我们以mWakeLockSuspendBlocker的处理流程为例,看看native的调用过程:
之前的博客也提到过,PMS在其构造函数中调用createSuspendBlockerLocked函数,创建出了mWakeLockSuspendBlocker:
从上面的代码,我们知道当PMS需要获取底层锁时,调用的是SuspendBlockerImpl的acquire函数:
在native层的com_android_server_power_PowerManagerService.cpp中,对应的native函数为:
从这里的代码我们不难发现,尽管PMS定义了不同的WakeLock等级,但当通过PMS的native函数调用HAL层函数acquire_wake_lock时,使用的都是PARTIAL_WAKE_LOCK。
个人觉得这是可以理解的,当其它进程向PMS申请保持屏幕唤醒的Framework层WakeLock后,PMS在Framework层就进行了对应的处理,例如将请求信息等地交给DisplayPowerController等处理。因此,对于底层的HAL层而言,只需要关注CPU是否需要保持唤醒即可。
HAL层函数acquire_wake_lock,最后会向/sys/power/wake_lock节点进行write操作。
总结
至此,updatePowerStateLocked的基本流程介绍完毕,大体上如下图所示:
![](http://img.blog.csdn.net/20161022170516383)
通过其中的源码,我们也能看出仅管理当前的状态,涉及的细节就非常的琐碎。
而屏幕和CPU的实际控制,还牵扯到大量其它对象和HAL层代码。
Android电源的管理实际上是基于Linux电源管理策略的,因此若要真正掌握,还需要对Linux的电源管理策略作进一步的了解。
由于个人水平有限,目前还无法高屋建瓴地整体分析宏观的电源管理架构,细节也有一些遗漏。
后续争取以此博客为基础,不断迭代,以求更进一步地了解PMS的知识。
http://blog.csdn.net/gaugamela/article/details/52838654
PMS收到申请和释放WakeLock的请求后,均需要调用updatePowerStateLocked来更新电源的状态,该函数是PMS的核心方法。
接下来,我们就结合代码,看一下updatePowerStateLocked的工作流程。
/** * Updates the global power state based on dirty bits recorded in mDirty. * * This is the main function that performs power state transitions. * We centralize them here so that we can recompute the power state completely * each time something important changes, and ensure that we do it the same * way each time. The point is to gather all of the transition logic here. */ private void updatePowerStateLocked() { //未启动完毕或mDirty没有记录变化 if (!mSystemReady || mDirty == 0) { return; } .......... try { // Basic state updates. // 1、更新基本状态 updateIsPoweredLocked(mDirty); updateStayOnLocked(mDirty); updateScreenBrightnessBoostLocked(mDirty); // Update wakefulness. // Loop because the wake lock and user activity computations are influenced // by changes in wakefulness. // 2、更新wakelock和用户活动 final long now = SystemClock.uptimeMillis(); int dirtyPhase2 = 0; for (;;) { int dirtyPhase1 = mDirty; dirtyPhase2 |= dirtyPhase1; mDirty = 0; updateWakeLockSummaryLocked(dirtyPhase1); updateUserActivitySummaryLocked(now, dirtyPhase1); if (!updateWakefulnessLocked(dirtyPhase1)) { break; } } // Update display power state. // 3、更新display power state boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2); // Update dream state (depends on display ready signal). // 4、更新dream state updateDreamLocked(dirtyPhase2, displayBecameReady); // Send notifications, if needed. finishWakefulnessChangeIfNeededLocked(); // Update suspend blocker. // Because we might release the last suspend blocker here, we need to make sure // we finished everything else first! // 5、更新suspend blocker updateSuspendBlockerLocked(); } finally { .......... } }
在PMS中有个很重要的变量mDirty,该变量按位存储PMS中的各种变化状态。
例如,之前介绍PMS的acquire WakeLock流程时,就进行了以下操作:
......... mDirty |= DIRTY_WAKE_LOCKS; ........
每当PMS检测到一些重要事件发生时,就会更新mDirty的相应的位。
从updatePowerStateLocked的代码可以看出,它将根据mDirty中的信息,来更新手机中的电源状态。
根据Android源码中的注释,可以看出updatePowerStateLocked的工作主要分为几个步骤,接下来我们一个一个步骤的来进行分析。
一、更新基本状态信息
1、updateIsPoweredLocked
我们先来看看updateIsPoweredLocked函数:
private void updateIsPoweredLocked(int dirty) { //DIRTY_BATTERY_STATE位置1时,表示终端的电源状态发生了改变 if ((dirty & DIRTY_BATTERY_STATE) != 0) { //记录过去的状态 final boolean wasPowered = mIsPowered; final int oldPlugType = mPlugType; final boolean oldLevelLow = mBatteryLevelLow; //得到终端现在是否在充电 mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); //得到充电的类型 mPlugType = mBatteryManagerInternal.getPlugType(); //得到当前的电量 mBatteryLevel = mBatteryManagerInternal.getBatteryLevel(); //判断是否为低电量 mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow(); //是否充电的状态发生改变,或者充电类型发生改变 if (wasPowered != mIsPowered || oldPlugType != mPlugType) { mDirty |= DIRTY_IS_POWERED; // Update wireless dock detection state. //无线充电相关,暂时不用管 final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update( mIsPowered, mPlugType, mBatteryLevel); final long now = SystemClock.uptimeMillis(); //判断插拔充电器或者USB是否需要唤醒屏幕 if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType, dockedOnWirelessCharger)) { //之前的博客中已经分析过这个函数,主要是做好唤醒终端屏幕前的准备工作 wakeUpNoUpdateLocked(now, "android.server.power:POWER", Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); } //触发一次用户活动,修改PMS中记录用户活动事件的时间,同时通知BatteryStatsService等 userActivityNoUpdateLocked( now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); // Tell the notifier whether wireless charging has started so that // it can provide feedback to the user. //无线充电相关的通知,暂时可以不管 if (dockedOnWirelessCharger) { mNotifier.onWirelessChargingStarted(); } } if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) { //结束低电的状态 if (oldLevelLow != mBatteryLevelLow && !mBatteryLevelLow) { ........ //从命名来看,该标志用于决定终端在低电模式下是否“打盹”(接近休眠) mAutoLowPowerModeSnoozing = false; } //更新低电模式相关的操作 updateLowPowerModeLocked(); } } }
从以上代码可以看出updateIsPoweredLocked主要用于:
更新PMS中的一些变量,包括记录终端是否在充电、充电的类型、电池的电量及电池电量是否处于低电状态;
当电源的充电状态,或者充电类型发生变化,判断出现插拔充电器等操作时,是否需要点亮或熄灭屏幕;
当电源充电状态发生变化,或者终端是否处于低电量的标志发生变化的时候,终端调用updateLowPowerModeLocked()更新低电模式相关的操作。
我们跟进一下updateLowPowerModeLocked函数:
private void updateLowPowerModeLocked() { //处于充电状态,并且设置过低电模式的标志位 if (mIsPowered && mLowPowerModeSetting) { ........ // Turn setting off if powered //更新数据库,关闭低电模式 Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 0); mLowPowerModeSetting = false; } //判断是否可以进入自动省电模式 //要求是:未充电 && 进行了自动省电的配置 && 没有设置低电“打盹” && 电池电量低 final boolean autoLowPowerModeEnabled = !mIsPowered && mAutoLowPowerModeConfigured && !mAutoLowPowerModeSnoozing && mBatteryLevelLow; //当前是否为低电模式 final boolean lowPowerModeEnabled = mLowPowerModeSetting || autoLowPowerModeEnabled; if (mLowPowerModeEnabled != lowPowerModeEnabled) { mLowPowerModeEnabled = lowPowerModeEnabled; //调用底层动态库的powerHint函数 powerHintInternal(POWER_HINT_LOW_POWER, lowPowerModeEnabled ? 1 : 0); //开机完成后才能执行的Runnable对象 postAfterBootCompleted(new Runnable() { //发送低电模式CHANGING的广播 Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING) .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, mLowPowerModeEnabled) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcast(intent); //PMS提供了registerLowPowerModeObserver的接口 //其它进程可以调用该接口,注册观察者 synchronized (mLock) { listeners = new ArrayList<PowerManagerInternal.LowPowerModeListener>( mLowPowerModeListeners); } for (int i=0; i<listeners.size(); i++) { //调用回调接口的onLowPowerModeChanged函数,通知其它进程低电模式发生改变 listeners.get(i).onLowPowerModeChanged(lowPowerModeEnabled); } //再次发送CHANGED广播 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcast(intent); // Send internal version that requires signature permission. mContext.sendBroadcastAsUser(new Intent( PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL), UserHandle.ALL, Manifest.permission.DEVICE_POWER); }); } }
从上面的代码可以看出updateLowPowerModeLocked函数,
首先判断手机是否在充电,如果手机在充电,退出LowPowerMode模式,同时更新数据库;
当手机的低电量模式发生了变化,就发送广播进行通知,并回调关于监听该模式变化的观察者的接口。
例如:UI对应的APK收到低电量省电模式的广播,就会弹出低电量省电模式的提醒界面。
可以看出这一部分除了更新PMS中的一些变量外,关注的重点还是集中在:
充电状态是否改变;
充电状态的改变,将引出对充电器插拔是否需要亮屏的考虑;
同样,充电状态的改变,将引出对终端的低电模式是否发生改变的考虑。
从这个角度来看,updateIsPoweredLocked函数的命名是实至名归的。
2、updateStayOnLocked
现在我们看看基本状态更新第二部分的updateStayOnLocked函数:
/** * Updates the value of mStayOn. * Sets DIRTY_STAY_ON if a change occurred. */ private void updateStayOnLocked(int dirty) { //电源状态或电源设置发生了改变 if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) { final boolean wasStayOn = mStayOn; //设置了充电器插入时亮屏(分为AC充电亮屏、USB充电亮屏或无线充电亮屏) if (mStayOnWhilePluggedInSetting != 0 //判断mMaximumScreenOffTimeoutFromDeviceAdmin的是否处于0与Integer.MAX_VALUE之间 //Android给出的注释是: //The maximum allowable screen off timeout according to the device // administration policy //初始为Integer.MAX_VALUE,因此这里是要求其它进程没有设置这个值 //应该对应于强制息屏时间 && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) { //判断是否充电亮屏,定义于BatteryService.java中 //从代码来看,只要mStayOnWhilePluggedInSetting设置了,就会亮屏 mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting); } else { mStayOn = false; } if (mStayOn != wasStayOn) { mDirty |= DIRTY_STAY_ON; } } }
这一部分的代码功能比较单一,主要用于更新变量mStayOn的值。
如果mStayOn如果为true,则屏幕保持长亮的状态。
3、updateScreenBrightnessBoostLocked
Android手机定义了一个最大屏幕亮度,用户可以手动或者让终端自动确定最大的屏幕亮度。
updateScreenBrightnessBoostLocked函数主要用于:更新终端可处于最大屏幕亮度的时间。
为了比较好的理解updateScreenBrightnessBoostLocked函数,
我们可以先分析一下与之相关的,PMS提供的对外的接口boostScreenBrightness。
该方法的作用是让屏幕在一段时间内保持最大的亮度,使屏幕在强光下有更好的可读性。
public void boostScreenBrightness(long eventTime) { .......... mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { boostScreenBrightnessInternal(eventTime, uid); } finally { Binder.restoreCallingIdentity(ident); } } private void boostScreenBrightnessInternal(long eventTime, int uid) { synchronized (mLock) { //系统没有准备好或者当前为Asleep状态, 不处理新到的事件 if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP //过时的事件不处理 || eventTime < mLastScreenBrightnessBoostTime) { return; } .............. //记录事件到来的事件,也可以认为是终端处于最亮屏幕状态的起始时间 mLastScreenBrightnessBoostTime = eventTime; //设置最亮屏幕的标志位true if (!mScreenBrightnessBoostInProgress) { mScreenBrightnessBoostInProgress = true; //发送广播 mNotifier.onScreenBrightnessBoostChanged(); } //修改mDirty的值,表示最大屏幕亮度的状态发生了变化 mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST; //记录 userActivityNoUpdateLocked(eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid); //更新电源的状态信息 updatePowerStateLocked(); } }
从上面的代码可以看出,该函数:
首先,使用mLastScreenBrightnessBoostTime变量记录了终端处于最大屏幕亮度的起始时间;
然后,将最大屏幕亮度的标志位置为true,并修改mDirty标志位,以表示最大屏幕亮度的状态发生了变化;
最后,调用updatePowerStateLocked方法更新电源状态信息。
我们已经知道,updatePowerStateLocked将会调用到updateScreenBrightnessBoostLocked。
接下来,我们看看updateScreenBrightnessBoostLocked对应的代码:
private void updateScreenBrightnessBoostLocked(int dirty) { //根据mDirty的标志位来判断终端屏幕最大可用亮度的状态是否发生了变化 if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) { //上面的代码已经提到过,当boostScreenBrightness接口被调用时,mScreenBrightnessBoostInProgress置为true if (mScreenBrightnessBoostInProgress) { //移除旧的超时事件 final long now = SystemClock.uptimeMillis(); mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT); //终端处于最大屏幕亮度的时间,在sleep的时间之后,说明终端还未息屏之类的 if (mLastScreenBrightnessBoostTime > mLastSleepTime) { //此时,重新计算终端可处于最大屏幕亮度的时间 final long boostTimeout = mLastScreenBrightnessBoostTime + SCREEN_BRIGHTNESS_BOOST_TIMEOUT; if (boostTimeout > now) { Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT); msg.setAsynchronous(true); //发送延迟的超时事件 //当屏幕离开最大亮度状态时,该事件将被发送 //当该事件被处理时,会再次进入到updateScreenBrightnessBoostLocked函数 mHandler.sendMessageAtTime(msg, boostTimeout); return; } } //进入到这个分支时,说明屏幕处于最大亮度状态的时间已经超时了 //将该标志置为false mScreenBrightnessBoostInProgress = false; //发送广播 mNotifier.onScreenBrightnessBoostChanged(); //触发一次用户活动,写入mDirty标志位,同时做一些其它记录 userActivityNoUpdateLocked(now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); } } }
至此,PMS第一阶段更新基本状态信息的流程结束。
二、更新wakelock和用户活动
for (;;) { int dirtyPhase1 = mDirty; dirtyPhase2 |= dirtyPhase1; mDirty = 0; updateWakeLockSummaryLocked(dirtyPhase1); updateUserActivitySummaryLocked(now, dirtyPhase1); //updateWakefulnessLocked将决定系统是否进入休眠或dreaming状态 //主要是更新DIRTY_WAKEFULNESS位,如果不需要更新,则返回false if (!updateWakefulnessLocked(dirtyPhase1)) { break; } }
1、updateWakeLockSummaryLocked
updateWakeLockSummaryLocked函数根据PMS当前持有的所有WakeLock,得到当前终端整体的信息,保存到mWakeLockSummary变量中。
/** * Updates the value of mWakeLockSummary to summarize the state of all active wake locks. * Note that most wake-locks are ignored when the system is asleep. */ private void updateWakeLockSummaryLocked(int dirty) { //PMS持有的WakeLock发生变化,或者唤醒状态发生变化时,才重新进行更新mWakeLockSummary //例如:调用PMS的acquireWakeLock时,就会将dirty的DIRTY_WAKE_LOCKS位置1 if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) { mWakeLockSummary = 0; final int numWakeLocks = mWakeLocks.size(); for (int i = 0; i < numWakeLocks; i++) { final WakeLock wakeLock = mWakeLocks.get(i); //这里只关注WakeLock的level //下面的代码其实就是实现每个level WakeLock对应的注释信息 switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { case PowerManager.PARTIAL_WAKE_LOCK: //在分析PMS acquireWakeLock的流程时,已经提到过 //在doze模式下,不在白名单内的非系统应用申请PARTIAL_WAKE_LOCK时,将被disabled if (!wakeLock.mDisabled) { // We only respect this if the wake lock is not disabled. mWakeLockSummary |= WAKE_LOCK_CPU; } break; case PowerManager.FULL_WAKE_LOCK: mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT; break; case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT; break; case PowerManager.SCREEN_DIM_WAKE_LOCK: mWakeLockSummary |= WAKE_LOCK_SCREEN_DIM; break; case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: mWakeLockSummary |= WAKE_LOCK_PROXIMITY_SCREEN_OFF; break; case PowerManager.DOZE_WAKE_LOCK: mWakeLockSummary |= WAKE_LOCK_DOZE; break; case PowerManager.DRAW_WAKE_LOCK: mWakeLockSummary |= WAKE_LOCK_DRAW; break; } } // Cancel wake locks that make no sense based on the current state. //从下面的代码可以看出,PMS中的mWakefulness变量记录了终端当前的状态 //下面就是移除在特定状态下,没有意义的WakeLock if (mWakefulness != WAKEFULNESS_DOZING) { //如果不是Dozing状态,移除相应的wakeLock标志位 mWakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW); } if (mWakefulness == WAKEFULNESS_ASLEEP || (mWakeLockSummary & WAKE_LOCK_DOZE) != 0) { //如果当前为Asleep或者有Doze的wakeLock锁的时候,应该移除掉屏幕亮度相关的wakeLock锁 mWakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM | WAKE_LOCK_BUTTON_BRIGHT); if (mWakefulness == WAKEFULNESS_ASLEEP) { //休眠时,sensor不再需要监听终端是否靠近物体,以触发亮灭屏 mWakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF; } } // Infer implied wake locks where necessary based on the current state. //根据当前的状态,及PMS持有的WakeLock,推断出隐含的持锁需求 //例如:当PMS持有亮屏锁WAKE_LOCK_SCREEN_BRIGHT时,若当前终端为唤醒态 //那么CPU显然也需要处于唤醒态 if ((mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) { if (mWakefulness == WAKEFULNESS_AWAKE) { mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE; } else if (mWakefulness == WAKEFULNESS_DREAMING) { mWakeLockSummary |= WAKE_LOCK_CPU; } } if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0) { mWakeLockSummary |= WAKE_LOCK_CPU; } ................... } }
结合每个WakeLock level的注释信息,以上代码还是比较好理解的。
这里唯一需要说明的是,Android定义一个mWakeLockSummary变量的原因是:
PMS将WakeLock定义为不同进程的请求信息,这些请求信息对CPU、屏幕和键盘有不同的需求。
对于每一种资源而言,只要有一个申请满足获取条件,PMS就需要为终端分配该申请对应的资源。
例如:假设PMS有20个WakeLock,只有1个申请亮屏,另外19个只申请CPU唤醒,PMS仍然需要保持终端亮屏。
因此,mWakeLockSummary就提供了一种整合多个WakeLock请求的功能,方便PMS进行集中的控制。
2、updateUserActivitySummaryLocked
updateUserActivitySummaryLocked主要根据用户最后的活动来决定当前屏幕的状态。
/** * Updates the value of mUserActivitySummary to summarize the user requested * state of the system such as whether the screen should be bright or dim. * Note that user activity is ignored when the system is asleep. */ private void updateUserActivitySummaryLocked(long now, int dirty) { if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) { mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT); long nextTimeout = 0; if (mWakefulness == WAKEFULNESS_AWAKE || mWakefulness == WAKEFULNESS_DREAMING || mWakefulness == WAKEFULNESS_DOZING) { //获取进入休眠状态的时间sleepTimeout //getSleepTimeoutLocked中会判断休眠时间和屏幕熄灭时间的关系 //如果休眠时间sleepTimeout小于屏幕熄灭时间screenOfftime, //则休眠时间被调整为屏幕熄灭时间,因为屏幕亮屏状态下,终端不能进入休眠 final int sleepTimeout = getSleepTimeoutLocked(); //获取屏幕熄灭的时间 final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout); //获取屏幕变暗的时间 final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); //当Window Manager判定用户inactive时,将此标志置为true final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager; //类似于之前的mWakeLockSummary,将当前的用户事件,转化为PMS可以处理的屏幕状态 mUserActivitySummary = 0; //在唤醒的状态下,发生过用户事件 if (mLastUserActivityTime >= mLastWakeTime) { //重新计算出屏幕需要变暗的时间 nextTimeout = mLastUserActivityTime + screenOffTimeout - screenDimDuration; if (now < nextTimeout) { //如果没有到达需要变暗的时间,那么当前屏幕的状态为USER_ACTIVITY_SCREEN_BRIGHT(亮屏) mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT; } else { //到达变暗的时间,则计算出屏幕熄灭的时间 nextTimeout = mLastUserActivityTime + screenOffTimeout; if (now < nextTimeout) { //还没到熄灭的时间,则当前屏幕的状态为USER_ACTIVITY_SCREEN_DIM(暗屏) mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM; } } } //注意mUserActivitySummary为0才会进入下面的分支 //即上面改变mUserActivitySummary的条件不满足时,才会进入这个分支(例如:唤醒状态下,没发生过改变屏幕状态的UserActivity) if (mUserActivitySummary == 0 //mLastUserActivityTimeNoChangeLights表示用户最后的活动不会改变屏幕当前的状态 && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) { //计算下次屏幕熄灭的时间 nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout; //还未到达熄屏时间 if (now < nextTimeout) { if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT) { //当前屏幕是亮屏,仍然设置为亮屏 mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT; } else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { //当前屏幕是变暗,仍然设置为变暗 mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM; } } } if (mUserActivitySummary == 0) { //若定义了有效的休眠时间 if (sleepTimeout >= 0) { //计算用户最后的活动时间 final long anyUserActivity = Math.max(mLastUserActivityTime, mLastUserActivityTimeNoChangeLights); //只有在唤醒状态下,进行了用户活动,才会重新更新休眠时间 (此时,应该是有过用户活动,但过了息屏时间了) if (anyUserActivity >= mLastWakeTime) { nextTimeout = anyUserActivity + sleepTimeout; if (now < nextTimeout) { //走到这个分支,应该是屏幕已经熄灭,但还未到达休眠状态,先进入dream态 mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM; } } } else { //直接进入dream态,后文的updateWakefulnessLocked将判断是否休眠 mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM; nextTimeout = -1; } } //如果屏幕未进入dream态,但Window Manager判定用户inactive,则进入下面分支 if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) { //如果屏幕未熄灭 if ((mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) { // Device is being kept awake by recent user activity if (nextTimeout >= now && mOverriddenTimeout == -1) { // Save when the next timeout would have occurred mOverriddenTimeout = nextTimeout; } } //Window Manager的权限很大,如果它判断用户inactive,直接进入dream态 mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM; nextTimeout = -1; } //根据nextTimeOut延迟发送信息,信息被处理后,将重新调用updatePowerStateLocked,于是再次进入到该方法 //通过不断进入该方法,不断评估是否根据用户动作亮、熄屏等 if (mUserActivitySummary != 0 && nextTimeout >= 0) { Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextTimeout); } } else { mUserActivitySummary = 0; } .......... } }
从上面的代码可以看出,在该函数中用mUserActivitySummary变量存储当前屏幕的状态。
一共有3中基本状态:
* USER_ACTIVITY_SCREEN_BRIGHT 点亮屏幕
* USER_ACTIVITY_SCREEN_DIM 屏幕变暗
* USER_ACTIVITY_SCREEN_DREAM 屏保状态
从代码可以看出,屏幕变化和userActivity活动有关,它根据最后的userActivity活动的时间决定点亮屏幕、调暗屏幕或熄灭屏幕。
之前的很多方法中都会调用userActivityNoUpdateLocked方法。该方法将触发一次用户活动,以更新用户活动的时间,这样屏幕变暗和熄灭时间就会重新进行计算。
这也就是为什么用户一直操作手机,屏幕不会熄灭或者变暗的原因。
大图地址
整体来讲,个人感觉这个函数的代码写的还是挺绕的,因此还是作一个图记录一下。
大家有兴趣可以看一下。
3、updateWakefulnessLocked
从之前的代码可以看出,updateWakefulnessLocked将决定第二阶段的电源状态更新是否结束。
我们看一下updateWakefulnessLocked函数:
/** * Updates the wakefulness of the device. * * This is the function that decides whether the device should start dreaming * based on the current wake locks and user activity state. It may modify mDirty * if the wakefulness changes. * * Returns true if the wakefulness changed and we need to restart power state calculation. */ private boolean updateWakefulnessLocked(int dirty) { boolean changed = false; //下面的条件还是比较容易满足的,基本上只要之前的流程更改过mDirty就会进入分支 if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE | DIRTY_DOCK_STATE)) != 0) { //如果当前的状态是唤醒的,isItBedTimeYetLocked判定不能再保持唤醒态 if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) { ................ final long time = SystemClock.uptimeMillis(); //主要根据设置信息,判断是否满足进入Dream状态的条件 if (shouldNapAtBedTimeLocked()) { //将mWakefullness的值置为WAKEFULNESS_DREAMING,修改mDirty变量,并进行通知等 changed = napNoUpdateLocked(time, Process.SYSTEM_UID); } else { //将mWakefullness的值置为WAKEFULNESS_DOZING //如果系统设置了跳过Dozing态,则将mWakefullness置为WAKEFULNESS_ASLEEP //同时修改mDirty变量,并进行通知等 changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); } //注意:napNoUpdateLocked和goToSleepNoUpdateLocked函数正常执行后, //都会将mSandmanSummoned(被"睡魔"眷顾了)置为true } } return changed; }
从上面的代码可以看出,如果终端可以一直保持唤醒状态,或一开始就是非唤醒态,
那么mWakefulness不会发生改变,第二阶段的for循环将会break;
如果终端要从唤醒态变为非唤醒态,那么for循环将再运行一次,即重新计算一次mWakeLockSummary和mUserActivitySummary。
这么做的原因是:updateWakeLockSummaryLocked和updateUserActivitySummaryLocked函数的一些计算,与终端是否处于唤醒状态,即mWakefulness的值有关。
由于这两个函数并不会修改mWakefulness,因此在这一次运行时,updateWakefulnessLocked将返回false,即第二阶段结束。
因此,我们可以得出结论:更新电源状态的第二阶段,正常情况下最多运行两次。
在第二阶段的最后,我们看一下isItBedTimeYetLocked函数:
/** * Returns true if the device should go to sleep now. * Also used when exiting a dream to determine whether we should go back * to being fully awake or else go to sleep for good. */ private boolean isItBedTimeYetLocked() { //主要由isBeingKeptAwakeLocked决定 return mBootCompleted && !isBeingKeptAwakeLocked(); } /** * Returns true if the device is being kept awake by a wake lock, user activity * or the stay on while powered setting. We also keep the phone awake when * the proximity sensor returns a positive result so that the device does not * lock while in a phone call. This function only controls whether the device * will go to sleep or dream which is independent of whether it will be allowed * to suspend. */ //根据状态,判断终端是否应该处于唤醒状态 private boolean isBeingKeptAwakeLocked() { return mStayOn || mProximityPositive || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0 || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0 || mScreenBrightnessBoostInProgress; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
参考原生代码的注释,这一部分代码还是比较好理解的。
三、更新display power state
第三阶段将负责更新屏幕的显示状态。
/** * Updates the display power state asynchronously. * When the update is finished, mDisplayReady will be set to true. The display * controller posts a message to tell us when the actual display power state * has been updated so we come back here to double-check and finish up. * * This function recalculates the display power state each time. */ private boolean updateDisplayPowerStateLocked(int dirty) { final boolean oldDisplayReady = mDisplayReady; //mDirty满足条件时,进入以下分支 if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) { //根据mWakefullness、mWakeLockSummary、mUserActivitySummary等,决定屏幕的policy //policy定义为DisplayPowerRequest.(POLICY_OFF、POLICY_DOZE、POLICY_BRIGHT和POLICY_DIM) mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(); // Determine appropriate screen brightness and auto-brightness adjustments. //决定屏幕的亮度 ................. // Update display power request. // 更新mDisplayPowerRequest的参数 ................... //实际上调用DisplayPowerController的requestPowerState函数 //在初始时,PMS注册了mDisplayPowerCallbacks到DisplayPowerController中, //当更新完成后,会回调定义的接口,重新updatePowerStateLocked mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest, mRequestWaitForNegativeProximity); ................... } return mDisplayReady && !oldDisplayReady; }
我们跟进一下DisplayPowerController的requestPowerState函数:
/** * Requests a new power state. * The controller makes a copy of the provided object and then * begins adjusting the power state to match what was requested. */ public boolean requestPowerState(DisplayPowerRequest request, boolean waitForNegativeProximity) { ......... synchronized (mLock) { boolean changed = false; //新需求增加:proximity sensor需要检测距离 if (waitForNegativeProximity //原来没有这个需求 && !mPendingWaitForNegativeProximityLocked) { mPendingWaitForNegativeProximityLocked = true; changed = true; } //以下表示,参数中的Request对于DisplayPowerController而言,是一个新的需求 if (mPendingRequestLocked == null) { mPendingRequestLocked = new DisplayPowerRequest(request); changed = true; } else if (!mPendingRequestLocked.equals(request)) { mPendingRequestLocked.copyFrom(request); changed = true; } if (changed) { //一但有新的需求,mDisplayReadyLocked就是false,表示屏幕有待调整 mDisplayReadyLocked = false; } //有新需求,同时有对应的request if (changed && !mPendingRequestChangedLocked) { mPendingRequestChangedLocked = true; //发送消息,更新屏幕状态 //最终通过DisplayPowerController的updatePowerState函数,进行屏幕状态更新 //这部分代码也极其复杂,暂时不在这里展开分析 //更新屏幕状态后,将回调PMS的接口 sendUpdatePowerStateLocked(); } return mDisplayReadyLocked; } }
根据requestPowerState的代码,我们知道:
当PMS传入一个新的mDisplayPowerRequest时,requestPowerState应该返回为false;当DisplayPowerController按照mDisplayPowerRequest修改完屏幕状态,再次进入回到updateDisplayPowerStateLocked函数,调用requestPowerState时才会返回true。
整体的代码流程大概可以抽象成下图:
这一阶段的代码,我们只是分析了整个过程的冰山一角,并没有分析更新屏幕状态的实际操作。
但从现有的代码可以看出,PMS的作用仅仅是维护终端电源相关状态,实际的工作还是通过类似发送Request的方式,让其它的服务协助完成。
例如:在整个阶段,PMS根据之前得到信息,构造出DisplayPowerRequest,然后发送给DisplayPowerController进行实际的处理。
当DisplayPowerController完成实际的工作(部分工作还依赖于PhoneWindowManager)后,再通知PMS进行复查。
因此PMS的定位,确实可以用一个”Manager”来形容;
负责整个终端信息的搜集和维护,然后将相应的工作指派给具体的“员工”执行;
“员工”执行完毕后,向”Manager”汇报;
“Manager”检查工作的完成情况后,然后做出下一步的指示。
四、更新dream state
updateDreamLocked函数主要用于更新屏保状态,当设备进入或者退出屏保的时候都会触发这个方法:
private void updateDreamLocked(int dirty, boolean displayBecameReady) { if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_USER_ACTIVITY | DIRTY_WAKE_LOCKS | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_IS_POWERED | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE | DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) { if (mDisplayReady) { //mDirty满足条件,同时屏幕状态调整完毕,才进入下一步 scheduleSandmanLocked(); } } private void scheduleSandmanLocked() { if (!mSandmanScheduled) { //mSandmanScheduled的作用就是让MessageQueue中仅保留一个MSG_SANDMAN mSandmanScheduled = true; //由handleSandman处理 Message msg = mHandler.obtainMessage(MSG_SANDMAN); msg.setAsynchronous(true); mHandler.sendMessage(msg); } }
handleSandman函数比较复杂,主要用于决定设备是否应该停留在dreaming或dozing状态。
我们分段介绍该函数的功能。
1、决定是否可以进入屏保状态
/** * Called when the device enters or exits a dreaming or dozing state. */ private void handleSandman() { final boolean startDreaming; final int wakefulness; synchronized (mLock) { mSandmanScheduled = false; wakefulness = mWakefulness; //前面提到过,当updateWakefulnessLocked判断进入dozing或sleep状态时, //会将mSandmanSummoned置为true //mDisplayReady主要确保前面屏幕状态更新完毕 if (mSandmanSummoned && mDisplayReady) { //判断device是否可以dream或dozing startDreaming = canDreamLocked() || canDozeLocked(); mSandmanSummoned = false; } else { startDreaming = false; } } ..........
这段代码主要用于确定,设备是否可以看是dreaming。
除去前置条件的限制外,此处的结果主要由canDreamLocked和canDozeLocked决定。
我们分别看看这两个函数:
/** * Returns true if the device is allowed to dream in its current state. */ private boolean canDreamLocked() { //mWakefulness等于WAKEFULNESS_DREAMING if (mWakefulness != WAKEFULNESS_DREAMING //设备支持dreaming || !mDreamsSupportedConfig //设置开关开启 || !mDreamsEnabledSetting //屏幕熄灭 || !mDisplayPowerRequest.isBrightOrDim() || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0 //初始化完成 || !mBootCompleted) { return false; } //以上条件均满足,才能进入后面的判断 //不处于唤醒态 if (!isBeingKeptAwakeLocked()) { //没充电,电源选项也未配置,不可dreaming if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) { return false; } //没充电,且电池电量过低,不可dreaming if (!mIsPowered && mDreamsBatteryLevelMinimumWhenNotPoweredConfig >= 0 && mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) { return false; } //充电,但电池电量过低,不可dreaming if (mIsPowered && mDreamsBatteryLevelMinimumWhenPoweredConfig >= 0 && mBatteryLevel < mDreamsBatteryLevelMinimumWhenPoweredConfig) { return false; } //充电和未充电分别有一个最低的dreaming电量门限 } return true; }
从上面的代码可以看出,dreaming除了对终端当前的状态、配置项有关外,在非唤醒状态下还与当前的电池电量有关系。
canDozeLocked函数相对简单:
private boolean canDozeLocked() { return mWakefulness == WAKEFULNESS_DOZING; }
2、在必要时,进入屏保状态
// Start dreaming if needed. final boolean isDreaming; if (mDreamManager != null) { if (startDreaming) { //结束旧梦 mDreamManager.stopDream(false /*immediate*/); //开启新梦 mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING); } //startDream成功后,一般isDreaming就会返回true isDreaming = mDreamManager.isDreaming(); } else { isDreaming = false; }
决定了是否可以进入屏保状态后,这一部分就开始进行实际的工作。
mDreamManager为DreamManagerService的Binder代理。
我们重点看看DreamManagerService的startDream函数,stopDream的工作内容与startDream相反,不做细致分析:
//定义于DreamManagerService的内部类中 public void startDream(boolean doze) { startDreamInternal(doze); } //定义于DreamManagerService private void startDreamInternal(boolean doze) { final int userId = ActivityManager.getCurrentUser(); //个人觉得这里应该是获取屏保对象 final ComponentName dream = chooseDreamForUser(doze, userId); if (dream != null) { synchronized (mLock) { startDreamLocked(dream, false /*isTest*/, doze, userId); } } } private void startDreamLocked(final ComponentName name, final boolean isTest, final boolean canDoze, final int userId) { //申请的屏保与当前的一致,不用进行修改 if (Objects.equal(mCurrentDreamName, name) && mCurrentDreamIsTest == isTest && mCurrentDreamCanDoze == canDoze && mCurrentDreamUserId == userId) { return; } //立即停止当前的屏保 stopDreamLocked(true /*immediate*/); final Binder newToken = new Binder(); mCurrentDreamToken = newToken; mCurrentDreamName = name; mCurrentDreamIsTest = isTest; mCurrentDreamCanDoze = canDoze; mCurrentDreamUserId = userId; mHandler.post(new Runnable() { @Override public void run() { //调用DreamController的startDream函数 mController.startDream(newToken, name, isTest, canDoze, userId); } }); } //定义于DreamController中 public void startDream(Binder token, ComponentName name, boolean isTest, boolean canDoze, int userId) { //移除当前屏保并回调通知 stopDream(true /*immediate*/); ......... try { .............. //记录dream mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId); mDreamStartTime = SystemClock.elapsedRealtime(); .............. //做好屏幕相关的准备工作 try { mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM); } catch (RemoteException ex) { Slog.e(TAG, "Unable to add window token for dream.", ex); stopDream(true /*immediate*/); return; } Intent intent = new Intent(DreamService.SERVICE_INTERFACE); intent.setComponent(name); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); try { //拉起屏保服务 if (!mContext.bindServiceAsUser(intent, mCurrentDream, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(userId))) { Slog.e(TAG, "Unable to bind dream service: " + intent); stopDream(true /*immediate*/); return; } catch (SecurityException ex) { ............ stopDream(true /*immediate*/); return; } mCurrentDream.mBound = true; //在DREAM_CONNECTION_TIMEOUT到期时,bind服务还未成功,runnable就负责结束dream mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT); } finally { ........ } }
从这部分代码我们知道了,所谓的屏保其实就是拉起一个特殊的服务。
3、更新屏保状态
// Update dream state. synchronized (mLock) { // Remember the initial battery level when the dream started. if (startDreaming && isDreaming) { mBatteryLevelWhenDreamStarted = mBatteryLevel; ................ } // If preconditions changed, wait for the next iteration to determine // whether the dream should continue (or be restarted). //例如:mDisplayReady为false时, mSandmanSummoned保持为false if (mSandmanSummoned || mWakefulness != wakefulness) { return; // wait for next cycle } ............. // Determine whether the dream should continue. if (wakefulness == WAKEFULNESS_DREAMING) { if (isDreaming && canDreamLocked()) { if (mDreamsBatteryLevelDrainCutoffConfig >= 0 //下面这句我是懵逼的,这不是必然成立的么? //也就是只要配置了mDreamsBatteryLevelDrainCutoffConfig就会成立 //按注释来讲,这里好歹重新取一次mBatteryLevel啊!!!怀疑是bug点 && mBatteryLevel < mBatteryLevelWhenDreamStarted - mDreamsBatteryLevelDrainCutoffConfig && !isBeingKeptAwakeLocked()) { // If the user activity timeout expired and the battery appears // to be draining faster than it is charging then stop dreaming // and go to sleep. } else { return; // continue dreaming } } // Dream has ended or will be stopped. Update the power state. if (isItBedTimeYetLocked()) { //休眠 goToSleepNoUpdateLocked(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); updatePowerStateLocked(); } else { //唤醒 wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), "android.server.power:DREAM", Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); updatePowerStateLocked(); } } else if (wakefulness == WAKEFULNESS_DOZING) { if (isDreaming) { return; // continue dozing } // Doze has ended or will be stopped. Update the power state. reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID); updatePowerStateLocked(); } } // Stop dream. //执行到这里说明退出了dreaming状态,如果之前拉起过屏保服务,此时应该停止它 if (isDreaming) { mDreamManager.stopDream(false /*immediate*/); } ...........
以上是PMS更新屏保状态的基本流程,整体来看相当的繁琐。
我们还是用一个图来整体整理一下:
大图链接
这部分代码最后太乱,每次更新状态后都会重新调用updatePowerStateLocked,然后再次进入到handleSandman函数中。
这种反复地递归调用,比较难以把控。
五、更新suspend blocker
updateSuspendBlockerLocked函数主要根据之前流程的执行结果,持有或者释放CPU和屏幕的锁。
我们一起来看看对应的函数:
private void updateSuspendBlockerLocked() { //根据是否有CPU的wakelock,来决定cpu是保持否唤醒 final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0); //根据前面屏幕相关的状态,来决定是否需要持有屏幕的锁 final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked(); //屏幕如果不需要保持开启状态,那么可自动熄灭 final boolean autoSuspend = !needDisplaySuspendBlocker; //应该是表示屏幕是否是可交互的 final boolean interactive = mDisplayPowerRequest.isBrightOrDim(); // Disable auto-suspend if needed. //autoSuspend为false,说明屏幕还需要点亮 if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) { //通过native函数,调用底层的autosuspend_disable setHalAutoSuspendModeLocked(false); } // First acquire suspend blockers if needed. //在需要的情况下,获取CPU和屏幕的锁 if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) { mWakeLockSuspendBlocker.acquire(); mHoldingWakeLockSuspendBlocker = true; } if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) { mDisplaySuspendBlocker.acquire(); mHoldingDisplaySuspendBlocker = true; } // Inform the power HAL about interactive mode. if (mDecoupleHalInteractiveModeFromDisplayConfig) { if (interactive || mDisplayReady) { //调用底层动态库的setInteractive函数,决定终端是否可以进行交互 setHalInteractiveModeLocked(interactive); } } // Then release suspend blockers if needed. //如果不需要,则释放CPU和屏幕的锁 if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) { mWakeLockSuspendBlocker.release(); mHoldingWakeLockSuspendBlocker = false; } if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) { mDisplaySuspendBlocker.release(); mHoldingDisplaySuspendBlocker = false; } // Enable auto-suspend if needed. //如果需要设置自动休眠模式 if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) { setHalAutoSuspendModeLocked(true); } }
从上面的代码可以看出PMS是非常依赖于native层的,真实的持锁、释放锁、设置交互状态等工作,均是移交到native层进行操作。
我们以mWakeLockSuspendBlocker的处理流程为例,看看native的调用过程:
.......... mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks"); .........
之前的博客也提到过,PMS在其构造函数中调用createSuspendBlockerLocked函数,创建出了mWakeLockSuspendBlocker:
private SuspendBlocker createSuspendBlockerLocked(String name) { //实际对象为PMS内部类SuspendBlockerImpl SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name); mSuspendBlockers.add(suspendBlocker); return suspendBlocker; }
从上面的代码,我们知道当PMS需要获取底层锁时,调用的是SuspendBlockerImpl的acquire函数:
public void acquire() { synchronized (this) { mReferenceCount += 1; if (mReferenceCount == 1) { ....... //调用到了native层 nativeAcquireSuspendBlocker(mName); } } }
在native层的com_android_server_power_PowerManagerService.cpp中,对应的native函数为:
static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) { ScopedUtfChars name(env, nameStr); //获取的是PARTIAL_WAKE_LOCK的类型,即保持CPU唤醒的 acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str()); }
从这里的代码我们不难发现,尽管PMS定义了不同的WakeLock等级,但当通过PMS的native函数调用HAL层函数acquire_wake_lock时,使用的都是PARTIAL_WAKE_LOCK。
个人觉得这是可以理解的,当其它进程向PMS申请保持屏幕唤醒的Framework层WakeLock后,PMS在Framework层就进行了对应的处理,例如将请求信息等地交给DisplayPowerController等处理。因此,对于底层的HAL层而言,只需要关注CPU是否需要保持唤醒即可。
HAL层函数acquire_wake_lock,最后会向/sys/power/wake_lock节点进行write操作。
总结
至此,updatePowerStateLocked的基本流程介绍完毕,大体上如下图所示:
通过其中的源码,我们也能看出仅管理当前的状态,涉及的细节就非常的琐碎。
而屏幕和CPU的实际控制,还牵扯到大量其它对象和HAL层代码。
Android电源的管理实际上是基于Linux电源管理策略的,因此若要真正掌握,还需要对Linux的电源管理策略作进一步的了解。
由于个人水平有限,目前还无法高屋建瓴地整体分析宏观的电源管理架构,细节也有一些遗漏。
后续争取以此博客为基础,不断迭代,以求更进一步地了解PMS的知识。
http://blog.csdn.net/gaugamela/article/details/52838654
相关文章推荐
- Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程
- Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程
- Android7.0 PowerManagerService 之亮灭屏(二) PMS 电源状态管理updatePowerStateLocked()
- Android7.0 PowerManagerService(4) Power按键流程
- Android7.0 PowerManagerService(2) WakeLock的使用及流程
- Android7.0 PowerManagerService(4) Power按键流程
- Android7.0 PowerManagerService(2) WakeLock的使用及流程
- Android7.0 电源管理-updatePowerStateLocked 基本流程图
- Android 7.0 ActivityManagerService(8) 进程管理相关流程分析(2) updateOomAdjLocked
- Android7.0 PowerManagerService(4) Power按键流程
- android7.0 ActivityManagerService(AMS)启动流程
- Android7.0 PackageManagerService (2) PKMS构造函数的主要工作
- PowerManagerService流程分析(电源管理)
- Android7.0 PowerManagerService(2) WakeLock的使用及流程
- android4.4 PowerManagerService流程分析
- Android7.0 PowerManagerService(1) 启动过程
- PowermanagerService的sleep和wakeup流程
- PowerManagerService 核心方法详解(转载,感觉不错)
- PowerManagerService的启动流程
- Android7.0 PowerManagerService(1) 启动过程