在我们分析完锁屏的一整个显示流程后,我们接着来分析一下上滑解锁的流程。
流程
首先,我们手指在锁屏界面上滑将会调用StatusBar中的onTrackingStopped方法
public void onTrackingStopped(boolean expand) {
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
if (!expand && !mKeyguardStateController.canDismissLockScreen()) {
mStatusBarKeyguardViewManager.showBouncer(false /* scrimmed */);
}
}
}
我们可以看到这里的代码有两项判断
1.判断是否处于锁屏界面
2.是否可以上滑以及有没有锁屏界面
接下来我们将要进入showBouncer方法,但实际上我们调用的就是KeyguardBouncer的show方法,所以
public void show(boolean resetSecuritySelection, boolean isScrimmed) {
final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser();
if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
// In split system user mode, we never unlock system user.
return;
}
ensureView();
mIsScrimmed = isScrimmed;
// On the keyguard, we want to show the bouncer when the user drags up, but it's
// not correct to end the falsing session. We still need to verify if those touches
// are valid.
// Later, at the end of the animation, when the bouncer is at the top of the screen,
// onFullyShown() will be called and FalsingManager will stop recording touches.
if (isScrimmed) {
setExpansion(EXPANSION_VISIBLE);
}
if (resetSecuritySelection) {
// showPrimarySecurityScreen() updates the current security method. This is needed in
// case we are already showing and the current security method changed.
showPrimarySecurityScreen();
}
if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
return;
}
final int activeUserId = KeyguardUpdateMonitor.getCurrentUser();
final boolean isSystemUser =
UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM;
final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId;
// If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is
// set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.
if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
return;
}
// This condition may indicate an error on Android, so log it.
if (!allowDismissKeyguard) {
Log.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId);
}
mShowingSoon = true;
// Split up the work over multiple frames.
DejankUtils.removeCallbacks(mResetRunnable);
if (mKeyguardStateController.isFaceAuthEnabled() && !needsFullscreenBouncer()
&& !mKeyguardUpdateMonitor.userNeedsStrongAuth()
&& !mKeyguardBypassController.getBypassEnabled()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
} else {
DejankUtils.postAfterTraversal(mShowRunnable);
}
mCallback.onBouncerVisiblityChanged(true /* shown */);
mExpansionCallback.onStartingToShow();
}
这个方法在上一篇文章中分析过了,那么用户解锁成功或者没有设置锁屏都将会调用dismiss方法
public boolean dismiss(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId,
bypassSecondaryLockScreen);
}
今天我们不往锁屏完成后接下来的流程进行分析,我们来分析一下九宫格锁屏方式的解锁验证流程。
在我们上滑进入九宫格解锁的画面时,首先我们就会来到LockPatternView这个控件里面,去显示渲染整个画面,那么接下来我们就来分析一下它是如何来进行各种方法的判断的。
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mInputEnabled || !isEnabled()) {
return false;
}
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
handleActionDown(event);
return true;
case MotionEvent.ACTION_UP:
handleActionUp();
return true;
case MotionEvent.ACTION_MOVE:
handleActionMove(event);
return true;
case MotionEvent.ACTION_CANCEL:
if (mPatternInProgress) {
setPatternInProgress(false);
resetPattern();
notifyPatternCleared();
}
if (PROFILE_DRAWING) {
if (mDrawingProfilingStarted) {
Debug.stopMethodTracing();
mDrawingProfilingStarted = false;
}
}
return true;
}
return false;
}
这里是当用户进行绘制的时候会产生的几个状态,那么我们在这里依次分析一下他们的作用
handle消息传入 | 作用 |
---|---|
ACTION_DOWN | 触发触摸事件,根据坐标获取命中的点 |
ACTION_MOVE | 会遍历历史获取到的坐标,并且在网上查找资料发现还会进行局部区域的重绘 |
ACTION_UP | 当按下手势已经完成,接下来将会进行图案的验证 |
ACTION_CANCEL | 当前手势中止,将不会再进行现在正在进行的图案验证流程。但是这个事件不是因为自己中止,而是因为父控件对他进行了拦截而最终返回。 |
所以如果用户完成了绘制并且没有意外情况,接下来我们将会发送ACTION_UP消息,并调用handleActionUp()方法,于是我们会发现会回调函数mOnPatternListener.onPatternDetected(mPattern),这个mOnPatternListener是一个接口,我们可以找到在KeyguardPatternView里面有一个类实现了这个接口
private class UnlockPatternListener implements LockPatternView.OnPatternListener {
//开始一个新的验证
@Override
public void onPatternStart() {
if (DEBUG) Log.d(TAG, "onPatternStart");
mLockPatternView.removeCallbacks(mCancelPatternRunnable);
/* UNISOC: Modify for Bug 1133395 {@ */
if (mResumed) {
mSecurityMessageDisplay.setMessage("");
}
/* @} */
}
//验证清空
@Override
public void onPatternCleared() {
}
//添加一个拓展单元格(不明白)
@Override
public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
mCallback.userActivity();
mCallback.onUserInput();
}
//检测是否匹配
@Override
public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
if (DEBUG) Log.d(TAG, "onPatternDetected");
//更新状态了解是否在锁屏的地方进行了尝试,不管有没有成功都将被记录
//当解锁成功时候将会被清零
mKeyguardUpdateMonitor.setCredentialAttempted();
//禁用输入
mLockPatternView.disableInput();
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
}
//连接的点小于4个,直接作为无效密码,方法返回
final int userId = KeyguardUpdateMonitor.getCurrentUser();
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
mLockPatternView.enableInput();
onPatternChecked(userId, false, 0, false /* not valid - too short */);
return;
}
if (LatencyTracker.isEnabled(mContext)) {
//检查密码所需要的时间
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
//完全检查密码所需要的时间,也包括的解锁的操作
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
//发布异步任务,检查锁屏的匹配
//这里有四个参数,分别是是确定匹配程序、匹配传入的pattern,用户ID,回调函数
mPendingLockCheck = LockPatternChecker.checkCredential(
mLockPatternUtils,
LockscreenCredential.createPattern(pattern),
userId,
new LockPatternChecker.OnCheckCallback() {
//在检查之前
@Override
public void onEarlyMatched() {
if (DEBUG) Log.d(TAG, "onEarlyMatched");
if (LatencyTracker.isEnabled(mContext)) {
LatencyTracker.getInstance(mContext).onActionEnd(
ACTION_CHECK_CREDENTIAL);
}
onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
true /* isValidPattern */);
}
//检查
@Override
public void onChecked(boolean matched, int timeoutMs) {
if (DEBUG) Log.d(TAG, "onChecked matched:" + matched);
if (LatencyTracker.isEnabled(mContext)) {
LatencyTracker.getInstance(mContext).onActionEnd(
ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
mLockPatternView.enableInput();
mPendingLockCheck = null;
if (!matched) {
onPatternChecked(userId, false /* matched */, timeoutMs,
true /* isValidPattern */);
}
}
//取消时
@Override
public void onCancelled() {
if (DEBUG) Log.d(TAG, "onCancelled");
// We already got dismissed with the early matched callback, so we
// cancelled the check. However, we still need to note down the latency.
if (LatencyTracker.isEnabled(mContext)) {
LatencyTracker.getInstance(mContext).onActionEnd(
ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
}
});
if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
mCallback.userActivity();
mCallback.onUserInput();
}
}
在确定解锁匹配时,会调用LockPatternChecker.checkCredential方法,返回一个AsyncTask进行启动,在doInBackground中调用checkCredential进行密码的验证,在这里
public boolean checkCredential(@NonNull LockscreenCredential credential, int userId,
@Nullable CheckCredentialProgressCallback progressCallback)
throws RequestThrottledException {
throwIfCalledOnMainThread();
try {
VerifyCredentialResponse response = getLockSettings().checkCredential(
credential, userId, wrapCallback(progressCallback));
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
return true;
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
throw new RequestThrottledException(response.getTimeout());
} else {
return false;
}
} catch (RemoteException re) {
Log.e(TAG, "failed to check credential", re);
return false;
}
}
原来的版本是把pattern使用patternToString算法转换成字符串,之后调用checkCredential进行验证
现在则是通过LockscreenCredential.createPattern去调用LockPatternUtils.patternToByteArray返回成一个字节数组
public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) {
if (pattern == null) {
return new byte[0];
}
final int patternSize = pattern.size();
byte[] res = new byte[patternSize];
for (int i = 0; i < patternSize; i++) {
LockPatternView.Cell cell = pattern.get(i);
res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1');
}
return res;
}
与此同时我们发现checkCredential中的if的response.getResponseCode都是在等于VerifyCredentialResponse的参数,OK和RETRY分别代表了通过以及重试(也就是失败了),这里提一下,失败次数过多会触发限制,在这里表现为抛出异常。
那么这个VerifyCredentialResponse是什么东西呢
首先我们根据代码,找到这个getLockSettings发现是一个外部调用的东西,它实际上就等于
ILockSettings service = ILockSettings.Stub.asInterface(
ServiceManager.getService(“lock_settings”));
这样一个服务,于是我们顺着去找,就找到android/frameworks/base/services/core/java/com/android/server/locksettings/LockSettingsService.java
@Override
public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId,
ICheckCredentialProgressCallback progressCallback) {
checkPasswordReadPermission(userId);
try {
return doVerifyCredential(credential, CHALLENGE_NONE, 0, userId, progressCallback);
} finally {
scheduleGc();
}
}
可以看到这里才是最终验证密码的地方
1.进行权限的验证
2.进行验证
3.垃圾清除的调度,释放内存
到这里整个流程就走完了,最底层密码验证的算法问题以后有机会再去分析