Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android7.0 PowerManagerService(3) 核心函數updatePowerStateLocked的主要流程

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

編輯:關於Android編程

前面的博客中,我們已經分析過,當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 state
        boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

        // Update dream state (depends on display ready signal).
        // 4、更新dream state
        updateDreamLocked(dirtyPhase2, displayBecameReady);

        // Send notifications, if needed.
        finishWakefulnessChangeIfNeededLocked();

        // Update suspend blocker.
        // Because we might release the last suspend blocker here, we need to make sure
        // we finished everything else first!
        // 5、更新suspend blocker
        updateSuspendBlockerLocked();
    } finally {
        ..........
    }
}

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

.........
mDirty |= DIRTY_WAKE_LOCKS;
........

每當PMS檢測到一些重要事件發生時,就會更新mDirty的相應的位。
從updatePowerStateLocked的代碼可以看出,它將根據mDirty中的信息,來更新手機中的電源狀態。

根據Android源碼中的注釋,可以看出updatePowerStateLocked的工作主要分為幾個步驟,接下來我們一個一個步驟的來進行分析。

一、更新基本狀態信息
1、updateIsPoweredLocked
我們先來看看updateIsPoweredLocked函數:

private void updateIsPoweredLocked(int dirty) {
    //DIRTY_BATTERY_STATE位置1時,表示終端的電源狀態發生了改變
    if ((dirty & DIRTY_BATTERY_STATE) != 0) {
        //記錄過去的狀態
        final boolean wasPowered = mIsPowered;
        final int oldPlugType = mPlugType;
        final boolean oldLevelLow = mBatteryLevelLow;

        //得到終端現在是否在充電
        mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);

        //得到充電的類型
        mPlugType = mBatteryManagerInternal.getPlugType();

        //得到當前的電量
        mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();

        //判斷是否為低電量
        mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();

        //是否充電的狀態發生改變,或者充電類型發生改變
        if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
            mDirty |= DIRTY_IS_POWERED;

            // Update wireless dock detection state.
            //無線充電相關,暫時不用管
            final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
                    mIsPowered, mPlugType, mBatteryLevel);

            final long now = SystemClock.uptimeMillis();
            //判斷插拔充電器或者USB是否需要喚醒屏幕  
            if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
                    dockedOnWirelessCharger)) {
                //之前的博客中已經分析過這個函數,主要是做好喚醒終端屏幕前的准備工作
                wakeUpNoUpdateLocked(now, "android.server.power:POWER", Process.SYSTEM_UID,
                        mContext.getOpPackageName(), Process.SYSTEM_UID);
            }
            //觸發一次用戶活動,修改PMS中記錄用戶活動事件的時間,同時通知BatteryStatsService等
            userActivityNoUpdateLocked(
                    now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);

            // Tell the notifier whether wireless charging has started so that
            // it can provide feedback to the user.
            //無線充電相關的通知,暫時可以不管
            if (dockedOnWirelessCharger) {
                mNotifier.onWirelessChargingStarted();
            }
        }

        if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) {
            //結束低電的狀態
            if (oldLevelLow != mBatteryLevelLow && !mBatteryLevelLow) {
                ........
                //從命名來看,該標志用於決定終端在低電模式下是否“打盹”(接近休眠)
                mAutoLowPowerModeSnoozing = false;
            }
            //更新低電模式相關的操作
            updateLowPowerModeLocked();
        }
    }
}

從以上代碼可以看出updateIsPoweredLocked主要用於:
更新PMS中的一些變量,包括記錄終端是否在充電、充電的類型、電池的電量及電池電量是否處於低電狀態;
當電源的充電狀態,或者充電類型發生變化,判斷出現插拔充電器等操作時,是否需要點亮或熄滅屏幕;
當電源充電狀態發生變化,或者終端是否處於低電量的標志發生變化的時候,終端調用updateLowPowerModeLocked()更新低電模式相關的操作。

我們跟進一下updateLowPowerModeLocked函數:

private void updateLowPowerModeLocked() {
    //處於充電狀態,並且設置過低電模式的標志位
    if (mIsPowered && mLowPowerModeSetting) {
        ........    
        // Turn setting off if powered
        //更新數據庫,關閉低電模式
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.LOW_POWER_MODE, 0);
        mLowPowerModeSetting = false;
    }

    //判斷是否可以進入自動省電模式
    //要求是:未充電 && 進行了自動省電的配置 && 沒有設置低電“打盹” && 電池電量低
    final boolean autoLowPowerModeEnabled = !mIsPowered && mAutoLowPowerModeConfigured
            && !mAutoLowPowerModeSnoozing && mBatteryLevelLow;

    //當前是否為低電模式
    final boolean lowPowerModeEnabled = mLowPowerModeSetting || autoLowPowerModeEnabled;

    if (mLowPowerModeEnabled != lowPowerModeEnabled) {
        mLowPowerModeEnabled = lowPowerModeEnabled;
        //調用底層動態庫的powerHint函數
        powerHintInternal(POWER_HINT_LOW_POWER, lowPowerModeEnabled ? 1 : 0);

        //開機完成後才能執行的Runnable對象
        postAfterBootCompleted(new Runnable() {
            //發送低電模式CHANGING的廣播
            Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
                    .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, mLowPowerModeEnabled)
                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
            mContext.sendBroadcast(intent);

            //PMS提供了registerLowPowerModeObserver的接口
            //其它進程可以調用該接口,注冊觀察者
            synchronized (mLock) {
                listeners = new ArrayList<powermanagerinternal.lowpowermodelistener>(
                        mLowPowerModeListeners);
            }
            for (int i=0; i<listeners.size(); intent="new" internal="" new="" permission.="" pre="" requires="" send="" signature="" that="" version="">

從上面的代碼可以看出updateLowPowerModeLocked函數, 首先判斷手機是否在充電,如果手機在充電,退出LowPowerMode模式,同時更新數據庫; 當手機的低電量模式發生了變化,就發送廣播進行通知,並回調關於監聽該模式變化的觀察者的接口。 例如:UI對應的APK收到低電量省電模式的廣播,就會彈出低電量省電模式的提醒界面。

可以看出這一部分除了更新PMS中的一些變量外,關注的重點還是集中在: 充電狀態是否改變; 充電狀態的改變,將引出對充電器插拔是否需要亮屏的考慮; 同樣,充電狀態的改變,將引出對終端的低電模式是否發生改變的考慮。 從這個角度來看,updateIsPoweredLocked函數的命名是實至名歸的。

2、updateStayOnLocked 現在我們看看基本狀態更新第二部分的updateStayOnLocked函數:

/**
* Updates the value of mStayOn.
* Sets DIRTY_STAY_ON if a change occurred.
*/
private void updateStayOnLocked(int dirty) {
    //電源狀態或電源設置發生了改變
    if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) {
        final boolean wasStayOn = mStayOn;
        //設置了充電器插入時亮屏(分為AC充電亮屏、USB充電亮屏或無線充電亮屏)
        if (mStayOnWhilePluggedInSetting != 0
                //判斷mMaximumScreenOffTimeoutFromDeviceAdmin的是否處於0與Integer.MAX_VALUE之間
                //Android給出的注釋是:
                //The maximum allowable screen off timeout according to the device
                // administration policy
                //初始為Integer.MAX_VALUE,因此這裡是要求其它進程沒有設置這個值
                //應該對應於強制息屏時間
                && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
            //判斷是否充電亮屏,定義於BatteryService.java中
            //從代碼來看,只要mStayOnWhilePluggedInSetting設置了,就會亮屏
            mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
        } else {
            mStayOn = false;
        }

        if (mStayOn != wasStayOn) {
            mDirty |= DIRTY_STAY_ON;
        }
    }
}

這一部分的代碼功能比較單一,主要用於更新變量mStayOn的值。 如果mStayOn如果為true,則屏幕保持長亮的狀態。

3、updateScreenBrightnessBoostLocked Android手機定義了一個最大屏幕亮度,用戶可以手動或者讓終端自動確定最大的屏幕亮度。 updateScreenBrightnessBoostLocked函數主要用於:更新終端可處於最大屏幕亮度的時間。

為了比較好的理解updateScreenBrightnessBoostLocked函數, 我們可以先分析一下與之相關的,PMS提供的對外的接口boostScreenBrightness。 該方法的作用是讓屏幕在一段時間內保持最大的亮度,使屏幕在強光下有更好的可讀性。

public void boostScreenBrightness(long eventTime) {
    ..........
    mContext.enforceCallingOrSelfPermission(
            android.Manifest.permission.DEVICE_POWER, null);

    final int uid = Binder.getCallingUid();
    final long ident = Binder.clearCallingIdentity();
    try {
        boostScreenBrightnessInternal(eventTime, uid);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

private void boostScreenBrightnessInternal(long eventTime, int uid) {
    synchronized (mLock) {
        //系統沒有准備好或者當前為Asleep狀態, 不處理新到的事件
        if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP
                //過時的事件不處理
                || eventTime < mLastScreenBrightnessBoostTime) {
            return;
        }
        ..............
        //記錄事件到來的事件,也可以認為是終端處於最亮屏幕狀態的起始時間
        mLastScreenBrightnessBoostTime = eventTime;

        //設置最亮屏幕的標志位true 
        if (!mScreenBrightnessBoostInProgress) {
            mScreenBrightnessBoostInProgress = true;
            //發送廣播
            mNotifier.onScreenBrightnessBoostChanged();
        }

        //修改mDirty的值,表示最大屏幕亮度的狀態發生了變化  
        mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;

        //記錄
        userActivityNoUpdateLocked(eventTime,
            PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);

        //更新電源的狀態信息
        updatePowerStateLocked();
    }
}

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

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

private void updateScreenBrightnessBoostLocked(int dirty) {
    //根據mDirty的標志位來判斷終端屏幕最大可用亮度的狀態是否發生了變化
    if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) {

        //上面的代碼已經提到過,當boostScreenBrightness接口被調用時,mScreenBrightnessBoostInProgress置為true
        if (mScreenBrightnessBoostInProgress) {

            //移除舊的超時事件
            final long now = SystemClock.uptimeMillis();
            mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);

            //終端處於最大屏幕亮度的時間,在sleep的時間之後,說明終端還未息屏之類的
            if (mLastScreenBrightnessBoostTime > mLastSleepTime) {

                //此時,重新計算終端可處於最大屏幕亮度的時間
                final long boostTimeout = mLastScreenBrightnessBoostTime +
                        SCREEN_BRIGHTNESS_BOOST_TIMEOUT;
                if (boostTimeout > now) {
                    Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
                    msg.setAsynchronous(true);

                    //發送延遲的超時事件
                    //當屏幕離開最大亮度狀態時,該事件將被發送
                    //當該事件被處理時,會再次進入到updateScreenBrightnessBoostLocked函數
                    mHandler.sendMessageAtTime(msg, boostTimeout);
                    return;
                }
            }

            //進入到這個分支時,說明屏幕處於最大亮度狀態的時間已經超時了
            //將該標志置為false
            mScreenBrightnessBoostInProgress = false;

            //發送廣播
            mNotifier.onScreenBrightnessBoostChanged();

            //觸發一次用戶活動,寫入mDirty標志位,同時做一些其它記錄
            userActivityNoUpdateLocked(now,
                    PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
        }
    }
}

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

二、更新wakelock和用戶活動

for (;;) {
    int dirtyPhase1 = mDirty;
    dirtyPhase2 |= dirtyPhase1;
    mDirty = 0;

    updateWakeLockSummaryLocked(dirtyPhase1);
    updateUserActivitySummaryLocked(now, dirtyPhase1);

    //updateWakefulnessLocked將決定系統是否進入休眠或dreaming狀態
    //主要是更新DIRTY_WAKEFULNESS位,如果不需要更新,則返回false
    if (!updateWakefulnessLocked(dirtyPhase1)) {
        break;
    }
}

1、updateWakeLockSummaryLocked updateWakeLockSummaryLocked函數根據PMS當前持有的所有WakeLock,得到當前終端整體的信息,保存到mWakeLockSummary變量中。

/**
* Updates the value of mWakeLockSummary to summarize the state of all active wake locks.
* Note that most wake-locks are ignored when the system is asleep.
*/
private void updateWakeLockSummaryLocked(int dirty) {
    //PMS持有的WakeLock發生變化,或者喚醒狀態發生變化時,才重新進行更新mWakeLockSummary
    //例如:調用PMS的acquireWakeLock時,就會將dirty的DIRTY_WAKE_LOCKS位置1
    if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
        mWakeLockSummary = 0;

        final int numWakeLocks = mWakeLocks.size();
        for (int i = 0; i < numWakeLocks; i++) {
            final WakeLock wakeLock = mWakeLocks.get(i);

            //這裡只關注WakeLock的level
            //下面的代碼其實就是實現每個level WakeLock對應的注釋信息
            switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
                case PowerManager.PARTIAL_WAKE_LOCK:
                    //在分析PMS acquireWakeLock的流程時,已經提到過
                    //在doze模式下,不在白名單內的非系統應用申請PARTIAL_WAKE_LOCK時,將被disabled
                    if (!wakeLock.mDisabled) {
                        // We only respect this if the wake lock is not disabled.
                        mWakeLockSummary |= WAKE_LOCK_CPU;
                    }
                    break;
                case PowerManager.FULL_WAKE_LOCK:
                    mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
                    break;
                case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                    mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT;
                    break;
                case PowerManager.SCREEN_DIM_WAKE_LOCK:
                    mWakeLockSummary |= WAKE_LOCK_SCREEN_DIM;
                    break;
                case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
                    mWakeLockSummary |= WAKE_LOCK_PROXIMITY_SCREEN_OFF;
                    break;
                case PowerManager.DOZE_WAKE_LOCK:
                    mWakeLockSummary |= WAKE_LOCK_DOZE;
                    break;
                case PowerManager.DRAW_WAKE_LOCK:
                    mWakeLockSummary |= WAKE_LOCK_DRAW;
                    break;
            }
        }

        // Cancel wake locks that make no sense based on the current state.
        //從下面的代碼可以看出,PMS中的mWakefulness變量記錄了終端當前的狀態
        //下面就是移除在特定狀態下,沒有意義的WakeLock
        if (mWakefulness != WAKEFULNESS_DOZING) {
            //如果不是Dozing狀態,移除相應的wakeLock標志位
            mWakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
        }
        if (mWakefulness == WAKEFULNESS_ASLEEP
                || (mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
            //如果當前為Asleep或者有Doze的wakeLock鎖的時候,應該移除掉屏幕亮度相關的wakeLock鎖
            mWakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
                    | WAKE_LOCK_BUTTON_BRIGHT);
            if (mWakefulness == WAKEFULNESS_ASLEEP) {
                //休眠時,sensor不再需要監聽終端是否靠近物體,以觸發亮滅屏
                mWakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;
            }
        }

        // Infer implied wake locks where necessary based on the current state.
        //根據當前的狀態,及PMS持有的WakeLock,推斷出隱含的持鎖需求
        //例如:當PMS持有亮屏鎖WAKE_LOCK_SCREEN_BRIGHT時,若當前終端為喚醒態
        //那麼CPU顯然也需要處於喚醒態
        if ((mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) {
            if (mWakefulness == WAKEFULNESS_AWAKE) {
                mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE;
            } else if (mWakefulness == WAKEFULNESS_DREAMING) {
                mWakeLockSummary |= WAKE_LOCK_CPU;
            }
        }
        if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0) {
            mWakeLockSummary |= WAKE_LOCK_CPU;
        }
        ...................
    }
}

結合每個WakeLock level的注釋信息,以上代碼還是比較好理解的。

這裡唯一需要說明的是,Android定義一個mWakeLockSummary變量的原因是: PMS將WakeLock定義為不同進程的請求信息,這些請求信息對CPU、屏幕和鍵盤有不同的需求。 對於每一種資源而言,只要有一個申請滿足獲取條件,PMS就需要為終端分配該申請對應的資源。 例如:假設PMS有20個WakeLock,只有1個申請亮屏,另外19個只申請CPU喚醒,PMS仍然需要保持終端亮屏。 因此,mWakeLockSummary就提供了一種整合多個WakeLock請求的功能,方便PMS進行集中的控制。

2、updateUserActivitySummaryLocked updateUserActivitySummaryLocked主要根據用戶最後的活動來決定當前屏幕的狀態。

/**
* Updates the value of mUserActivitySummary to summarize the user requested
* state of the system such as whether the screen should be bright or dim.
* Note that user activity is ignored when the system is asleep.
*/
private void updateUserActivitySummaryLocked(long now, int dirty) {
    if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
            | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
        mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);

        long nextTimeout = 0;
        if (mWakefulness == WAKEFULNESS_AWAKE
                || mWakefulness == WAKEFULNESS_DREAMING
                || mWakefulness == WAKEFULNESS_DOZING) {

            //獲取進入休眠狀態的時間sleepTimeout
            //getSleepTimeoutLocked中會判斷休眠時間和屏幕熄滅時間的關系
            //如果休眠時間sleepTimeout小於屏幕熄滅時間screenOfftime,  
            //則休眠時間被調整為屏幕熄滅時間,因為屏幕亮屏狀態下,終端不能進入休眠
            final int sleepTimeout = getSleepTimeoutLocked();

            //獲取屏幕熄滅的時間
            final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);

            //獲取屏幕變暗的時間
            final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);

            //當Window Manager判定用戶inactive時,將此標志置為true
            final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;

            //類似於之前的mWakeLockSummary,將當前的用戶事件,轉化為PMS可以處理的屏幕狀態
            mUserActivitySummary = 0;

            //在喚醒的狀態下,發生過用戶事件
            if (mLastUserActivityTime >= mLastWakeTime) {

                //重新計算出屏幕需要變暗的時間
                nextTimeout = mLastUserActivityTime
                        + screenOffTimeout - screenDimDuration;
                if (now < nextTimeout) {
                    //如果沒有到達需要變暗的時間,那麼當前屏幕的狀態為USER_ACTIVITY_SCREEN_BRIGHT(亮屏)
                    mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                } else {
                    //到達變暗的時間,則計算出屏幕熄滅的時間
                    nextTimeout = mLastUserActivityTime + screenOffTimeout;

                    if (now < nextTimeout) {
                        //還沒到熄滅的時間,則當前屏幕的狀態為USER_ACTIVITY_SCREEN_DIM(暗屏)
                        mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                    }
                }
            }

            //注意mUserActivitySummary為0才會進入下面的分支
            //即上面改變mUserActivitySummary的條件不滿足時,才會進入這個分支(例如:喚醒狀態下,沒發生過改變屏幕狀態的UserActivity)
            if (mUserActivitySummary == 0
                    //mLastUserActivityTimeNoChangeLights表示用戶最後的活動不會改變屏幕當前的狀態
                    && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {

                //計算下次屏幕熄滅的時間
                nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;

                //還未到達熄屏時間
                if (now < nextTimeout) {
                    if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT) {
                        //當前屏幕是亮屏,仍然設置為亮屏
                        mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                    } else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
                        //當前屏幕是變暗,仍然設置為變暗
                        mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                    }
                }
            }

            if (mUserActivitySummary == 0) {
                //若定義了有效的休眠時間
                if (sleepTimeout >= 0) {
                    //計算用戶最後的活動時間
                    final long anyUserActivity = Math.max(mLastUserActivityTime,
                            mLastUserActivityTimeNoChangeLights);

                    //只有在喚醒狀態下,進行了用戶活動,才會重新更新休眠時間 (此時,應該是有過用戶活動,但過了息屏時間了)
                    if (anyUserActivity >= mLastWakeTime) {
                        nextTimeout = anyUserActivity + sleepTimeout;
                        if (now < nextTimeout) {
                            //走到這個分支,應該是屏幕已經熄滅,但還未到達休眠狀態,先進入dream態
                            mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                        }
                    }
                } else {
                    //直接進入dream態,後文的updateWakefulnessLocked將判斷是否休眠
                    mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                    nextTimeout = -1;
                }
            }

            //如果屏幕未進入dream態,但Window Manager判定用戶inactive,則進入下面分支
            if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
                //如果屏幕未熄滅
                if ((mUserActivitySummary &
                        (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
                    // Device is being kept awake by recent user activity
                    if (nextTimeout >= now && mOverriddenTimeout == -1) {
                        // Save when the next timeout would have occurred
                        mOverriddenTimeout = nextTimeout;
                    }
                }
                //Window Manager的權限很大,如果它判斷用戶inactive,直接進入dream態
                mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                nextTimeout = -1;
            }

            //根據nextTimeOut延遲發送信息,信息被處理後,將重新調用updatePowerStateLocked,於是再次進入到該方法
            //通過不斷進入該方法,不斷評估是否根據用戶動作亮、熄屏等
            if (mUserActivitySummary != 0 && nextTimeout >= 0) {
                Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextTimeout);
            }
        } else {
            mUserActivitySummary = 0;
        }
        ..........
    }
}

從上面的代碼可以看出,在該函數中用mUserActivitySummary變量存儲當前屏幕的狀態。 一共有3中基本狀態: * USER_ACTIVITY_SCREEN_BRIGHT 點亮屏幕 * USER_ACTIVITY_SCREEN_DIM 屏幕變暗 * USER_ACTIVITY_SCREEN_DREAM 屏保狀態 從代碼可以看出,屏幕變化和userActivity活動有關,它根據最後的userActivity活動的時間決定點亮屏幕、調暗屏幕或熄滅屏幕。

之前的很多方法中都會調用userActivityNoUpdateLocked方法。該方法將觸發一次用戶活動,以更新用戶活動的時間,這樣屏幕變暗和熄滅時間就會重新進行計算。 這也就是為什麼用戶一直操作手機,屏幕不會熄滅或者變暗的原因。 大圖地址 整體來講,個人感覺這個函數的代碼寫的還是挺繞的,因此還是作一個圖記錄一下。 大家有興趣可以看一下。

3、updateWakefulnessLocked 從之前的代碼可以看出,updateWakefulnessLocked將決定第二階段的電源狀態更新是否結束。 我們看一下updateWakefulnessLocked函數:

/**
* Updates the wakefulness of the device.
*
* This is the function that decides whether the device should start dreaming
* based on the current wake locks and user activity state.  It may modify mDirty
* if the wakefulness changes.
*
* Returns true if the wakefulness changed and we need to restart power state calculation.
*/
private boolean updateWakefulnessLocked(int dirty) {
    boolean changed = false;
    //下面的條件還是比較容易滿足的,基本上只要之前的流程更改過mDirty就會進入分支
    if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
            | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
            | DIRTY_DOCK_STATE)) != 0) {
        //如果當前的狀態是喚醒的,isItBedTimeYetLocked判定不能再保持喚醒態
        if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
            ................
            final long time = SystemClock.uptimeMillis();
            //主要根據設置信息,判斷是否滿足進入Dream狀態的條件
            if (shouldNapAtBedTimeLocked()) {
                //將mWakefullness的值置為WAKEFULNESS_DREAMING,修改mDirty變量,並進行通知等
                changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
            } else {
                //將mWakefullness的值置為WAKEFULNESS_DOZING
                //如果系統設置了跳過Dozing態,則將mWakefullness置為WAKEFULNESS_ASLEEP
                //同時修改mDirty變量,並進行通知等
                changed = goToSleepNoUpdateLocked(time,
                        PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
            }
            //注意:napNoUpdateLocked和goToSleepNoUpdateLocked函數正常執行後,
            //都會將mSandmanSummoned(被"睡魔"眷顧了)置為true
        }
    }
    return changed;
}

從上面的代碼可以看出,如果終端可以一直保持喚醒狀態,或一開始就是非喚醒態, 那麼mWakefulness不會發生改變,第二階段的for循環將會break;

如果終端要從喚醒態變為非喚醒態,那麼for循環將再運行一次,即重新計算一次mWakeLockSummary和mUserActivitySummary。 這麼做的原因是:updateWakeLockSummaryLocked和updateUserActivitySummaryLocked函數的一些計算,與終端是否處於喚醒狀態,即mWakefulness的值有關。 由於這兩個函數並不會修改mWakefulness,因此在這一次運行時,updateWakefulnessLocked將返回false,即第二階段結束。

因此,我們可以得出結論:更新電源狀態的第二階段,正常情況下最多運行兩次。 在第二階段的最後,我們看一下isItBedTimeYetLocked函數:

/**
* Returns true if the device should go to sleep now.
* Also used when exiting a dream to determine whether we should go back
* to being fully awake or else go to sleep for good.
*/
private boolean isItBedTimeYetLocked() {
    //主要由isBeingKeptAwakeLocked決定
    return mBootCompleted && !isBeingKeptAwakeLocked();
}

/**
* Returns true if the device is being kept awake by a wake lock, user activity
* or the stay on while powered setting.  We also keep the phone awake when
* the proximity sensor returns a positive result so that the device does not
* lock while in a phone call.  This function only controls whether the device
* will go to sleep or dream which is independent of whether it will be allowed
* to suspend.
*/
//根據狀態,判斷終端是否應該處於喚醒狀態
private boolean isBeingKeptAwakeLocked() {
    return mStayOn
            || mProximityPositive
            || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
            || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                    | USER_ACTIVITY_SCREEN_DIM)) != 0
            || mScreenBrightnessBoostInProgress;
}

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

三、更新display power state 第三階段將負責更新屏幕的顯示狀態。

/**
* Updates the display power state asynchronously.
* When the update is finished, mDisplayReady will be set to true.  The display
* controller posts a message to tell us when the actual display power state
* has been updated so we come back here to double-check and finish up.
*
* This function recalculates the display power state each time.
*/
private boolean updateDisplayPowerStateLocked(int dirty) {
    final boolean oldDisplayReady = mDisplayReady;
    //mDirty滿足條件時,進入以下分支
    if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
            | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
            | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {
        //根據mWakefullness、mWakeLockSummary、mUserActivitySummary等,決定屏幕的policy
        //policy定義為DisplayPowerRequest.(POLICY_OFF、POLICY_DOZE、POLICY_BRIGHT和POLICY_DIM)
        mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();

        // Determine appropriate screen brightness and auto-brightness adjustments.
        //決定屏幕的亮度
        .................

        // Update display power request.
        // 更新mDisplayPowerRequest的參數
        ...................
        //實際上調用DisplayPowerController的requestPowerState函數
        //在初始時,PMS注冊了mDisplayPowerCallbacks到DisplayPowerController中,
        //當更新完成後,會回調定義的接口,重新updatePowerStateLocked
        mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                mRequestWaitForNegativeProximity);
        ...................
    }
    return mDisplayReady && !oldDisplayReady;
}

我們跟進一下DisplayPowerController的requestPowerState函數:

/**
* Requests a new power state.
* The controller makes a copy of the provided object and then
* begins adjusting the power state to match what was requested.
*/
public boolean requestPowerState(DisplayPowerRequest request,
        boolean waitForNegativeProximity) {
    .........
    synchronized (mLock) {
        boolean changed = false;
        //新需求增加:proximity sensor需要檢測距離
        if (waitForNegativeProximity
                //原來沒有這個需求
                && !mPendingWaitForNegativeProximityLocked) {
            mPendingWaitForNegativeProximityLocked = true;
            changed = true;
        }

        //以下表示,參數中的Request對於DisplayPowerController而言,是一個新的需求
        if (mPendingRequestLocked == null) {
            mPendingRequestLocked = new DisplayPowerRequest(request);
            changed = true;
        } else if (!mPendingRequestLocked.equals(request)) {
            mPendingRequestLocked.copyFrom(request);
            changed = true;
        }

        if (changed) {
            //一但有新的需求,mDisplayReadyLocked就是false,表示屏幕有待調整
            mDisplayReadyLocked = false;
        }

        //有新需求,同時有對應的request
        if (changed && !mPendingRequestChangedLocked) {
            mPendingRequestChangedLocked = true;
            //發送消息,更新屏幕狀態
            //最終通過DisplayPowerController的updatePowerState函數,進行屏幕狀態更新
            //這部分代碼也極其復雜,暫時不在這裡展開分析
            //更新屏幕狀態後,將回調PMS的接口
            sendUpdatePowerStateLocked();
        }

        return mDisplayReadyLocked;
    }
}

根據requestPowerState的代碼,我們知道: 當PMS傳入一個新的mDisplayPowerRequest時,requestPowerState應該返回為false;當DisplayPowerController按照mDisplayPowerRequest修改完屏幕狀態,再次進入回到updateDisplayPowerStateLocked函數,調用requestPowerState時才會返回true。

整體的代碼流程大概可以抽象成下圖:

這一階段的代碼,我們只是分析了整個過程的冰山一角,並沒有分析更新屏幕狀態的實際操作。 但從現有的代碼可以看出,PMS的作用僅僅是維護終端電源相關狀態,實際的工作還是通過類似發送Request的方式,讓其它的服務協助完成。 例如:在整個階段,PMS根據之前得到信息,構造出DisplayPowerRequest,然後發送給DisplayPowerController進行實際的處理。 當DisplayPowerController完成實際的工作(部分工作還依賴於PhoneWindowManager)後,再通知PMS進行復查。

因此PMS的定位,確實可以用一個”Manager”來形容; 負責整個終端信息的搜集和維護,然後將相應的工作指派給具體的“員工”執行; “員工”執行完畢後,向”Manager”匯報; “Manager”檢查工作的完成情況後,然後做出下一步的指示。

四、更新dream state updateDreamLocked函數主要用於更新屏保狀態,當設備進入或者退出屏保的時候都會觸發這個方法:

private void updateDreamLocked(int dirty, boolean displayBecameReady) {
    if ((dirty & (DIRTY_WAKEFULNESS
            | DIRTY_USER_ACTIVITY
            | DIRTY_WAKE_LOCKS
            | DIRTY_BOOT_COMPLETED
            | DIRTY_SETTINGS
            | DIRTY_IS_POWERED
            | DIRTY_STAY_ON
            | DIRTY_PROXIMITY_POSITIVE
            | DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) {
    if (mDisplayReady) {
        //mDirty滿足條件,同時屏幕狀態調整完畢,才進入下一步
        scheduleSandmanLocked();
    }
}

private void scheduleSandmanLocked() {
    if (!mSandmanScheduled) {
        //mSandmanScheduled的作用就是讓MessageQueue中僅保留一個MSG_SANDMAN
        mSandmanScheduled = true;
        //由handleSandman處理
        Message msg = mHandler.obtainMessage(MSG_SANDMAN);
        msg.setAsynchronous(true);
        mHandler.sendMessage(msg);
    }
}

handleSandman函數比較復雜,主要用於決定設備是否應該停留在dreaming或dozing狀態。 我們分段介紹該函數的功能。

1、決定是否可以進入屏保狀態

/**
* Called when the device enters or exits a dreaming or dozing state.
*/
private void handleSandman() {
    final boolean startDreaming;
    final int wakefulness;
    synchronized (mLock) {
        mSandmanScheduled = false;
        wakefulness = mWakefulness;
        //前面提到過,當updateWakefulnessLocked判斷進入dozing或sleep狀態時,
        //會將mSandmanSummoned置為true
        //mDisplayReady主要確保前面屏幕狀態更新完畢
        if (mSandmanSummoned && mDisplayReady) {
            //判斷device是否可以dream或dozing
            startDreaming = canDreamLocked() || canDozeLocked();
            mSandmanSummoned = false;
        } else {
            startDreaming = false;
        }
    }
..........

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

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

/**
* Returns true if the device is allowed to dream in its current state.
*/
private boolean canDreamLocked() {
    //mWakefulness等於WAKEFULNESS_DREAMING
    if (mWakefulness != WAKEFULNESS_DREAMING
            //設備支持dreaming
            || !mDreamsSupportedConfig
            //設置開關開啟
            || !mDreamsEnabledSetting
            //屏幕熄滅
            || !mDisplayPowerRequest.isBrightOrDim()
            || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                    | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0
            //初始化完成
            || !mBootCompleted) {
        return false;
    }
    //以上條件均滿足,才能進入後面的判斷

    //不處於喚醒態
    if (!isBeingKeptAwakeLocked()) {
        //沒充電,電源選項也未配置,不可dreaming
        if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) {
            return false;
        }

        //沒充電,且電池電量過低,不可dreaming
        if (!mIsPowered
                 && mDreamsBatteryLevelMinimumWhenNotPoweredConfig >= 0
                 && mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) {
            return false;
        }

        //充電,但電池電量過低,不可dreaming
        if (mIsPowered
                && mDreamsBatteryLevelMinimumWhenPoweredConfig >= 0
                && mBatteryLevel < mDreamsBatteryLevelMinimumWhenPoweredConfig) {
            return false;
        }
        //充電和未充電分別有一個最低的dreaming電量門限
    }

    return true;
}

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

canDozeLocked函數相對簡單:

private boolean canDozeLocked() {
    return mWakefulness == WAKEFULNESS_DOZING;
}

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

// Start dreaming if needed.
final boolean isDreaming;
if (mDreamManager != null) {
    if (startDreaming) {
        //結束舊夢
        mDreamManager.stopDream(false /*immediate*/);
        //開啟新夢
        mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
    }
    //startDream成功後,一般isDreaming就會返回true
    isDreaming = mDreamManager.isDreaming();
} else {
    isDreaming = false;
}

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

mDreamManager為DreamManagerService的Binder代理。 我們重點看看DreamManagerService的startDream函數,stopDream的工作內容與startDream相反,不做細致分析:

//定義於DreamManagerService的內部類中
public void startDream(boolean doze) {
    startDreamInternal(doze);
}

//定義於DreamManagerService
private void startDreamInternal(boolean doze) {
    final int userId = ActivityManager.getCurrentUser();
    //個人覺得這裡應該是獲取屏保對象
    final ComponentName dream = chooseDreamForUser(doze, userId);
    if (dream != null) {
        synchronized (mLock) {
            startDreamLocked(dream, false /*isTest*/, doze, userId);
        }
    }
}

private void startDreamLocked(final ComponentName name,
        final boolean isTest, final boolean canDoze, final int userId) {
    //申請的屏保與當前的一致,不用進行修改
    if (Objects.equal(mCurrentDreamName, name)
            && mCurrentDreamIsTest == isTest
            && mCurrentDreamCanDoze == canDoze
            && mCurrentDreamUserId == userId) {
        return;
    }

    //立即停止當前的屏保
    stopDreamLocked(true /*immediate*/);

    final Binder newToken = new Binder();
    mCurrentDreamToken = newToken;
    mCurrentDreamName = name;
    mCurrentDreamIsTest = isTest;
    mCurrentDreamCanDoze = canDoze;
    mCurrentDreamUserId = userId;

    mHandler.post(new Runnable() {
        @Override
        public void run() {
            //調用DreamController的startDream函數
            mController.startDream(newToken, name, isTest, canDoze, userId);
        }
    });
}

//定義於DreamController中
public void startDream(Binder token, ComponentName name,
        boolean isTest, boolean canDoze, int userId) {
    //移除當前屏保並回調通知
    stopDream(true /*immediate*/);
    .........
    try {
        ..............
        //記錄dream
        mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId);
        mDreamStartTime = SystemClock.elapsedRealtime();
        ..............
        //做好屏幕相關的准備工作
        try {
            mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
        } catch (RemoteException ex) {
            Slog.e(TAG, "Unable to add window token for dream.", ex);
            stopDream(true /*immediate*/);
            return;
        }

        Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
        intent.setComponent(name);
        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        try {
            //拉起屏保服務
            if (!mContext.bindServiceAsUser(intent, mCurrentDream,
                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
                    new UserHandle(userId))) {
                Slog.e(TAG, "Unable to bind dream service: " + intent);
                stopDream(true /*immediate*/);
                return;
            } catch (SecurityException ex) {
                ............
                stopDream(true /*immediate*/);
                return;
            }

            mCurrentDream.mBound = true;
            //在DREAM_CONNECTION_TIMEOUT到期時,bind服務還未成功,runnable就負責結束dream
            mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
    } finally {
        ........
    }
}

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

3、更新屏保狀態

// Update dream state.
synchronized (mLock) {
    // Remember the initial battery level when the dream started.
    if (startDreaming && isDreaming) {
        mBatteryLevelWhenDreamStarted = mBatteryLevel;
        ................
    }

    // If preconditions changed, wait for the next iteration to determine
    // whether the dream should continue (or be restarted).
    //例如:mDisplayReady為false時, mSandmanSummoned保持為false
    if (mSandmanSummoned || mWakefulness != wakefulness) {
        return; // wait for next cycle
    }

    .............
    // Determine whether the dream should continue.
    if (wakefulness == WAKEFULNESS_DREAMING) {
        if (isDreaming && canDreamLocked()) {
            if (mDreamsBatteryLevelDrainCutoffConfig >= 0
                    //下面這句我是懵逼的,這不是必然成立的麼?
                    //也就是只要配置了mDreamsBatteryLevelDrainCutoffConfig就會成立
                    //按注釋來講,這裡好歹重新取一次mBatteryLevel啊!!!懷疑是bug點
                    && mBatteryLevel < mBatteryLevelWhenDreamStarted
                            - mDreamsBatteryLevelDrainCutoffConfig
                    && !isBeingKeptAwakeLocked()) {
                // If the user activity timeout expired and the battery appears
                // to be draining faster than it is charging then stop dreaming
                // and go to sleep.
            } else {
                return; // continue dreaming
            }
        }

        // Dream has ended or will be stopped.  Update the power state.
        if (isItBedTimeYetLocked()) {
            //休眠
            goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
                    PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
            updatePowerStateLocked();
        } else {
            //喚醒
            wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), "android.server.power:DREAM",
                    Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID);
            updatePowerStateLocked();
        }
    } else if (wakefulness == WAKEFULNESS_DOZING) {
        if (isDreaming) {
            return; // continue dozing
        }

        // Doze has ended or will be stopped.  Update the power state.
        reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
        updatePowerStateLocked();
    }
}

// Stop dream.
//執行到這裡說明退出了dreaming狀態,如果之前拉起過屏保服務,此時應該停止它
if (isDreaming) {
    mDreamManager.stopDream(false /*immediate*/);
}
...........

以上是PMS更新屏保狀態的基本流程,整體來看相當的繁瑣。 我們還是用一個圖來整體整理一下: 大圖鏈接 這部分代碼最後太亂,每次更新狀態後都會重新調用updatePowerStateLocked,然後再次進入到handleSandman函數中。 這種反復地遞歸調用,比較難以把控。

五、更新suspend blocker updateSuspendBlockerLocked函數主要根據之前流程的執行結果,持有或者釋放CPU和屏幕的鎖。 我們一起來看看對應的函數:

private void updateSuspendBlockerLocked() {
    //根據是否有CPU的wakelock,來決定cpu是保持否喚醒 
    final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);

    //根據前面屏幕相關的狀態,來決定是否需要持有屏幕的鎖
    final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();

    //屏幕如果不需要保持開啟狀態,那麼可自動熄滅
    final boolean autoSuspend = !needDisplaySuspendBlocker;

    //應該是表示屏幕是否是可交互的
    final boolean interactive = mDisplayPowerRequest.isBrightOrDim();

    // Disable auto-suspend if needed.
    //autoSuspend為false,說明屏幕還需要點亮
    if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        //通過native函數,調用底層的autosuspend_disable
        setHalAutoSuspendModeLocked(false);
    }

    // First acquire suspend blockers if needed.
    //在需要的情況下,獲取CPU和屏幕的鎖
    if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.acquire();
        mHoldingWakeLockSuspendBlocker = true;
    }
    if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.acquire();
        mHoldingDisplaySuspendBlocker = true;
    }

    // Inform the power HAL about interactive mode.
    if (mDecoupleHalInteractiveModeFromDisplayConfig) {
        if (interactive || mDisplayReady) {
            //調用底層動態庫的setInteractive函數,決定終端是否可以進行交互
            setHalInteractiveModeLocked(interactive);
        }
    }

    // Then release suspend blockers if needed.
    //如果不需要,則釋放CPU和屏幕的鎖 
    if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.release();
        mHoldingWakeLockSuspendBlocker = false;
    }
    if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.release();
        mHoldingDisplaySuspendBlocker = false;
    }

    // Enable auto-suspend if needed.
    //如果需要設置自動休眠模式
    if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        setHalAutoSuspendModeLocked(true);
    }
}

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

..........
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
.........

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

private SuspendBlocker createSuspendBlockerLocked(String name) {
    //實際對象為PMS內部類SuspendBlockerImpl
    SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
    mSuspendBlockers.add(suspendBlocker);
    return suspendBlocker;
}

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

public void acquire() {
    synchronized (this) {
        mReferenceCount += 1;
        if (mReferenceCount == 1) {
            .......
            //調用到了native層
            nativeAcquireSuspendBlocker(mName);
        }
    }
}

在native層的com_android_server_power_PowerManagerService.cpp中,對應的native函數為:

static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) {
    ScopedUtfChars name(env, nameStr);
    //獲取的是PARTIAL_WAKE_LOCK的類型,即保持CPU喚醒的
    acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}

從這裡的代碼我們不難發現,盡管PMS定義了不同的WakeLock等級,但當通過PMS的native函數調用HAL層函數acquire_wake_lock時,使用的都是PARTIAL_WAKE_LOCK。 個人覺得這是可以理解的,當其它進程向PMS申請保持屏幕喚醒的Framework層WakeLock後,PMS在Framework層就進行了對應的處理,例如將請求信息等地交給DisplayPowerController等處理。因此,對於底層的HAL層而言,只需要關注CPU是否需要保持喚醒即可。 HAL層函數acquire_wake_lock,最後會向/sys/power/wake_lock節點進行write操作。

總結 至此,updatePowerStateLocked的基本流程介紹完畢,大體上如下圖所示:

通過其中的源碼,我們也能看出僅管理當前的狀態,涉及的細節就非常的瑣碎。 而屏幕和CPU的實際控制,還牽扯到大量其它對象和HAL層代碼。 Android電源的管理實際上是基於Linux電源管理策略的,因此若要真正掌握,還需要對Linux的電源管理策略作進一步的了解。 由於個人水平有限,目前還無法高屋建瓴地整體分析宏觀的電源管理架構,細節也有一些遺漏。 後續爭取以此博客為基礎,不斷迭代,以求更進一步地了解PMS的知識。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved