Keyguard解锁屏模块的框架类图如下:
图中仅列出了Keyguard解锁屏模块涉及的重要类,这些类实现了Keyguard模块的主要功能,它们的作用如下:
PhoneWindowManager是解锁屏模块对外交互的接口,窗口管理Service、电源管理Service等外部模块都是通过PhoneWindowManager访问Keyguard内部功能。
KeyguardViewMediator类为解锁屏模块的中介者,以中介的身份处理keyguard状态变化,处理event、power管理、PhoneWindowManager通知等请求,并作为回调对象供解锁屏模块的其它类回调。
KeyguardUpdateMonitor类为解锁屏模块的监听者,它负责监听时间、sim卡、运营商信息、电池信息、电话信息等状态的变化,并通知keyguard View模块更新显示。
KeyguardViewManager类为解锁屏view模块的管理者,管理解锁屏界面的创建、显示、隐藏以及重置等。
LockPatternKeyguardView类为解锁屏模块的View界面,为所有解锁屏界面的host view。根据设置的安全策略,显示不同的解锁屏界面。Google原生代码中实现了6种解锁屏界面:
1) LockScreen:用于显示屏幕加锁状态
2) PatternUnlockScreen:实现图案解锁模式
3) SimPukUnlockScreen:屏幕实现SIM PUK码解锁模式
4) SimUnlockScreen:实现Sim PIN码解锁模式
5) AccountUnlockScreen:实现 GOOGLE 帐户解锁
6) PasswordUnlockScreen:实现自定义密码解锁模式
二、主要逻辑
1、Keyguard模块启动、显示逻辑
即手机开机进入系统到锁屏界面显示的过程。手机系统启动过程中会自动启动Keyguard解锁屏模块,该模块的创建始于WindowManagerService类,时序图如下:
1)WindowManagerService在启动时会实例化PhoneWindowManager对象mPolicy,并在窗口管理Policy线程PolicyThread中初始化,代码如下:
public void run() {
Looper.prepare();
WindowManagerPolicyThread.set(this, Looper.myLooper());
......
mPolicy.init(mContext, mService, mService, mPM);
......
Looper.loop();
}
从代码中可以看到PhoneWindowManager在独立的线程和Looper消息队列中处理Message事件,该Looper对象也为解锁屏模块使用以处理所有handler消息。
public KeyguardViewMediator(Context context, PhoneWindowManager callback,
LocalPowerManager powerManager) {
……
mUpdateMonitor = new KeyguardUpdateMonitor(context);
mUpdateMonitor.registerInfoCallback(this);
mUpdateMonitor.registerSimStateCallback(this);
mLockPatternUtils = new LockPatternUtils(mContext);
mKeyguardViewProperties
= new LockPatternKeyguardViewProperties(mLockPatternUtils, mUpdateMonitor);
mKeyguardViewManager = new KeyguardViewManager(
context, WindowManagerImpl.getDefault(), this,
mKeyguardViewProperties, mUpdateMonitor);
……
}
KeyguardViewMediator中记录了PhoneWindowManager、PowerManager等对象,同时也保存了LockPatternKeyguardViewProperties、KeyguardUpdateMonitor、KeyguardViewManager等模块内的重要对象,这样该类以中介者身份在Keyguard模块对外交互以及内部各对象间的交互中发挥了重要作用。
public void systemReady() {
mPolicy.systemReady();
}
6)PhoneWindowManager的systemReady函数中通知解锁屏模块的中介者KeyguardViewMediator对象系统就绪
7)中介者KeyguardViewMediator类中处理系统就绪情形:调用doKeyguardLocked函数显示解锁屏界面:
public void onSystemReady() {
synchronized (this) {
mSystemReady = true;
doKeyguardLocked();
}
}
private void doKeyguardLocked() {
......
// if the keyguard is already showing, don't bother
if (mKeyguardViewManager.isShowing()) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
return;
}
.....
if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
showLocked();
}
showLocked函数中发送SHOW消息异步处理解锁屏界面显示的请求。
public synchronized void show() {
......
if (mKeyguardHost == null) {
if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
mKeyguardHost = new KeyguardViewHost(mContext, mCallback);
......
mViewManager.addView(mKeyguardHost, lp);
}
......
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
if (mKeyguardView == null) {
mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this);
......
mKeyguardHost.addView(mKeyguardView, lp);
......
}
......
}
该函数中主要创建了Keyguard显示View中的两个重要的对象:mKeyguardHost和mKeyguardView,它们都是继承于FrameLayout,为解锁屏视图的根view。protected void updateScreen(Mode mode, boolean force) {
......
// Re-create the lock screen if necessary
if (mode == Mode.LockScreen || mShowLockBeforeUnlock) {
if (force || mLockScreen == null) {
recreateLockScreen();
}
}
// Re-create the unlock screen if necessary. This is primarily required to properly handle
// SIM state changes. This typically happens when this method is called by reset()
if (mode == Mode.UnlockScreen) {
final UnlockMode unlockMode = getUnlockMode();
if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) {
recreateUnlockScreen(unlockMode);
}
}
......
}
10)在函数createLockScreen或者createUnlockScreenFor中创建具体的Lock或者Unlock View界面,并调用show函数进行显示2、两次按下Power按钮屏幕亮->暗->亮过程中锁屏模块处理逻辑
连续两次按下Power按钮屏幕亮->暗->亮过程中解锁屏模块处理逻辑的时序图如下:
1)在函数PowerManagerService:setPowerState中响应Power按钮的按下,代码如下:
private void setPowerState(int newState, boolean noChangeLights, int reason)
{
synchronized (mLocks) {
……
if (oldScreenOn != newScreenOn) {
if (newScreenOn) {
// When the user presses the power button, we need to always send out the
// notification that it's going to sleep so the keyguard goes on. But
// we can't do that until the screen fades out, so we don't show the keyguard
// too early.
if (mStillNeedSleepNotification) {
sendNotificationLocked(false, WindowManagerPolicy.OFF_BECAUSE_OF_USER);
}
……
if (err == 0) {
sendNotificationLocked(true, -1);
// Update the lights *after* taking care of turning the
// screen on, so we do this after our notifications are
// enqueued and thus will delay turning on the screen light
// until the windows are correctly displayed.
if (stateChanged) {
updateLightsLocked(newState, 0);
}
mPowerState |= SCREEN_ON_BIT;
}
} else {
……
if (!mScreenBrightness.animating) {
err = screenOffFinishedAnimatingLocked(reason);
}
……
}
}
……
}
}
根据上面的代码逻辑,屏幕即将变暗时调用函数screenOffFinishedAnimatingLocked,屏幕即将变亮时调用函数sendNotificationLocked
。
private Runnable mNotificationTask = new Runnable()
{
public void run()
{
while (true) {
......
if (value == 1) {
policy.screenTurningOn(mScreenOnListener);
......
}
else if (value == 0) {
policy.screenTurnedOff(why);
......
}
else {
// If we're in this case, then this handler is running for a previous
// paired transaction. mBroadcastWakeLock will already have been released.
break;
}
}
}
};
上面的线程函数run中分别处理了屏幕变暗和变亮的情形。按下Power按钮屏幕变暗时调用了函数screenTurnedOff,why为变暗的原因,此处值为OFF_BECAUSE_OF_USER。
/**
* Called to let us know the screen was turned off.
* @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER},
* {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
* {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
*/
public void onScreenTurnedOff(int why) {
synchronized (this) {
……
else if (mShowing) { //若是(mShowing)则重置显示界面,否则重新显示锁屏界面
notifyScreenOffLocked();
resetStateLocked();
} else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT) {
// if the screen turned off because of timeout, set an alarm
// to enable it a little bit later (i.e, give the user a chance
// to turn the screen back on within a certain window without
// having to unlock the screen)
……
if (timeout <= 0) {
// Lock now
mSuppressNextLockSound = true;
doKeyguardLocked();
} else {
// Lock in the future
long when = SystemClock.elapsedRealtime() + timeout;
Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
intent.putExtra("seq", mDelayedShowingSequence);
PendingIntent sender = PendingIntent.getBroadcast(mContext,
0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when,
sender);
}
} else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
// Do not enable the keyguard if the prox sensor forced the screen off.
} else { //显示锁屏屏幕
doKeyguardLocked();
}
}
}
4)调用doKeyguardLocked重新显示锁屏界面,随后的锁屏界面显示逻辑与Keyguard模块启动显示中的8~10步相同,不再赘述。
public void show() {
if (mMode == Mode.LockScreen) {
((KeyguardScreen) mLockScreen).onResume();
} else {
((KeyguardScreen) mUnlockScreen).onResume();
}
......
}
至此,逻辑处理完成。
1)解锁屏界面输入密码点击确定按钮后,在函数onEditorAction中进行响应:
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_NEXT) {
verifyPasswordAndUnlock();
return true;
}
return false;
}
2)在函数verifyPasswordAndUnlock中对输入的密码进行判断,如果输入正确,测调用keyguardDone响应解锁完成的操作。mCallback.keyguardDone(true)调用是所有解锁屏mode情形在解锁成功后必须调用的函数,随后的处理逻辑对于不同的解锁屏界面也是相同的。
public void keyguardDone(boolean authenticated, boolean wakeup) {
synchronized (this) {
……
Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
msg.arg1 = wakeup ? 1 : 0;
mHandler.sendMessage(msg);
……
}
}
4)函数KeyguardViewMediator:handleKeyguardDone异步处理keyguardDone事件,调用handleHide隐藏锁屏界面。
public void cleanUp() {
if (mLockScreen != null) {
((KeyguardScreen) mLockScreen).onPause();
((KeyguardScreen) mLockScreen).cleanUp();
this.removeView(mLockScreen);
mLockScreen = null;
}
if (mUnlockScreen != null) {
((KeyguardScreen) mUnlockScreen).onPause();
((KeyguardScreen) mUnlockScreen).cleanUp();
this.removeView(mUnlockScreen);
mUnlockScreen = null;
}
......
}
至此,解锁完成。