前幾天遇到一個低機率複現鎖屏界面不顯示,隻顯示狀态欄的問題,跟了下鎖屏界面啟動顯示的流程,在這分享下,也友善以後自己檢視。前面簡單介紹了下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的,就不在一步步跟了
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiQ3chVEa0V3bT9CX5RXa2Fmcn9CXwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwVP9EkT5VFRPJTUU1keBRVT1x2VlZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TM4UzN1IDM2ETMyIDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
通過上面的分析知道,無論是在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來顯示不同鎖屏界面。