我们知道Android系统启动在SystemServer中会启动PhoneWindowManager,在服务启动完成之后会调用它的systemReady方法,我们今天就从这里开始分析系统开机后锁屏界面是如何展示出来的.
以下是系统systemReady相关代码
/** {@inheritDoc} */
@Override
public void systemReady() {
//KeyguardServiceDelegate初始化
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();
mImmersiveModeConfirmation.systemReady();
}
KeyguardServiceDelegate是锁屏相关的类,它是一个代理类,是上层应用和KeyguardService通信的桥梁。
public KeyguardServiceDelegate(Context context) {
mContext = context;
//初始化Handler
mScrimHandler = UiThread.getHandler();
//创建锁屏透明背景视图并且显示
mScrim = createScrim(context, mScrimHandler);
}
通过构造方法我们知道了,里面主要做了两件事情:
1,mScrimHandler的初始化;
2,创建锁屏界面背景:
以下是KeyguardServiceDelegate的onSystemReady方法
//第一次启动mKeyguardService为null
public void onSystemReady() {
if (mKeyguardService != null) {
mKeyguardService.onSystemReady();
} else {
mKeyguardState.systemIsReady = true;
}
}
当第一次初始化的时候mKeyguardService是为null的,它只是进行了状态值得更新;
我们在PhoneWindowManager的systemReady方法中看到了它会执行如下一段方法
//手机开机完成如果启动了锁屏,默认一开机完成就会自动启动系统锁屏界面
if (bindKeyguardNow) {
mKeyguardDelegate.bindService(mContext);
mKeyguardDelegate.onBootCompleted();
}
接着我们进入KeyguardServiceDelegate的bindService方法,
定义如下
//启动KeyguardService,如果启动失败就将锁屏背景界面移除掉
public void bindService(Context context) {
Intent intent = new Intent();
final Resources resources = context.getApplicationContext().getResources();
final ComponentName keyguardComponent = ComponentName.unflattenFromString(
//KeyguardService
resources.getString(com.android.internal.R.string.config_keyguardComponent));
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
intent.setComponent(keyguardComponent);
if (!context.bindServiceAsUser(intent, mKeyguardConnection,
Context.BIND_AUTO_CREATE, mScrimHandler, UserHandle.SYSTEM)) {
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");
}
}
通过代码我们知道它主要做了两件事情:
1,启动KeyguardService;
2,如果KeyguardService没有启动成功,就重置前面对锁屏的准备工作,比如移除系统锁屏背景界面;
接着我们看下它的ServiceConnection
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();
if (mKeyguardState.currentUser != UserHandle.USER_NULL) {
// There has been a user switch earlier
mKeyguardService.setCurrentUser(mKeyguardState.currentUser);
}
// 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;
}
};
通过上面我们知道在服务绑定成功后,会间接着调用KeyguardViewMediator的onSystemReady方法,定义如下
/**
* Let us know that the system is ready after startup.
*/
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);
}
mIsPerUserLock = StorageManager.isFileEncryptedNativeOrEmulated();
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
maybeSendUserPresentBroadcast();
}
我们进入doKeyguardLocked这个方法
/**
* Enable the keyguard if the settings are appropriate.
*/
private void doKeyguardLocked(Bundle options) {
// if another app is disabling us, don't show
//如果第三方APP禁用了锁屏,就不显示系统的锁屏
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;
}
// In split system user mode, we never unlock system user.
//如果当前系统没有多个系统用户或者不是处于测试状态或者当前的用户不是系统用户
或者设备不是处于预备状态
if (!mustNotUnlockCurrentUser()
|| !mUpdateMonitor.isDeviceProvisioned()) {
// if the setup wizard hasn't run yet, don't show
//锁屏是否需要SIM卡
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);
}
我们看到这面最终会调用showLocked方法,开始显示系统锁屏界面
/**
* Handle message sent by {@link #showLocked}.
* @see #SHOW
*/
//开始显示系统锁屏界面
private void handleShow(Bundle options) {
Trace.beginSection("KeyguardViewMediator#handleShow");
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
}
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();
Trace.endSection();
}
我们进入mKeyguardDisplayManager.show();这个方法
//显示锁屏界面
public void show() {
if (!mShowing) {
if (DEBUG) Slog.v(TAG, "show");
mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
updateDisplays(true);
}
mShowing = true;
}
接着调用updateDisplays
//处理锁屏界面的显示与隐藏
protected void updateDisplays(boolean showing) {
if (showing) {
MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
boolean useDisplay = route != null
&& route.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
Display presentationDisplay = useDisplay ? route.getPresentationDisplay() : null;
if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
if (DEBUG) Slog.v(TAG, "Display gone: " + mPresentation.getDisplay());
mPresentation.dismiss();
mPresentation = null;
}
if (mPresentation == null && presentationDisplay != null) {
if (DEBUG) Slog.i(TAG, "Keyguard enabled on display: " + presentationDisplay);
mPresentation = new KeyguardPresentation(mContext, presentationDisplay,
R.style.keyguard_presentation_theme);
mPresentation.setOnDismissListener(mOnDismissListener);
try {
mPresentation.show();
} catch (WindowManager.InvalidDisplayException ex) {
Slog.w(TAG, "Invalid display:", ex);
mPresentation = null;
}
}
} else {
if (mPresentation != null) {
mPresentation.dismiss();
mPresentation = null;
}
}
}
我们看到系统进过一系列的判断会调用mPresentation.show();这个.
KeyguardPresentation类是Dialog的子类,它就是用于显示系统锁屏界面的.到此系统锁屏开启启动流程分析结束了。
如下是KeyguardPresentation的定义
private final static class KeyguardPresentation extends Presentation {
private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
private View mClock;
private int mUsableWidth;
private int mUsableHeight;
private int mMarginTop;
private int mMarginLeft;
Runnable mMoveTextRunnable = new Runnable() {
@Override
public void run() {
int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
mClock.setTranslationX(x);
mClock.setTranslationY(y);
mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
}
};
public KeyguardPresentation(Context context, Display display, int theme) {
super(context, display, theme);
getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
@Override
public void onDetachedFromWindow() {
mClock.removeCallbacks(mMoveTextRunnable);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Point p = new Point();
getDisplay().getSize(p);
mUsableWidth = VIDEO_SAFE_REGION * p.x/100;
mUsableHeight = VIDEO_SAFE_REGION * p.y/100;
mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
//keyguard_presentation这个就是系统锁屏界面xml布局
setContentView(R.layout.keyguard_presentation);
mClock = findViewById(R.id.clock);
// Avoid screen burn in
mClock.post(mMoveTextRunnable);
}
}
}