天天看點

Android 7.1.1 鎖屏界面啟動流程

      前幾天遇到一個低機率複現鎖屏界面不顯示,隻顯示狀态欄的問題,跟了下鎖屏界面啟動顯示的流程,在這分享下,也友善以後自己檢視。前面簡單介紹了下Zygote啟動流程, Zygote程序啟動後會首先建立一個SystemServer程序,SystemServer程序在調用startOtherServices同時也會調用WindowManagerService的systemReady()方法

//frameworks/base/services/java/com/android/server/SystemServer.java  
  private void startOtherServices() {
       ...
       wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
        ...
        try {
            wm.systemReady();
            Slog.i("jason11", "SystemServer wm.systemReady");
        } catch (Throwable e) {
            reportWtf("making Window Manager Service ready", e);
        }
        ...
    }
           

 在WindowManagerService中直接調用了PhoneWindowManager裡的systemReady()

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
  //final WindowManagerPolicy mPolicy = new PhoneWindowManager();  
    public void systemReady() {
        mPolicy.systemReady();
    }
           

在 PhoneWindowManager的systemReady()會根據一個Boolean值bindKeyguardNow來決定是否綁定keyguard service

//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java  
  /** {@inheritDoc} */
    @Override
    public void systemReady() {
        mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
        mKeyguardDelegate.onSystemReady();

        readCameraLensCoverState();
        updateUiMode();
        boolean bindKeyguardNow;
        synchronized (mLock) {
            updateOrientationListenerLp();
            mSystemReady = true;
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    updateSettings();
                }
            });

            bindKeyguardNow = mDeferBindKeyguard;
            if (bindKeyguardNow) {
                // systemBooted ran but wasn't able to bind to the Keyguard, we'll do it now.
                mDeferBindKeyguard = false;
            }
        }
        if (bindKeyguardNow) {
            mKeyguardDelegate.bindService(mContext);
            mKeyguardDelegate.onBootCompleted();
        }
        mSystemGestures.systemReady();
    }
           

看到這裡,可能會想到如果bindKeyguardNow為false就會不綁定,後面通過繼續跟蹤發現在PhoneWindowManager的systemBooted()裡也會去綁定keyguard service,如果在systemBooted裡綁定了就不在systemReady裡再去綁定,自己測試的時候是在systemBooted綁定的

//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java  
     /** {@inheritDoc} */
    @Override
    public void systemBooted() {
        boolean bindKeyguardNow = false;
        synchronized (mLock) {
            // Time to bind Keyguard; take care to only bind it once, either here if ready or
            // in systemReady if not.
            if (mKeyguardDelegate != null) {
                bindKeyguardNow = true;
            } else {
                // Because mKeyguardDelegate is null, we know that the synchronized block in
                // systemReady didn't run yet and setting this will actually have an effect.
                mDeferBindKeyguard = true;
            }
        }
        if (bindKeyguardNow) {
            mKeyguardDelegate.bindService(mContext);
            mKeyguardDelegate.onBootCompleted();
        }
        synchronized (mLock) {
            mSystemBooted = true;
        }
        startedWakingUp();
        screenTurningOn(null);
        screenTurnedOn();
    }
           

下面就通過如下的時序圖看看是如何調用到systemBooted的,就不在一步步跟了

Android 7.1.1 鎖屏界面啟動流程

通過上面的分析知道,無論是在systemReady或systemBooted,都調用了KeyguardServiceDelegate對象的bindService方法,下面就以這個方法開始,看看鎖屏界面是怎麼顯示出來的,先看看下面的時序圖,再來分步講解

Android 7.1.1 鎖屏界面啟動流程

1、先來看看在KeyguardServiceDelegate如何綁定KeyguardService的

//frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
public class KeyguardServiceDelegate {
    ...
    public void bindService(Context context) {
        Intent intent = new Intent();
        final Resources resources = context.getApplicationContext().getResources();

        final ComponentName keyguardComponent = ComponentName.unflattenFromString(
                resources.getString(com.android.internal.R.string.config_keyguardComponent));
        intent.setComponent(keyguardComponent);

        if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
            Log.v(TAG, "*** Keyguard: can't bind to " + keyguardComponent);
            mKeyguardState.showing = false;
            mKeyguardState.showingAndNotOccluded = false;
            mKeyguardState.secure = false;
            synchronized (mKeyguardState) {
                // TODO: Fix synchronisation model in this class. The other state in this class
                // is at least self-healing but a race condition here can lead to the scrim being
                // stuck on keyguard-less devices.
                mKeyguardState.deviceHasKeyguard = false;
                hideScrim();
            }
        } else {
            if (DEBUG) Log.v(TAG, "*** Keyguard started");
        }
    }
    ...
}
           

在bindService中調用了bindServiceAsUser綁定指定intent的service,config_keyguardComponent的定義如下

//frameworks/base/core/res/res/values/config.xml
    <!-- Keyguard component -->
    <string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
           

當綁定成功後會調用mKeyguardConnection裡的onServiceConnected方法

//frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
public class KeyguardServiceDelegate {
    ...
      private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");
            mKeyguardService = new KeyguardServiceWrapper(mContext,
                    IKeyguardService.Stub.asInterface(service));
            if (mKeyguardState.systemIsReady) {
                // If the system is ready, it means keyguard crashed and restarted.
                mKeyguardService.onSystemReady();
                // This is used to hide the scrim once keyguard displays.
                if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
                    mKeyguardService.onStartedWakingUp();
                }
                if (mKeyguardState.screenState == SCREEN_STATE_ON
                        || mKeyguardState.screenState == SCREEN_STATE_TURNING_ON) {
                    mKeyguardService.onScreenTurningOn(
                            new KeyguardShowDelegate(mDrawnListenerWhenConnect));
                }
                if (mKeyguardState.screenState == SCREEN_STATE_ON) {
                    mKeyguardService.onScreenTurnedOn();
                }
                mDrawnListenerWhenConnect = null;
            }
            if (mKeyguardState.bootCompleted) {
                mKeyguardService.onBootCompleted();
            }
            if (mKeyguardState.occluded) {
                mKeyguardService.setOccluded(mKeyguardState.occluded);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
            mKeyguardService = null;
        }

    };
    ...
}
           

當mKeyguardState.systemIsReady為true是,就會通過KeyguardServiceWrapper的執行個體mKeyguardService調用onSystemReady方法,在 KeyguardServiceWrapper的onSystemReady裡調用了上面剛剛綁定成功的KeyguardService的onSystemReady方法

//frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
public class KeyguardServiceWrapper implements IKeyguardService {
    ...
    @Override // Binder interface
    public void onSystemReady() {
        try {
            mService.onSystemReady();
        } catch (RemoteException e) {
            Slog.w(TAG , "Remote Exception", e);
        }
    }
    ...
}
           

在KeyguardService的onSystemReady裡調用了KeyguardViewMediator裡的onSystemReady,在這裡就不貼這個代碼了,直接看看KeyguardViewMediator.onSystemReady這個裡面幹啥了

2、KeyguardViewMediator.onSystemReady

//frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
public class KeyguardViewMediator extends SystemUI {
    ...
    public void onSystemReady() {
        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
        synchronized (this) {
            if (DEBUG) Log.d(TAG, "onSystemReady");
            mSystemReady = true;
            doKeyguardLocked(null);
            mUpdateMonitor.registerCallback(mUpdateCallback);
        }
        // Most services aren't available until the system reaches the ready state, so we
        // send it here when the device first boots.
        maybeSendUserPresentBroadcast();
    }
    ...
}
           

在這個方法裡主要調用了doKeyguardLocked和注冊了KeyguardUpdateMonitorCallback

3、通過調用doKeyguardLocked顯示鎖屏界面

//frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
public class KeyguardViewMediator extends SystemUI {
    ...
    private void doKeyguardLocked(Bundle options) {
        // if another app is disabling us, don't show
        if (!mExternallyEnabled) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");

            // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
            // for an occasional ugly flicker in this situation:
            // 1) receive a call with the screen on (no keyguard) or make a call
            // 2) screen times out
            // 3) user hits key to turn screen back on
            // instead, we reenable the keyguard when we know the screen is off and the call
            // ends (see the broadcast receiver below)
            // TODO: clean this up when we have better support at the window manager level
            // for apps that wish to be on top of the keyguard
            return;
        }

        // if the keyguard is already showing, don't bother
        if (mStatusBarKeyguardViewManager.isShowing()) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
            resetStateLocked();
            return;
        }

        // if the setup wizard hasn't run yet, don't show
        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
        final boolean absent = SubscriptionManager.isValidSubscriptionId(
                mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));
        final boolean disabled = SubscriptionManager.isValidSubscriptionId(
                mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
        final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()
                || ((absent || disabled) && requireSim);

        if (!lockedOrMissing && shouldWaitForProvisioning()) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
                    + " and the sim is not locked or missing");
            return;
        }

        if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
                && !lockedOrMissing) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
            return;
        }

        if (mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())) {
            if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
            // Without this, settings is not enabled until the lock screen first appears
            setShowingLocked(false);
            hideLocked();
            mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
            return;
        }

        if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
        showLocked(options);
    }
    ...
}
           

這段代碼主要是在是否要顯示鎖屏之前做了5個判斷:1.如果啟用第三方鎖屏界面,不顯示原生界面;2.鎖屏界面已經顯示了話,重新更新下狀态;3.如果第一次開機引導界面setup wizard 還沒有運作,也先不顯示;4.螢幕沒有亮不顯示;5.目前正在解密界面不顯示。如果這幾個條件都不滿足,則調用showLocked顯示鎖屏界面。在 showLocked通過mHandler發送Message,在handleMessage裡“case SHOW:”時調用handleShow

4、在handleShow裡設定一些鎖屏狀态和顯示鎖屏界面

//frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
public class KeyguardViewMediator extends SystemUI {
    ...
        private void handleShow(Bundle options) {
        synchronized (KeyguardViewMediator.this) {
            if (!mSystemReady) {
                if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready.");
                return;
            } else {
                if (DEBUG) Log.d(TAG, "handleShow");
            }

            setShowingLocked(true);
            mStatusBarKeyguardViewManager.show(options);
            mHiding = false;
            mWakeAndUnlocking = false;
            resetKeyguardDonePendingLocked();
            mHideAnimationRun = false;
            updateActivityLockScreenState();
            adjustStatusBarLocked();
            userActivity();

            mShowKeyguardWakeLock.release();
        }
        mKeyguardDisplayManager.show();
    }
    ...
}
           

5、通過調用StatusBarKeyguardViewManager的show重置目前狀态顯示keyguard

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
public class StatusBarKeyguardViewManager {
    ...
    public void show(Bundle options) {
        mShowing = true;
        mStatusBarWindowManager.setKeyguardShowing(true);
        mScrimController.abortKeyguardFadingOut();
        reset();
    }
    ...
}
           

在reset裡調用本類的showBouncerOrKeyguard,在這個方法裡通過KeyguardBouncer的執行個體mBouncer調用prepare(),在 prepare裡調用了KeyguardHostView的showPrimarySecurityScreen

6、KeyguardSecurityContainer.showPrimarySecurityScreen

在KeyguardHostView的showPrimarySecurityScreen裡調用KeyguardSecurityContainer的showPrimarySecurityScreen方法,如下

//frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
    ...
    void showPrimarySecurityScreen(boolean turningOff) {
        SecurityMode securityMode = mSecurityModel.getSecurityMode();
        if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
        showSecurityScreen(securityMode);
    }
    ...
}
           

在這個方法裡調用了showSecurityScreen,根據mSecurityModel.getSecurityMode()擷取的SecurityMode來顯示不同界面,SecurityMode定義如下

//frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
public class KeyguardSecurityModel {
    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
    }
    ...
}
           

showSecurityScreen方法如下:

//frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
    ...
    private void showSecurityScreen(SecurityMode securityMode) {
        if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");

        if (securityMode == mCurrentSecuritySelection) return;

        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
        KeyguardSecurityView newView = getSecurityView(securityMode);//根據securityMode擷取對應的view

        // Emulate Activity life cycle
        if (oldView != null) {
            oldView.onPause();
            oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
        }
        if (securityMode != SecurityMode.None) {
            newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
            newView.setKeyguardCallback(mCallback);
        }

        // Find and show this child.
        final int childCount = mSecurityViewFlipper.getChildCount();

        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
        for (int i = 0; i < childCount; i++) {
            if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
                mSecurityViewFlipper.setDisplayedChild(i);
                break;
            }
        }

        mCurrentSecuritySelection = securityMode;
        mSecurityCallback.onSecurityModeChanged(securityMode,
                securityMode != SecurityMode.None && newView.needsInput());
    }
    ...
}
           

到這裡鎖屏就啟動完成了,這裡簡單總結一下:

   1. 在KeyguardServiceDelegate裡綁定KeyguardService,并調用onSystemReady方法。

   2. KeyguardViewMediator裡調用doKeyguardLocked來決定是否需要顯示鎖屏界面;如果顯示則調用StatusBarKeyguardViewManager的show,最後調用到KeyguardHostView的showPrimarySecurityScreen()。

   3. 在KeyguardSecurityContainer的showPrimarySecurityScreen利用mSecurityModel.getSecurityMode()擷取目前的securityMode,傳入showSecurityScreen來顯示不同鎖屏界面。

繼續閱讀