1. 程式人生 > >Android 7.1.1 鎖屏界面啟動流程

Android 7.1.1 鎖屏界面啟動流程

gis eset sed reset ram 啟動流程 com enter on()

前幾天遇到一個低概率復現鎖屏界面不顯示,僅僅顯示狀態欄的問題,跟了下鎖屏界面啟動顯示的流程,在這分享下,也方便以後自己查看。前面簡介了下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();
    }
PhoneWindowManagersystemReady()會依據一個Boolean值bindKeyguardNow來決定是否綁定keyguard service

  //frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java  
  /** [email protected]} */
    @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  
     /** [email protected]} */
    @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的,就不在一步步跟了

技術分享

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

技術分享

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來顯示不同鎖屏界面。

Android 7.1.1 鎖屏界面啟動流程