search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

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; }在

PhoneWindowManagersystemReady會根據一個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的,就不在一步步跟了

通過上面的分析知道,無論是在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.xmlcom.android.systemui/com.android.systemui.keyguard.KeyguardService當綁定成功後會調用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來顯示不同鎖屏界面。



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦