1. 程式人生 > >keyguard分析之二:息屏與亮屏流程篇

keyguard分析之二:息屏與亮屏流程篇

息屏與亮屏時, Keyguard繪製的基本流程  

手機的Power鍵在滅屏是會載入keyguard, 保證使用者在亮屏時,第一時間看到鎖屏介面,以保證使用者的良好體驗.

在亮屏過程中涉及到了兩條主要的執行緒: PowerManager Thread 和DisplayManager Thread

> PowerManager: 主要是Power的按鍵事件(PhoneWindowManager.java)傳遞到PowerManagerService.java, 然後進行一系列的操作.

> DisplayManager: 會負責進行螢幕狀態的設定以及螢幕的熄滅與點亮(與螢幕有關),此外跟螢幕相關的一些可見性操作. 顯示操作.

根據上面所述, 找到power按鍵的事件, 手機的實體按鍵基本都是在PhoneWindowManaer.java 被處理的.找到入口:

# PhoneWindowManger

    private void powerPress(long eventTime, boolean interactive, int count) {
        ...
        if (interactive && !mBeganFromNonInteractive) {
            switch (mShortPressOnPowerBehavior) {
                ...
                case SHORT_PRESS_POWER_GO_TO_SLEEP:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    launchHomeFromHotKey();
                    break;
                case SHORT_PRESS_POWER_GO_HOME://進入home介面,應該是亮屏操作
                    launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/);
                    break;
            }
        }
    }

息屏操作的入口在:  mPowerManager.goToSleep(), 這是強制手機進入休眠狀態.

# PowerManager

    public void goToSleep(long time, int reason, int flags) {
        try {
            mService.goToSleep(time, reason, flags);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

mService是一個Ipowermanager物件, 其使用的aidl機制進行資料共享, 其AIDL Service為 PowerMangerService.java

# PowerManagerService$BinderService

    public void goToSleep(long eventTime, int reason, int flags) {
        ...      
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.DEVICE_POWER, null);

        final int uid = Binder.getCallingUid();
        final long ident = Binder.clearCallingIdentity();
        try {
            goToSleepInternal(eventTime, reason, flags, uid);
        } finally {
            Binder.restoreCallingIdentity(ident);//恢復遠端呼叫端的uid和pid資訊
        }
    }
    
    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            ...//判斷與sensor有關的息屏
            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                updatePowerStateLocked();
            }
        }
    }
    
    //全域性更新power狀態. 主要告知手機即將息屏.
    private void updatePowerStateLocked() {
        ...
        try {
            ...
            for (;;) {
                ...                         
                if (!updateWakefulnessLocked(dirtyPhase1)) {
                    break;
                }
            }
        ...
        }
    }

    private boolean updateWakefulnessLocked(int dirty) {
        ...//多重判斷    
                if (shouldNapAtBedTimeLocked()) {
                    changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
                } else {
                    changed = goToSleepNoUpdateLocked(time,
                            PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                }
            
        //多重判斷結束
        return changed;
    }

通過分析: goToSleepNoUpdateLocked() 和 napNoUpdateLocked() 最終會呼叫:

# PowerManagerService.java

    private void setWakefulnessLocked(int wakefulness, int reason) {
        if (mWakefulness != wakefulness) {
            mWakefulness = wakefulness;
            mWakefulnessChanging = true;
            mDirty |= DIRTY_WAKEFULNESS;
            mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
        }
    }

只是這兩個方法的區別是: 是否是自動去息屏操作. 

Notifier 主要是用來發送 Power 狀態的廣播. 在這裡邊經過一些列的判斷操作, 直接呼叫了 handleEarlyInteractiveChange() -> mPolicy.startedGoingToSleep(why), 此時PhoneWindowManager開始事件分發:

# PhoneWindowManager.java
    
    public void startedGoingToSleep(int why) {
        ...
        mGoingToSleep = true;
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onStartedGoingToSleep(why);
        }
    }

這裡就不看KeyguardServiceDelegate.java 與 KeyguardService.java, 只要瞭解他們之間的aidl的跨程序服務. Keyguard.java 是 服務端. KeyguardServiceWrapper.java 是客戶端.

息屏操作, 此時進入到了SystemUI, 通知SystemUI的手機進入息屏狀態.

# KeyguardViewMediator.java

    public void onStartedGoingToSleep(int why) {
        ...
        synchronized (this) {
            ...
            if (mShowing) {
                mPendingReset = true;
            } else if ((why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT && timeout > 0)
                    || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately/*keyguard的狀態,非wipe狀態*/)
                    && !mIsIPOShutDown/*非關機*/) {
                doKeyguardLaterLocked(timeout);
                mLockLater = true;
            } 
            ...
        }
        KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedGoingToSleep(why);//稍後分析
        notifyStartedGoingToSleep(); //通知息屏結束,鎖屏了.
    }
    
    
    private void doKeyguardLaterLocked(long timeout) {
        long when = SystemClock.elapsedRealtime() + timeout;
        Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
        intent.putExtra("seq", mDelayedShowingSequence);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        //傳送一個廣播,呼叫鎖屏函式.
        PendingIntent sender = PendingIntent.getBroadcast(mContext,
                0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
        doKeyguardLaterForChildProfilesLocked();
    }
    
    //DELAYED_KEYGUARD_ACTION 作為廣播資訊
    private final BroadcastReceiver mBroadcastReceiver = new roadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (DELAYED_KEYGUARD_ACTION.equals(action)) {
                final int sequence = intent.getIntExtra("seq", 0);
                ...
                synchronized (KeyguardViewMediator.this) {
                    if (mDelayedShowingSequence == sequence) {
                        mSuppressNextLockSound = true;
                        doKeyguardLocked(null); //鎖屏路徑
                    }
                }
            }
            ...
        }
    };

總這裡看出: 其實在息屏操作中, KeyguardViewMediator最終通過廣播呼叫了  doKeyguardLocked(null), 這個函式應該有印象, 在開機的時候就被呼叫過. 不過呼叫它的函式為 onSystemReady(). 這樣,我們就不用再通過這條路徑往下走了, 其結果應該差不多. 也就證明了keyguard 是在息屏時, 被建立的.

> 在開機狀態中, Keyguar是在息屏時被建立.

> 息屏下建立Keyguard, 與是否是主動息屏這個行為無關.   

<br/>

螢幕的亮與滅

在點選 Power 按鍵進行亮屏時:

# PhoneWindowManager

void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
        if (respectKeyguard) {
            if (isKeyguardShowingAndNotOccluded()) {
                // don't launch home if keyguard showing
                return;
            }
            ...    
        }
        ...
    }

這裡僅僅是判斷是有 keyguard 這個介面. 如果有, 將停留在這個 keyguard 的介面,; 如果沒有, 將進行後續其他的處理.

螢幕的亮起與熄滅,主要在 DisplayPowerController 中驅動. 之前已經說了 Power 的 的控制主要在 Powermanager. 

# PowerMnagerService

    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            if (mProximityPositive && reason == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
                ...
                updatePowerStateLocked();
                return;
            }

            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                updatePowerStateLocked();
            }
        }
    }

    private void updatePowerStateLocked() {
        ...
        try {
            ...
            // Phase 2: Update display power state.
            boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
            ...
        }
    }

    private boolean updateDisplayPowerStateLocked(int dirty) {
        final boolean oldDisplayReady = mDisplayReady;
        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 | DIRTY_BOOT_IPO)) != 0) {
            ...
            mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                    mRequestWaitForNegativeProximity);
            ...
        }
        return mDisplayReady && !oldDisplayReady;
    }

上述可以看出, 在息屏的時候, PowerManager服務通過一定的操作, 呼叫了 DisplayPowerController, 來進行顯示操作. DisplayManagerInternal 和DisplayManagerService$LocalService作為中間類來進行處理.

首先從requestPowerState()方法開始分析. 

# DisplayPowerController.java

 public boolean requestPowerState(DisplayPowerRequest request,
            boolean waitForNegativeProximity) {
        synchronized (mLock) {
            boolean changed = false;
            ...
            if (changed && !mPendingRequestChangedLocked) {
                mPendingRequestChangedLocked = true;
                sendUpdatePowerStateLocked(); //螢幕發生變化
            }
            ...
            return mDisplayReadyLocked;
        }
    }
    
    
    private void sendUpdatePowerStateLocked() {
        if (!mPendingUpdatePowerStateLocked) {
            ...
            Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);//傳送到Handler子執行緒去執行.
            msg.setAsynchronous(true);
            mHandler.sendMessage(msg);
        }
    }


    private void updatePowerState() {//一系列的更新操作.
        ...
        //驅動螢幕狀態變化.
        animateScreenStateChange(state, performScreenOffTransition);
        state = mPowerState.getScreenState();
        }
    }
    
    
    private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
        ...
        //息屏操作, 指向了setScreenState();
        if (mPendingScreenOff && target != Display.STATE_OFF) {
            setScreenState(Display.STATE_OFF);
            mPendingScreenOff = false;
            ...
        }
        
        //亮屏操作, 指向了setScreenState()
        if (target == Display.STATE_ON) {
            if (!setScreenState(Display.STATE_ON)) {
                return; // screen on blocked
            }
        }
        ...
    }
    //從上述可以看出,亮屏操作與息屏操作都是需要走setScreenState()方法的.
    
    private boolean setScreenState(int state) {
        ...
        //告訴窗體管理WindowManagerPolicy的螢幕狀態.
        final boolean isOff = (state == Display.STATE_OFF);
        if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
                && !mScreenOffBecauseOfProximity) {
            unblockScreenOn();
            mWindowManagerPolicy.screenTurnedOff();
        } else if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) {
            if (mPowerState.getColorFadeLevel() == 0.0f) {//控制顯示狀態
                blockScreenOn(); //阻止螢幕亮
            } else {
                unblockScreenOn();//解除阻止螢幕狀態.
            }
            mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);
        }

        // Return true if the screen isn't blocked.
        return mPendingScreenOnUnblocker == null;
    }
    

可以看出, 息屏與亮屏過程都會觸發WindowManagerPolicy進行一系列事件的操作. 在 PhoneWindowManagerPolicy.java  中, 亮屏與息屏這個訊息都會觸發 keyguard 代理進行處理.

# PhoneWindowManagerPolicy
    //息屏時
    public void screenTurnedOff() {
        ...
        synchronized (mLock) {
            ...
            if (mKeyguardDelegate != null) {
                mKeyguardDelegate.onScreenTurnedOff();
            }
        }
    }

    //亮屏時
    public void screenTurningOn(final ScreenOnListener screenOnListener) {
        ...
        synchronized (mLock) {
            ...
            if (mKeyguardDelegate != null) {
                mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
                mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);
                mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
            } else {
               ...
                finishKeyguardDrawn();
            }
        }
    }

接著對 KeyguardServiceDelegate 的物件進行判斷.如果為空, 即表示未進行keyguard的設定;如果為非空,即表明:keyguard設定了某一 model 形式.

# KeyguardServiceDelegate.java
    //息屏
    public void onScreenTurnedOff() {
        if (mKeyguardService != null) {
            mKeyguardService.onScreenTurnedOff();
        }
        mKeyguardState.screenState = SCREEN_STATE_OFF;
    }

    //亮屏
    public void onScreenTurningOn(final DrawnListener drawnListener) {
        if (mKeyguardService != null) {
            //當手機在息狀態時,必然是開啟了keyguard的服務.如果設定了keyguard.
            mKeyguardService.onScreenTurningOn(new KeyguardShowDelegate(drawnListener));
        } else {
            mDrawnListenerWhenConnect = drawnListener;
            showScrim();
        }
        mKeyguardState.screenState = SCREEN_STATE_TURNING_ON;
    }
    
    public void screenTurnedOn() {
        synchronized (mLock) {
            if (mKeyguardDelegate != null) {
                mKeyguardDelegate.onScreenTurnedOn();
            }
        }
    }

接著進入到SystemUI,開啟Keyguard服務,KeyguardService.java.我們可以發現KeyguardServiceWrapper這個類物件是啟動KeyguardService的一箇中間類,代理分派任務到Systemui.

# KeyguardService.java

    private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
        //息屏
        public void onScreenTurnedOff() {
            checkPermission();
            mKeyguardViewMediator.onScreenTurnedOff();
        }
        
        //亮屏
        ...
        @Override // Binder interface
        public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
            checkPermission();
            mKeyguardViewMediator.onScreenTurningOn(callback);
        }
        ...
    }

Keyguard服務開啟後,也是先檢查許可權,然後再將事件分派出去.

#KeyguardViewMediator.java
    
    //息屏
    public void onScreenTurnedOff() {
        notifyScreenTurnedOff();
        mUpdateMonitor.dispatchScreenTurnedOff();
    }
    
    //> 息屏做了兩件事:
    //1. notifyScreenTurnedOff()將事件交由StatusBarKeyguardViewManager處理
    //2. dispatchScreenTurnedOff()將事件交於KeyguardUpdateMonitor處理.
    
    
    //亮屏
    public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
        notifyScreenOn(callback);
    }

先看看息屏狀態時, 流程走向

notifyScreenTurnedOff(): 通知StatusBarKeyguardViewManager去進行處理

# StatusBarKeyguardViewManager.java
    
    public void onScreenTurnedOff() {
        mScreenTurnedOn = false;
        mPhoneStatusBar.onScreenTurnedOff();
    }
    
# PhoneStatusBar.java

    public void onScreenTurnedOff() {
        mFalsingManager.onScreenOff();
    }

# FalsingManager.jva
 
   public void onScreenOff() {
        mDataCollector.onScreenOff(); 
        mScreenOn = false;
        sessionExitpoint(false /* force */); //感測器接觸註冊, 停止感測器的使用
    }
    
# DataCollector.java

    public void onScreenOff() {
        addEvent(PhoneEvent.ON_SCREEN_OFF); //感測器log日誌手機
        sessionExitpoint(Session.FAILURE);  //touch事件收集器
    }

 
dispatchScreenTurnedOff() 通知 KeyguardUpdateMonitor進行處理

# KeyguardUpdateMonitor.java

    private void handleScreenTurnedOff() {
        final int count = mCallbacks.size();
        for (int i = 0; i < count; i++) {
            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onScreenTurnedOff();
            }
        }
    }

看看cb的實現體
# KeyguardHostView.java

    public void onScreenTurnedOff() {
        mSecurityContainer.onScreenTurnedOff(); //空實現
    }

對於亮屏的這個過程, 其餘息屏的方式是一樣的.都是需要經過

# KeyguardViewMediator.java

    private void handleNotifyScreenTurningOn(IKeyguardDrawnCallback callback) {
        synchronized (KeyguardViewMediator.this) {
            //更新notification的時間和感測器註冊, 以及事件的收集.
            mStatusBarKeyguardViewManager.onScreenTurningOn();
            if (callback != null) {
                if (mWakeAndUnlocking) {//判斷鎖的形式.
                    mDrawnCallback = callback;
                } else {
                    //回撥一個介面物件進行回撥繪製.
                    notifyDrawn(callback);
                }
            }
        }
    }

可以看出, "息屏"與"正在亮屏"最後都會呼叫介面物件實現其息屏與亮屏的操作.

,那麼也存在"完成開機"這個實現, 再次回到原點.直接看 StatusBarKeyguardViewManager.java

# StatusBarKeyguardViewManager.java

    public void onScreenTurnedOn() {
        mScreenTurnedOn = true;
        if (mDeferScrimFadeOut) {
            mDeferScrimFadeOut = false;
            animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
                    true /* skipFirstFrame */);
            updateStates();
        }
        if (mPhoneStatusBar != null) {
            mPhoneStatusBar.onScreenTurnedOn();//後續操作, 也是一層一層的通知
        }
    }

---

> 在亮屏與滅屏階段, keyguard的繪製其實在滅屏階段.

> 螢幕的顯示亮屏與滅屏, 與keyguard的並無直接關係.