Android 7.1.1 鎖屏界面啟動流程介紹,前幾天遇到一個低概率復現鎖屏界面不顯示,隻顯示狀態欄的問題,跟瞭下鎖屏界面啟動顯示的流程,在這分享下,也方便以後自己查看。前面簡單介紹瞭下Zygote啟動流程,Zygote進程啟動後會首先創建一個SystemServer進程,SystemServer進程在調用startOtherServices同時也會調用WindowManagerService的systemReady()方法
[java]view plaincopy
//frameworks/base/services/java/com/android/server/SystemServer.java
privatevoidstartOtherServices(){
…
wm=WindowManagerService.main(context,inputManager,
mFactoryTestMode!=FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot,mOnlyCore);
…
try{
wm.systemReady();
Slog.i("jason11","SystemServerwm.systemReady");
}catch(Throwablee){
reportWtf("makingWindowManagerServiceready",e);
}
…
}
在WindowManagerService中直接調用瞭PhoneWindowManager裡的systemReady()
[java]view plaincopy
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
//finalWindowManagerPolicymPolicy=newPhoneWindowManager();
publicvoidsystemReady(){
mPolicy.systemReady();
} 在PhoneWindowManager的systemReady()會根據一個Boolean值bindKeyguardNow來決定是否綁定keyguard service
[java]view plaincopy
//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
/**{@inheritDoc}*/
@Override
publicvoidsystemReady(){
mKeyguardDelegate=newKeyguardServiceDelegate(mContext);
mKeyguardDelegate.onSystemReady();
readCameraLensCoverState();
updateUiMode();
booleanbindKeyguardNow;
synchronized(mLock){
updateOrientationListenerLp();
mSystemReady=true;
mHandler.post(newRunnable(){
@Override
publicvoidrun(){
updateSettings();
}
});
bindKeyguardNow=mDeferBindKeyguard;
if(bindKeyguardNow){
//systemBootedranbutwasn'tabletobindtotheKeyguard,we'lldoitnow.
mDeferBindKeyguard=false;
}
}
if(bindKeyguardNow){
mKeyguardDelegate.bindService(mContext);
mKeyguardDelegate.onBootCompleted();
}
mSystemGestures.systemReady();
}
看到這裡,可能會想到如果bindKeyguardNow為false就會不綁定,後面通過繼續跟蹤發現在PhoneWindowManager的systemBooted()裡也會去綁定keyguard service,如果在systemBooted裡綁定瞭就不在systemReady裡再去綁定,自己測試的時候是在systemBooted綁定的
[java]view plaincopy
//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
/**{@inheritDoc}*/
@Override
publicvoidsystemBooted(){
booleanbindKeyguardNow=false;
synchronized(mLock){
//TimetobindKeyguard;takecaretoonlybinditonce,eitherhereifreadyor
//insystemReadyifnot.
if(mKeyguardDelegate!=null){
bindKeyguardNow=true;
}else{
//BecausemKeyguardDelegateisnull,weknowthatthesynchronizedblockin
//systemReadydidn'trunyetandsettingthiswillactuallyhaveaneffect.
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的
[java]view plaincopy
//frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
publicclassKeyguardServiceDelegate{
…
publicvoidbindService(Contextcontext){
Intentintent=newIntent();
finalResourcesresources=context.getApplicationContext().getResources();
finalComponentNamekeyguardComponent=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'tbindto"+keyguardComponent);
mKeyguardState.showing=false;
mKeyguardState.showingAndNotOccluded=false;
mKeyguardState.secure=false;
synchronized(mKeyguardState){
//TODO:Fixsynchronisationmodelinthisclass.Theotherstateinthisclass
//isatleastself-healingbutaraceconditionherecanleadtothescrimbeing
//stuckonkeyguard-lessdevices.
mKeyguardState.deviceHasKeyguard=false;
hideScrim();
}
}else{
if(DEBUG)Log.v(TAG,"***Keyguardstarted");
}
}
…
} 在bindService中調用瞭bindServiceAsUser綁定指定intent的service,config_keyguardComponent的定義如下
[html]view plaincopy
//frameworks/base/core/res/res/values/config.xml
com.android.systemui/com.android.systemui.keyguard.KeyguardService 當綁定成功後會調用mKeyguardConnection裡的onServiceConnected方法
[java]view plaincopy
//frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
publicclassKeyguardServiceDelegate{
…
privatefinalServiceConnectionmKeyguardConnection=newServiceConnection(){
@Override
publicvoidonServiceConnected(ComponentNamename,IBinderservice){
if(DEBUG)Log.v(TAG,"***Keyguardconnected(yay!)");
mKeyguardService=newKeyguardServiceWrapper(mContext,
IKeyguardService.Stub.asInterface(service));
if(mKeyguardState.systemIsReady){
//Ifthesystemisready,itmeanskeyguardcrashedandrestarted.
mKeyguardService.onSystemReady();
//Thisisusedtohidethescrimoncekeyguarddisplays.
if(mKeyguardState.interactiveState==INTERACTIVE_STATE_AWAKE){
mKeyguardService.onStartedWakingUp();
}
if(mKeyguardState.screenState==SCREEN_STATE_ON
||mKeyguardState.screenState==SCREEN_STATE_TURNING_ON){
mKeyguardService.onScreenTurningOn(
newKeyguardShowDelegate(mDrawnListenerWhenConnect));
}
if(mKeyguardState.screenState==SCREEN_STATE_ON){
mKeyguardService.onScreenTurnedOn();
}
mDrawnListenerWhenConnect=null;
}
if(mKeyguardState.bootCompleted){
mKeyguardService.onBootCompleted();
}
if(mKeyguardState.occluded){
mKeyguardService.setOccluded(mKeyguardState.occluded);
}
}
@Override
publicvoidonServiceDisconnected(ComponentNamename){
if(DEBUG)Log.v(TAG,"***Keyguarddisconnected(boo!)");
mKeyguardService=null;
}
};
…
} 當mKeyguardState.systemIsReady為true是,就會通過KeyguardServiceWrapper的實例mKeyguardService調用onSystemReady方法,在KeyguardServiceWrapper的onSystemReady裡調用瞭上面剛剛綁定成功的KeyguardService的onSystemReady方法
[java]view plaincopy
//frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
publicclassKeyguardServiceWrapperimplementsIKeyguardService{
…
@Override//Binderinterface
publicvoidonSystemReady(){
try{
mService.onSystemReady();
}catch(RemoteExceptione){
Slog.w(TAG,"RemoteException",e);
}
}
…
}
在KeyguardService的onSystemReady裡調用瞭KeyguardViewMediator裡的onSystemReady,在這裡就不貼這個代碼瞭,直接看看KeyguardViewMediator.onSystemReady這個裡面幹啥瞭
2、KeyguardViewMediator.onSystemReady
[java]view plaincopy
//frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
publicclassKeyguardViewMediatorextendsSystemUI{
…
publicvoidonSystemReady(){
mSearchManager=(SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE);
synchronized(this){
if(DEBUG)Log.d(TAG,"onSystemReady");
mSystemReady=true;
doKeyguardLocked(null);
mUpdateMonitor.registerCallback(mUpdateCallback);
}
//Mostservicesaren'tavailableuntilthesystemreachesthereadystate,sowe
//senditherewhenthedevicefirstboots.
maybeSendUserPresentBroadcast();
}
…
} 在這個方法裡主要調用瞭doKeyguardLocked和註冊瞭KeyguardUpdateMonitorCallback
3、通過調用doKeyguardLocked顯示鎖屏界面
[java]view plaincopy
//frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
publicclassKeyguardViewMediatorextendsSystemUI{
…
privatevoiddoKeyguardLocked(Bundleoptions){
//ifanotherappisdisablingus,don'tshow
if(!mExternallyEnabled){
if(DEBUG)Log.d(TAG,"doKeyguard:notshowingbecauseexternallydisabled");
//note:we*should*setmNeedToReshowWhenReenabled=truehere,butthatmakes
//foranoccasionaluglyflickerinthissituation:
//1)receiveacallwiththescreenon(nokeyguard)ormakeacall
//2)screentimesout
//3)userhitskeytoturnscreenbackon
//instead,wereenablethekeyguardwhenweknowthescreenisoffandthecall
//ends(seethebroadcastreceiverbelow)
//TODO:cleanthisupwhenwehavebettersupportatthewindowmanagerlevel
//forappsthatwishtobeontopofthekeyguard
return;
}
//ifthekeyguardisalreadyshowing,don'tbother
if(mStatusBarKeyguardViewManager.isShowing()){
if(DEBUG)Log.d(TAG,"doKeyguard:notshowingbecauseitisalreadyshowing");
resetStateLocked();
return;
}
//ifthesetupwizardhasn'trunyet,don'tshow
finalbooleanrequireSim=!SystemProperties.getBoolean("keyguard.no_require_sim",false);
finalbooleanabsent=SubscriptionManager.isValidSubscriptionId(
mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));
finalbooleandisabled=SubscriptionManager.isValidSubscriptionId(
mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
finalbooleanlockedOrMissing=mUpdateMonitor.isSimPinSecure()
||((absent||disabled)&&requireSim);
if(!lockedOrMissing&&shouldWaitForProvisioning()){
if(DEBUG)Log.d(TAG,"doKeyguard:notshowingbecausedeviceisn'tprovisioned"
+"andthesimisnotlockedormissing");
return;
}
if(mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
&&!lockedOrMissing){
if(DEBUG)Log.d(TAG,"doKeyguard:notshowingbecauselockscreenisoff");
return;
}
if(mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())){
if(DEBUG)Log.d(TAG,"Notshowinglockscreensincejustdecrypted");
//Withoutthis,settingsisnotenableduntilthelockscreenfirstappears
setShowingLocked(false);
hideLocked();
mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
return;
}
if(DEBUG)Log.d(TAG,"doKeyguard:showingthelockscreen");
showLocked(options);
}
…
} 這段代碼主要是在是否要顯示鎖屏之前做瞭5個判斷:1.如果啟用第三方鎖屏界面,不顯示原生界面;2.鎖屏界面已經顯示瞭話,重新更新下狀態;3.如果第一次開機引導界面setup wizard還沒有運行,也先不顯示;4.屏幕沒有亮不顯示;5.當前正在解密界面不顯示。如果這幾個條件都不滿足,則調用showLocked顯示鎖屏界面。在showLocked通過mHandler發送Message,在handleMessage裡“case SHOW:”時調用handleShow
4、在handleShow裡設置一些鎖屏狀態和顯示鎖屏界面
[java]view plaincopy
//frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
publicclassKeyguardViewMediatorextendsSystemUI{
…
privatevoidhandleShow(Bundleoptions){
synchronized(KeyguardViewMediator.this){
if(!mSystemReady){
if(DEBUG)Log.d(TAG,"ignoringhandleShowbecausesystemisnotready.");
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
[java]view plaincopy
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
publicclassStatusBarKeyguardViewManager{
…
publicvoidshow(Bundleoptions){
mShowing=true;
mStatusBarWindowManager.setKeyguardShowing(true);
mScrimController.abortKeyguardFadingOut();
reset();
}
…
} 在reset裡調用本類的showBouncerOrKeyguard,在這個方法裡通過KeyguardBouncer的實例mBouncer調用prepare(),在prepare裡調用瞭KeyguardHostView的showPrimarySecurityScreen
6、KeyguardSecurityContainer.showPrimarySecurityScreen
在KeyguardHostView的showPrimarySecurityScreen裡調用KeyguardSecurityContainer的showPrimarySecurityScreen方法,如下
[java]view plaincopy
//frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
publicclassKeyguardSecurityContainerextendsFrameLayoutimplementsKeyguardSecurityView{
…
voidshowPrimarySecurityScreen(booleanturningOff){
SecurityModesecurityMode=mSecurityModel.getSecurityMode();
if(DEBUG)Log.v(TAG,"showPrimarySecurityScreen(turningOff="+turningOff+")");
showSecurityScreen(securityMode);
}
…
} 在這個方法裡調用瞭showSecurityScreen,根據mSecurityModel.getSecurityMode()獲取的SecurityMode來顯示不同界面,SecurityMode定義如下
[java]view plaincopy
//frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
publicclassKeyguardSecurityModel{
publicenumSecurityMode{
Invalid,//NULLstate
None,//Nosecurityenabled
Pattern,//Unlockbydrawingapattern.
Password,//Unlockbyenteringanalphanumericpassword
PIN,//Strictlynumericpassword
SimPin,//Unlockbyenteringasimpin.
SimPuk//Unlockbyenteringasimpuk
}
…
}
showSecurityScreen方法如下:
[java]view plaincopy
//frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
publicclassKeyguardSecurityContainerextendsFrameLayoutimplementsKeyguardSecurityView{
…
privatevoidshowSecurityScreen(SecurityModesecurityMode){
if(DEBUG)Log.d(TAG,"showSecurityScreen("+securityMode+")");
if(securityMode==mCurrentSecuritySelection)return;
KeyguardSecurityViewoldView=getSecurityView(mCurrentSecuritySelection);
KeyguardSecurityViewnewView=getSecurityView(securityMode);//根據securityMode獲取對應的view
//EmulateActivitylifecycle
if(oldView!=null){
oldView.onPause();
oldView.setKeyguardCallback(mNullCallback);//ignorerequestsfromoldview
}
if(securityMode!=SecurityMode.None){
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
newView.setKeyguardCallback(mCallback);
}
//Findandshowthischild.
finalintchildCount=mSecurityViewFlipper.getChildCount();
finalintsecurityViewIdForMode=getSecurityViewIdForMode(securityMode);
for(inti=0;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來顯示不同鎖屏界面。