手指在指紋傳感器上摸一下就能解鎖,Keyguard是怎么做到的呢?
下面我們就跟著源碼产还,解析這整個過程厘熟。
何時開始監(jiān)聽指紋傳感器屯蹦?
先來看下IKeyguardService這個binder接口有哪些回調(diào)吧
// 當另一個窗口使用FLAG_SHOW_ON_LOCK_SCREEN解除Keyguard時PhoneWindowManager調(diào)用
public void setOccluded(boolean isOccluded, boolean animate) throws android.os.RemoteException;
// 添加鎖屏狀態(tài)回調(diào)
public void addStateMonitorCallback(com.android.internal.policy.IKeyguardStateCallback callback) throws android.os.RemoteException;
// 核驗解鎖(用于快捷啟動)
public void verifyUnlock(com.android.internal.policy.IKeyguardExitCallback callback) throws android.os.RemoteException;
// 解除鎖屏
public void dismiss(com.android.internal.policy.IKeyguardDismissCallback callback) throws android.os.RemoteException;
// 屏保開始(Intent.ACTION_DREAMING_STARTED)
public void onDreamingStarted() throws android.os.RemoteException;
// 屏保結(jié)束(Intent.ACTION_DREAMING_STOPPED)
public void onDreamingStopped() throws android.os.RemoteException;
// 設備開始休眠 reason:OFF_BECAUSE_OF_USER/OFF_BECAUSE_OF_ADMIN/OFF_BECAUSE_OF_TIMEOUT
public void onStartedGoingToSleep(int reason) throws android.os.RemoteException;
// 休眠完成
public void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered) throws android.os.RemoteException;
// 設備開始喚醒
public void onStartedWakingUp() throws android.os.RemoteException;
// 喚醒完成
public void onFinishedWakingUp() throws android.os.RemoteException;
// 正在亮屏
public void onScreenTurningOn(com.android.internal.policy.IKeyguardDrawnCallback callback) throws android.os.RemoteException;
// 已經(jīng)亮屏完成
public void onScreenTurnedOn() throws android.os.RemoteException;
// 正在滅屏
public void onScreenTurningOff() throws android.os.RemoteException;
// 滅屏完成
public void onScreenTurnedOff() throws android.os.RemoteException;
// 外部應用取消Keyguard接口
public void setKeyguardEnabled(boolean enabled) throws android.os.RemoteException;
// 開機系統(tǒng)準備完成回調(diào)
public void onSystemReady() throws android.os.RemoteException;
// 延時鎖屏 (用于自動休眠)
public void doKeyguardTimeout(android.os.Bundle options) throws android.os.RemoteException;
// 切換用戶中
public void setSwitchingUser(boolean switching) throws android.os.RemoteException;
// 設置當前用戶
public void setCurrentUser(int userId) throws android.os.RemoteException;
// 系統(tǒng)啟動完成回調(diào)
public void onBootCompleted() throws android.os.RemoteException;
// Keyguard后面的activity已經(jīng)繪制完成维哈,可以開始移除壁紙和Keyguard flag
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) throws android.os.RemoteException;
// 通知Keyguard對power鍵做特殊處理,使設備不進行休眠或喚醒而是啟動Home(目前是空實現(xiàn))
public void onShortPowerPressedGoHome() throws android.os.RemoteException;
在這么多接口里登澜,有
onStartedGoingToSleep/
onFinishedGoingToSleep/
onScreenTurningOff/
onScreenTurnedOff
這四個接口是在power鍵按下后觸發(fā)阔挠,其中onStartedGoingToSleep最先被觸發(fā),他們的
調(diào)用順序我會在后文里講解脑蠕。
而我們的指紋傳感器監(jiān)聽,就是在onStartedGoingToSleep時開始的购撼。
代碼邏輯由在KeyguardService中由中間類KeyguardMediator調(diào)用到KeyguardUpdateMonitor
android/frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
protected void handleStartedGoingToSleep(int arg1) {
...
updateFingerprintListeningState();
}
private void updateFingerprintListeningState() {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
if (mHandler.hasMessages(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE)) {
return;
}
mHandler.removeCallbacks(mRetryFingerprintAuthentication);
boolean shouldListenForFingerprint = shouldListenForFingerprint();
if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING && !shouldListenForFingerprint) {
stopListeningForFingerprint();
} else if (mFingerprintRunningState != FINGERPRINT_STATE_RUNNING
&& shouldListenForFingerprint) {
startListeningForFingerprint();
}
}
在同時判斷mFingerprintRunningState和shouldListenForFingerprint后,
Keyguard在startListeningForFingerprint中真正使用FingerprintManager監(jiān)聽指紋傳感器
指紋傳感器的監(jiān)聽方法
private void startListeningForFingerprint() {
if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING) {
setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING_RESTARTING);
return;
}
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
int userId = ActivityManager.getCurrentUser();
if (isUnlockWithFingerprintPossible(userId)) {
if (mFingerprintCancelSignal != null) {
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null, userId);
setFingerprintRunningState(FINGERPRINT_STATE_RUNNING);
}
}
真正到了使用指紋識別功能的時候谴仙,你會發(fā)現(xiàn)其實很簡單迂求,只是調(diào)用 FingerprintManager 類的的方法authenticate()而已,然后系統(tǒng)會有相應的回調(diào)反饋給我們晃跺,該方法如下:
public void authenticate(CryptoObject crypto, CancellationSignal cancel, int flags, AuthenticationCallback callback, Handler handler, int userId)
該方法的幾個參數(shù)解釋如下:
- 第一個參數(shù)是一個加密對象揩局。目前為null
- 第二個參數(shù)是一個 CancellationSignal 對象,該對象提供了取消操作的能力掀虎。創(chuàng)建該對象也很簡單凌盯,使用 new CancellationSignal() 就可以了。
- 第三個參數(shù)是一個標志烹玉,默認為0驰怎。
- 第四個參數(shù)是 AuthenticationCallback 對象,它本身是 FingerprintManager 類里面的一個抽象類二打。該類提供了指紋識別的幾個回調(diào)方法县忌,包括指紋識別成功、失敗等继效。需要我們重寫芹枷。
- 最后一個 Handler,可以用于處理回調(diào)事件莲趣,可以傳null鸳慈。
- 用戶id
下面只需要在mAuthenticationCallback繼承AuthenticationCallback這個抽象方法,重寫回調(diào)接口
private FingerprintManager.AuthenticationCallback mAuthenticationCallback
= new AuthenticationCallback() {
@Override
public void onAuthenticationFailed() {
handleFingerprintAuthFailed();
};
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) {
Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
handleFingerprintAuthenticated(result.getUserId());
Trace.endSection();
}
@Override
// 指紋驗證
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
handleFingerprintHelp(helpMsgId, helpString.toString());
}
@Override
// 指紋驗證時回調(diào)
public void onAuthenticationError(int errMsgId, CharSequence errString) {
}
@Override
// 獲取到指紋時回調(diào)
public void onAuthenticationAcquired(int acquireInfo) {
handleFingerprintAcquired(acquireInfo);
}
};
獲取指紋后喧伞,Keyguard做了哪些事走芋?
從AuthenticationCallback里可以看出,獲取指紋回調(diào)首先發(fā)生在 onAuthenticationAcquired 中潘鲫, 我們先看代碼
private void handleFingerprintAcquired(int acquireInfo) {
if (acquireInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
return;
}
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onFingerprintAcquired();
}
}
}
首先用acquireInfo參數(shù)判斷是否正確獲取指紋翁逞,之后遍歷KeyguardUpdateMonitorCallback,進行回調(diào)溉仑。
重寫onFingerprintAcquired方法的只有FingerprintUnlockController挖函,F(xiàn)ingerprintUnlockController就是
用于協(xié)調(diào)UI的所有指紋解鎖操作的控制器。
@Override
public void onFingerprintAcquired() {
...
mWakeLock = mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
mWakeLock.acquire();
mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
FINGERPRINT_WAKELOCK_TIMEOUT_MS);
}
...
}
onFingerprintAcquired的核心邏輯全部是和WakeLock相關(guān)的浊竟,
獲取WakeLock怨喘,并發(fā)送一條延時消息津畸,15秒后,釋放WakeLock必怜。
下一步就發(fā)生在肉拓,onFingerprintAuthenticated回調(diào)中了,實現(xiàn)onFingerprintAuthenticated接口的不止一處梳庆,但真正實現(xiàn)解鎖的還是在FingerprintUnlockController中
@Override
public void onFingerprintAuthenticated(int userId) {
...
startWakeAndUnlock(calculateMode());
}
private int calculateMode() {
boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
boolean deviceDreaming = mUpdateMonitor.isDreaming();
if (!mUpdateMonitor.isDeviceInteractive()) {
if (!mStatusBarKeyguardViewManager.isShowing()) {
return MODE_ONLY_WAKE;
} else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
return MODE_WAKE_AND_UNLOCK_PULSING;
} else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
return MODE_WAKE_AND_UNLOCK;
} else {
return MODE_SHOW_BOUNCER;
}
}
if (unlockingAllowed && deviceDreaming) {
return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
}
if (mStatusBarKeyguardViewManager.isShowing()) {
if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed) {
return MODE_UNLOCK;
} else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
return MODE_SHOW_BOUNCER;
}
}
return MODE_NONE;
}
這段代碼邏輯很清晰暖途,就是根據(jù)鎖屏的狀態(tài)計算指紋解鎖的模式
public void startWakeAndUnlock(int mode) {
...
boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
mMode = mode;
if (!wasDeviceInteractive) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
}
releaseFingerprintWakeLock();
switch (mMode) {
...
}
}
startWakeAndUnlock中的代碼經(jīng)過簡化后,只剩三部分:
1.先判斷設備喚醒狀態(tài)膏执,是用PowerManager的wakeUp接口點亮屏幕
2.然后釋放在acquire階段獲取的WakeLock
3.最后在根據(jù)上面calculateMode得出的解鎖模式驻售,進行真正的解鎖動作,這在之前的解鎖流程中已經(jīng)分析過更米,這里不再做分析欺栗。
這里面值得我們注意的是wakeUp接口, 下面我們稍微對該接口進行一點探究
PowerManager的wakeUp接口
我們知道上層應用要喚醒系統(tǒng)一般只能依靠兩種方式:
1.在應用啟動Activity時候設置相應的window的flags壳快,通過WMS來喚醒系統(tǒng);
即通過WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
2.在應用申請wakelock鎖時附帶ACQUIRE_CAUSES_WAKEUP標志
PowerManager的wakeup接口屬性是@hide的镇草,對一般應用是不可見的眶痰,而我們的SystemUI就不存在調(diào)用問題。
SystemUI通過調(diào)用第三種方式:PowerManager的wakeup接口梯啤,來強制喚醒系統(tǒng)竖伯,如果該設備處于睡眠狀態(tài),調(diào)用該接口會立即喚醒系統(tǒng)因宇,比如按Power鍵七婴,來電,鬧鐘等場景都會調(diào)用該接口察滑。喚醒系統(tǒng)需要android.Manifest.permission#DEVICE_POWER的權(quán)限打厘,我們可以看到SystemUI的Manifest文件里已經(jīng)添加了該權(quán)限。
PowerManagerService喚醒的流程請看流程圖:
從流程可以看到贺辰,亮屏流程可以和KeyguardService中的回調(diào)對應上了户盯。
總結(jié)
其實指紋解鎖的本質(zhì)是在KeyguardService收到從PMS到WMS的調(diào)用中,在StartedGoingToSleep時就開始使用FingerprintManager的authticate開始監(jiān)聽感器饲化,在FIngerManager驗證成功時莽鸭,使用PowerManagerService點亮屏幕,進行解鎖流程吃靠。