日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程

發布時間:2025/3/15 Android 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前面的博客中,我們已經分析過,當Android中的進程要使用電量時,需要向PMS申請WakeLock;當進程完成工作后,需要釋放對應的WakeLock。?
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 stateboolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);// Update dream state (depends on display ready signal).// 4、更新dream stateupdateDreamLocked(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 blockerupdateSuspendBlockerLocked();} finally {..........} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

在PMS中有個很重要的變量mDirty,該變量按位存儲PMS中的各種變化狀態。?
例如,之前介紹PMS的acquire WakeLock流程時,就進行了以下操作:

......... mDirty |= DIRTY_WAKE_LOCKS; ........
  • 1
  • 2
  • 3

每當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();}} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

從以上代碼可以看出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);});} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

從上面的代碼可以看出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;}} }
  • 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
  • 28
  • 29

這一部分的代碼功能比較單一,主要用于更新變量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();} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

從上面的代碼可以看出,該函數:?
首先,使用mLastScreenBrightnessBoostTime變量記錄了終端處于最大屏幕亮度的起始時間;?
然后,將最大屏幕亮度的標志位置為true,并修改mDirty標志位,以表示最大屏幕亮度的狀態發生了變化;?
最后,調用updatePowerStateLocked方法更新電源狀態信息。?
我們已經知道,updatePowerStateLocked將會調用到updateScreenBrightnessBoostLocked。

接下來,我們看看updateScreenBrightnessBoostLocked對應的代碼:

private void updateScreenBrightnessBoostLocked(int dirty) {//根據mDirty的標志位來判斷終端屏幕最大可用亮度的狀態是否發生了變化if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) {//上面的代碼已經提到過,當boostScreenBrightness接口被調用時,mScreenBrightnessBoostInProgress置為trueif (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;}}//進入到這個分支時,說明屏幕處于最大亮度狀態的時間已經超時了//將該標志置為falsemScreenBrightnessBoostInProgress = false;//發送廣播mNotifier.onScreenBrightnessBoostChanged();//觸發一次用戶活動,寫入mDirty標志位,同時做一些其它記錄userActivityNoUpdateLocked(now,PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);}} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

至此,PMS第一階段更新基本狀態信息的流程結束。

二、更新wakelock和用戶活動

for (;;) {int dirtyPhase1 = mDirty;dirtyPhase2 |= dirtyPhase1;mDirty = 0;updateWakeLockSummaryLocked(dirtyPhase1);updateUserActivitySummaryLocked(now, dirtyPhase1);//updateWakefulnessLocked將決定系統是否進入休眠或dreaming狀態//主要是更新DIRTY_WAKEFULNESS位,如果不需要更新,則返回falseif (!updateWakefulnessLocked(dirtyPhase1)) {break;} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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位置1if ((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時,將被disabledif (!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變量記錄了終端當前的狀態//下面就是移除在特定狀態下,沒有意義的WakeLockif (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;}...................} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

結合每個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時,將此標志置為truefinal 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 activityif (nextTimeout >= now && mOverriddenTimeout == -1) {// Save when the next timeout would have occurredmOverriddenTimeout = 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;}..........} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125

從上面的代碼可以看出,在該函數中用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; }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

從上面的代碼可以看出,如果終端可以一直保持喚醒狀態,或一開始就是非喚醒態,?
那么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

參考原生代碼的注釋,這一部分代碼還是比較好理解的。

三、更新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中,//當更新完成后,會回調定義的接口,重新updatePowerStateLockedmDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,mRequestWaitForNegativeProximity);...................}return mDisplayReady && !oldDisplayReady; }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

我們跟進一下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;}//有新需求,同時有對應的requestif (changed && !mPendingRequestChangedLocked) {mPendingRequestChangedLocked = true;//發送消息,更新屏幕狀態//最終通過DisplayPowerController的updatePowerState函數,進行屏幕狀態更新//這部分代碼也極其復雜,暫時不在這里展開分析//更新屏幕狀態后,將回調PMS的接口sendUpdatePowerStateLocked();}return mDisplayReadyLocked;} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

根據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_SANDMANmSandmanScheduled = true;//由handleSandman處理Message msg = mHandler.obtainMessage(MSG_SANDMAN);msg.setAsynchronous(true);mHandler.sendMessage(msg);} }
  • 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

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或dozingstartDreaming = canDreamLocked() || canDozeLocked();mSandmanSummoned = false;} else {startDreaming = false;}} ..........
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

這段代碼主要用于確定,設備是否可以看是dreaming。?
除去前置條件的限制外,此處的結果主要由canDreamLocked和canDozeLocked決定。

我們分別看看這兩個函數:

/** * Returns true if the device is allowed to dream in its current state. */ private boolean canDreamLocked() {//mWakefulness等于WAKEFULNESS_DREAMINGif (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()) {//沒充電,電源選項也未配置,不可dreamingif (!mIsPowered && !mDreamsEnabledOnBatteryConfig) {return false;}//沒充電,且電池電量過低,不可dreamingif (!mIsPowered&& mDreamsBatteryLevelMinimumWhenNotPoweredConfig >= 0&& mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) {return false;}//充電,但電池電量過低,不可dreamingif (mIsPowered&& mDreamsBatteryLevelMinimumWhenPoweredConfig >= 0&& mBatteryLevel < mDreamsBatteryLevelMinimumWhenPoweredConfig) {return false;}//充電和未充電分別有一個最低的dreaming電量門限}return true; }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

從上面的代碼可以看出,dreaming除了對終端當前的狀態、配置項有關外,在非喚醒狀態下還與當前的電池電量有關系。

canDozeLocked函數相對簡單:

private boolean canDozeLocked() {return mWakefulness == WAKEFULNESS_DOZING; }
  • 1
  • 2
  • 3

2、在必要時,進入屏保狀態

// Start dreaming if needed. final boolean isDreaming; if (mDreamManager != null) {if (startDreaming) {//結束舊夢mDreamManager.stopDream(false /*immediate*/);//開啟新夢mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);}//startDream成功后,一般isDreaming就會返回trueisDreaming = mDreamManager.isDreaming(); } else {isDreaming = false; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

決定了是否可以進入屏保狀態后,這一部分就開始進行實際的工作。

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() {@Overridepublic 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 {..............//記錄dreammCurrentDream = 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就負責結束dreammHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);} finally {........} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

從這部分代碼我們知道了,所謂的屏保其實就是拉起一個特殊的服務。

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保持為falseif (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*/); } ...........
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

以上是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_disablesetHalAutoSuspendModeLocked(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);} }
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

從上面的代碼可以看出PMS是非常依賴于native層的,真實的持鎖、釋放鎖、設置交互狀態等工作,均是移交到native層進行操作。?
我們以mWakeLockSuspendBlocker的處理流程為例,看看native的調用過程:

.......... mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks"); .........
  • 1
  • 2
  • 3

之前的博客也提到過,PMS在其構造函數中調用createSuspendBlockerLocked函數,創建出了mWakeLockSuspendBlocker:

private SuspendBlocker createSuspendBlockerLocked(String name) {//實際對象為PMS內部類SuspendBlockerImplSuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);mSuspendBlockers.add(suspendBlocker);return suspendBlocker; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

從上面的代碼,我們知道當PMS需要獲取底層鎖時,調用的是SuspendBlockerImpl的acquire函數:

public void acquire() {synchronized (this) {mReferenceCount += 1;if (mReferenceCount == 1) {.......//調用到了native層nativeAcquireSuspendBlocker(mName);}} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在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()); }
  • 1
  • 2
  • 3
  • 4
  • 5

從這里的代碼我們不難發現,盡管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

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。