PowerManagerService分析(四)之亮屏流程分析

收藏待读

PowerManagerService分析(四)之亮屏流程分析

PowerManagerService分析(四)之亮屏流程分析
PowerManagerService分析(四)之亮屏流程分析

極力推薦Android 開發大總結文章:歡迎收藏

程序員Android 力薦 ,Android 開發者需要的必備技能
PowerManagerService分析(四)之亮屏流程分析

PowerManagerService之前系列文章請參考如下

1. PowerManagerService分析(一)之PMS啟動

2. PowerManagerService分析(二)之updatePowerStateLocked()核心

3. PowerManagerService分析(三)之WakeLock機制

註: 本文轉自網絡, 原文地址

PowerManagerService分析(四)之亮屏流程分析

本篇分析PMS中涉及到亮屏的部分,以及PMS相關的兩個類: PowerManagerNotifier

1.亮屏流程

1.1.Power鍵亮屏

這裡直接從 PhoneWindowManager 開始分析。按 power 鍵後,會觸發 PhoneWindowManagerinterceptKeyBeforeQueueing() 方法:

@Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
           .......
            case KeyEvent.KEYCODE_POWER: {
                cancelPendingAccessibilityShortcutAction();
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    interceptPowerKeyDown(event, interactive);
                } else {
                    interceptPowerKeyUp(event, interactive, canceled);
                }
                break;
            }
            .......

在這個方法中,對 Power 鍵的按下和抬起做了處理,按下時,調用 interceptPowerKeyDown():

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
    // Hold a wake lock until the power key is released.
    if (!mPowerKeyWakeLock.isHeld()) {
       //1.申請一個喚醒鎖,使CPU保持喚醒
        mPowerKeyWakeLock.acquire();
    }
   ......
if (!mPowerKeyHandled) {
       if (interactive) {
       ......
       } else {
    //2.進行亮屏處理
        wakeUpFromPowerKey(event.getDownTime());
    ........
        }
    }
}

在這個方法中,首先是申請了一個喚醒鎖,然後會對一些特定功能進行處理,如截屏、結束通話,等等,然後如果此時處於非交互狀態 (interactive=false) ,進行亮屏操作。該鎖實例化如下:

mPowerKeyWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "PhoneWindowManager.mPowerKeyWakeLock");

申請鎖流程在 PowerManagerService 分析第三篇 PowerManagerService分析(三)之WakeLock機制 已經分析過了。繼續看 wakeUpFromPowerKey() 方法:

private void wakeUpFromPowerKey(long eventTime) {
    //第三個參數為亮屏原因,因此如果是power鍵亮屏,則log中會出現android.policy:POWER
    wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER");
}

private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {
    final boolean theaterModeEnabled = isTheaterModeEnabled();
    if (!wakeInTheaterMode && theaterModeEnabled) {
        return false;
    }
    if (theaterModeEnabled) {
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.THEATER_MODE_ON, 0);
    }
    mPowerManager.wakeUp(wakeTime, reason);
    return true;
}

在這個方法中,首先判斷是否允許在劇院模式下點亮屏幕(這個模式不常用,未進行詳細分析),之後通過 PowerManagerPMS 進行屏幕的喚醒,先來看看 PowerManagerwakeup() 方法:

public void wakeUp(long time) {
    try {
        mService.wakeUp(time, "wakeUp", mContext.getOpPackageName());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

現在到 PMS 中繼續分析流程:

@Override // Binder call
public void wakeUp(long eventTime, String reason, String opPackageName) {
    if (eventTime > SystemClock.uptimeMillis()) {
        throw new IllegalArgumentException("event time must not be in the future");
    }
    //權限檢查
    mContext.enforceCallingOrSelfPermission(
            android.Manifest.permission.DEVICE_POWER, null);
    final int uid = Binder.getCallingUid();
    //清除IPC標誌
    final long ident = Binder.clearCallingIdentity();
    try {
        //調用內部方法
        wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
    } finally {
        //重置IPC標誌
        Binder.restoreCallingIdentity(ident);
    }
}

PMS 中暴露給 Binder 客戶端的方法中,進行了權限的檢查,然後調用 wakeUpInternal() 方法,該方法如下:

private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName,
        int opUid) {
    synchronized (mLock) {
        if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {
            updatePowerStateLocked();
        }
    }
}

這裡又調用了 wakeUpNoUpdateLocked() 方法,如果這個方法返回 true ,則會執行 updatePowerStateLocked() 方法,如果返回 false ,則整個過程結束。這個方法在我們分析wakelock申請時提到過,如果申請的 wakelock 鎖帶有喚醒屏幕的標誌,也只執行這個方法,因此,這個方法是喚醒屏幕的主要方法之一,來看看這個方法:

private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
        String opPackageName, int opUid) {
    if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
            || !mBootCompleted || !mSystemReady) {
        return false;
    }
    try {
    //根據當前wakefulness狀態打印log,這些log很有用
        switch (mWakefulness) {
            case WAKEFULNESS_ASLEEP:
                Slog.i(TAG, "Waking up from sleep (uid " + reasonUid +")...");
                break;
            case WAKEFULNESS_DREAMING:
                Slog.i(TAG, "Waking up from dream (uid " + reasonUid +")...");
                break;
            case WAKEFULNESS_DOZING:
                Slog.i(TAG, "Waking up from dozing (uid " + reasonUid +")...");
                break;
        }
        //設置最後一次亮屏時間,即該次的時間
        mLastWakeTime = eventTime;
        //設置wakefulness為WAKEFULNESS_AWAKE
        setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
        //Notifier中通知BatteryStatsService統計亮屏
        mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);
        //更新用戶活動時間
        userActivityNoUpdateLocked(
                eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
    } finally {
    }
    return true;
}

在這個方法中, Log 中的 reason 需要注意一下:

  • Power 鍵亮屏:
    則reason是PWM中傳入的android.policy:POWER;
  • 來電亮屏:
    android.server.am:TURN_ON;
  • USB插拔時:
    android.server.power:POWER

所以不管是哪種亮屏方式,最終都會在這裡匯合的。之後通過 setWakefulnessLocked() 方法設置 wakefulness ,再通過 Notifier 進行處理和通知其他系統服務 wakefulness 的改變,最後更新用戶活動的時間,重置下次超時滅屏時間。繼續看看 setWakefulnessLocked():

void setWakefulnessLocked(int wakefulness, int reason) {
       if (mWakefulness != wakefulness) {
       //改變Wakefulness
        mWakefulness = wakefulness;
        mWakefulnessChanging = true;
        //置位操作
        mDirty |= DIRTY_WAKEFULNESS;
        if (mNotifier != null) {
            //處理wakefulness改變前的操作
            mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
        }
    }
}

首先,改變當前 mWakefulness 值,將 mWakefulnessChanging 標記為 true ,將 mWakefulness 值標誌為 DIRTY_WAKEFULNESS ,然後通過 Notifier 進行改變 wakefulness 之前的一些處理, Notifier 負責 PMS 和其他系統服務的交互。而 Notifier 中的 onWakefulnessChangeStarted() 方法,就是亮屏的主要方法之一,如發送亮屏或者滅屏的廣播等。

為了不顯得凌亂,將關於 Notifier 的具體細節的分析放在了3小節中。這裡對其大概的進行下闡述。 onWakefulnessChangeStarted() 方法部分如下:

public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
        if (DEBUG) {
            Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
                    + ", reason=" + reason + ", interactive=" + interactive);
        }
        // Handle any early interactive state changes.
        // Finish pending incomplete ones from a previous cycle.
        if (mInteractive != interactive) {
            // Finish up late behaviors if needed.
            if (mInteractiveChanging) {
                handleLateInteractiveChange();
            }
            // Handle early behaviors.
            mInteractive = interactive;
            mInteractiveChangeReason = reason;
            mInteractiveChanging = true;
            //做亮屏早期工作
            handleEarlyInteractiveChange();
        }
    }

亮屏操作,此時部分變量值為:

interactive為true; mInteractive為false; mInteractiveChanging = true;

因此會開始執行 handleEarlyInteractiveChange() 方法,做一些亮屏前期的工作,該方法亮屏部分如下:

private void handleEarlyInteractiveChange() {
        synchronized (mLock) {
            if (mInteractive) {
                // Waking up...
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //回調PhoneWindowManager
                        mPolicy.startedWakingUp();
                    }
                });
                // Send interactive broadcast.
                mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
                mPendingWakeUpBroadcast = true;
                //發送亮/滅屏廣播
                updatePendingBroadcastLocked();
            } else {
                // Going to sleep...
            }
        }
    }

首先,會回調 PhoneWindowManager 中的 startedWakingUp() ,然後發送亮屏廣播。在 startedWakingUp() 中的工作如下:

@Override
    public void startedWakingUp() {
        if (DEBUG_WAKEUP) Slog.i(TAG, "Started waking up...");
        synchronized (mLock) {
            mAwake = true;
            updateWakeGestureListenerLp();
            updateOrientationListenerLp();
            updateLockScreenTimeout();
        }
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onStartedWakingUp();
        }
    }

繼續分析 PMS 中的剩餘流程, setWakefulnessLocked() 執行完畢後,接下來執行的是 NotifieronWakeUp() 方法,這個方法負責和 BatteryStatsServiceAppService 進行交互,將 wakeup 信息傳遞給它們,如下:

public void onWakeUp(String reason, int reasonUid, String opPackageName, int opUid) {
    try {
        //開始統計亮屏時間
        mBatteryStats.noteWakeUp(reason, reasonUid);
        if (opPackageName != null) {
            mAppOps.noteOperation(AppOpsManager.OP_TURN_SCREEN_ON, 
                opUid, opPackageName);
        }
    } catch (RemoteException ex) {
    }
}

接下來,執行 userActivityNoUpdateLocked() 方法,這個方法任務只有一個,負責更新系統和用戶最後交互時間,計算的時間在 updateUserActivitySummary() 方法中會用於判斷何時滅屏,該方法如下:

private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {

        // ......
       
        /**
         * USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS標識
         * 只有帶有PowerManager.ON_AFTER_RELEASE類型的鎖在釋放時才會有該flag,在亮屏流程中沒有該標識,因此不滿足該條
         * 件,如果滿足條件,改變mLastUserActivityTimeNoChangeLights的值,否則進入else語句,改變
         * mLastUserActivityTime的值
         */
        if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO
                  _CHANGE_LIGHTS) != 0) {
            if (eventTime > mLastUserActivityTimeNoChangeLights
                    && eventTime > mLastUserActivityTime) {
                mLastUserActivityTimeNoChangeLights = eventTime;
                mDirty |= DIRTY_USER_ACTIVITY;
                if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
                    mDirty |= DIRTY_QUIESCENT;
                }
                return true;
            }
        } else {
            if (eventTime > mLastUserActivityTime) {
                mLastUserActivityTime = eventTime;
                mDirty |= DIRTY_USER_ACTIVITY;
                if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
                    mDirty |= DIRTY_QUIESCENT;
                }
                return true;
            }
        }
    // ......
    return false;
}

在這個方法中來看下 PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHT 這個值,這個值和釋放 wakelock 有關係,在分析 WakeLock 釋放流程時分析到,如果帶有 PowerManager.ON_AFTER_RELEASE 標記,則在釋放該 WakeLock 時會先亮一小會之後才會滅屏,這裡正是為何會亮一小會才會滅屏的關鍵。我們可以在釋放 WakeLock 鎖的流程方法中看到:

private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
    if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0
            && isScreenLock(wakeLock)) {
        userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
                PowerManager.USER_ACTIVITY_EVENT_OTHER,
                PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
                wakeLock.mOwnerUid);
    }
}

因此,如果是釋放帶有 ON_AFTER_REALESE 標記的鎖,則會給該方法傳入 USER_ACTIVITY_FLAG_NO_CHANGE_LIGHT 標記,其他場景下不會帶有該標識。

這些方法執行完後,執行 updatePowerStateLocked() 方法更新所有信息,這個方法作為PMS的核心方法,在 PowerManagerService 分析第二篇 PowerManagerService分析(二)之updatePowerStateLocked()核心 中分析過了,同時在這個方法中會進行亮屏的最關鍵的操作: updateDisplayPowerStateLocked() ,這裡對 updateDisplayPowerStateLocked() 方法進行分析:

private boolean updateDisplayPowerStateLocked(int dirty) {
        final boolean oldDisplayReady = mDisplayReady;
           //策略值,請求Display的重要屬性
            mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
            //確定亮度值部分省略......
            
            //亮度值
            mDisplayPowerRequest.screenBrightness = screenBrightness;
            //自動調節亮度比例值
            mDisplayPowerRequest.screenAutoBrightnessAdjustment =
                    screenAutoBrightnessAdjustment;
            //是否是用戶設置亮度
            mDisplayPowerRequest.brightnessSetByUser = brightnessSetByUser;
            //是否使用自動調節亮度
            mDisplayPowerRequest.useAutoBrightness = autoBrightness;
            //是否使用PSensor
            mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
            //是否進行亮度增強
            mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
            //低電量模式一些值的設置
            updatePowerRequestFromBatterySaverPolicy(mDisplayPowerRequest);
            //Doze狀態下相關設置值
            if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
                mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
                if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND
                        && (mWakeLockSummary & WAKE_LOCK_DRAW) != 0) {
                    mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE;
                }
                mDisplayPowerRequest.dozeScreenBrightness =
                        mDozeScreenBrightnessOverrideFromDreamManager;
            } else {
                mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
                mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
            }
            //通過DMS本地服務DisplayManagerInternal請求DisplayManagerService
            mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                    mRequestWaitForNegativeProximity);
            mRequestWaitForNegativeProximity = false;

            if ((dirty & DIRTY_QUIESCENT) != 0) {
                sQuiescent = false;
            }
           
        }
        //mDisplayReady表示請求的新的顯示是否完成
        return mDisplayReady && !oldDisplayReady;
    }

這個方法中,將 PMS 中和 Display 相關的值都封裝在了 DisplayPowerRequest 中,然後向 DisplayManagerService 請求新的狀態, DisplayManagerService 會交給 DisplayPowerController 去處理請求。

在請求時, DisplayPowerRequest.policy 作為 DisplayPowerRequset 的屬性,有四種值,分別為 off、doze、dim、bright、vr 。在向 DisplayManagerService 請求時,會根據當前PMS中的喚醒狀態和統計的 wakelock 來決定 policy 值,從而確定要請求的 Display 狀態,這部分源碼如下:

@VisibleForTesting
int getDesiredScreenPolicyLocked() {
    //asleep時,policy值為0
    if (mWakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
        return DisplayPowerRequest.POLICY_OFF;//0
    }
    if (mWakefulness == WAKEFULNESS_DOZING) {
        if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
            return DisplayPowerRequest.POLICY_DOZE;//1
        }
        if (mDozeAfterScreenOffConfig) {
            return DisplayPowerRequest.POLICY_OFF;
        }
    }    
    if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
            || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
            || !mBootCompleted
            || mScreenBrightnessBoostInProgress) {
        return DisplayPowerRequest.POLICY_BRIGHT;//3
    }

    return DisplayPowerRequest.POLICY_DIM;
}

這裡對 DisplayPowerRequest.policy 進行下說明,在該方法中,由於是亮屏操作,所以此時 mWakefulness=WAKEFULNESS_AWAKE(1) ,綜合判斷條件,所以policy的值為 POLICY_BRIGHT 。同樣地,如果是滅屏操作的話, policy 值就是 POLICY_OFF ,還有一種情況,如果系統配置了 dozeComponent 組件,那麼在按 Power 鍵滅屏時,不會立即進入 Sleep 裝填,而是會先進入 Doze 狀態,所以這種情況下 policy 值為 POLICY_DOZE

現在回到 updateDisplayPowerStateLocked() 方法中,再來看看當「請求體」確定後,是如何進行請求的。 PMS 中向 DisplayManagerService 發起請求後, DisplayManagerService 交給了 DisplayController 中進行處理,這個過程圖示如下:

PowerManagerService分析(四)之亮屏流程分析

對應相關代碼如下(有刪減):

DisplayController.java:
public boolean requestPowerState(DisplayPowerRequest request,
        boolean waitForNegativeProximity) {
    synchronized (mLock) {
        boolean changed = false;
        //開機後第一次進入
        if (mPendingRequestLocked == null) {
            mPendingRequestLocked = new DisplayPowerRequest(request);
            changed = true;
         //如果該次請求和上次請求不同,說明已經改變,需要更新Display
        } else if (!mPendingRequestLocked.equals(request)) {
            mPendingRequestLocked.copyFrom(request);
            changed = true;
        }
        /**
         * changed為true,說明有改變發生,這個改變交給Handler異步去處理,此時說
         * 明顯示沒有準備好,mDisplayReadyLocked=false
         * 直到改變處理成功,mDisplayReadyLocked又被置為true,
         */
        if (changed) {
            mDisplayReadyLocked = false;
        }
        //mPendingRequestChangedLocked:用於標識電源請求狀態或者PSensor標籤是
        //否改變
        if (changed && !mPendingRequestChangedLocked) {
            mPendingRequestChangedLocked = true;
            sendUpdatePowerStateLocked();
        }
        return mDisplayReadyLocked;
    }
}

在這個方法中,會判斷請求時攜帶的 DisplayPowerRequest 對象是否和上一次發生請求的 DisplayRequest 對象相同,如果相同,則返回值 mDisplayReadyLocked為true ,如果不同,則表示發生了改變,此時會異步去請求新的Display狀態,並向PMS返回false,表示Display狀態正在更新,沒有準備完成,直到更新完成後,會將 mDisplayReadyLocked 值置為true,表示更新完成。同時回調PMS中的 onStateChanged() 方法通知PMS更新完成。這個返回值會在PMS中作為下一步的執行條件。

處理完成後回調PMS中的 onStateChanged() 方法通知PMS,最終完成Display的更新。關於 DisplayPowerControllerDisplayManagerService 以及其他模塊中如何處理的,這裡暫不分析。只需要知道當 DisplayPowerController 處理完請求後,回調 DisplayManagerInternal.DisplayPowerCallbacksonStateChanged() 方法,再來看看這個方法:

private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks =
            new DisplayManagerInternal.DisplayPowerCallbacks() {
        private int mDisplayState = Display.STATE_UNKNOWN;

        @Override
        public void onStateChanged() {
            synchronized (mLock) {
                mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;
                updatePowerStateLocked();
            }
        }
        .........
   }

在這個方法中,對 mDirty 進行了置位操作,然後由調用了 updatePowerState() 方法,這次調用時狀態已經沒有發生過改變,因此會根據執行條件而在某一階段跳出。

當請求完畢後,對於 updatePowerStateLocked() 方法中剩餘的其他三個方法主要作用是和屏保、 wakefulness 改變的收尾工作和申請/釋放 SuspendBlocker 鎖,這裡略去。

關於按power鍵亮屏PMS相關部分就分析完了,當然,亮屏完整流程涉及模塊較多,絕非幾句就可以說完,這裡僅僅將PMS相關的進行分析,至於其他模塊如 DMS、LightsService 中的,會在下篇文章中進行分析。整個Power鍵亮屏時序圖如下:

PowerManagerService分析(四)之亮屏流程分析

PMS發起請求後其他模塊的處理時序圖:

PowerManagerService分析(四)之亮屏流程分析

1.2.插拔USB亮屏

當插拔USB時,會發送 BATTERY_CHANGED 廣播, PMS 中對該廣播進行監聽,如果收到廣播後,配置了插播USB時亮屏,則會進行亮屏操作。

BatteryService 中,如果電池狀態發生改變,則會發送一個 ACTION_Battery_CAHNGED 廣播:

private void sendIntentLocked() {
    //  Pack up the values and broadcast them to everyone
    final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
            | Intent.FLAG_RECEIVER_REPLACE_PENDING);
    ....
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
        }
    });
}

在PMS中,註冊了廣播接受者,會接收該廣播:

public void systemReady(IAppOpsService appOps) {
     ....
IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
    filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
    mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
    ......
}

因此當 BatteryService 中檢測到底層電池狀態發生變化後,會發送該廣播,PMS中的 BatteryReceiver 用於接受該廣播並進行處理,如下:

private final class BatteryReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        synchronized (mLock) {
            handleBatteryStateChangedLocked();
        }
    }
}

繼續看看:

private void handleBatteryStateChangedLocked() {
    mDirty |= DIRTY_BATTERY_STATE;
    updatePowerStateLocked();
}

在這裡對 mDirty 進行了置位,之後開始調用 updatePowerStateLocked() 方法。在之前已經分析過該方法了,其中調用的 updateIsPowerLocked() 方法之前分析過了,是插播USB亮屏的入口方法,所有和電池相關的都是在這裡處理。這裡看看具體是如何進行亮屏的:

private void updateIsPoweredLocked(int dirty) {
    if ((dirty & DIRTY_BATTERY_STATE) != 0) {
           ........
            if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, 
           oldPlugType, dockedOnWirelessCharger)) {
                wakeUpNoUpdateLocked(now, "android.server.power:POWER", 
                         Process.SYSTEM_UID,mContext.getOpPackageName(), 
                         Process.SYSTEM_UID);
            }
        }
    }
}

因此,如果 shouldWakeUpWhenPluggedOrUnpluggedLocked() 方法返回true,則會開始亮屏,否則不會亮屏。該方法如下:

private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(
        boolean wasPowered, int oldPlugType, boolean dockedOnWirelessCharger) {
    // 如果配置config_unplugTurnsOnScreen為false,則不會亮屏
    if (!mWakeUpWhenPluggedOrUnpluggedConfig) {
        return false;
    }
    //斷開無線充電不會亮屏
    if (wasPowered && !mIsPowered
            && oldPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
        return false;
    }
    //連接無線充電不會亮屏
    if (!wasPowered && mIsPowered
            && mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS
            && !dockedOnWirelessCharger) {
        return false;
    }
    // 插入充電時屏保狀態下不會亮屏
    if (mIsPowered && mWakefulness == WAKEFULNESS_DREAMING) {
        return false;
    }
    //劇院模式下,且配置了劇院模式下充電是否亮屏為false,則不會亮屏
    if (mTheaterModeEnabled && !mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig) {
        return false;
    }
    //如果屏幕保持常亮且當前wakefulness為Doze狀態
    if (mAlwaysOnEnabled && mWakefulness == WAKEFULNESS_DOZING) {
        return false;
    }
    // 其他情況下則亮屏
    return true;
}

當該方法返回true後,開始調用 wakeUpNoUpdateLocked() 方法開始喚醒屏幕,該方法是喚醒屏幕的入口方法,在前面有詳細的分析。

2.PowerManager類

PowerManager 可以說是 PMSApplication 層提供信息的一個接口。 PowerManager 用來控制設備電源狀態。在上面分析了 PMS ,是一個系統服務,由 SystemServer 啟動並運行,並沒有提供上層調用的接口,因此呢, PowerManager 作為PMS的一個代理類,向上層應用層提供開放接口,供Application層調用,實現對電源的管理,其實現原理和上文談到的 Binider 註冊有關。

PowerManager 作為系統級別服務,在獲取其實例時,通過以下方式進行獲取:

PowerManager pm = 
          (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);

通過 Context.POWER_SERVICE ,獲取了 PowerManager 實例,而這個字段在 PMS 進行 Binder 註冊的時候使用了,因此,實際上 PowerManager 對象中包含了一個 PMS.BindService 對象,當應用層調用 PowerManager 開放接口後, PowerManager 再通過了 PMS.BindService 向下調用到了 PMS 中。這點可以在 PowerManager 的構造方法中看出:

public PowerManager(Context context, IPowerManager service, Handler handler) {
    mContext = context;
    mService = service;
    mHandler = handler;
}

PowerManager 中,提供了許多 public 方法,當應用層調用這些方法時, PowerManager 將向下調用 PMS

3.Notifier類

Notifier 類好比 PMS 和其他系統服務交互的」中介「, NotifierPMS 在結構上可以說是組合關係, PMS 中需要和其他組件交互的大部分都由 Notifier 處理,如亮滅屏通知其他服務等,亮滅屏廣播也是在該類中發出。這裡介紹其中的部分方法,有些可能已經在上面內容的分析中涉及到了。(註:如果是第一次看到這部分內容,那麼應該先看看之前的內容。)

3.1.onWakefulnessChangeStarted()

該方法用於亮屏或者滅屏時邏輯的處理,和 onWakefulnessChangeFinished() 方法對應,分別負責操作開始和結束的邏輯處理,當 wakefulness 改變時進行回調,因此當亮屏、滅屏、進入 Doze 模式時都會調用這個方法,看看這個方法:

public void onWakefulnessChangeStarted(final int wakefulness, int reason) {

//是否可以和用戶進行交互
//既wakefulness == WAKEFULNESS_AWAKE || WAKEFULNESS_DREAM
    final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
    mHandler.post(new Runnable() {
        @Override
        public void run() {
        //和AMS交互,通知AMS wakefulness發生改變
            mActivityManagerInternal.onWakefulnessChanged(wakefulness);
        }
    });
  //如果為false,表示交互狀態發生改變,即從亮屏到滅屏或者從滅屏到亮屏
      if (mInteractive != interactive) {
        // Finish up late behaviors if needed.
    //交互狀態發生了改變
        if (mInteractiveChanging) {
            handleLateInteractiveChange();//處理交互改變後的任務
        }
        // Start input as soon as we start waking up or going to sleep.
    //和IMS交互
        mInputManagerInternal.setInteractive(interactive);
        mInputMethodManagerInternal.setInteractive(interactive);
        //和BatteryStatsService交互
        try {
            mBatteryStats.noteInteractive(interactive);
        } catch (RemoteException ex) { }
        //處理交互完成前的操作
        mInteractive = interactive;
        mInteractiveChangeReason = reason;
        mInteractiveChanging = true;
        handleEarlyInteractiveChange();
    }
}

首先判斷系統是否可以進行交互,如果處於 Dream 或者 Awake 狀態,表示可以進行交互, interactive為true ;在這個方法中有兩個關鍵方法, handleLateInteractiveChange()handleEarlyInteractiveChange(), 分別表示處理交互狀態改變後的操作和改變前的操作,如果是亮屏場景,則在執行到該方法時,在 setWakeFulnessLocked() 方法中將 wakefulness 設置為了 WAKEFULNESS_AWAKE ,所以 interactive為true,mInteractive是false ,因此會先執行 handleEarlyInteractiveChange() ,繼續看看 handleEarlyInteractiveChange() 方法:

//mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
private void sendWakeUpBroadcast() {
    if (DEBUG) {
        Slog.d(TAG, "Sending wake up broadcast.");
    }

    if (mActivityManagerInternal.isSystemReady()) {
        mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, 
                UserHandle.ALL, null,
                mWakeUpBroadcastDone, mHandler, 0, null, null);
    } else {
        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 
                                          1);
        sendNextBroadcast();
    }
}

3.2.onWakefulnessChangeFinished()

該方法負責 wakefulness 狀態改變完成後的工作,和3.1方法相對應。這個方法較簡單,當 PMS 中調用它後,它會調用 handleLaterInteractiveChanged() 方法,如下:

/**
 * Notifies that the device has finished changing wakefulness.
 */
public void onWakefulnessChangeFinished() {
    if (mInteractiveChanging) {
        mInteractiveChanging = false;
        handleLateInteractiveChange();
    }
}

繼續:

private void handleLateInteractiveChange() {
    synchronized (mLock) {
        //mInteractive在onWakefulnessCHangeStated()中進行了改變,以用來確定是否
        //是亮屏或者滅屏
        if (mInteractive) {
           //亮屏...
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mPolicy.finishedWakingUp();
                }
            });
        } else {
            //滅屏...
            if (mUserActivityPending) {
                mUserActivityPending = false;
                mHandler.removeMessages(MSG_USER_ACTIVITY);
            }
            final int why = translateOffReason(mInteractiveChangeReason);
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    LogMaker log = new LogMaker(MetricsEvent.SCREEN);
                    log.setType(MetricsEvent.TYPE_CLOSE);
                    log.setSubtype(why);
                    MetricsLogger.action(log);
                    EventLogTags.writePowerScreenState(0, why, 0, 0, 0);
                    mPolicy.finishedGoingToSleep(why);
                }
            });
            // 發送完成後的廣播
            mPendingInteractiveState = INTERACTIVE_STATE_ASLEEP;
            mPendingGoToSleepBroadcast = true;
            updatePendingBroadcastLocked();
        }
    }
}

在這個方法中,如果是亮屏,則調用 PWMfinishedWakingUp() 表示亮屏處理成功,如果是滅屏,則調用 PWMfinishedGoingToSleep()

3.3.updatePendingBroadcaseLocked()

這個方法用於交互狀態改變時發送廣播,最常見的就是由亮屏-滅屏之間的改變了,都會發送這個廣播。亮屏時,在 handlerEarlyInteractiveChang() 方法中調用該方法發送廣播,滅屏時,在 handlerLateInteractiveChang() 中調用方法發送廣播。接下來會分兩種情況進行分析。

當系統由不可交互變成可交互時,如由滅屏-亮屏,首先做了如下處理:

// Send interactive broadcast.
mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
mPendingWakeUpBroadcast = true;
updatePendingBroadcastLocked();

現在進入這個方法進行分析:

private void updatePendingBroadcastLocked() {
    /**
     * 廣播沒有進行中&&要發送的廣播狀態!= UNKNOW
     * && (發送亮屏廣播||發送滅屏廣播||發送廣播狀態!=當前廣播交互狀態)
     * mBroadcastedInteractiveState值實際上是上次發送廣播交互狀態的值
     */
    if (!mBroadcastInProgress
            && mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
            && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
                    || mPendingInteractiveState != mBroadcastedInteractiveState)) {
        mBroadcastInProgress = true;
        //申請一個Suspend鎖,以防廣播發送未完成系統休眠而失敗
        mSuspendBlocker.acquire();
        Message msg = mHandler.obtainMessage(MSG_BROADCAST);
        msg.setAsynchronous(true);
        mHandler.sendMessage(msg);
    }
}

在這個方法中,使用到的幾個屬性值意義如下:

//要廣播的交互狀態
private int mPendingInteractiveState;
//是否廣播亮屏
private boolean mPendingWakeUpBroadcast;
//是否廣播滅屏
private boolean mPendingGoToSleepBroadcast;
//當前要廣播的交互狀態
private int mBroadcastedInteractiveState;
//是否廣播正在進行中
private boolean mBroadcastInProgress;

在這個方法中,首先申請了一個 suspend 鎖,這個鎖是通過在 PMS 中創建 Notifier 對象時創建傳入的, namePowerManagerService.Broadcast 在廣播發送完成後又進行了釋放,這樣作的目的是避免在發送廣播過程中系統休眠而導致廣播未發送完成;可以通過如下命令查看該鎖:

adb root
adb remount
adb shell
cat /sys/power/wake_lock

之後通過 Handler 中調用 sendNextBroadcaset() 方法發送廣播,看看這個方法:

private void sendNextBroadcast() {
    final int powerState;
    synchronized (mLock) {
        //當前廣播的交互狀態=0(成員變量默認0)
        if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {
            // Broadcasted power state is unknown.  Send wake up.
            mPendingWakeUpBroadcast = false;
            mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
            //當前廣播的交互狀態為亮屏
        } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
            // Broadcasted power state is awake.  Send asleep if needed.
            //廣播亮屏||廣播滅屏||最終要廣播的交互狀態為滅屏
            if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
                    || mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) {
                mPendingGoToSleepBroadcast = false;
                mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
            } else {
                finishPendingBroadcastLocked();
                return;
            }
            //當前廣播的交互狀態為滅屏
        } else {
            if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
                    || mPendingInteractiveState == INTERACTIVE_STATE_AWAKE) {
                mPendingWakeUpBroadcast = false;
                mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
            } else {
                finishPendingBroadcastLocked();
                return;
            }
        }
        mBroadcastStartTime = SystemClock.uptimeMillis();
        powerState = mBroadcastedInteractiveState;
    }
    if (powerState == INTERACTIVE_STATE_AWAKE) {
        //發送亮屏廣播
        sendWakeUpBroadcast();
    } else {
        //發送滅屏廣播
        sendGoToSleepBroadcast();
    }
}

在這裡總結下該方法的判斷條件:

  • 1.如果 mBroadcastedInteractiveStateINTERACTIVE_STATE_UNKNOWN ,即0,由於是成員變量,因此初始值就等於0.開機後滅屏滿足這個情景,此時會將 mBroadcastedInteractiveState 置為 INTERACTIVE_STATE_AWAKE
  • 2.如果 mBroadcastedInteractiveStateINTERACTIVE_STATE_AWAKE ,表示當前廣播的交互狀態為可交互狀態,即此時處於亮屏狀態;(下同)
  • 3.如果 mBroadcastedInteractiveStateINTERACTIVE_STATE_ASLEEP ,表示此時處於滅屏狀態。
    到這裡為止,我們理清了發送廣播的大概流程,可以確定交互狀態改變時對應的操作了,現在分別對亮屏和滅屏的流程進行分析。

3.3.1.由滅屏-亮屏:

onWakefulnessChangeStarted() 方法中部分變量初始值和調用流程:

wakefulness=1, 即WAKEFULNESS_AWAKE;
reason=0, PMS中傳入;
interactive=true,即處於AWAKE或者DREAM狀態
mInteractive=false,即前一次不出於交互狀態,
mInteractiveChanging=false,剛進入該函數該值初始值;
handleEarlyInteractiveChange()被調用;  --->
handleEarlyInteractiveChange()方法中相關變量賦值:
mPendingInteractiveState = INTERACTIVE_STATE_AWAKE,即為1,最終   要廣播的交互狀態;
mPendingWakeUpBroadcast = true;表示是否最終發送的為wakeup廣播;
updatePendingBroadcastLocked()被調用;   --->
updatePendingBroadcastLocked()中的變量值:
mBroadcastInProgress=false, 表示當前沒有正在進行的廣播;
mPendingInteractiveState=1, handleEarlyInteractiveChange()中賦值;
mPendingWakeUpBroadcast=true,handleEarlyInteractiveChange()中賦值
mPendingGoToSleepBroadcast=false,是否最終發送的為asleep廣播;
mBroadcastedInteractiveState=2,當前廣播的狀態(還未發送wakeup廣播狀態),為INTERACTIVE_STATE_ASLEEP,2;
sendNextBroadcast()中根據上述變量值,走else語句:
mPendingWakeUpBroadcast=false,
mBroadcastedInteractiveState=INTERACTIVE_STATE_AWAKE;
sendWakeUpBroadcast()被調用。      --->

sendWakeUpBroadcast() 中發送 Intent_SCREEN_ON 廣播:

private void sendWakeUpBroadcast() {
    if (mActivityManagerInternal.isSystemReady()) {
        //廣播發送完成後最後被mWakeUpBroadcastDone接受
        mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, 
               UserHandle.ALL, null, mWakeUpBroadcastDone, mHandler,
                0, null, null);
    } else {
        sendNextBroadcast();
    }
}

mWakeUpBroadcastDone 會在最後接受觸發onReceive()方法,繼續看看 MWakeUpBroadcastDone 這個廣播接受器:

private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
                SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
        sendNextBroadcast();
    }
};

在這裡,又調用了 sendNextBroadcast() 方法,並根據條件判斷,走 else if 語句,此時相關變量值如下:

mPendingWakeUpBroadcast = false;上次else if中設置;
mPendingGoToSleepBoradcast = false;發送滅屏廣播時才會設置為true;
mPendingInteractiveState = 1;handleEarlyInteractiveChange()中設置;

結合以上變量值,在 else if 中走 else 語句,else中直接調用了 finishPendingBroadcastLocked() 方法,該方法如下:

private void finishPendingBroadcastLocked() {
    //表示此時沒有正在進行的廣播
    mBroadcastInProgress = false;
    //釋放suspend鎖
    mSuspendBlocker.release();
}

在這個方法中,將 mBroadcastInProgress 值設置為false,表示當前沒有正在進行中的廣播,並釋 sendGoToSleepBroadcast 放了避免系統 CPU 休眠的 Suspend 鎖。亮屏廣播就發送完畢了。

3.3.2.由亮屏-滅屏:

onWakefulnessChangeStarted() 方法中部分變量初始值:

wakefulness=3, WAKE_LOCK_DOZE,即先進入doze模式;
reason=4, PM.GO_TO_SLEEP_POWER_BUTTTON,即由power鍵滅屏;
interactive=false,由於wakefulness已變為doze,因此不可交互狀態;
mInteractive=true,上次狀態為可交互狀態;
mInteractiveChanging=false,剛進入該函數該值初始值;
handleEarlyInteractiveState()被調用;
handleEarlyInteractiveState()中:只做了通過WindowManagerPolicy開始睡眠的操作,之後返回。
在PMS的handleSandman()中,調用reallyGoToSleepNoUpdateLocke()方法進入睡眠,因此又會調用到onWakefulnessChangeStarted()方法中:
onWakefulnessChangeStarted()中部分變量初始值:
wakefulness=0,WAKEFULNESS_ASLEEP=0;表示進入睡眠狀態
 reason=2,PM.GO_TO_SLEEP_REASON_TIMEOUT.
interactive=false,當前為asleep狀態,表示不可交互
mInteractive=false,上次為doze狀態,也是不可交互
mInteractiveChanging=true,是上次由awake->doze時設置;
handleEarlyInteractiveState()被調用;
handleEarlyInteractiveState()中:只做了通過WindowManagerPolicy開始睡眠的操作,之後返回。

最終,在 onWakefulnessChangeFinished() 方法中,調用了 handleLateInteractiveChanged(), 發送滅屏廣播前設置值如下:

mPendingInteractiveState = INTERACTIVE_STATE_ASLEEP;
mPendingGoToSleepBroadcast = true;
updatePendingBroadcastLocked();

進入到 sendNextBroadcast() 中,此時部分變量值如下:

mBroadcastedInteractiveState=1,表示當前廣播的交互狀態為awake;
mPendingWakeUpBroadcast=false,是否最終發送的為wakeup廣播;
mPendingGoToSleepBroadcast=true,表示最終發送的為sleep廣播;

因此,走 sendNextBroadcast() 中的else if->if語句, sendGoToSleepBroadcast() 被調用;

sendGoToSleepBroadcast() 中發送 Intent.SCREEN_OFF 廣播:

private void sendGoToSleepBroadcast() {
    if (mActivityManagerInternal.isSystemReady()) {
        //廣播發送後最後一個廣播接受器mGoToSleepBroadcastDone
        mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, 
               UserHandle.ALL, null,
                mGoToSleepBroadcastDone, mHandler, 0, null, null);
    } else {
        sendNextBroadcast();
    }
}

滅屏廣播發出後, mGoToSleepBroadcastDone 會在最後接受到,這裡進行收尾處理:

private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        sendNextBroadcast();
    }
};

這裡又調用了 sendNextBroadcast() 方法,此時相關變量值如下:

mPendingWakeUpBroadcast=false,
mPendingGoToSleepBroadcast=false,
mPendingInteractiveState=true,
mBroadcastInteractiveState=ASLEEP(2);

因此,走else->else語句,調用了 finishPendingBroadcastLocked() ,在這個方法中重置了 mBroadcastInPorgress 和釋放了 Suspend

Notifier 類比較簡單,稍微複雜的就是發送廣播這塊。

PowerManagerService分析(四)之亮屏流程分析
PowerManagerService分析(四)之亮屏流程分析

長按識別二維碼,領福利

至此,本篇已結束,如有不對的地方,歡迎您的建議與指正。同時期待您的關注,感謝您的閱讀,謝謝!

PowerManagerService分析(四)之亮屏流程分析

如有侵權,請聯繫小編,小編對此深感抱歉,屆時小編會刪除文章,立即停止侵權行為,請您多多包涵。

PowerManagerService分析(四)之亮屏流程分析

既然都看到這裡,領兩個紅包在走吧!

以下兩個紅包每天都可以領取

1.支付寶搜索 522398497 ,或掃碼支付寶紅包海報。

PowerManagerService分析(四)之亮屏流程分析

支付寶掃一掃,每天領取大紅包

2.微信紅包,微信掃一掃即可領取紅包

PowerManagerService分析(四)之亮屏流程分析

微信掃一掃,每天領取微信紅包

原文 : 簡書

免责声明:本文内容来源于簡書,已注明原文出处和链接,文章观点不代表立场,如若侵犯到您的权益,或涉不实谣言,敬请向我们提出检举。