1. 程式人生 > >Keyguard分析之一:開機流程篇

Keyguard分析之一:開機流程篇

Keyguard 與 SystemUI執行在同一程序中,即com.android.systemui程序。這點可以從manifest.xml檔案就可以看出:

# Keyguard Android.mk
    
    <application android:label="@string/app_name"
        android:process="com.android.systemui"
        android:persistent="true"
        android:supportsRtl="true"
        android:defaultToDeviceProtectedStorage="true"
        android:directBootAware="true">
        
        
# SystemUI Android.mk

<application
        android:name=".SystemUIApplication"
        android:persistent="true"
        android:allowClearUserData="false"
        android:allowBackup="false"
        android:hardwareAccelerated="true"
        android:label="@string/app_label"
        android:icon="@drawable/icon"
        android:process="com.android.systemui"
        android:supportsRtl="true"
        android:theme="@style/systemui_theme"
        android:defaultToDeviceProtectedStorage="true"
        android:directBootAware="true">

通過Android.mk可知:在編譯過程中,keyguard被打包成靜態Java庫. Keygaurd實際是由SystemUI來管理的.

# Keyguard Android.mk

include $(BUILD_STATIC_JAVA_LIBRARY)

# SystemUI:

LOCAL_STATIC_JAVA_LIBRARIES := \
    com.mediatek.systemui.ext \
    Keyguard \

開機時, keyguard檢視的繪製流程  

開機,  Keyguard檢視的形成過程:伴隨著系統的啟動,SystemServer將啟動各種服務.對於Keyguard的服務而言.首先從startOtherServices()方法開始.

# SystemServer.java  

    private void startOtherServices() {
         ...
       wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
        ...
        try {
            wm.systemReady();
        } catch (Throwable e) {
            reportWtf("making Window Manager Service ready", e);
        }
}

因為開機需要對Keyguard的檢視進行繪製, 所以進入到了WindowManagerService服務中(對系統中的所有視窗進行管理是視窗管理服務WindowManagerService的職責).在WindowManagerService.java中直接呼叫了WindowManagerPolicy.java的systemReady()方法,而具體方法的實現在PhoneWindowManager.java中。

# PhoneWindowManager.java

public void systemReady() {
        mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
        mKeyguardDelegate.onSystemReady(); //分派到KeyguardDelegate進行處理,開機時不做任何處理.
        ...
        synchronized (mLock) {
            ...
            bindKeyguardNow = mDeferBindKeyguard;
            if (bindKeyguardNow) {
                // systemBooted ran but wasn't able to bind to the Keyguard, we'll do it now.
                mDeferBindKeyguard = false;
            }
        }

        if (bindKeyguardNow) {//如果已經繫結KeyguardService,進行處理.
            mKeyguardDelegate.bindService(mContext);
            mKeyguardDelegate.onBootCompleted();
        }
        ...
    }

在進入KeyguardServiceDelegate.java, 將繫結keyguardService服務.

private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
                ...
                mKeyguardService.onSystemReady();
                ...
        }
     ...
    };

可以發現 mKeyguardDelegate.onSystemReady()進入到了KeyguardServiceWrapper,並將任務分派到KeyguardService,啟動Keyguard服務.

# KeyguardServiceWrapper.java

public class KeyguardServiceWrapper implements IKeyguardService {}



# KeyguardService.java

   private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {   
        public void onSystemReady() {
            checkPermission(); //許可權的檢查.
            mKeyguardViewMediator.onSystemReady();
        }
    }

可以看出,其實這兩個類就是跨程序通訊.通過IKeyguardService這個介面來進行framework與Systemui之間的跨程序通訊.此時,流程就進入到了SystemUI.

進入SystemUI這個應用後, 將進入Keyguard的一個重要的類KeyguardViewMediator.先看看呼叫的方法.該方法主要是進行廣播:標誌這系統在啟動後已經準備好了.接著,doKeyguardLocked()方法收到了null引數. 可以看到這個方法主要是針對不同的keyguard模式進行判別.

# KeyguardViewMediator.java
    //Let us know that the system is ready after startup.
    public void onSystemReady() {
        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
        synchronized (this) {
            ...
            mSystemReady = true;
            doKeyguardLocked(null); //鎖屏路徑
            mUpdateMonitor.registerCallback(mUpdateCallback);
            mPowerOffAlarmManager.onSystemReady();
        }
        mIsPerUserLock = StorageManager.isFileEncryptedNativeOrEmulated();
        // Most services aren't available until the system reaches the ready state, so we
        // send it here when the device first boots.
        maybeSendUserPresentBroadcast();
    }
    
    
    private void doKeyguardLocked(Bundle options) {
        // if another app is disabling us, don't show
        if (!mExternallyEnabled) {//外接apk的keyguard等
            return;
        }

        // if the keyguard is already showing, don't bother
        if (mStatusBarKeyguardViewManager.isShowing()) {
            resetStateLocked();
            return;
        }
        ...

        //doKeyguard: not showing because device isn't provisioned
        //and the sim is not locked or missing
        if (!lockedOrMissing && shouldWaitForProvisioning() && !antiTheftLocked) {
            return;
        }
        //doKeyguard: not showing because lockscreen is off
        if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
            && !lockedOrMissing && !antiTheftLocked) {
            return;
        }
        //解密後的狀態也不顯示keyguard.
        if (mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())
            && KeyguardUtils.isSystemEncrypted()) {
           
            setShowingLocked(false);
            hideLocked();
            mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
              return;
        }
        ...
        showLocked(options);
    }
    
     private void showLocked(Bundle options) {
        setReadyToShow(true) ;設定引數.
        updateActivityLockScreenState();//更新鎖屏狀態,使用程序間通訊技術
        // ensure we stay awake until we are finished displaying the keyguard
        mShowKeyguardWakeLock.acquire(); //獲取喚醒鎖.不受電源影響,不讓cpu進入休眠狀態.
        Message msg = mHandler.obtainMessage(SHOW, options);
        mHandler.sendMessage(msg); //使用handler子執行緒來處理顯示
    }
    
    private void handleShow(Bundle options) {
        final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
        if (mLockPatternUtils.isSecure(currentUser)) {
            mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
        }
        synchronized (KeyguardViewMediator.this) {
            setShowingLocked(true);
            mStatusBarKeyguardViewManager.show(options);//keyguard的顯示.
            mHiding = false;
            mWakeAndUnlocking = false;
            resetKeyguardDonePendingLocked();
            ///M: reset mReadyToShow
            setReadyToShow(false) ;
            mHideAnimationRun = false;
            updateActivityLockScreenState();
            adjustStatusBarLocked();
            userActivity();
            mShowKeyguardWakeLock.release();
        }

        if (mKeyguardDisplayManager != null) {
            mKeyguardDisplayManager.show();
        } 
    }

最後跳轉到Keyguard中去顯示.通過開機時的log,亦發現開機顯示keyguard的一瞬間,打印出了"show". 

# KeyguardDisplayManager.java

    public void show() {
        if (!mShowing) {
            ...
            mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
                    mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
            updateDisplays(true);
        }
        mShowing = true;
    }

現在顯示keyguard的流程就結束了.當然這個流程顯示的是 ++滑動進入鎖屏介面前的那個介面++.

<br/>

再來看看mStatusBarKeyguardViewManager.show(options).

# StatusBarKeyguardViewManager.java

    public void show(Bundle options) {
        mShowing = true;
        mStatusBarWindowManager.setKeyguardShowing(true);
        mScrimController.abortKeyguardFadingOut();
        reset();
    }

reset()這個方法判斷顯示狀態,再進行處理

# StatusBarKeyguardViewManager.java

    public void reset() {
        ...
        if (mShowing) {
            if (mOccluded) {//是否被其他視窗中斷
                mPhoneStatusBar.hideKeyguard();
                mPhoneStatusBar.stopWaitingForKeyguardExit();
                mBouncer.hide(false /* destroyView */);
            } else {
                showBouncerOrKeyguard();//滑動鎖或Keyguard鎖.
            }
            KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset();
            updateStates();
        }
    }
    // showBouncerOrKeyguard()方法使用KeyguardBouncer.java的needsFullscreenBo
    // uncer()方法判斷顯示常規鎖屏還是Bouncer安全鎖屏(比如圖案鎖屏、密碼鎖屏
    // PIN碼鎖屏等);

    protected void showBouncerOrKeyguard() {//判斷鎖使用的鎖的型別.
        ...
        if (mBouncer.needsFullscreenBouncer()) {
            // The keyguard might be showing (already). So we need to hide it.
            mPhoneStatusBar.hideKeyguard();
            mBouncer.show(true /* resetSecuritySelection */);
        } else {
            mPhoneStatusBar.showKeyguard();
            mBouncer.hide(false /* destroyView */);
            mBouncer.prepare();
        }
    }

看看mBouncer是哪幾種keyguard.這類被稱為: Bouncer安全鎖屏, 包括: PIM碼 , PUK碼等鎖屏方式的鎖屏介面,這類所直接在systemui中處理了.通過KeyguardBouncer.java來開始控制show()和hide();

# KeyguardBouncer.java

    public boolean needsFullscreenBouncer() {
        ...
        return mode == SecurityMode.SimPinPukMe1
                || mode == SecurityMode.SimPinPukMe2
                || mode == SecurityMode.SimPinPukMe3
                || mode == SecurityMode.SimPinPukMe4
                || mode == SecurityMode.AntiTheft
                || mode == SecurityMode.AlarmBoot;
    }
    

# KeyguardSecuritymodel.java

    public enum SecurityMode {
        Invalid, // NULL state
        None, // No security enabled
        Pattern, // Unlock by drawing a pattern.
        Password, // Unlock by entering an alphanumeric password
        PIN, // Strictly numeric password
        // SimPin, // Unlock by entering a sim pin.
        // SimPuk, // Unlock by entering a sim puk
        SimPinPukMe1, // Unlock by entering a sim pin/puk/me for sim or gemini sim1.
        SimPinPukMe2, // Unlock by entering a sim pin/puk/me for sim or gemini sim2.
        SimPinPukMe3, // Unlock by entering a sim pin/puk/me for sim or gemini sim3.
        SimPinPukMe4, // Unlock by entering a sim pin/puk/me for sim or gemini sim4.
        AlarmBoot, // add for power-off alarm.
        Biometric,  // Unlock with a biometric key (e.g. voice unlock)
        Voice, // Unlock with voice password
        AntiTheft // Antitheft feature
    }

則其他為一般解鎖, 其將進入到Keyguard中去進行處理. 稱為:notification keyguard, 包括 品pattern, password,PIN,None(滑動解鎖).這中解鎖方式有個特點:在進入解鎖介面前,會出現我們之前分析的介面, ++滑動進入鎖屏介面前的那個介面++. 這種keyguard的檢視的父檢視為KeyguardSecurityContainer.

<br/>

接著,還是進入KeyguardBouncer進行keyguard檢視的繪製.

# KeyguardBouncer.java

    public void prepare() {
        ...
        if (wasInitialized) {
            mKeyguardView.showPrimarySecurityScreen();
        }
        ...
    }

之後直接跳到了KeyguardHostView.java,並傳入了false引數, 進入到KeyguardSecurityContainer.java. 

# KeyguardSecurityContainer.java

    void showPrimarySecurityScreen(boolean turningOff) {
        ...
        showSecurityScreen(securityMode);
    }

最後根據不同的keyguard的模式來進行介面的重繪

# KeyguardSecurityContainer.java

private void showSecurityScreen(SecurityMode securityMode) {
        ...
        mCurrentSecuritySelection) ;
        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
        Log.d(TAG, "showSecurityScreen() - get newview for" + securityMode) ;
        KeyguardSecurityView newView = getSecurityView(securityMode);
        ...
    }

通過該方法通過傳入的mode來繪製出相應的keyguard檢視.

# KeyguardSecurityContainer.java 

    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
        ...
        int layoutId = getLayoutIdFor(securityMode);//通過mode選擇對應的keyguard的view檢視.
        if (view == null && layoutId != 0) {
            final LayoutInflater inflater = LayoutInflater.from(mContext);
            
            View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
            view = (KeyguardSecurityView) v;
            ...
            mSecurityViewFlipper.addView(v);
            updateSecurityView(v);
            // view = (KeyguardSecurityView)v;
        }
        ...
        return view;
    }

在完成一系列的繪製檢視工作後.

<br/>

1. 從SystemServer開始, 通過WindowManagerPolicy進入到KeyguardServiceDelegate. 2. KeyguardServiceDelegate繫結KeyService服務,此時進入到SystemUI.並呼叫KeyguardViewMediator決定是否顯示檢視. 3. 若顯示檢視,將StatusBarKeyguardViewManager來重置,並進入到KeyguardBouncer. 4. 最後進入到KeyguardSecurityContainer,根據不同的模式繪製不同的keyguard檢視.

---

> KeyguardServiceDelegate.java 繼承了介面IKeyguardService; 同時KeyguardService完成了IKeyguardService的遠端實現. 即這兩個類通過底層aidl進行遠端通訊. 進入系統可以控制Keyguard服務的啟動.

> KeyguardServiceDelegate.java 是一個區域性類,儲存keyguard在崩潰之前的狀態,用來恢復. 同時它也可以執行時選擇本地或遠端的keyguard例項.

> KeyguardViewMediator.java 作為一箇中介類, 連線Keyguard與Systemui.其職能包括查詢keyguard的狀態, Keyguard在開關鍵的事件的動作,如顯示或重置等.

> KeyguardSecurityContainer.java 這個類主要用於裝載Pattern, password, pin等解鎖檢視的容器.

> KeyguardUpdateMonitor.java 類似於一個監視器, 用於監聽keyguard的更新變化. 

> StatusBarKeyguardViewManger.java 在status bar中, 管理建立, 顯示, 隱藏, 重置 狀態列中的 Keyguard.

---

<br/>

現在根據上述流程, 列印log, 來驗證流程的正確性.

``` 01-03 21:49:03.050   698   698 D @ying   : SystemServer.startOtherservices.wm.systemReady begin 01-03 21:49:03.050   698   698 D @ying   : PhoneWindowManager.systemReady begin 01-03 21:49:03.051   698   698 D @ying   : PhoneWindowManager.systemReady end 01-03 21:49:03.054   698   698 D @ying   : SystemServer.startOtherservices.wm.systemReady end 01-03 21:49:06.938   698   725 D @ying   : KeyguardServiceDelegate.mKeyguardConnection begin 01-03 21:49:06.942   698   725 D @ying   : KeyguardServiceDelegate.mKeyguardConnection:mKeyguardState.systemIsReady begin 01-03 21:49:06.942   698   725 D @ying   : KeyguardServiceWrapper.onSystemReady begin 01-03 21:49:06.942   698   725 D @ying   : KeyguardServiceWrapper.onSystemReady end 01-03 21:49:06.942   698   725 D @ying   : KeyguardServiceDelegate.mKeyguardConnection:mKeyguardState.systemIsReady begin 01-03 21:49:06.943   698   725 D @ying   : KeyguardServiceDelegate.mKeyguardConnection begin 01-03 21:49:06.945   935  1204 D @ying   : KeyguardService.onSystemReady begin 01-03 21:49:06.945   935  1204 D @ying   : KeyguardViewMediator.onSystemReady begin 01-03 21:49:06.958   935  1204 D @ying   : KeyguardViewMediator.doKeyguardLocked begin 01-03 21:49:06.958   935  1204 D @ying   : KeyguardViewMediator.showLocked begin 01-03 21:49:06.965   935  1204 D @ying   : KeyguardViewMediator.showLocked end 01-03 21:49:06.966   935  1204 D @ying   : KeyguardViewMediator.doKeyguardLocked end 01-03 21:49:06.966   935  1204 D @ying   : KeyguardViewMediator.onSystemReady begin 01-03 21:49:06.976   935  1204 D @ying   : KeyguardService.onSystemReady end 01-03 21:49:07.104   935   935 D @ying   : KeyguardViewMeditor.handleShow begin 01-03 21:49:07.104   935   935 D @ying   : StatusBarKeyguardViewManager.show begin 01-03 21:49:07.105   935   935 D @ying   : StatusBarKeyguardViewManager.reset begin 01-03 21:49:07.105   935   935 D @ying   : StatusBarKeyguardViewManager.reset:mOccluded-false begin 01-03 21:49:07.105   935   935 D @ying   : StatusBarKeyguardViewManager.showBouncerOrKeyguard begin 01-03 21:49:07.118   935   935 D @ying   : KeyguardSecurityContainer.showPrimarySecurityScreen begin 01-03 21:49:07.120   935   935 D @ying   : KeyguardSecurityContainer.showPrimarySecurityScreen:showSecurityScreen 01-03 21:49:07.121   935   935 D @ying   : KeyguardSecurityContainer.showSecurityScreen begin 01-03 21:49:07.122   935   935 D @ying   : KeyguardSecurityContainer.showSecurityScreen newView begin 01-03 21:49:07.202   935   935 D @ying   : KeyguardSecurityContainer.showSecurityScreen newView end 01-03 21:49:07.225   935   935 D @ying   : KeyguardSecurityContainer.showSecurityScreen end 01-03 21:49:07.225   935   935 D @ying   : KeyguardSecurityContainer.showPrimarySecurityScreen end 01-03 21:49:07.336   935   935 D @ying   : needsFullscreenBouncer() is false,show "Notification Keyguard" view. 01-03 21:49:07.397   935   935 D @ying   : KeyguardBouncer.prepare begin 01-03 21:49:07.397   935   935 D @ying   : KeyguardBouncer.prepare:wasInitialized 01-03 21:49:07.398   935   935 D @ying   : KeyguardSecurityContainer.showPrimarySecurityScreen begin 01-03 21:49:07.401   935   935 D @ying   : KeyguardSecurityContainer.showPrimarySecurityScreen:showSecurityScreen 01-03 21:49:07.401   935   935 D @ying   : KeyguardSecurityContainer.showSecurityScreen begin 01-03 21:49:07.401   935   935 D @ying   : KeyguardSecurityContainer.showPrimarySecurityScreen end 01-03 21:49:07.422   935   935 D @ying   : KeyguardBouncer.prepare end 01-03 21:49:07.422   935   935 D @ying   : StatusBarKeyguardViewManager.showBouncerOrKeyguard end 01-03 21:49:07.422   935   935 D @ying   : StatusBarKeyguardViewManager.reset:mOccluded-false end 01-03 21:49:07.442   935   935 D @ying   : StatusBarKeyguardViewManager.reset end 01-03 21:49:07.443   935   935 D @ying   : StatusBarKeyguardViewManager.show end 01-03 21:49:07.443   935   935 D @ying   : KeyguardViewMeditor.handleShow end 01-03 21:49:07.451   935   935 D @ying   : KeyguardViewMediator.handleShow:mKeyguardDisplayManager begin 01-03 21:49:07.451   935   935 D @ying   : KeyguardViewMediator.handleShow:mKeyguardDisplayManager end

```

從 Log 日誌再來梳理一遍開機時, Keyguard的流程走向.

>1. 開機, 完成開機動畫後, SystemServer這個系統服務執行緒由底層開啟, 該系統服務將開啟一系列的服務, 其中Keyguard的流程會流到WindowManagerPolicy進行處理.

>2. WindowManagerPolicy的實現類是PhoneWindowManager. 其將 keyguard 事件分發到 KeyguardServiceDelegate進行處理.

>3. KeyguardServiceDelegate 開啟了Keyguard服務, IkeyguardService由KeyguardServiceWrapper包裝類繼承,由KeyguardService遠端實現. 實現底層與Systemui的通訊,保證兩者之間資料共享.

>4. keyguard事件傳遞到了SystemUI, 並由KeyguardViewMediator進行事件的分派. 如部分檢視由SystemUI顯示,即如StatusBarKeyguardViewManager來處理.  部分檢視將傳遞到Keyguard,由Keyguard來繪製, 如 KeyguardSecurityContainer來處理.