學(xué)習(xí)筆記:
首先一起看看下面兩張圖:
通過前面鎖屏加載流程可以知道在KeyguardSecurityContainer中使用getSecurityView()根據(jù)不同的securityMode inflate出來,并添加到界面上的流妻。
我們知道,Pattern鎖所使用的layout是 R.layout.keyguard_pattern_view;
<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>
那么圖案解鎖的滑動(dòng)事件處理椭豫,就是在LockPatternView,是一個(gè)系統(tǒng)公共控件倡鲸,下面我們就分析一下這個(gè)view是如何處理觸摸輸入的:
// frameworks/base/core/java/com/android/internal/widget/LockPatternView.java
@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;
}
幾種事件類型:不同的MotionEvent對(duì)應(yīng)幾個(gè)不同的handle方法處理特漩,代碼行數(shù)太多,我們這里大致總結(jié)如下:
- ACTION_DOWN(handleActionDown):根據(jù)觸摸事件的坐標(biāo)嘹黔,使用算法detectAndAddHit(x, y)獲取是否有命中的點(diǎn)账嚎,如果有,會(huì)調(diào)用addCellToPattern將命中的Cell添加到mPattern中儡蔓,后即回調(diào)mOnPatternListener.onPatternStart()通知監(jiān)聽器郭蕉,KeyguardPatternView實(shí)現(xiàn)并監(jiān)聽了OnPatternListener,做了清除安全提示內(nèi)容的動(dòng)作喂江。另外計(jì)算需要重繪區(qū)域召锈,并調(diào)用invalidate進(jìn)行局部重繪。
- ACTION_MOVE(handleActionMove):在這里 LockPatternView會(huì)對(duì)所有的歷史坐標(biāo)加當(dāng)前事件坐標(biāo)遍歷for (int i = 0; i < historySize + 1; i++)获询,獲取命中點(diǎn)涨岁,另外如果ACTION_DOWN時(shí)沒有獲取到命中點(diǎn)拐袜,流程同上面的ACTION_UP,然后也會(huì)回調(diào)mOnPatternListener.onPatternStart()。最后會(huì)把所有motionevent對(duì)應(yīng)的重繪區(qū)域進(jìn)行union梢薪,并調(diào)用invalidate進(jìn)行局部重繪蹬铺。
關(guān)于歷史坐標(biāo)
??為了效率,Android系統(tǒng)在處理ACTION_MOVE事件時(shí)會(huì)將連續(xù)的幾個(gè)多觸點(diǎn)移動(dòng)事件打包到一個(gè)MotionEvent對(duì)象中秉撇。我們可以通過getX(int)和getY(int)來獲得最近發(fā)生的一個(gè)觸摸點(diǎn)事件的坐標(biāo)甜攀,然后使用getHistorical(int,int)和getHistorical(int,int)來獲得時(shí)間稍早的觸點(diǎn)事件的坐標(biāo),二者是發(fā)生時(shí)間先后的關(guān)系琐馆。所以赴邻,我們應(yīng)該先處理通過getHistoricalXX相關(guān)函數(shù)獲得的事件信息,然后在處理當(dāng)前的事件信息啡捶。
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不為空的話姥敛,會(huì)重置mPatternInProgress,取消動(dòng)畫瞎暑,然后回調(diào)mOnPatternListener.onPatternDetected(final List<LockPatternView.Cell> pattern)彤敛,這時(shí)候就開始圖案解鎖的驗(yàn)證了。
ACTION_CANCEL:重置pattern狀態(tài)了赌,回調(diào)mOnPatternListener.onPatternCleared()
圖案解鎖驗(yàn)證
// src/com/android/keyguard/KeyguardPatternView.java
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 /* not valid - too short */);
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), // 這里跟進(jìn)去墨榄,會(huì)發(fā)現(xiàn)將 pattern轉(zhuǎn)化成了 byte[]
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();
}
}
...
}
在繪制密碼后手指抬起的時(shí)候,如果已存的有效點(diǎn)數(shù)達(dá)到4個(gè)及以上勿她,就會(huì)使用LockPatternChecker.checkCredential 方法調(diào)用 task.execute() 啟動(dòng)一個(gè)AsyncTask袄秩, 并在doInBackground中調(diào)用LockPatternUtils.checkCredential 進(jìn)行密碼驗(yàn)證,此時(shí)pattern會(huì)被轉(zhuǎn)化成字節(jié)形式(LockscreenCredential.createPattern(pattern) 這里跟進(jìn)去逢并,會(huì)發(fā)現(xiàn)將 pattern轉(zhuǎn)化成了 byte[])
// LockPatternUtils.java
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碼鎖一樣之剧,都是遠(yuǎn)程調(diào)用到LockSettingsService 的 checkCredential 接口進(jìn)行驗(yàn)證。
Keyguard接收用戶輸入的密碼會(huì)通過Binder到framework層的LockSettingsService砍聊,LockSettingsService經(jīng)過一系列調(diào)用會(huì)通過getGateKeeperService獲取GateKeeperService然后調(diào)用verifyChallenge方法將密碼繼續(xù)忘底層傳遞背稼,framework的調(diào)用棧如下:
驗(yàn)證流程:
// LockSettingsService.java
private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
@ChallengeType int challengeType, long challenge, int userId,
ICheckCredentialProgressCallback progressCallback,
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
// 省略部分代碼......
if (credential == null || credential.isNone()) {
throw new IllegalArgumentException("Credential can't be null or empty");
}
if (userId == USER_FRP && mInjector.settingsGlobalGetInt(mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
return VerifyCredentialResponse.ERROR;
}
// response是驗(yàn)證響應(yīng),spBasedDoVerifyCredential發(fā)起驗(yàn)證玻蝌,返回 response 響應(yīng)
VerifyCredentialResponse response = null;
response = spBasedDoVerifyCredential(credential, challengeType, challenge,
userId, progressCallback, resetLockouts);
// The user employs synthetic password based credential.
if (response != null) {
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
sendCredentialsOnUnlockIfRequired(credential, userId);
}
return response;
}
// 省略部分代碼......
return response;
}
這里先看verifyChallenge方法返回有三個(gè)狀態(tài)蟹肘,也就是response.getResponseCode():
??//密碼匹配失敗
??public static final int RESPONSE_ERROR = -1;
??//密碼匹配成功
??public static final int RESPONSE_OK = 0;
??//重試
??public static final int RESPONSE_RETRY = 1;
接下來我們看spBasedDoVerifyCredential()方法做基于 sp 的做驗(yàn)證憑證:
// LockSettingsService.java
private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential,
@ChallengeType int challengeType, long challenge,
int userId, ICheckCredentialProgressCallback progressCallback,
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
// 判斷是否具有生物識(shí)別
final boolean hasEnrolledBiometrics = mInjector.hasEnrolledBiometrics(userId);
// 省略部分代碼......
final AuthenticationResult authResult;
VerifyCredentialResponse response;
synchronized (mSpManager) {
if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
return null;
}
if (userId == USER_FRP) {
return mSpManager.verifyFrpCredential(getGateKeeperService(),
userCredential, progressCallback);
}
long handle = getSyntheticPasswordHandleLocked(userId);
// 解開基于密碼的合成密碼
authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), handle, userCredential, userId, progressCallback);
response = authResult.gkResponse;
// 省略部分代碼......
}
// 省略部分代碼......
return response;
}
在這方法里首先進(jìn)行了判斷是不是生物解鎖,然后在調(diào)用合成密碼管理器SyntheticPasswordManager里的unwrapPasswordBasedSyntheticPassword()方法進(jìn)行解密俯树,并將鎖屏密碼往下傳帘腹。
// SyntheticPasswordManager.java
public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
long handle, @NonNull LockscreenCredential credential, int userId,
ICheckCredentialProgressCallback progressCallback) {
// 省略部分代碼......
else {
byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
GateKeeperResponse response;
try {
Log.d("yexiao","yexiao:",new Throwable());
response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
pwd.passwordHandle, gkPwdToken);
} catch (RemoteException e) {
Slog.e(TAG, "gatekeeper verify failed", e);
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
// 省略部分代碼......
}
// 省略部分代碼......
return result;
}
unwrapPasswordBasedSyntheticPassword中的gatekeeper是LockSettingsService的getGateKeeperService方法獲取的IGateKeeperService Binder代理端:
// LockSettingsService.java
protected synchronized IGateKeeperService getGateKeeperService() {
if (mGateKeeperService != null) {
return mGateKeeperService;
}
final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
if (service != null) {
try {
service.linkToDeath(new GateKeeperDiedRecipient(), 0);
} catch (RemoteException e) {
Slog.w(TAG, " Unable to register death recipient", e);
}
mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
return mGateKeeperService;
}
Slog.e(TAG, "Unable to acquire GateKeeperService");
return null;
}
這里有個(gè)問題,我們發(fā)現(xiàn)IGateKeeperService的Binder實(shí)現(xiàn)端找不到许饿,而且在Framework層也找不到在那里注冊(cè)的service阳欲,為何能getService?
其實(shí)IGateKeeperService這個(gè)AIDL文件的具體實(shí)現(xiàn)類不像傳統(tǒng)的Framework Binder服務(wù),它的實(shí)現(xiàn)端在native層胸完,我們前面說了GateKeeper的架構(gòu)书释,提到GateKeeper是一種C++的Binder服務(wù),與java層接口相對(duì)應(yīng)
我們就先來來看看GateKeeper server端赊窥,目錄system/core/gatekeeperd下的gatekeeperd.cpp類
// gatekeeperd.cpp
int main(int argc, char* argv[]) {
ALOGI("Starting gatekeeperd...");
if (argc < 2) {
ALOGE("A directory must be specified!");
return 1;
}
if (chdir(argv[1]) == -1) {
ALOGE("chdir: %s: %s", argv[1], strerror(errno));
return 1;
}
android::sp<android::IServiceManager> sm = android::defaultServiceManager();
android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy();
android::status_t ret = sm->addService(
android::String16("android.service.gatekeeper.IGateKeeperService"), proxy);
if (ret != android::OK) {
ALOGE("Couldn't register binder service!");
return -1;
}
/*
* We're the only thread in existence, so we're just going to process
* Binder transaction as a single-threaded program.
*/
android::IPCThreadState::self()->joinThreadPool();
return 0;
}
在main函數(shù)中爆惧,首先獲取BpSeviceManager,然后創(chuàng)建GateKeeperProxy類锨能,在調(diào)用addService函數(shù)將GateKeeperProxy注冊(cè)到SeviceManager扯再,名稱為"android.service.gatekeeper.IGateKeeperService",前面我們?cè)贔ramework層通過getService(Context.GATEKEEPER_SERVICE)獲取的gatekeeper服務(wù)其實(shí)獲取的就是這個(gè)服務(wù)址遇,Context中定義的服務(wù)名稱也是一樣的熄阻。
至于native層與Java層如何相互調(diào)用關(guān)聯(lián)的,這里就省略了。
前面解密過程調(diào)的verifyChallenge方法調(diào)到了gatekeeperd.cpp中的GateKeeperProxy類的同名verifyChallenge函數(shù),但我們又發(fā)現(xiàn)這兩個(gè)verifyChallenge方法參數(shù)并不一致免绿,這無所謂的,Binder調(diào)用并不需要client端和server端參數(shù)一致钾军,調(diào)用方法的匹配是通過Binder code來決定的。
到這里绢要,上層的鎖屏密碼就已經(jīng)傳遞到了natice層吏恭,還記得前面說的gatekeeper架構(gòu)嗎,native層過了之后就該通過HIDL忘HAL層發(fā)送密碼了重罪,來看看GateKeeperProxy中的verifyChallenge具體實(shí)現(xiàn):
// gatekeeperd.cpp
Status verifyChallenge(int32_t uid, int64_t challenge,
const std::vector<uint8_t>& enrolledPasswordHandle,
const std::vector<uint8_t>& providedPassword,
GKResponse* gkResponse) override {
//省略掉一些權(quán)限相關(guān)檢查 ......
//省略一些數(shù)據(jù)類型轉(zhuǎn)換.....
Return<void> hwRes = hw_device->verify(
hw_uid, challenge, curPwdHandle, enteredPwd,
[&gkResponse](const GatekeeperResponse& rsp) {
if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
ALOGD("gatekeeperd verify process ok");
*gkResponse = GKResponse::ok(
{rsp.data.begin(), rsp.data.end()},
rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
} else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) {
ALOGD("gatekeeperd verify process retry timeout");
*gkResponse = GKResponse::retry(rsp.timeout);
} else {
ALOGD("gatekeeperd verify fail,maybe not match");
*gkResponse = GKResponse::error();
}
});
if (!hwRes.isOk()) {
LOG(ERROR) << "verify transaction failed";
return GK_ERROR;
}
// 省略部分代碼......
return Status::ok();
}
到這里基本整個(gè)解鎖流程結(jié)束了樱哼,native層也不在繼續(xù)深入分析了。
在整個(gè)操作過程中剿配,mOnPatternListener 被用于通知LockPatternView進(jìn)行安全鎖提示內(nèi)容和Pattern狀態(tài)的刷新搅幅。
我們可以對(duì)整個(gè)密碼匹配的流程進(jìn)行總結(jié)了:
- 上層Keyguard接收用戶的密碼輸入。
- 收到密碼后通過Binder將密碼傳遞到LockSettingsService惨篱。
- 在LockSettingsService中獲取到實(shí)現(xiàn)在native層的GateKeeperService盏筐,調(diào)用其verifyChallenge函數(shù)围俘。
- verifyChallenge中調(diào)用HIDL服務(wù)IGatekeeper的verify函數(shù)繼續(xù)向HAL中發(fā)送密碼砸讳。
- IGatekeeper中獲取名為GATEKEEPER_HARDWARE_MODULE_ID的HAL模塊,并打開其下的device界牡,調(diào)用device的verify函數(shù)在TEE硬件中進(jìn)最終密碼匹配簿寂。
參考:Android P keyguard 初始化,Pattern解鎖等介紹 宿亡、SystemUI 之圖案鎖驗(yàn)證流程常遂、AndroidQ 鎖屏密碼驗(yàn)證解析