首先一起看看下面这张图:

通过前面锁屏加载流程可以知道在KeyguardSecurityContainer中使用getSecurityView()根据不同的securityMode inflate出来,并添加到界面上的。
我们知道,Pattern锁所使用的layout是 R.layout.keyguard_pattern_view;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <com.android.keyguard.KeyguardPatternView ...> ... <com.android.internal.widget.LockPatternView android:id="@+id/lockPatternView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:layout_marginEnd="8dip" android:layout_marginBottom="4dip" android:layout_marginStart="8dip" android:layout_gravity="center_horizontal" android:gravity="center" android:clipChildren="false" android:clipToPadding="false" /> ... </FrameLayout> </com.android.keyguard.KeyguardPatternView>
|
那么图案解锁的滑动事件处理,就是在LockPatternView,是一个系统公共控件,下面我们就分析一下这个view是如何处理触摸输入的:
frameworks/base/core/java/com/android/internal/widget/LockPatternView.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @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; }
|
几种事件类型:
事件 |
简介 |
ACTION_DOWN |
手指初次触摸到屏幕时触发 |
ACTION_MOVE |
手指在屏幕上滑动时触发,会多次触发 |
ACTION_UP |
手指离开屏幕时触发 |
ACTION_CANCEL |
事件被上层拦截时触发 |
ACTION_OUTSIDE |
手指不在控件区域时触发 |
不同的MotionEvent对应几个不同的handle方法处理,代码行数太多,我们这里大致总结如下:
- ACTION_DOWN(handleActionDown):根据触摸事件的坐标,使用算法detectAndAddHit(x, y)获取是否有命中的点,如果有,会调用addCellToPattern将命中的Cell添加到mPattern中,后即回调mOnPatternListener.onPatternStart()通知监听器,KeyguardPatternView实现并监听了OnPatternListener,做了清除安全提示内容的动作。另外计算需要重绘区域,并调用invalidate进行局部重绘。
- ACTION_MOVE(handleActionMove):在这里 LockPatternView会对所有的历史坐标加当前事件坐标遍历for (int i = 0; i < historySize + 1; i++),获取命中点,另外如果ACTION_DOWN时没有获取到命中点,流程同上面的ACTION_UP,然后也会回调mOnPatternListener.onPatternStart()。最后会把所有motionevent对应的重绘区域进行union,并调用invalidate进行局部重绘。
关于历史坐标
为了效率,Android系统在处理ACTION_MOVE事件时会将连续的几个多触点移动事件打包到一个MotionEvent对象中。我们可以通过getX(int)和getY(int)来获得最近发生的一个触摸点事件的坐标,然后使用getHistorical(int,int)和getHistorical(int,int)来获得时间稍早的触点事件的坐标,二者是发生时间先后的关系。所以,我们应该先处理通过getHistoricalXX相关函数获得的事件信息,然后在处理当前的事件信息。
1 2 3 4 5
| for (int i = 0; i < historySize + 1; i++) { final float x = i < historySize ? event.getHistoricalX(i) : event.getX(); final float y = i < historySize ? event.getHistoricalY(i) : event.getY(); ... }
|
- ACTION_UP(handleActionUp):如果mPattern不为空的话,会重置mPatternInProgress,取消动画,然后回调mOnPatternListener.onPatternDetected(final List<LockPatternView.Cell> pattern),这时候就开始图案解锁的验证了。
- ACTION_CANCEL:重置pattern状态,回调mOnPatternListener.onPatternCleared()
图案解锁验证
src/com/android/keyguard/KeyguardPatternView.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| private class UnlockPatternListener implements LockPatternView.OnPatternListener { ... @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); } final int userId = KeyguardUpdateMonitor.getCurrentUser(); if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { mLockPatternView.enableInput(); onPatternChecked(userId, false, 0, false ); return; } if (LatencyTracker.isEnabled(mContext)) { LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); } 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 , 0 , true ); } @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 , timeoutMs, true ); } } @Override public void onCancelled() { if (DEBUG) Log.d(TAG, "onCancelled"); if (LatencyTracker.isEnabled(mContext)) { LatencyTracker.getInstance(mContext).onActionEnd( ACTION_CHECK_CREDENTIAL_UNLOCKED); } } }); if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { mCallback.userActivity(); mCallback.onUserInput(); } } ... }
|
在绘制密码后手指抬起的时候,如果已存的有效点数达到4个及以上,就会使用LockPatternChecker.checkCredential 方法调用 task.execute() 启动一个AsyncTask, 并在doInBackground中调用LockPatternUtils.checkCredential 进行密码验证,此时pattern会被转化成字节形式(LockscreenCredential.createPattern(pattern) 这里跟进去,会发现将 pattern转化成了 byte[])
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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; }
|
最终和密码锁PIN码锁一样,都是远程调用到LockSettingsService 的 checkCredential 接口进行验证。
Keyguard接收用户输入的密码会通过Binder到framework层的LockSettingsService,LockSettingsService经过一系列调用会通过getGateKeeperService获取GateKeeperService然后调用verifyChallenge方法将密码继续忘底层传递,framework的调用栈如下:
1 2 3 4
| java.lang.Throwab comandroid.locksettings.1locksettings.LockSettingsService. spBasedDoVerifyCredential(LockSettingsService. java:2724)COT3767075server.ocksettingsLockSettingsService.doVerifyCredentia1(LockSettingsSeryice.java:2020comandroservercom android.server.locksettings.LockSettingsService.doVerifyCredential(LockSettingsService. jaya:1999atcom android.server.locksettings.LockSettingsService. checkCredential(LockSettingsService. java:1972)com android.internal.widget.ILockSettings$Stub.onTransact(ILockSettings.java:542atatexecTransactInternal(Binder.java:1159)execTransact(Binder.java:1123)at SyntheticPasswordllanager. unwrapPasswordBasedSyntheticPassword(SyntheticPasswordllanager. java: 1016 android.os.Binderandroid.os.Binder
|
本文链接:
http://longzhiye.top/2023/12/09/2023-12-09/