天天看點

Android 9.0滅屏流程Android 9.0滅屏流程Power鍵滅屏

Android 9.0滅屏流程

Power鍵滅屏

當power鍵滅屏時,會在PhoneWindowManager中處理按鍵事件後,調用到PMS的gotoSleep()進行滅屏處理,下面直接看看PhoneWindowManger中對Power鍵滅屏的處理以及和PMS的互動。在按power後,PWS中如下:

//frameworks/base/services/core/java/com/android/
//server/policy/PhoneWindowManager.java
case KeyEvent.KEYCODE_POWER: {
    // Any activity on the power button stops the accessibility shortcut
    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鍵擡起事件時,開始了滅屏流程:

private void powerPress(long eventTime, boolean interactive, int count) {
    if (mScreenOnEarly && !mScreenOnFully) {
        Slog.i(TAG, "Suppressed redundant power key press while "
                + "already in the process of turning the screen on.");
        return;
    }
    if (count == 2) {
       ......
    } else if (interactive && !mBeganFromNonInteractive) {
        switch (mShortPressOnPowerBehavior) {
            //滅屏
            case SHORT_PRESS_POWER_GO_TO_SLEEP:
                goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                break;
            //滅屏,直接跳過Doze狀态
            case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                        PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                break;
                } else {
                    shortPressPowerGoHome();
                }
                break;
            }
        }
    }
}

           

在這裡調用了goToSleep()方法,該方法如下:

private void goToSleep(long eventTime, int reason, int flags) {
    mRequestedOrGoingToSleep = true;
    mPowerManager.goToSleep(eventTime, reason, flags);
}

           

最終,PhoneWindowManager中調用了PowerManager的goToSleep()方法來滅屏。我們進入到PowerManager.goToSleep()方法:

///home/wll/source_code/s219_qun/frameworks/base/core/java/
//android/os/PowerManager.java
public void goToSleep(long time, int reason, int flags) {
    try {
        mService.goToSleep(time, reason, flags);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

           

可以看到,在PowerManger中開始向下調用到了PoweManagerService(以下簡稱PMS)中的goToSleep()中。

我們進入PMS中,就需要詳細分析其中的方法了,先來看看goToSleep()方法:

/**
 * @param eventTime 時間
 * @param reason 原因,Power鍵滅屏則是powerManager.GO_TO_SLEEP_REASON_POWER_BUTTON
 * @param flags 目前隻有兩個值:0和1(GO_TO_SLEEP_FLAG_NO_DOZE)
 */
@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {
    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();
    final long ident = Binder.clearCallingIdentity();
    try {
        //調用gotToSleepInternal
        goToSleepInternal(eventTime, reason, flags, uid);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

           

這個方法的參數和PowerManager,PhoneWindowManager中的同名方法對應,需要注意的是第二個參數和第三個參數;

第二個參數:表示滅屏原因,在PowerManager中定義了一些常量值來表示;

第三個參數:是一個辨別,用來表示是否直接進入滅屏,一般的滅屏流程,都會先進入Doze狀态,然後才會進入Sleep狀态,如果将flag設定為1,則将會直接進入Sleep狀态,在goToSleep()方法中,檢查權限之後,開始調用了goToSleepInternal()方法,該方法如下:

private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
    synchronized (mLock) {
        if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
            updatePowerStateLocked();
        }
    }
}
           

這個方法邏輯很簡單,首先是調用了goToSleepNoUpdateLocked()方法,并根據該方法傳回值來決定是否調用updatePowerStateLocked()方法。

一般來說,goToSleepNoUpdateLocked()都會傳回true,現在看看該方法:

///frameworks/base/services/core/java/com/android/
//server/power/PowerManagerService.java
@SuppressWarnings("deprecation")
private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {

    if (eventTime < mLastWakeTime
            || mWakefulness == WAKEFULNESS_ASLEEP
            || mWakefulness == WAKEFULNESS_DOZING
            || !mBootCompleted || !mSystemReady) {
        return false;
    }
    try {
        switch (reason) {
            case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
                Slog.i(TAG, "Going to sleep due to device administration policy "
                        + "(uid " + uid +")...");
                break;
            case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
                Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");
                break;
            case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:
                Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");
                break;
            case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
                Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");
                break;
            case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:
                Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")...");
                break;
            case PowerManager.GO_TO_SLEEP_REASON_HDMI:
                Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");
                break;
            case PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY:
                Slog.i(TAG, "Going to sleep by an accessibility service request (uid "
                        + uid +")...");
                break;
            default:
                Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");
                reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;
                break;
        }
        //标記最後一次滅屏時間
        mLastSleepTime = eventTime;
        //用于判定是否進入屏保
        mSandmanSummoned = true;
        //設定wakefulness值為WAKEFULNESS_DOZING,是以先進Doze狀态
        setWakefulnessLocked(WAKEFULNESS_DOZING, reason);

        // Report the number of wake locks that will be cleared by going to sleep.
        //滅屏時,将清除以下三種使得螢幕保持亮屏的wakelock鎖,numWakeLocksCleared統計下個數
        int numWakeLocksCleared = 0;
        final int numWakeLocks = mWakeLocks.size();
        for (int i = 0; i < numWakeLocks; i++) {
            final WakeLock wakeLock = mWakeLocks.get(i);
            switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
                case PowerManager.FULL_WAKE_LOCK:
                case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                case PowerManager.SCREEN_DIM_WAKE_LOCK:
                    numWakeLocksCleared += 1;
                    break;
            }
        }
        // Skip dozing if requested.
        //如果帶有PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE的flag,則直接進入Sleep狀态,不再進入Doze狀态
        if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
            //該方法才會真正地進入睡眠
            reallyGoToSleepNoUpdateLocked(eventTime, uid);
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }
    return true;
}

           

在這個方法中:

首先,是判斷調用該方法的原因并列印log,然後,通過setWakefulnessLocked()将目前wakefulness設定為Doze狀态;最後,通過flag判斷,如果flag為1,則調用reallyGoToSleepNoUpdateLocked()方法直接進入Sleep狀态。

是以,系統其他子產品在調用PM.goToSleep()滅屏時,在除指定flag為PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE的情況外,都會首先進入Doze,再由Doze進入Sleep。

setWakefulnessLocked()方法用來設定wakefulness值,同時将會調用Notifier中wakefulness相關的邏輯,這裡再來看下:

@VisibleForTesting
void setWakefulnessLocked(int wakefulness, int reason) {
    if (mWakefulness != wakefulness) {
        //設定mWakefulness
        mWakefulness = wakefulness;
        mWakefulnessChanging = true;
        //置位操作
        mDirty |= DIRTY_WAKEFULNESS;
        if (mNotifier != null) {
            //調用Notifier中的方法,做wakefulness改變開始時的工作
            mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
        }
    }
}

           

首先,改變目前mWakefulness值,将mWakefulnessChanging标記為true,将mWakefulness值标志為DIRTY_WAKEFULNESS,然後通過Notifier進行改變wakefulness之前的一些處理.

我們跟着執行流程來進行分析,Notifier是PMS子產品中用于進行“通知”的一個元件類,比如發送亮滅屏廣播就是它來負責,具體詳細的分析後面會分析到。這裡針對于滅屏場景,再來看下其中的邏輯:

public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
    //由于wakefulness為Doze,故interactive為false
    final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
    // ............................................
    // 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();
    }
}

           

在這個方法中,首先根據wakefulness值判斷了系統目前的互動狀态,如果是處于Awake狀态和Dream狀态,則表示可互動;如果處于Doze和Asleep狀态,則表示不可互動;

由于在setWakefulnessLocked()中已經設定了wakefulness為DOZE狀态,是以此時處于不可互動狀态,接下來開始執行handleEarlyInteractiveChange()方法:

private void handleEarlyInteractiveChange() {
    synchronized (mLock) {
        //此時為false
        if (mInteractive) {
            // Waking up...
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    // Note a SCREEN tron event is logged in PowerManagerService.
                    mPolicy.startedWakingUp();
                }
            });
            // Send interactive broadcast.
            mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
            mPendingWakeUpBroadcast = true;
            updatePendingBroadcastLocked();
        } else {
            final int why = translateOffReason(mInteractiveChangeReason);
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    //通過PhoneWindowManager設定鎖屏
                    mPolicy.startedGoingToSleep(why);
                }
            });
        }
    }
}

           

在這個方法中,将調用mPolicy.startedGoingToSleep(why)進行鎖屏流程(Keyguard的繪制)。

回到PMS中,在處理完setWakefulnessLocked()方法後,由于沒有PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE,是以不會立即執行reallyGoToSleepNoUpdateLocked()方法,此時goToSleepNoUpdateLocked()方法完畢并傳回true。

之後開始執行updatePowerStateLocked()方法了,它是整個PMS的核心,後面會詳細的分析 , 在這裡我們隻看其滅屏時的一些處理。

在updatePowerStateLocked()方法中,和滅屏直接相關的有如下部分:

// 更新螢幕狀态
boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
//更新屏保資訊
updateDreamLocked(dirtyPhase2, displayBecameReady);
// 收尾工作
finishWakefulnessChangeIfNeededLocked();
//釋放鎖
updateSuspendBlockerLocked();

           
  • updateDisplayPowerStateLocked()将會向DisplayPowerController請求新的螢幕狀态,完成螢幕的更新;
  • updateDreamLocked()方法用來更新屏保資訊,除此之外還有一個任務——調用reallyGoToSleep()方法進入休眠,即由DOZE狀态進入Sleep狀态。
  • finishWakefulnessChangeIfNeededLocked()方法用來做最後的收尾工作,當然,在這裡會調用到Notifier中進行收尾。
  • updateSuspendBlockerLocked()方法将用來更新SuspendBlocker鎖,會根據目前的WakeLock類型以及螢幕狀态來決定是否需要申請SuspendBlocker鎖。

在updateDreamLocked()中更新屏保狀态時,如果此時處于Doze狀态且沒有進行屏保,則将調用reallyGoToSleepNoUpdateLocked()方法,将wakefulness值設定為了Sleep,部分代碼如下:

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 更新狀态
    			updatePowerStateLocked();
            }

           

再來看看該方法:

private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) {
    if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP
            || !mBootCompleted || !mSystemReady) {
        return false;
    }

    try {
        //設定為ASLEEP狀态
        setWakefulnessLocked(WAKEFULNESS_ASLEEP, 
                  PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }
    return true;
}

           

繼續閱讀