欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

PowerManagerService之手動滅屏流程示例分析

 更新時間:2022年10月28日 08:39:52   作者:大胃粥  
這篇文章主要為大家介紹了PowerManagerService之手動滅屏流程的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

PowerManagerService之亮屏流程分析 分析了亮屏的流程,并歸納出了一個適用于亮屏或滅屏的通用的流程。 但是,滅屏流程還有一些獨特的東西,例如 dream 這個晦澀的東西,就是在滅屏流程中啟動的。

滅屏分為手動滅屏和自動滅屏,手動滅屏一般就是指 Power 鍵滅屏,自動滅屏就是大家常說的屏幕超時而導致的滅屏。本文以 Power 鍵滅屏為例來分析手動滅屏,自動滅屏留到下一篇文章分析。

本文以 PowerManagerService之亮屏流程分析 作為基礎,重復內容不做過多分析,希望讀者務必先打好基礎,再來閱讀本文。

1. 滅屏流程

Power 鍵滅屏會調用 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)。

不過,Power 鍵滅屏的更新 wakefulness 過程,有點小插曲,如下

private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason,
        int flags, int uid) {
    // ...
    try {
        // ...
        // 召喚睡夢精靈,其實就是保存一個狀態(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;
}

默認地,wakefulness 更新為 WAKEFULNESS_DOZING,然而,如果參數(shù) flags 帶有 PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE,wakefulness 更新為 WAKEFULNESS_ASLEEP。

wakefulness 表示設備處于何種狀態(tài),其實它有四個值,如下

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)寫得很詳細,但是有些概念你可能還真不知道,我獻丑來描述下這四種設備狀態(tài)。

  • WAKEFULNESS_ASLEEP : 表示設備處于休眠狀態(tài)。屏幕處于滅屏狀態(tài),或者處于滅屏的過程中。設備只能被 wakeup() 喚醒,例如 Power 鍵喚醒設備。
  • WAKEFULNESS_AWAKE :表示設備處于喚醒狀態(tài)。屏幕處于亮屏或者暗屏的狀態(tài)。當用戶行為超時(屏幕超時),設備可能會開啟夢境(指屏保)或者進入休眠狀態(tài)。當然 goToSleep() 也能使設備進入休眠狀態(tài)。
  • WAKEFULNESS_DREAMING :設備處于夢境狀態(tài),這里指的是顯示屏保。可以通過 wakeup() 結束屏保,喚醒設備,例如點擊屏保。 也可以通過 goToSleep() 結束屏保,使設備進入休眠,例如,屏保時按 Power 鍵。
  • WAKEFULNESS_DOZING : 設備處于打盹狀態(tài)(dozing)。這種狀態(tài)幾乎是一種休眠狀態(tài),但是屏幕處于一種低功耗狀態(tài)。系統(tǒng)會讓 dream manager 啟動一個 doze 組件,這個組件會繪制一些簡單的信息在屏幕上,但是前提是屏幕要支持 AOD(always on display)功能。我曾經(jīng)買了一款 OLED 屏的手機,當關閉屏幕時,屏幕會顯示時間、通知圖標,等等一些信息,當時就覺得相當炫酷,其實這就是 AOD 功能的實現(xiàn)。如果 doze 組件啟動失敗,那么設備就進入休眠狀態(tài)??梢酝ㄟ^ wakeup() 結束這個打盹狀態(tài),使設備處于喚醒狀態(tài),例如 Power 鍵亮屏。

好,回歸正題,剛剛我們分析到,當 Power 鍵滅屏的時候,根據(jù)參數(shù) flags 不同,wakefulness 可能會更新為 WAKEFULNESS_DOZING 或 WAKEFULNESS_ASLEEP,也就是設備進入打盹或者休眠狀態(tài)。

設備進入休眠狀態(tài)的過程與前面一篇文章分析的設備亮屏的過程是一致的,并且流程上更簡單,請讀者自行分析,本文分析設備進入打盹狀態(tài)的流程。

2. 設備進入打盹狀態(tài)

根據(jù)剛才的分析,wakefulness 更新為 WAKEFULNESS_DOZING,使設備進入打盹狀態(tài)。并且根據(jù)前面一篇文章可知,此時 mDirty 設置了 DIRTY_DISPLAY_GROUP_WAKEFULNESS 和 DIRTY_WAKEFULNESS 標記位,分別表示顯示屏分組的 wakefulness 改變 和 PowerManagerService 的 wakefulness 改變,其實就是表示設備狀態(tài)改變。

現(xiàn)在根據(jù) mDirty 來更新電源狀態(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 鍵滅屏流程相關步驟如下

  • updateUserActivitySummaryLocked() 更新用戶行為。屏幕請求策略的影響之一的因素就是用戶行為。詳見【2.1 更新用戶行為
  • updateDisplayPowerStateLocked() 更新顯示屏的電源狀態(tài),它會對 DisplayManagerService 發(fā)起電源請求,從而決定屏幕屏的狀態(tài),例如亮、滅、暗,等等。詳見【2.2 更新顯示屏的電源狀態(tài)
  • updateDreamLocked() 更新夢境狀態(tài),其實就是通過 dream manager 啟動 doze 組件,然后更新 PowerManagerService 的夢境狀態(tài)。詳見【2.3 更新夢境狀態(tài)

屏保和 doze 組件都是由 dream manager 啟動的,在 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)是無法決定用戶行為的
        if (mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != WAKEFULNESS_ASLEEP) {
            final long lastUserActivityTime =
                    mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(groupId);
            final long lastUserActivityTimeNoChangeLights =
                    mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
                            groupId);
            // 1. 獲取用戶行為與超時時間
            // 上一次用戶行為的時間 >= 上一次喚醒屏幕的時間
            if (lastUserActivityTime >= mLastWakeTime) {
                groupNextTimeout = lastUserActivityTime + screenOffTimeout - screenDimDuration;
                if (now < groupNextTimeout) { // 沒有到 dim 時間
                    groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                } else {
                    groupNextTimeout = lastUserActivityTime + screenOffTimeout;
                    if (now < groupNextTimeout) { // 處于 dim 時間段
                        groupUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                    }
                }
            }
            // ...
        }
        // 2. DisplayGroupPowerStateMapper 保存用戶行為
        mDisplayGroupPowerStateMapper.setUserActivitySummaryLocked(groupId,
                groupUserActivitySummary);
        }
    } // 遍歷 display group id 結束
    // ...
    // 3. 定時更新電源狀態(tài)
    // 這一步?jīng)Q定自動滅屏
    if (hasUserActivitySummary && nextTimeout >= 0) {
        scheduleUserInactivityTimeout(nextTimeout);
    }
}

更新用戶行為,其實就是 DisplayGroupPowerStateMapper 保存用戶行為。現(xiàn)在我們分析的情況是,Power 鍵導致屏幕由亮到滅的過程,因此這里獲取的用戶行為是 USER_ACTIVITY_SCREEN_BRIGHT。如果是由暗到滅的過程,那么用戶行為是 USER_ACTIVITY_SCREEN_DIM。

那么,你是否有疑惑,系統(tǒng)正在進入打盹狀態(tài),為何用戶行為是 USER_ACTIVITY_SCREEN_BRIGHT 或 USER_ACTIVITY_SCREEN_DIM,它們并不是滅屏的用戶行為。因為系統(tǒng)根本沒有定義滅屏的用戶行為。

所有的用戶行為如下

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;

用戶行為只有三種,屏幕變亮,變暗,或者進入屏保,根本沒有打盹的用戶行為。那么為何是這樣呢?

雖然此時 wakefulness 為 WAKEFULNESS_DOZING,只是表示正在進入打盹狀態(tài),實際上還沒有正式處于打盹狀態(tài)。系統(tǒng)真正進入打盹狀態(tài)的標志是,dream manager 成功啟動 doze 組件,并且獲取到喚醒鎖 PowerManager.DOZE_WAKE_LOCK。因此,不好定義一個打盹的用戶行為。

2.2 更新顯示屏的電源狀態(tài)

根據(jù)前篇文章可知,更新顯示屏的電源狀態(tài),其實就是向 DisplayManagerService 發(fā)起請求,并且請求策略才是真正決定屏幕狀態(tài)(亮、滅、暗,等等)。

請求策略的獲取方式如下

// 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 默認為 false
        // 不過,可以被 SystemUI 設置為 true,表示屏幕先滅屏,再進入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,但是此時決定請求策略的仍然是用戶行為。而根據(jù)剛剛的分析,此時用戶行為是 USER_ACTIVITY_SCREEN_BRIGHT 或 USER_ACTIVITY_SCREEN_DIM,因此策略為 DisplayPowerRequest.POLICY_BRIGHT 或 DisplayPowerRequest.POLICY_DIM。也就是說屏幕狀態(tài)繼續(xù)保持不變。

看到這里,我和我的小伙伴都驚呆了!現(xiàn)在的狀況明明是 Power 鍵滅屏,為何現(xiàn)在的分析結果是屏幕狀態(tài)繼續(xù)保持不變。你沒有看錯,我也沒有寫錯。剛剛我們還提到,現(xiàn)在設備還沒有正式處于打盹狀態(tài),因為還沒有啟動 doze dream。當成功啟動 doze dream 后,dream manager 會獲取喚醒鎖 PowerManager.DOZE_WAKE_LOCK,此時 PowerManagerService 再次更新電源狀態(tài)時,策略就會更新為 DisplayPowerRequest.POLICY_DOZE,它會讓屏幕進入一個doze狀態(tài),并開啟 AOD 功能。

如果你不想在滅屏時暫時保持屏幕狀態(tài)不變,可以把 mDozeAfterScreenOff 設置為 true,這會導致請求策略更新為 DisplayPowerRequest.POLICY_OFF,也就是立即讓屏幕進入休眠。

2.3 更新夢境狀態(tài)

我們一直在強調,設備真正進入打盹,是在啟動 doze dream 之后。doze dream 的啟動過程是在更新夢境狀態(tài)的過程中

// 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()) {
            // 最終調用 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. 決定是否能啟動夢境
            // canDreamLocked() 表示是否啟動夢境中的屏保
            // canDozeLocked() 表示是否啟動夢境中的doze組件,判斷條件就是 wakefulness 為 WAKEFULNESS_DOZING
            startDreaming = canDreamLocked(groupId) || canDozeLocked();
            // 重置"召喚睡夢精靈"狀態(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. 啟動夢境 doze 組件
            mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
        }
        // 判斷是否正在啟動中
        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. 正在啟動夢境的doze組件,那繼續(xù)
            if (isDreaming) {
                return; // continue dozing
            }
            // 4. 沒有啟動成功,或者doze組件自己結束夢境,進入更新 wakefulness 為 WAKEFULNESS_ASLEEP 流程
            // Doze has ended or will be stopped.  Update the power state.
            reallySleepDisplayGroupNoUpdateLocked(groupId, now, Process.SYSTEM_UID);
            updatePowerStateLocked();
        }
    }
    // ...
}

更新夢境狀態(tài)過程如下

  • 判斷是否能進入夢境。夢境其實有兩種功能,一種是屏保,另一種是 doze 組件。由于此時 wakefulness 為 WAKEFULNESS_DOZING,因此可以滿足啟動 doze dream 的條件。
  • 通過 dream manager 啟動 dream。此時,啟動的就是 doze dream,而不是屏保。
  • 如果正在啟動,那就繼續(xù)。繼續(xù)什么呢?詳見【啟動doze dream
  • 如果啟動失敗,進入休眠的流程。

休眠的流程最簡單了,首先更新 wakefulenss 為 WAKEFULNESS_ASLEEP,然后更新請求策略為 DisplayPowerRequest.POLICY_OFF, 這使屏幕直接滅屏。更新請求策略的過程如下

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 時,也就是設備處于休眠狀態(tài)時,請求策略不受任何因素影響,直接為DisplayPowerRequest.POLICY_OFF,它會使屏幕休眠。

3. 啟動doze dream

現(xiàn)在我們離設備正式進入打盹狀態(tài),只有一步之遙,而這一步就是調用 DreamManagerService#startDreamInternal() 啟動 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) {
            // 啟動 dream,此時啟動的 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)沒有配置 doze dream 組件,但是沒關系,經(jīng)過研究源碼中的一些已經(jīng)實現(xiàn)的 doze 組件,我們可以發(fā)現(xiàn), doze 組件都是繼承自一個名為 DreamService 的 Service,這個 Service 就是四大組件的 Service。

現(xiàn)在繼續(xù)看看啟動 doze 組件的過程

// 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 后釋放鎖。
    // 也就是說,保持 CPU 運行,直到啟動 doze dream 完畢。
    mHandler.post(wakeLock.wrap(() -> {
        mAtmInternal.notifyDreamStateChanged(true);
        if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) {
            mUiEventLogger.log(DreamManagerEvent.DREAM_START);
        }
        // 開啟 dream
        mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock);
    }));
}

通過 DreamController#startDream() 啟動 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 {
        // 其實是發(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 實現(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 是一個 Service,那么啟動它的過程就是綁定 Service 的過程。

由于 DreamRecord 實現(xiàn)了 ServiceConnection,因此當服務成功連接上,會調用 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)聽 Service 生死
        service.asBinder().linkToDeath(mCurrentDream, 0);
        // 第三個參數(shù)是一個binder回調,用于接收結果
        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;
    }
}

可以看到,成功啟動 Service 后,會調用 DreamService#attach() ,然后發(fā)送 dream 啟動的廣播 Intent.ACTION_DREAMING_STARTED。

這里要結合具體的 doze dream Service 來分析,既然系統(tǒng)沒有配置,那么以 SystemUI 中的 DozeService 為例進行分析,首先看看它的 onCreate() 過程

public class DozeService extends DreamService
        implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> {
    @Override
    public void onCreate() {
        super.onCreate();
        // 設置為無窗口模式
        setWindowless(true);
        mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */);
        DozeComponent dozeComponent = mDozeComponentBuilder.build(this);
        mDozeMachine = dozeComponent.getDozeMachine();
    }
}    

注意,它設置了一個無窗口模式,后面會用到。

剛才分析過,DreamService 綁定成功后,會調用 DreamService#attach()

private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) {
    // ...
    mDreamToken = dreamToken;
    mCanDoze = canDoze;
    // 只有 doze dream 才能無窗口
    if (mWindowless && !mCanDoze) {
        throw new IllegalStateException("Only doze dreams can be windowless");
    }
    mDispatchAfterOnAttachedToWindow = () -> {
        if (mWindow != null || mWindowless) {
            mStarted = true;
            try {
                // 由子類實現(xiàn)
                onDreamingStarted();
            } finally {
                try {
                    started.sendResult(null);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
        }
    };
    if (!mWindowless) {
        // ...
    } else {
        // 無窗口下,直接調用 onDreamingStarted()
        mDispatchAfterOnAttachedToWindow.run();
    }
}

對于 SystemUI 的 DozeService,它是無窗口的,因此直接調用了它的 onDreamingStarted() 方法

// DozeService.java
public void onDreamingStarted() {
    super.onDreamingStarted();
    mDozeMachine.requestState(DozeMachine.State.INITIALIZED);
    // 調用父類的方法
    startDozing();
    if (mDozePlugin != null) {
        mDozePlugin.onDreamingStarted();
    }
}

再次進入 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 正在啟動 doze 功能
            mDreamManager.startDozing(mDreamToken, mDozeScreenState, mDozeScreenBrightness);
        } catch (RemoteException ex) {
            // system server died
        }
    }
}

最終,把啟動 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 也只是簡單保存了這些信息。

然后獲取了一個 PowerManager.DOZE_WAKE_LOCK,這就是設備進入打盹狀態(tài)的最重要的一步。它會影響屏幕請求策略,如下

// 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 喚醒鎖后,請求策略現(xiàn)在為 DisplayPowerRequest.POLICY_DOZE,這會導致屏幕進入 doze 狀態(tài),這是一種幾乎休眠的狀態(tài),也是一種低功耗狀態(tài),并且開啟了 AOD 功能,當然前提是屏幕支持這個功能,據(jù)說只有 OLED 屏支持 AOD 功能。

此時,設備才真正的處于了打盹狀態(tài)。而 PowerManagerService 更新電源狀態(tài)的后續(xù)過程,我覺得就不重要了,留給讀者自行分析。

結束

本文分析了滅屏過程,設備如何進入打盹狀態(tài),可謂是波瀾起伏。但是注意,這只是手動滅屏。而我們平??吹搅似聊怀瑫r導致的自動滅屏,下一篇文章繼續(xù)分析。

在本文中,我們還提到設備有一種狀態(tài),夢境狀態(tài),其實指的就是屏保。你說它沒用吧,有時候還是有點作用的,你說它有用吧,現(xiàn)在幾乎看不到用它的地方。對于這種食之無味,棄之可惜的東西,要不要分析呢,我還在猶豫。

以上就是PowerManagerService之手動滅屏的詳細內容,更多關于PowerManagerService 滅屏的資料請關注腳本之家其它相關文章!

相關文章

  • Android編程設計模式之責任鏈模式詳解

    Android編程設計模式之責任鏈模式詳解

    這篇文章主要介紹了Android編程設計模式之責任鏈模式,詳細分析了Android設計模式中責任鏈模式的概念、原理、應用場景、使用方法及相關操作技巧,需要的朋友可以參考下
    2017-12-12
  • Android仿荷包APP啟動動畫

    Android仿荷包APP啟動動畫

    這篇文章主要為大家詳細介紹了Android仿荷包APP啟動動畫的相關代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • 優(yōu)化Gradle提升Gradle編譯速度

    優(yōu)化Gradle提升Gradle編譯速度

    今天小編就為大家分享一篇關于優(yōu)化Gradle提升Gradle編譯速度的文章,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • Android 優(yōu)雅的實現(xiàn)通用格式化編輯

    Android 優(yōu)雅的實現(xiàn)通用格式化編輯

    這篇文章主要介紹了Android 優(yōu)雅的實現(xiàn)通用格式化編輯,幫助大家更好的理解和學習使用Android,感興趣的朋友可以了解下
    2021-03-03
  • Android 開發(fā)隨手筆記之使用攝像頭拍照

    Android 開發(fā)隨手筆記之使用攝像頭拍照

    在Android中,使用攝像頭拍照一般有兩種方法, 一種是調用系統(tǒng)自帶的Camera,另一種是自己寫一個攝像的界面,本篇文章給大家介紹android開發(fā)隨手筆記之使用攝像頭拍照,感興趣的朋友一起學習吧
    2015-11-11
  • flutter的環(huán)境安裝配置問題及解決方法

    flutter的環(huán)境安裝配置問題及解決方法

    Flutter是Google推出的基于Dart語言開發(fā)的跨平臺開源UI框架,旨在統(tǒng)一紛紛擾擾的跨平臺開發(fā)框架,在UI層面上多端共用一套Dart代碼來實現(xiàn)多平臺適配開發(fā),這篇文章主要介紹了flutter的環(huán)境安裝配置問題,需要的朋友可以參考下
    2020-06-06
  • android基礎教程之開機啟動示例

    android基礎教程之開機啟動示例

    這篇文章主要介紹了android開機啟動示例,開機自動啟動程序后開機啟動廣播功能實現(xiàn),需要的朋友可以參考下
    2014-02-02
  • Android編程實現(xiàn)ImageView圖片拋物線動畫效果的方法

    Android編程實現(xiàn)ImageView圖片拋物線動畫效果的方法

    這篇文章主要介紹了Android編程實現(xiàn)ImageView圖片拋物線動畫效果的方法,實例分析了Android實現(xiàn)拋物線運動的算法原理與相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-10-10
  • Android 應用的歡迎界面實現(xiàn)代碼

    Android 應用的歡迎界面實現(xiàn)代碼

    本文主要介紹Android 應用歡迎界面的開發(fā),這里提供實現(xiàn)方法和實現(xiàn)代碼以供大家參考,有需要的朋友可以參考下
    2016-07-07
  • android 九宮格滑動解鎖開機實例源碼學習

    android 九宮格滑動解鎖開機實例源碼學習

    開機密碼的樣式種類多種多樣,五花八門.本文接下來介紹滑動九宮格來達到開機目的,感興趣的朋友可以了解下
    2013-01-01

最新評論