Android11 九宫格解锁流程

在我们分析完锁屏的一整个显示流程后,我们接着来分析一下上滑解锁的流程。

流程

首先,我们手指在锁屏界面上滑将会调用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.垃圾清除的调度,释放内存

到这里整个流程就走完了,最底层密码验证的算法问题以后有机会再去分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值