之前已经分析过 锁屏的结构 ,今天具体分析一下power键上锁流程。
SystemUI\src\com\android\systemui\keyguard\KeyguardViewMediator.java 等等,也就是流程图顶部一行显示的类。
其中: KeyguardViewMediator是大脑,控制着整个锁屏; KeyguardService 负责Keyguard模块与PWM和PMS的沟通; KeyguardUpdateMonitor 负责将锁屏等的状态变化传递下去; StatusBarKeyguardViewManager 负责View管理; KeyguardBouncer 专门负责Bouncer界面的管理。
这里重点说一下KeyguardViewMediator 和 KeyguardUpdateMonitor的作用: KeyguardViewMediator是整个待机解/锁屏业务的调度器,负责调度锁屏界面的相关动作及查询解锁屏状态, 可以进行的调度操作包括: 1) 响应PowerManagerService对KeyguardService的调度(通过PhoneWindowManager); 2) 实施真正的锁屏doKeyguardLocked()与解锁keyguardDone(boolean); 3) 响应SIM卡状态变化并对锁屏界面做相应的调整onSimStateChanged(); 4) 调度待机锁屏UI界面的管理,包括显示handleShow ()、隐藏handleHide ()、重置handleReset ()、等,提供状态查询接口,比如:isSecure(),isHiding(),KeyguardViewMediator实现这部分调度是通过持有mStatusBarKeyguardViewManager来实现的。 另外,Keyguard相关的api可能会被其他的线程调用,比如InputManagerService和windowManagerService。因此在keyguardViewMediator的方法是同步的,并且任何一个和Keyguard画面相关的事件都投掷到Handler中以确保在UI线程中处理。
KeyguardUpdateMonitor是所有会影响整个待机解/锁屏业务的事件的监控器。(除了作为监控器,它还发挥着类似上下文的作用,也许我们应该把这个类命名为(KeyguardContext)。它监控诸如时间改变、电池状态改变、时区改变、SIM卡状态变化、电话状态变化、电话信号变化等事件。它是一个观察者模式的被观察对象。观察者通过调用KeyguardUpdateMonitor的registerInfoCallback(InfoCallback)和registerSimStateCallback(SimStateCallback)方法进行注册,观察自己感兴趣的变化。KeyguardUpdateMonitor的观察者包括KeyguardViewMediator、LockScreen、PatternUnlockScreen、AccountUnlockScreen、PasswordUnlockScreen、SimUnlockScreen等。观察者通过调用KeyguardUpdateMonitor的removeCallback(Object)取消观察。
锁屏界面跟解锁后的状态栏是共用的布局。尤其是通知栏和QS部分,是完全共用的。都包含在StatusBarWindowView中。 Bouncer的界面比较特殊,是动态创建并添加到StatusBarWindowView中的。 布局结构如下图:
Bouncer根据SecurityMode会呈现不同的界面,一般有5种。Pattern、PIN、Password、SimPIN、SimPUK(都实现了KeyguardSecurityView)。 Bouncer界面结构如下:
为了方便分析,现将锁屏界面分为:非安全锁屏界面(亮屏后看到的界面,有时钟、通知消息等) 、 安全锁屏界面(即Bouncer界面,比如图案锁界面、密码锁界面、SIM卡的PIN码验证界面PUK码验证界面等)。
1)、锁屏流程牵扯到灭屏流程。相关内容可以看这篇文章:灭屏流程分析 锁屏流程其实就是从PMS发起的,重点在goToSleep()之后的Notifier中。
亮屏和锁屏都是这里发起的:
private void handleEarlyInteractiveChange() { synchronized (mLock) { //此时为false if (mInteractive) { // Waking up... mHandler.post(new Runnable() { @Override public void run() { // Note a SCREEN tron event is logged in PowerManagerService. mPolicy.startedWakingUp(); } }); // Send interactive broadcast. mPendingInteractiveState = INTERACTIVE_STATE_AWAKE; mPendingWakeUpBroadcast = true; updatePendingBroadcastLocked(); } else { // doze 和 sleep这两种状态属于 not interactive final int why = translateOffReason(mInteractiveChangeReason);//锁屏的原因 mHandler.post(new Runnable() { @Override public void run() { //通过PhoneWindowManager设置锁屏 mPolicy.startedGoingToSleep(why);//这里的mPolicy就是PhoneWindowManager。 } }); } } }2)、锁屏内部startedGoingToSleep()这一部分主要是下面这个方法:
/** * Called to let us know the screen was turned off. * @param why either {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_USER} or * {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_TIMEOUT}. */ public void onStartedGoingToSleep(int why) { //这里的why就是PMS中gotosleep的Notifier传递过来的why。而power键灭屏这个why是PWM传递给PMS的,是PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON if (DEBUG) Log.d(TAG, "onStartedGoingToSleep(" + why + ")"); synchronized (this) { mDeviceInteractive = false; mGoingToSleep = true; // Lock immediately based on setting if secure (user has a pin/pattern/password). // This also "locks" the device when not secure to provide easy access to the // camera while preventing unwanted input. int currentUser = KeyguardUpdateMonitor.getCurrentUser(); final boolean lockImmediately = mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser) || !mLockPatternUtils.isSecure(currentUser); long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser()); mLockLater = false; if (mExitSecureCallback != null) {//实在没找到哪里赋值了这个变量。那它就是null喽? 估计应该是从verifyUnlock()传递过来的,但本地并没有备份,如果备份了就会走这里了。 if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled"); try { mExitSecureCallback.onKeyguardExitResult(false); } catch (RemoteException e) { Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e); } mExitSecureCallback = null; if (!mExternallyEnabled) { hideLocked(); } } else if (mShowing) {//如果本身就是在锁屏界面,并没有解锁,按power键会走这里。 mPendingReset = true; } else if ((why == WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT && timeout > 0) || (why == WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER && !lockImmediately)) { doKeyguardLaterLocked(timeout); mLockLater = true; } else if (!mLockPatternUtils.isLockScreenDisabled(currentUser)) {//所以,power键锁屏一般会走这里。 mPendingLock = true; } if (mPendingLock) { playSounds(true); } } KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedGoingToSleep(why);//这里会去调用 updateFingerprintListeningState(); notifyStartedGoingToSleep(); }上面代码里最后注释处的updateFingerprintListeningState(); 就是指纹解锁开始的地方。就是因为这里,在锁屏状态下指纹sensor也是活跃的,能解锁屏幕。
3)、进入锁屏主流程:onFinishedGoingToSleep()
这里的notifyFinishedGoingToSleep(); 只有在安全锁屏界面按power键时才会运行,重新显示对应的KeyguardSecurityView并调用其onPause()方法。每种KeyguardSecurityView都有各自的onPause()实现,主要做一些锁屏前的清理和状态保持工作。 当然这也是在锁屏界面按power键,mPendingReset = true,锁屏会运行
if (mPendingReset) { resetStateLocked(); mPendingReset = false; }更新锁屏界面。 如果不是在锁屏界面按power键,就会调用:
if (mPendingLock) { doKeyguardLocked(null); mPendingLock = false; }最终都是调用了StatusBarKeyguardViewManager的reset方法。 二者的区别是,前者因为已经在锁屏界面,故不需要AMS进行activity栈调度、修改锁屏界面laygoutparam等工作。
doKeyguardLocked(Bundle options) 中主要对一些条件进行验证,都验证通过才会真正去show。
看看StatusBarKeyguardViewManager的reset方法:
public void reset(boolean hideBouncerWhenShowing) { if (mShowing) { if (mOccluded && !mDozing) { occlude 是 特殊情况,例如来电,音乐锁屏等显示在锁屏界面上的activity mStatusBar.hideKeyguard(); if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) { hideBouncer(false /* destroyView */); } } else { showBouncerOrKeyguard(hideBouncerWhenShowing); } KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset(); updateStates(); } }一般会调用showBouncerOrKeyguard():
/** * Shows the notification keyguard or the bouncer depending on * {@link KeyguardBouncer#needsFullscreenBouncer()}. */ protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) { if (mBouncer.needsFullscreenBouncer() && !mDozing) {//存在锁定的sim卡会走到这里 // The keyguard might be showing (already). So we need to hide it. mStatusBar.hideKeyguard(); mBouncer.show(true /* resetSecuritySelection */); } else {//一般会走这里 mStatusBar.showKeyguard(); if (hideBouncerWhenShowing) { hideBouncer(shouldDestroyViewOnReset() /* destroyView */);//destroyView表示要从container中remove掉当前的bouncer。 mBouncer.prepare();// 加载bouncer的布局;在灭屏情况下,因为用户可能已经修改了锁屏方式,之前的bouncer可能已经不适用了,所以要重新准备 } } updateStates(); }之后就兵分两路: 通过mStatusBar.showKeyguard()去显示非安全锁屏界面,主要是修改相关View的状态,比如: setBarState(StatusBarState.KEYGUARD); mNotificationPanel.expand(false /* animate */);
通过mBouncer.prepare()去更新安全锁屏界面。 如果之前没有创建过Bouncer界面(比如开机),则会先创建Bouncer界面:
protected void inflateView() { removeView(); mHandler.removeCallbacks(mRemoveViewRunnable); mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null); mKeyguardView = mRoot.findViewById(R.id.keyguard_host_view); mKeyguardView.setLockPatternUtils(mLockPatternUtils); mKeyguardView.setViewMediatorCallback(mCallback); mContainer.addView(mRoot, mContainer.getChildCount());//这里的container来自StatusBar.java的startkeyguard()中的registerStatusbar,其实就是StatusbarWindowview mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset( com.android.systemui.R.dimen.status_bar_height); mRoot.setVisibility(View.INVISIBLE);//当前还不能显示bouncer mRoot.setAccessibilityPaneTitle(mKeyguardView.getAccessibilityTitleForCurrentMode()); final WindowInsets rootInsets = mRoot.getRootWindowInsets(); if (rootInsets != null) { mRoot.dispatchApplyWindowInsets(rootInsets); } }然后会根据具体的SecurityMode去填充对应的KeyguardSecurityView(如果是开机过程,则是在上述的 inflateView() 后接着在 KeyguardHostView的onFinishInflate()中会调用 mSecurityContainer.showPrimarySecurityScreen()来填充securieMode对应的界面):
/** * Switches to the given security view unless it's already being shown, in which case * this is a no-op. * * @param securityMode */ private void showSecurityScreen(SecurityMode securityMode) { if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); if (securityMode == mCurrentSecuritySelection) return; //这里的KeyguardSecurityView应该都是 KeyguardSecurityViewFlipper(KeyguardSecurityViewFlipper实现了KeyguardSecurityView接口) KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); KeyguardSecurityView newView = getSecurityView(securityMode); // 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()); }这样锁屏界面就构建完成了。只是因为当前已经灭屏,看不到,需要再次亮屏才能看到,而上滑后显示的就是安全锁屏界面。 相信,结合顶部的流程图,还是比较好理解的。_
