PowerManagerService之手動(dòng)滅屏流程示例分析
前言
PowerManagerService之亮屏流程分析 分析了亮屏的流程,并歸納出了一個(gè)適用于亮屏或滅屏的通用的流程。 但是,滅屏流程還有一些獨(dú)特的東西,例如 dream 這個(gè)晦澀的東西,就是在滅屏流程中啟動(dòng)的。
滅屏分為手動(dòng)滅屏和自動(dòng)滅屏,手動(dòng)滅屏一般就是指 Power 鍵滅屏,自動(dòng)滅屏就是大家常說(shuō)的屏幕超時(shí)而導(dǎo)致的滅屏。本文以 Power 鍵滅屏為例來(lái)分析手動(dòng)滅屏,自動(dòng)滅屏留到下一篇文章分析。
本文以 PowerManagerService之亮屏流程分析 作為基礎(chǔ),重復(fù)內(nèi)容不做過(guò)多分析,希望讀者務(wù)必先打好基礎(chǔ),再來(lái)閱讀本文。
1. 滅屏流程
Power 鍵滅屏?xí){(diào)用 PowerManagerService#goToSleep()
// PowerManagerService.java @Override // Binder call public void goToSleep(long eventTime, int reason, int flags) { // ... try { sleepDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, flags, uid); } } private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags, int uid) { synchronized (mLock) { // 1. 更新 wakefulness if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) { // 2. 更新電源狀態(tài) updatePowerStateLocked(); } } }
很熟悉的流程,先更新 wakefulness ,再更新電源狀態(tài)。
不過(guò),Power 鍵滅屏的更新 wakefulness 過(guò)程,有點(diǎn)小插曲,如下
private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason, int flags, int uid) { // ... try { // ... // 召喚睡夢(mèng)精靈,其實(shí)就是保存一個(gè)狀態(tài) mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true); // 更新 wakefulness 為 WAKEFULNESS_DOZING setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null); // 如果 flags 帶有 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE,更新 wakefulness 為 WAKEFULNESS_ASLEEP if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) { reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid); } } return true; }
默認(rèn)地,wakefulness 更新為 WAKEFULNESS_DOZING,然而,如果參數(shù) flags 帶有 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE,wakefulness 更新為 WAKEFULNESS_ASLEEP。
wakefulness 表示設(shè)備處于何種狀態(tài),其實(shí)它有四個(gè)值,如下
public abstract class PowerManagerInternal { /** * Wakefulness: The device is asleep. It can only be awoken by a call to wakeUp(). * The screen should be off or in the process of being turned off by the display controller. * The device typically passes through the dozing state first. */ public static final int WAKEFULNESS_ASLEEP = 0; /** * Wakefulness: The device is fully awake. It can be put to sleep by a call to goToSleep(). * When the user activity timeout expires, the device may start dreaming or go to sleep. */ public static final int WAKEFULNESS_AWAKE = 1; /** * Wakefulness: The device is dreaming. It can be awoken by a call to wakeUp(), * which ends the dream. The device goes to sleep when goToSleep() is called, when * the dream ends or when unplugged. * User activity may brighten the screen but does not end the dream. */ public static final int WAKEFULNESS_DREAMING = 2; /** * Wakefulness: The device is dozing. It is almost asleep but is allowing a special * low-power "doze" dream to run which keeps the display on but lets the application * processor be suspended. It can be awoken by a call to wakeUp() which ends the dream. * The device fully goes to sleep if the dream cannot be started or ends on its own. */ public static final int WAKEFULNESS_DOZING = 3; }
雖然注釋已經(jīng)寫(xiě)得很詳細(xì),但是有些概念你可能還真不知道,我獻(xiàn)丑來(lái)描述下這四種設(shè)備狀態(tài)。
- WAKEFULNESS_ASLEEP : 表示設(shè)備處于休眠狀態(tài)。屏幕處于滅屏狀態(tài),或者處于滅屏的過(guò)程中。設(shè)備只能被 wakeup() 喚醒,例如 Power 鍵喚醒設(shè)備。
- WAKEFULNESS_AWAKE :表示設(shè)備處于喚醒狀態(tài)。屏幕處于亮屏或者暗屏的狀態(tài)。當(dāng)用戶行為超時(shí)(屏幕超時(shí)),設(shè)備可能會(huì)開(kāi)啟夢(mèng)境(指屏保)或者進(jìn)入休眠狀態(tài)。當(dāng)然 goToSleep() 也能使設(shè)備進(jìn)入休眠狀態(tài)。
- WAKEFULNESS_DREAMING :設(shè)備處于夢(mèng)境狀態(tài),這里指的是顯示屏保??梢酝ㄟ^(guò) wakeup() 結(jié)束屏保,喚醒設(shè)備,例如點(diǎn)擊屏保。 也可以通過(guò) goToSleep() 結(jié)束屏保,使設(shè)備進(jìn)入休眠,例如,屏保時(shí)按 Power 鍵。
- WAKEFULNESS_DOZING : 設(shè)備處于打盹狀態(tài)(dozing)。這種狀態(tài)幾乎是一種休眠狀態(tài),但是屏幕處于一種低功耗狀態(tài)。系統(tǒng)會(huì)讓 dream manager 啟動(dòng)一個(gè) doze 組件,這個(gè)組件會(huì)繪制一些簡(jiǎn)單的信息在屏幕上,但是前提是屏幕要支持 AOD(always on display)功能。我曾經(jīng)買(mǎi)了一款 OLED 屏的手機(jī),當(dāng)關(guān)閉屏幕時(shí),屏幕會(huì)顯示時(shí)間、通知圖標(biāo),等等一些信息,當(dāng)時(shí)就覺(jué)得相當(dāng)炫酷,其實(shí)這就是 AOD 功能的實(shí)現(xiàn)。如果 doze 組件啟動(dòng)失敗,那么設(shè)備就進(jìn)入休眠狀態(tài)??梢酝ㄟ^(guò) wakeup() 結(jié)束這個(gè)打盹狀態(tài),使設(shè)備處于喚醒狀態(tài),例如 Power 鍵亮屏。
好,回歸正題,剛剛我們分析到,當(dāng) Power 鍵滅屏的時(shí)候,根據(jù)參數(shù) flags 不同,wakefulness 可能會(huì)更新為 WAKEFULNESS_DOZING 或 WAKEFULNESS_ASLEEP,也就是設(shè)備進(jìn)入打盹或者休眠狀態(tài)。
設(shè)備進(jìn)入休眠狀態(tài)的過(guò)程與前面一篇文章分析的設(shè)備亮屏的過(guò)程是一致的,并且流程上更簡(jiǎn)單,請(qǐng)讀者自行分析,本文分析設(shè)備進(jìn)入打盹狀態(tài)的流程。
2. 設(shè)備進(jìn)入打盹狀態(tài)
根據(jù)剛才的分析,wakefulness 更新為 WAKEFULNESS_DOZING,使設(shè)備進(jìn)入打盹狀態(tài)。并且根據(jù)前面一篇文章可知,此時(shí) mDirty 設(shè)置了 DIRTY_DISPLAY_GROUP_WAKEFULNESS 和 DIRTY_WAKEFULNESS 標(biāo)記位,分別表示顯示屏分組的 wakefulness 改變 和 PowerManagerService 的 wakefulness 改變,其實(shí)就是表示設(shè)備狀態(tài)改變。
現(xiàn)在根據(jù) mDirty 來(lái)更新電源狀態(tài)
// PowerManagerService.java private void updatePowerStateLocked() { if (!mSystemReady || mDirty == 0) { return; } if (!Thread.holdsLock(mLock)) { Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked"); } Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState"); try { // Phase 0: Basic state updates. updateIsPoweredLocked(mDirty); updateStayOnLocked(mDirty); updateScreenBrightnessBoostLocked(mDirty); // Phase 1: Update wakefulness. // Loop because the wake lock and user activity computations are influenced // by changes in wakefulness. final long now = mClock.uptimeMillis(); int dirtyPhase2 = 0; for (;;) { int dirtyPhase1 = mDirty; dirtyPhase2 |= dirtyPhase1; mDirty = 0; updateWakeLockSummaryLocked(dirtyPhase1); updateUserActivitySummaryLocked(now, dirtyPhase1); updateAttentiveStateLocked(now, dirtyPhase1); if (!updateWakefulnessLocked(dirtyPhase1)) { break; } } // Phase 2: Lock profiles that became inactive/not kept awake. updateProfilesLocked(now); // Phase 3: Update display power state. final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2); // Phase 4: Update dream state (depends on display ready signal). updateDreamLocked(dirtyPhase2, displayBecameReady); // Phase 5: Send notifications, if needed. finishWakefulnessChangeIfNeededLocked(); // 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(); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } }
根據(jù)前面文章可知,這里包含了很多功能,但是與 Power 鍵滅屏流程相關(guān)步驟如下
- updateUserActivitySummaryLocked() 更新用戶行為。屏幕請(qǐng)求策略的影響之一的因素就是用戶行為。詳見(jiàn)【2.1 更新用戶行為】
- updateDisplayPowerStateLocked() 更新顯示屏的電源狀態(tài),它會(huì)對(duì) DisplayManagerService 發(fā)起電源請(qǐng)求,從而決定屏幕屏的狀態(tài),例如亮、滅、暗,等等。詳見(jiàn)【2.2 更新顯示屏的電源狀態(tài)】
- updateDreamLocked() 更新夢(mèng)境狀態(tài),其實(shí)就是通過(guò) dream manager 啟動(dòng) doze 組件,然后更新 PowerManagerService 的夢(mèng)境狀態(tài)。詳見(jiàn)【2.3 更新夢(mèng)境狀態(tài)】
屏保和 doze 組件都是由 dream manager 啟動(dòng)的,在 PowerManagerService 中,屏保被稱為 dream,doze 組件稱為 doze dream。
2.1 更新用戶行為
// PowerManagerService.java private void updateUserActivitySummaryLocked(long now, int dirty) { // ... // 遍歷 display group id for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { int groupUserActivitySummary = 0; long groupNextTimeout = 0; // 注意,休眠狀態(tài)是無(wú)法決定用戶行為的 if (mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != WAKEFULNESS_ASLEEP) { final long lastUserActivityTime = mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(groupId); final long lastUserActivityTimeNoChangeLights = mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked( groupId); // 1. 獲取用戶行為與超時(shí)時(shí)間 // 上一次用戶行為的時(shí)間 >= 上一次喚醒屏幕的時(shí)間 if (lastUserActivityTime >= mLastWakeTime) { groupNextTimeout = lastUserActivityTime + screenOffTimeout - screenDimDuration; if (now < groupNextTimeout) { // 沒(méi)有到 dim 時(shí)間 groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT; } else { groupNextTimeout = lastUserActivityTime + screenOffTimeout; if (now < groupNextTimeout) { // 處于 dim 時(shí)間段 groupUserActivitySummary = USER_ACTIVITY_SCREEN_DIM; } } } // ... } // 2. DisplayGroupPowerStateMapper 保存用戶行為 mDisplayGroupPowerStateMapper.setUserActivitySummaryLocked(groupId, groupUserActivitySummary); } } // 遍歷 display group id 結(jié)束 // ... // 3. 定時(shí)更新電源狀態(tài) // 這一步?jīng)Q定自動(dòng)滅屏 if (hasUserActivitySummary && nextTimeout >= 0) { scheduleUserInactivityTimeout(nextTimeout); } }
更新用戶行為,其實(shí)就是 DisplayGroupPowerStateMapper 保存用戶行為?,F(xiàn)在我們分析的情況是,Power 鍵導(dǎo)致屏幕由亮到滅的過(guò)程,因此這里獲取的用戶行為是 USER_ACTIVITY_SCREEN_BRIGHT。如果是由暗到滅的過(guò)程,那么用戶行為是 USER_ACTIVITY_SCREEN_DIM。
那么,你是否有疑惑,系統(tǒng)正在進(jìn)入打盹狀態(tài),為何用戶行為是 USER_ACTIVITY_SCREEN_BRIGHT 或 USER_ACTIVITY_SCREEN_DIM,它們并不是滅屏的用戶行為。因?yàn)橄到y(tǒng)根本沒(méi)有定義滅屏的用戶行為。
所有的用戶行為如下
private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0; private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1; private static final int USER_ACTIVITY_SCREEN_DREAM = 1 << 2;
用戶行為只有三種,屏幕變亮,變暗,或者進(jìn)入屏保,根本沒(méi)有打盹的用戶行為。那么為何是這樣呢?
雖然此時(shí) wakefulness 為 WAKEFULNESS_DOZING,只是表示正在進(jìn)入打盹狀態(tài),實(shí)際上還沒(méi)有正式處于打盹狀態(tài)。系統(tǒng)真正進(jìn)入打盹狀態(tài)的標(biāo)志是,dream manager 成功啟動(dòng) doze 組件,并且獲取到喚醒鎖 PowerManager.DOZE_WAKE_LOCK。因此,不好定義一個(gè)打盹的用戶行為。
2.2 更新顯示屏的電源狀態(tài)
根據(jù)前篇文章可知,更新顯示屏的電源狀態(tài),其實(shí)就是向 DisplayManagerService 發(fā)起請(qǐng)求,并且請(qǐng)求策略才是真正決定屏幕狀態(tài)(亮、滅、暗,等等)。
請(qǐng)求策略的獲取方式如下
// PowerManagerService.java int getDesiredScreenPolicyLocked(int groupId) { final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); final int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId); if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { return DisplayPowerRequest.POLICY_OFF; } else if (wakefulness == WAKEFULNESS_DOZING) { // 如下的條件表示dream manager獲取了 PowerManager.DOZE_WAKE_LOCK 喚醒鎖 if ((wakeLockSummary & WAKE_LOCK_DOZE) != 0) { return DisplayPowerRequest.POLICY_DOZE; } // mDozeAfterScreenOff 默認(rèn)為 false // 不過(guò),可以被 SystemUI 設(shè)置為 true,表示屏幕先滅屏,再進(jìn)入doze狀態(tài) if (mDozeAfterScreenOff) { return DisplayPowerRequest.POLICY_OFF; } } if (mIsVrModeEnabled) { return DisplayPowerRequest.POLICY_VR; } if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0 || !mBootCompleted || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId) & USER_ACTIVITY_SCREEN_BRIGHT) != 0 || mScreenBrightnessBoostInProgress) { return DisplayPowerRequest.POLICY_BRIGHT; } return DisplayPowerRequest.POLICY_DIM; }
雖然現(xiàn)在的 wakefulness 為 WAKEFULNESS_DOZING,但是此時(shí)決定請(qǐng)求策略的仍然是用戶行為。而根據(jù)剛剛的分析,此時(shí)用戶行為是 USER_ACTIVITY_SCREEN_BRIGHT 或 USER_ACTIVITY_SCREEN_DIM,因此策略為 DisplayPowerRequest.POLICY_BRIGHT 或 DisplayPowerRequest.POLICY_DIM。也就是說(shuō)屏幕狀態(tài)繼續(xù)保持不變。
看到這里,我和我的小伙伴都驚呆了!現(xiàn)在的狀況明明是 Power 鍵滅屏,為何現(xiàn)在的分析結(jié)果是屏幕狀態(tài)繼續(xù)保持不變。你沒(méi)有看錯(cuò),我也沒(méi)有寫(xiě)錯(cuò)。剛剛我們還提到,現(xiàn)在設(shè)備還沒(méi)有正式處于打盹狀態(tài),因?yàn)檫€沒(méi)有啟動(dòng) doze dream。當(dāng)成功啟動(dòng) doze dream 后,dream manager 會(huì)獲取喚醒鎖 PowerManager.DOZE_WAKE_LOCK,此時(shí) PowerManagerService 再次更新電源狀態(tài)時(shí),策略就會(huì)更新為 DisplayPowerRequest.POLICY_DOZE,它會(huì)讓屏幕進(jìn)入一個(gè)doze狀態(tài),并開(kāi)啟 AOD 功能。
如果你不想在滅屏?xí)r暫時(shí)保持屏幕狀態(tài)不變,可以把 mDozeAfterScreenOff 設(shè)置為 true,這會(huì)導(dǎo)致請(qǐng)求策略更新為 DisplayPowerRequest.POLICY_OFF,也就是立即讓屏幕進(jìn)入休眠。
2.3 更新夢(mèng)境狀態(tài)
我們一直在強(qiáng)調(diào),設(shè)備真正進(jìn)入打盹,是在啟動(dòng) doze dream 之后。doze dream 的啟動(dòng)過(guò)程是在更新夢(mèng)境狀態(tài)的過(guò)程中
// PowerManagerService.java private void updateDreamLocked(int dirty, boolean displayBecameReady) { if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_USER_ACTIVITY | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_ATTENTIVE | DIRTY_WAKE_LOCKS | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_IS_POWERED | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE | DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) { if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { // 最終調(diào)用 handleSandman() scheduleSandmanLocked(); } } } private void handleSandman(int groupId) { // runs on handler thread // Handle preconditions. final boolean startDreaming; final int wakefulness; synchronized (mLock) { mSandmanScheduled = false; final int[] ids = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); if (!ArrayUtils.contains(ids, groupId)) { // Group has been removed. return; } wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) && mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) && mDisplayGroupPowerStateMapper.isReady(groupId)) { // 1. 決定是否能啟動(dòng)夢(mèng)境 // canDreamLocked() 表示是否啟動(dòng)夢(mèng)境中的屏保 // canDozeLocked() 表示是否啟動(dòng)夢(mèng)境中的doze組件,判斷條件就是 wakefulness 為 WAKEFULNESS_DOZING startDreaming = canDreamLocked(groupId) || canDozeLocked(); // 重置"召喚睡夢(mèng)精靈"狀態(tài) mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, false); } else { startDreaming = false; } } final boolean isDreaming; if (mDreamManager != null) { // Restart the dream whenever the sandman is summoned. if (startDreaming) { mDreamManager.stopDream(false /*immediate*/); // 2. 啟動(dòng)夢(mèng)境 doze 組件 mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING); } // 判斷是否正在啟動(dòng)中 isDreaming = mDreamManager.isDreaming(); } else { isDreaming = false; } mDozeStartInProgress = false; synchronized (mLock) { final int[] ids = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); if (!ArrayUtils.contains(ids, groupId)) { return; } if (startDreaming && isDreaming) { // ... } if (mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != wakefulness) { return; // wait for next cycle } // Determine whether the dream should continue. long now = mClock.uptimeMillis(); if (wakefulness == WAKEFULNESS_DREAMING) { // ... } else if (wakefulness == WAKEFULNESS_DOZING) { // 3. 正在啟動(dòng)夢(mèng)境的doze組件,那繼續(xù) if (isDreaming) { return; // continue dozing } // 4. 沒(méi)有啟動(dòng)成功,或者doze組件自己結(jié)束夢(mèng)境,進(jìn)入更新 wakefulness 為 WAKEFULNESS_ASLEEP 流程 // Doze has ended or will be stopped. Update the power state. reallySleepDisplayGroupNoUpdateLocked(groupId, now, Process.SYSTEM_UID); updatePowerStateLocked(); } } // ... }
更新夢(mèng)境狀態(tài)過(guò)程如下
- 判斷是否能進(jìn)入夢(mèng)境。夢(mèng)境其實(shí)有兩種功能,一種是屏保,另一種是 doze 組件。由于此時(shí) wakefulness 為 WAKEFULNESS_DOZING,因此可以滿足啟動(dòng) doze dream 的條件。
- 通過(guò) dream manager 啟動(dòng) dream。此時(shí),啟動(dòng)的就是 doze dream,而不是屏保。
- 如果正在啟動(dòng),那就繼續(xù)。繼續(xù)什么呢?詳見(jiàn)【啟動(dòng)doze dream】
- 如果啟動(dòng)失敗,進(jìn)入休眠的流程。
休眠的流程最簡(jiǎn)單了,首先更新 wakefulenss 為 WAKEFULNESS_ASLEEP,然后更新請(qǐng)求策略為 DisplayPowerRequest.POLICY_OFF, 這使屏幕直接滅屏。更新請(qǐng)求策略的過(guò)程如下
int getDesiredScreenPolicyLocked(int groupId) { final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); final int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId); if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { return DisplayPowerRequest.POLICY_OFF; } else if (wakefulness == WAKEFULNESS_DOZING) { // ... } // ... }
可以看到,wakefulness 為 WAKEFULNESS_ASLEEP 時(shí),也就是設(shè)備處于休眠狀態(tài)時(shí),請(qǐng)求策略不受任何因素影響,直接為DisplayPowerRequest.POLICY_OFF,它會(huì)使屏幕休眠。
3. 啟動(dòng)doze dream
現(xiàn)在我們離設(shè)備正式進(jìn)入打盹狀態(tài),只有一步之遙,而這一步就是調(diào)用 DreamManagerService#startDreamInternal() 啟動(dòng) doze dream
// DreamManagerService.java private void startDreamInternal(boolean doze) { final int userId = ActivityManager.getCurrentUser(); // 獲取 doze dream 或 screensaver final ComponentName dream = chooseDreamForUser(doze, userId); if (dream != null) { synchronized (mLock) { // 啟動(dòng) dream,此時(shí)啟動(dòng)的 doze dream, startDreamLocked(dream, false /*isTest*/, doze, userId); } } } private ComponentName chooseDreamForUser(boolean doze, int userId) { if (doze) { ComponentName dozeComponent = getDozeComponent(userId); return validateDream(dozeComponent) ? dozeComponent : null; } // ... } private ComponentName getDozeComponent(int userId) { if (mForceAmbientDisplayEnabled || mDozeConfig.enabled(userId)) { // 從配置文件 config.xml 中獲取 config_dozeComponent return ComponentName.unflattenFromString(mDozeConfig.ambientDisplayComponent()); } else { return null; } }
系統(tǒng)沒(méi)有配置 doze dream 組件,但是沒(méi)關(guān)系,經(jīng)過(guò)研究源碼中的一些已經(jīng)實(shí)現(xiàn)的 doze 組件,我們可以發(fā)現(xiàn), doze 組件都是繼承自一個(gè)名為 DreamService 的 Service,這個(gè) Service 就是四大組件的 Service。
現(xiàn)在繼續(xù)看看啟動(dòng) doze 組件的過(guò)程
// DreamManagerService.java private void startDreamLocked(final ComponentName name, final boolean isTest, final boolean canDoze, final int userId) { // ... final Binder newToken = new Binder(); mCurrentDreamToken = newToken; mCurrentDreamName = name; mCurrentDreamIsTest = isTest; mCurrentDreamCanDoze = canDoze; mCurrentDreamUserId = userId; PowerManager.WakeLock wakeLock = mPowerManager .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream"); // WakeLock#wrap() 方法,是保證在執(zhí)行 Runnbale 之前獲取鎖,并且執(zhí)行完 Runnable 后釋放鎖。 // 也就是說(shuō),保持 CPU 運(yùn)行,直到啟動(dòng) doze dream 完畢。 mHandler.post(wakeLock.wrap(() -> { mAtmInternal.notifyDreamStateChanged(true); if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) { mUiEventLogger.log(DreamManagerEvent.DREAM_START); } // 開(kāi)啟 dream mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock); })); }
通過(guò) DreamController#startDream() 啟動(dòng) doze dream
// DreamController.java public void startDream(Binder token, ComponentName name, boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { stopDream(true /*immediate*/, "starting new dream"); try { // 其實(shí)是發(fā)送 Intent.ACTION_CLOSE_SYSTEM_DIALOGS 廣播 // Close the notification shade. No need to send to all, but better to be explicit. mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); // 1. 創(chuàng)建一條記錄 dream 記錄 // 注意,DreamRecord 實(shí)現(xiàn)了 ServiceConnection mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock); mDreamStartTime = SystemClock.elapsedRealtime(); MetricsLogger.visible(mContext, mCurrentDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING); Intent intent = new Intent(DreamService.SERVICE_INTERFACE); intent.setComponent(name); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); try { // 2. 連接Service 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*/, "bindService failed"); return; } } catch (SecurityException ex) { Slog.e(TAG, "Unable to bind dream service: " + intent, ex); stopDream(true /*immediate*/, "unable to bind service: SecExp."); return; } mCurrentDream.mBound = true; mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } }
既然 doze dream 是一個(gè) Service,那么啟動(dòng)它的過(guò)程就是綁定 Service 的過(guò)程。
由于 DreamRecord 實(shí)現(xiàn)了 ServiceConnection,因此當(dāng)服務(wù)成功連接上,會(huì)調(diào)用 DreamRecord#onServiceConnected()
// DreamRecord.java public void onServiceConnected(ComponentName name, final IBinder service) { mHandler.post(() -> { mConnected = true; if (mCurrentDream == DreamRecord.this && mService == null) { attach(IDreamService.Stub.asInterface(service)); // Wake lock will be released once dreaming starts. } else { releaseWakeLockIfNeeded(); } }); } private void attach(IDreamService service) { try { // DreamRecord 監(jiān)聽(tīng) Service 生死 service.asBinder().linkToDeath(mCurrentDream, 0); // 第三個(gè)參數(shù)是一個(gè)binder回調(diào),用于接收結(jié)果 service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze, mCurrentDream.mDreamingStartedCallback); } catch (RemoteException ex) { Slog.e(TAG, "The dream service died unexpectedly.", ex); stopDream(true /*immediate*/, "attach failed"); return; } mCurrentDream.mService = service; // 發(fā)送 Intent.ACTION_DREAMING_STARTED 廣播 if (!mCurrentDream.mIsTest) { mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL); mCurrentDream.mSentStartBroadcast = true; } }
可以看到,成功啟動(dòng) Service 后,會(huì)調(diào)用 DreamService#attach() ,然后發(fā)送 dream 啟動(dòng)的廣播 Intent.ACTION_DREAMING_STARTED。
這里要結(jié)合具體的 doze dream Service 來(lái)分析,既然系統(tǒng)沒(méi)有配置,那么以 SystemUI 中的 DozeService 為例進(jìn)行分析,首先看看它的 onCreate() 過(guò)程
public class DozeService extends DreamService implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> { @Override public void onCreate() { super.onCreate(); // 設(shè)置為無(wú)窗口模式 setWindowless(true); mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */); DozeComponent dozeComponent = mDozeComponentBuilder.build(this); mDozeMachine = dozeComponent.getDozeMachine(); } }
注意,它設(shè)置了一個(gè)無(wú)窗口模式,后面會(huì)用到。
剛才分析過(guò),DreamService 綁定成功后,會(huì)調(diào)用 DreamService#attach()
private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) { // ... mDreamToken = dreamToken; mCanDoze = canDoze; // 只有 doze dream 才能無(wú)窗口 if (mWindowless && !mCanDoze) { throw new IllegalStateException("Only doze dreams can be windowless"); } mDispatchAfterOnAttachedToWindow = () -> { if (mWindow != null || mWindowless) { mStarted = true; try { // 由子類(lèi)實(shí)現(xiàn) onDreamingStarted(); } finally { try { started.sendResult(null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } }; if (!mWindowless) { // ... } else { // 無(wú)窗口下,直接調(diào)用 onDreamingStarted() mDispatchAfterOnAttachedToWindow.run(); } }
對(duì)于 SystemUI 的 DozeService,它是無(wú)窗口的,因此直接調(diào)用了它的 onDreamingStarted() 方法
// DozeService.java public void onDreamingStarted() { super.onDreamingStarted(); mDozeMachine.requestState(DozeMachine.State.INITIALIZED); // 調(diào)用父類(lèi)的方法 startDozing(); if (mDozePlugin != null) { mDozePlugin.onDreamingStarted(); } }
再次進(jìn)入 DreamService#startDozing()
public void startDozing() { if (mCanDoze && !mDozing) { mDozing = true; updateDoze(); } } private void updateDoze() { if (mDreamToken == null) { Slog.w(TAG, "Updating doze without a dream token."); return; } if (mDozing) { try { // 通知 dream mananger,service 正在啟動(dòng) doze 功能 mDreamManager.startDozing(mDreamToken, mDozeScreenState, mDozeScreenBrightness); } catch (RemoteException ex) { // system server died } } }
最終,把啟動(dòng) doze 的信息返回給 DreamManagerService
// DreamManagerService.java private void startDozingInternal(IBinder token, int screenState, int screenBrightness) { synchronized (mLock) { if (mCurrentDreamToken == token && mCurrentDreamCanDoze) { mCurrentDreamDozeScreenState = screenState; mCurrentDreamDozeScreenBrightness = screenBrightness; // 1. 通知PowerManagerService,保存doze狀態(tài)下的屏幕狀態(tài)和屏幕亮度 mPowerManagerInternal.setDozeOverrideFromDreamManager( screenState, screenBrightness); if (!mCurrentDreamIsDozing) { mCurrentDreamIsDozing = true; //2. 獲取 PowerManager.DOZE_WAKE_LOCK 喚醒鎖 mDozeWakeLock.acquire(); } } } }
DreamManagerService 又把信息傳遞給了 PowerManagerService,這些信息包括 doze 下的屏幕狀態(tài),以及屏幕亮度,而PowerManagerService 也只是簡(jiǎn)單保存了這些信息。
然后獲取了一個(gè) PowerManager.DOZE_WAKE_LOCK,這就是設(shè)備進(jìn)入打盹狀態(tài)的最重要的一步。它會(huì)影響屏幕請(qǐng)求策略,如下
// PowerManagerService.java int getDesiredScreenPolicyLocked(int groupId) { final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); final int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId); if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { return DisplayPowerRequest.POLICY_OFF; } else if (wakefulness == WAKEFULNESS_DOZING) { // 如下的條件表示dream manager獲取了 PowerManager.DOZE_WAKE_LOCK 喚醒鎖 if ((wakeLockSummary & WAKE_LOCK_DOZE) != 0) { return DisplayPowerRequest.POLICY_DOZE; } if (mDozeAfterScreenOff) { return DisplayPowerRequest.POLICY_OFF; } } // ... }
可以看到 DreamManagerService 成功獲取到 PowerManager.DOZE_WAKE_LOCK 喚醒鎖后,請(qǐng)求策略現(xiàn)在為 DisplayPowerRequest.POLICY_DOZE,這會(huì)導(dǎo)致屏幕進(jìn)入 doze 狀態(tài),這是一種幾乎休眠的狀態(tài),也是一種低功耗狀態(tài),并且開(kāi)啟了 AOD 功能,當(dāng)然前提是屏幕支持這個(gè)功能,據(jù)說(shuō)只有 OLED 屏支持 AOD 功能。
此時(shí),設(shè)備才真正的處于了打盹狀態(tài)。而 PowerManagerService 更新電源狀態(tài)的后續(xù)過(guò)程,我覺(jué)得就不重要了,留給讀者自行分析。
結(jié)束
本文分析了滅屏過(guò)程,設(shè)備如何進(jìn)入打盹狀態(tài),可謂是波瀾起伏。但是注意,這只是手動(dòng)滅屏。而我們平常看到了屏幕超時(shí)導(dǎo)致的自動(dòng)滅屏,下一篇文章繼續(xù)分析。
在本文中,我們還提到設(shè)備有一種狀態(tài),夢(mèng)境狀態(tài),其實(shí)指的就是屏保。你說(shuō)它沒(méi)用吧,有時(shí)候還是有點(diǎn)作用的,你說(shuō)它有用吧,現(xiàn)在幾乎看不到用它的地方。對(duì)于這種食之無(wú)味,棄之可惜的東西,要不要分析呢,我還在猶豫。
以上就是PowerManagerService之手動(dòng)滅屏的詳細(xì)內(nèi)容,更多關(guān)于PowerManagerService 滅屏的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android編程設(shè)計(jì)模式之責(zé)任鏈模式詳解
這篇文章主要介紹了Android編程設(shè)計(jì)模式之責(zé)任鏈模式,詳細(xì)分析了Android設(shè)計(jì)模式中責(zé)任鏈模式的概念、原理、應(yīng)用場(chǎng)景、使用方法及相關(guān)操作技巧,需要的朋友可以參考下2017-12-12Android仿荷包APP啟動(dòng)動(dòng)畫(huà)
這篇文章主要為大家詳細(xì)介紹了Android仿荷包APP啟動(dòng)動(dòng)畫(huà)的相關(guān)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Android 優(yōu)雅的實(shí)現(xiàn)通用格式化編輯
這篇文章主要介紹了Android 優(yōu)雅的實(shí)現(xiàn)通用格式化編輯,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03Android 開(kāi)發(fā)隨手筆記之使用攝像頭拍照
在Android中,使用攝像頭拍照一般有兩種方法, 一種是調(diào)用系統(tǒng)自帶的Camera,另一種是自己寫(xiě)一個(gè)攝像的界面,本篇文章給大家介紹android開(kāi)發(fā)隨手筆記之使用攝像頭拍照,感興趣的朋友一起學(xué)習(xí)吧2015-11-11flutter的環(huán)境安裝配置問(wèn)題及解決方法
Flutter是Google推出的基于Dart語(yǔ)言開(kāi)發(fā)的跨平臺(tái)開(kāi)源UI框架,旨在統(tǒng)一紛紛擾擾的跨平臺(tái)開(kāi)發(fā)框架,在UI層面上多端共用一套Dart代碼來(lái)實(shí)現(xiàn)多平臺(tái)適配開(kāi)發(fā),這篇文章主要介紹了flutter的環(huán)境安裝配置問(wèn)題,需要的朋友可以參考下2020-06-06android基礎(chǔ)教程之開(kāi)機(jī)啟動(dòng)示例
這篇文章主要介紹了android開(kāi)機(jī)啟動(dòng)示例,開(kāi)機(jī)自動(dòng)啟動(dòng)程序后開(kāi)機(jī)啟動(dòng)廣播功能實(shí)現(xiàn),需要的朋友可以參考下2014-02-02Android編程實(shí)現(xiàn)ImageView圖片拋物線動(dòng)畫(huà)效果的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)ImageView圖片拋物線動(dòng)畫(huà)效果的方法,實(shí)例分析了Android實(shí)現(xiàn)拋物線運(yùn)動(dòng)的算法原理與相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Android 應(yīng)用的歡迎界面實(shí)現(xiàn)代碼
本文主要介紹Android 應(yīng)用歡迎界面的開(kāi)發(fā),這里提供實(shí)現(xiàn)方法和實(shí)現(xiàn)代碼以供大家參考,有需要的朋友可以參考下2016-07-07android 九宮格滑動(dòng)解鎖開(kāi)機(jī)實(shí)例源碼學(xué)習(xí)
開(kāi)機(jī)密碼的樣式種類(lèi)多種多樣,五花八門(mén).本文接下來(lái)介紹滑動(dòng)九宮格來(lái)達(dá)到開(kāi)機(jī)目的,感興趣的朋友可以了解下2013-01-01