第一部分、Fingerprint HIDL
在配有指紋傳感器的設備上,用戶可以註冊一個或多個指紋,並使用這些指紋來解鎖設備以及執行其他任務。Android 會利用 Fingerprint HIDL
(硬件接口定義語言)連接到供應商專用庫和指紋硬件(例如指紋傳感器)。
要實現Fingerprint HIDL
,你必須在某個供應商專用庫中實現 IBiometricsFingerprint.hal
指紋匹配條件
設備的指紋傳感器通常處於閒置狀態。不過,爲了響應對authenticate
或 enroll
的調用,指紋傳感器會監聽觸摸操作(並且屏幕可能會在用戶觸摸傳感器時被喚醒)。指紋匹配的概要流程包括以下步驟:
1.用戶將手指放在傳感器上。
2.供應商專用庫會根據當前已經註冊指紋模板集來判斷是否匹配。
3.匹配結果會傳遞到FingerprintService
。
指紋匹配架構
Fingerprint HAL會與以下組件交互 :
· BiometricManager 會在應用進程中與應用直接交互,每個應用都有一個 IBiometricsFringerprint.hal 實例。
· FingerprintService 在系統進程中運行,可處理與 Fingerprint HAL 之間的通信。
· Fingerprint HAL 是 IBiometricsFringerprint HIDL 接口 C/C++實現。它包含可與設備專與硬件進行通信的供應商專用庫。
· Keystore API 和 KeyMaster 組件提供由硬件支持的加密功能,以便在安全環境(例如可信執行環境( TEE ))中安全地存儲密鑰。
第二部分、指紋模塊流程分析
要實現Fingerprint HIDL
,你必須在某個供應商專用庫中實現IBiometricsFingerprint.hal.
IBiometricsFingerprint.hal
中主要包含以下主要方法:enroll();preEnroll();getAuthenticatorld();cancel();enumerate();remove();setActiveGroup();authenticate();setNotify();postEnroll()。
指紋模塊中的錄入,匹配,移除是三個大部分;
一、指紋錄入
指紋錄入的入口在Settings
中的FingerprintEnrollEnrolling.java
類中
在這個類中沒有看到明顯的錄入的方法,只有一些UI的加載和一些錄入動畫的邏輯。
在此類中的updateProgress(boolean animate)
更新錄入進度的方法中用
int progress = getProgress(
mSidecar.getEnrollmentSteps(), mSidecar.getEnrollmentRemaining());
來獲取實際的進度;
此類中沒有mSidecar
的獲取方法,但是此類繼承自BiometricsEnrolling
,我們去他的父類中找找
BiometricsEnrollEnrolling.java
在此類中我們可以看到mSidecar
的獲取邏輯
public void startEnrollment() {
mSidecar = (BiometricEnrollSidecar) getSupportFragmentManager()
.findFragmentByTag(TAG_SIDECAR);
if (mSidecar == null) {
mSidecar = getSidecar();/*重點關注*/
getSupportFragmentManager().beginTransaction().add(mSidecar, TAG_SIDECAR)
.commitAllowingStateLoss();
}
mSidecar.setListener(this);
}
通過BiometricEnrollSider
強轉類型或是getSider()方法,而getSider()
在此類中是一個抽象方法,具體在子類中實現,具體在他的子類中也是返回了一個繼承自BiometricEnrollSidecar
的FingerprintEnrollSider
對象
FingerprintEnrollSidecar.java
在此類中我們終於發現了核心代碼在該類的startEnrollment()
方法中使用mFingerprintManager.enroll(mToken,mEnrollmentCancel,0,mUserId,mEnrollmentCallback);
來監聽錄入過程中的onEnrollmentProgress,onEnrollmentHelp,onEnrollmentError
的錄入狀態
而FingerprintEnrollSidecar
的父類是BiometricEnrollSidecar
,該類是繼承自InstrumentedFragment
,所以說明該類爲一個Fragment
,該類的生命週期也遵循Fragment
;
在BiometricEnrollSidecar
的onStart
方法中有執行startEnrollment()
frameworks/base/core/java/android/hardware/fingerprint/
FingerprintManager.java
從調用FingerprintManager.enroll
方法開始錄入的
public void enroll(byte [] token, CancellationSignal cancel, int flags,
int userId, EnrollmentCallback callback) {
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentUserId();
}
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
if (cancel != null) {
if (cancel.isCanceled()) {
Slog.w(TAG, "enrollment already canceled");
return;
} else {
cancel.setOnCancelListener(new OnEnrollCancelListener());
}
}
if (mService != null) try {
mEnrollmentCallback = callback;
/*重點關注*/
mService.enroll(mToken, token, userId, mServiceReceiver, flags,
mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */));
}
}
}
從FingerprintManager
的enroll
方法可以看出主要是通過調用mService.enroll
開始進行錄入的,而這個mService
也就是傳進來的
FIngerprintManager(Context context, IFingerprintService){
...
mService = service;
...
}
的實現類的參數。
frameworks /base /services/ core/ java com/ android /server /biometrics/ fingerprint /
FingerprintService.java
在FingerprintService
中註冊了IFingerprintService
private final class FingerprintServiceWrapper extends IFingerPrintService.Stub
實現了enroll
方法
public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
checkPermission(MANAGE_FINGERPRINT);
final boolean restricted = isRestricted();
final int groupId = userId; // default group for fingerprint enrollment
final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */) {
@Override
public boolean shouldVibrate() {
return true;
}
@Override
protected int statsModality() {
return FingerprintService.this.statsModality();
}
};
/*重點關注*/
enrollInternal(client, userId);
}
在FingerprintService的父類BiometricServiceBase中
frameworks/base/services/core/java/com/android/server/biometrics/
BiometricServiceBase.java
protected void enrollInternal(EnrollClientImpl client, int userId) {
if (hasReachedEnrollmentLimit(userId)) {
return;
}
// Group ID is arbitrarily set to parent profile user ID. It just represents
// the default biometrics for the user.
if (!isCurrentUserOrProfile(userId)) {
return;
}
mHandler.post(() -> {
/*重點關注*/
startClient(client, true /* initiatedByClient */);
});
}
startClient(client, true /* initiatedByClient */);
private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
// This check only matters for FingerprintService, since enumerate may call back
// multiple times.
if (currentClient instanceof InternalEnumerateClient
|| currentClient instanceof InternalRemovalClient) {
// This condition means we're currently running internal diagnostics to
// remove extra templates in the hardware and/or the software
// TODO: design an escape hatch in case client never finishes
if (newClient != null) {
Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
+ ", initiatedByClient = " + initiatedByClient);
}
} else {
currentClient.stop(initiatedByClient);
// Only post the reset runnable for non-cleanup clients. Cleanup clients should
// never be forcibly stopped since they ensure synchronization between HAL and
// framework. Thus, we should instead just start the pending client once cleanup
// finishes instead of using the reset runnable.
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
}
mPendingClient = newClient;
} else if (newClient != null) {
// For BiometricPrompt clients, do not start until
// <Biometric>Service#startPreparedClient is called. BiometricService waits until all
// modalities are ready before initiating authentication.
if (newClient instanceof AuthenticationClient) {
AuthenticationClient client = (AuthenticationClient) newClient;
if (client.isBiometricPrompt()) {
if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
mCurrentClient = newClient;
if (mBiometricService == null) {
mBiometricService = IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE));
}
try {
mBiometricService.onReadyForAuthentication(client.getCookie(),
client.getRequireConfirmation(), client.getTargetUserId());
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception", e);
}
return;
}
}
// We are not a BiometricPrompt client, start the client immediately
mCurrentClient = newClient;
/*重點關注*/
startCurrentClient(mCurrentClient.getCookie());
}
}
在此將EnrollClient
的對象傳進去
startCurrentClient(mCurrentClient.getCookie());
protected void startCurrentClient(int cookie) {
if (mCurrentClient == null) {
Slog.e(getTag(), "Trying to start null client!");
return;
}
if (DEBUG) Slog.v(getTag(), "starting client "
+ mCurrentClient.getClass().getSuperclass().getSimpleName()
+ "(" + mCurrentClient.getOwnerString() + ")"
+ " cookie: " + cookie + "/" + mCurrentClient.getCookie());
if (cookie != mCurrentClient.getCookie()) {
Slog.e(getTag(), "Mismatched cookie");
return;
}
notifyClientActiveCallbacks(true);
/*重點關注*/
mCurrentClient.start();
}
frameworks/base/services/core/java/com/android/server/biometrics/
EnrollClient.java
@Override
public int start() {
mEnrollmentStartTimeMs = System.currentTimeMillis();
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
final ArrayList<Integer> disabledFeatures = new ArrayList<>();
for (int i = 0; i < mDisabledFeatures.length; i++) {
disabledFeatures.add(mDisabledFeatures[i]);
}
/*重點關注*/
final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout,
disabledFeatures);
if (result != 0) {
Slog.w(getLogTag(), "startEnroll failed, result=" + result);
mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result);
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
return result;
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "startEnroll failed", e);
}
return 0; // success
}
start
方法會調用fingerprintd
,調用底層的指紋庫,底層庫返回結果後會調用onEnrollResult
來反饋結果receiver
,再往上層反饋。這就是指紋的錄製流程。在onEnrollResult
中當remaining
等於0的時候完成錄製,調用addBiometricForUser
。
FingerprintManager.java
中註冊了IFingerprintServiceReceiver
,實現onEnrollResult
方法發送 MSG_ENROLL_RESULT
frameworks/base/core/java/android/hardware/fingerprint/
FingerprintManager.java
private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
@Override // binder call
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
/*重點關注*/
mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
}
.......
};
}
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_ENROLL_RESULT:
/*重點關注*/
sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
break;
.......
}
}
};
調用回調方法
private void sendEnrollResult(Fingerprint fp, int remaining) {
if (mEnrollmentCallback != null) {
/*重點關注*/
mEnrollmentCallback.onEnrollmentProgress(remaining);
}
}
再看看Settings
在FingerprintEnrollSidecar
中通過實現錄入的回調接口FingerprintManager.EnrollmentCallback
來監聽錄入過程中onEnrollmentProgress
, onEnrollmentHelp
, onEnrollmentError
的錄入狀態
並在他的父類BiometricEnrollSidercar
寫有一個內部接口Listener
packages/apps/Settings/src/com/android/settings/biometrics/
BiometricEnrollSidecar.java
public interface Listener {
void onEnrollmentHelp(int helpMsgId, CharSequence helpString);
void onEnrollmentError(int errMsgId, CharSequence errString);
void onEnrollmentProgressChange(int steps, int remaining);
}
BiometricsEnrollEnrolling
實現了 FingerprintEnrollSidecar.Listener
,從而實現了 CallBack
二、指紋匹配
指紋解鎖的入口在Keyguard
中
系統滅屏之後會調用PhoneWindowManager
的startedGoingToSleep
方法,繼而調用KeyguardDelegate.onStartedGoingToSleep
方法。
繼而又會調用KeyguardServiceWrapper.java
==》onStartedGoingToSleep()
方法;再調用KeyguardService.java
==》onStartedGoingToSleep()
方法;並最終在KeyguardViewMediator.java
==》dispatchStartedGoingToSleep()
達到對GoingToSleep
事件的監聽
frameworks/base/packages/SystemUI/src/com/android/keyguard/
KeyguardUpdateMonitor.java
public void dispatchStartedGoingToSleep(int why) {
/*重點關注*/
mHandler.sendMessage(mHandler.obtainMessage(MSG_STARTED_GOING_TO_SLEEP, why, 0));
}
MSG_STARTED_GOING_TO_SLEEP
case MSG_STARTED_GOING_TO_SLEEP:
/*重點關注*/
handleStartedGoingToSleep(msg.arg1);
break;
handleStartedGoingToSleep(msg.arg1);
protected void handleStartedGoingToSleep(int arg1) {
clearBiometricRecognized();
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onStartedGoingToSleep(arg1);
}
}
mGoingToSleep = true;
/*重點關注*/
updateBiometricListeningState();//更新生物識別狀態
}
updateBiometricListeningState()
//在這個方法中我們可以看到同時更新了指紋和人臉的狀態
private void updateBiometricListeningState() {
/*重點關注*/
updateFingerprintListeningState();
updateFaceListeningState();
}
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_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
return;
}
mHandler.removeCallbacks(mRetryFingerprintAuthentication);
boolean shouldListenForFingerprint = shouldListenForFingerprint();
boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
|| mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
if (runningOrRestarting && !shouldListenForFingerprint) {
stopListeningForFingerprint();
} else if (!runningOrRestarting && shouldListenForFingerprint) {
/*重點關注*/
startListeningForFingerprint();
}
}
startListeningForFingerprint();
private void startListeningForFingerprint() {
if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) {
setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
return;
}
if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
// Waiting for restart via handleFingerprintError().
return;
}
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
int userId = getCurrentUser();
if (isUnlockWithFingerprintPossible(userId)) {
if (mFingerprintCancelSignal != null) {
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
/*重點關注*/
mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
null, userId);
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
}
frameworks/base/core/java/android/hardware/fingerprint/
FingerprintManager.java
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
if (cancel != null) {
if (cancel.isCanceled()) {
Slog.w(TAG, "authentication already canceled");
return;
} else {
cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
}
}
if (mService != null) try {
useHandler(handler);
mAuthenticationCallback = callback;
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
/*重點關注*/
//這裏又是調用FingerprintService的authenticate方法
mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */));
}
}
}
frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/
FingerprintService.java
@Override // Binder call
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
updateActiveGroup(groupId, opPackageName);
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, groupId, opId, restricted, opPackageName,
0 /* cookie */, false /* requireConfirmation */);
/*重點關注*/
authenticateInternal(client, opId, opPackageName);
}
frameworks/base/services/core/java/com/android/server/biometrics/
BiometricServiceBase.java
protected void authenticateInternal(AuthenticationClientImpl client, long opId,
String opPackageName, int callingUid, int callingPid, int callingUserId) {
if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
callingUserId)) {
if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
return;
}
mHandler.post(() -> {
mMetricsLogger.histogram(getConstants().tagAuthToken(), opId != 0L ? 1 : 0);
// Get performance stats object for this user.
HashMap<Integer, PerformanceStats> pmap
= (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
PerformanceStats stats = pmap.get(mCurrentUserId);
if (stats == null) {
stats = new PerformanceStats();
pmap.put(mCurrentUserId, stats);
}
mPerformanceStats = stats;
mIsCrypto = (opId != 0);
/*重點關注*/
startAuthentication(client, opPackageName);
});
}
startAuthentication(client, opPackageName);
private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
int lockoutMode = getLockoutMode();
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
Slog.w(getTag(), "Cannot send permanent lockout message to client");
}
return;
}
/*重點關注*/
startClient(client, true /* initiatedByClient */);
//這裏將AuthenticationClient傳遞進去
}
startClient(client, true /* initiatedByClient */);
private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
// This check only matters for FingerprintService, since enumerate may call back
// multiple times.
if (currentClient instanceof InternalEnumerateClient
|| currentClient instanceof InternalRemovalClient) {
// This condition means we're currently running internal diagnostics to
// remove extra templates in the hardware and/or the software
// TODO: design an escape hatch in case client never finishes
if (newClient != null) {
Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
+ ", initiatedByClient = " + initiatedByClient);
}
} else {
currentClient.stop(initiatedByClient);
// Only post the reset runnable for non-cleanup clients. Cleanup clients should
// never be forcibly stopped since they ensure synchronization between HAL and
// framework. Thus, we should instead just start the pending client once cleanup
// finishes instead of using the reset runnable.
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
}
mPendingClient = newClient;
} else if (newClient != null) {
// For BiometricPrompt clients, do not start until
// <Biometric>Service#startPreparedClient is called. BiometricService waits until all
// modalities are ready before initiating authentication.
if (newClient instanceof AuthenticationClient) {
AuthenticationClient client = (AuthenticationClient) newClient;
if (client.isBiometricPrompt()) {
if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
mCurrentClient = newClient;
if (mBiometricService == null) {
mBiometricService = IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE));
}
try {
mBiometricService.onReadyForAuthentication(client.getCookie(),
client.getRequireConfirmation(), client.getTargetUserId());
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception", e);
}
return;
}
}
// We are not a BiometricPrompt client, start the client immediately
mCurrentClient = newClient;
/*重點關注*/
startCurrentClient(mCurrentClient.getCookie());
//這裏繼續將AuthenticationClient傳遞進去
}
}
startCurrentClient(mCurrentClient.getCookie());
protected void startCurrentClient(int cookie) {
if (mCurrentClient == null) {
Slog.e(getTag(), "Trying to start null client!");
return;
}
if (DEBUG) Slog.v(getTag(), "starting client "
+ mCurrentClient.getClass().getSuperclass().getSimpleName()
+ "(" + mCurrentClient.getOwnerString() + ")"
+ " cookie: " + cookie + "/" + mCurrentClient.getCookie());
if (cookie != mCurrentClient.getCookie()) {
Slog.e(getTag(), "Mismatched cookie");
return;
}
notifyClientActiveCallbacks(true);
/*重點關注*/
mCurrentClient.start();
//這裏調用的是AuthenticationClient的start方法
}
frameworks/base/services/core/java/com/android/server/biometrics/
AuthenticationClient.java
public int start() {
mStarted = true;
onStart();
try {
/*重點關注*/
final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
if (result != 0) {
Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
mMetricsLogger.histogram(mConstants.tagAuthStartError(), result);
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
return result;
}
if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
} catch (RemoteException e) {
Slog.e(getLogTag(), "startAuthentication failed", e);
return ERROR_ESRCH;
}
return 0; // success
}
start
方法會調用fingerprintd
,調用底層的指紋庫,底層庫返回結果後會調用onAuthenticated
來反饋結果給receiver
,在往上層反饋
三、指紋解鎖屏幕
frameworks/base/services/core/java/com/android/server/biometrics/
AuthenticationClient.java
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {
super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
getTargetUserId(), isBiometricPrompt());
final BiometricServiceBase.ServiceListener listener = getListener();
mMetricsLogger.action(mConstants.actionBiometricAuth(), authenticated);
boolean result = false;
try {
if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + authenticated + ")"
+ ", ID:" + identifier.getBiometricId()
+ ", Owner: " + getOwnerString()
+ ", isBP: " + isBiometricPrompt()
+ ", listener: " + listener
+ ", requireConfirmation: " + mRequireConfirmation
+ ", user: " + getTargetUserId());
if (authenticated) {
mAlreadyDone = true;
if (listener != null) {
vibrateSuccess();
}
result = true;
if (shouldFrameworkHandleLockout()) {
resetFailedAttempts();
}
onStop();
final byte[] byteToken = new byte[token.size()];
for (int i = 0; i < token.size(); i++) {
byteToken[i] = token.get(i);
}
if (isBiometricPrompt() && listener != null) {
// BiometricService will add the token to keystore
listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
} else if (!isBiometricPrompt() && listener != null) {
KeyStore.getInstance().addAuthToken(byteToken);
try {
// Explicitly have if/else here to make it super obvious in case the code is
// touched in the future.
if (!getIsRestricted()) {
/*重點關注*/
listener.onAuthenticationSucceeded(
getHalDeviceId(), identifier, getTargetUserId());
} else {
listener.onAuthenticationSucceeded(
getHalDeviceId(), null, getTargetUserId());
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception", e);
}
} else {
// Client not listening
Slog.w(getLogTag(), "Client not listening");
result = true;
}
} else {
if (listener != null) {
vibrateError();
}
// Allow system-defined limit of number of attempts before giving up
final int lockoutMode = handleFailedAttempt();
if (lockoutMode != LOCKOUT_NONE && shouldFrameworkHandleLockout()) {
Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
+ lockoutMode + ")");
stop(false);
final int errorCode = lockoutMode == LOCKOUT_TIMED
? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
: BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
} else {
// Don't send onAuthenticationFailed if we're in lockout, it causes a
// janky UI on Keyguard/BiometricPrompt since "authentication failed"
// will show briefly and be replaced by "device locked out" message.
if (listener != null) {
if (isBiometricPrompt()) {
listener.onAuthenticationFailedInternal(getCookie(),
getRequireConfirmation());
} else {
listener.onAuthenticationFailed(getHalDeviceId());
}
}
}
result = lockoutMode != LOCKOUT_NONE; // in a lockout mode
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception", e);
result = true;
}
return result;
}
frameworks/base/services/core/java/com/android/server/biometrics/
BiometricServiceBase.java
protected interface ServiceListener {
default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException;
/*重點關注*/
default void onAuthenticationSucceeded(long deviceId,
BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
default void onAuthenticationFailed(long deviceId) throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException;
default void onRemoved(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
default void onEnumerated(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
}
onAuthenticationSucceeded
/**
* Wraps the callback interface from Service -> BiometricPrompt
*/
protected abstract class BiometricServiceListener implements ServiceListener {
private IBiometricServiceReceiverInternal mWrapperReceiver;
public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) {
mWrapperReceiver = wrapperReceiver;
}
public IBiometricServiceReceiverInternal getWrapperReceiver() {
return mWrapperReceiver;
}
@Override
public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
throws RemoteException {
if (getWrapperReceiver() != null) {
/*重點關注*/
getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
}
}
@Override
public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
throws RemoteException {
if (getWrapperReceiver() != null) {
getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
}
}
}
frameworks/base/core/java/android/hardware/fingerprint/
FingerprintManager.java
private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
@Override // binder call
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
}
@Override // binder call
public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode,
deviceId).sendToTarget();
}
@Override // binder call
/*重點關注*/
public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
}
@Override // binder call
public void onAuthenticationFailed(long deviceId) {
mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
}
@Override // binder call
public void onError(long deviceId, int error, int vendorCode) {
mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
}
@Override // binder call
public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
}
@Override // binder call
public void onEnumerated(long deviceId, int fingerId, int groupId, int remaining) {
// TODO: propagate remaining
mHandler.obtainMessage(MSG_ENUMERATED, fingerId, groupId, deviceId).sendToTarget();
}
};
}
MSG_AUTHENTICATION_SUCCEEDED
case MSG_AUTHENTICATION_SUCCEEDED:
/*重點關注*/
sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */);
break;
sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */);
private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
if (mAuthenticationCallback != null) {
final AuthenticationResult result =
new AuthenticationResult(mCryptoObject, fp, userId);
/*重點關注*/
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
AuthenticationCallback
是Fpm
的一個內部回調接口
public static abstract class AuthenticationCallback
extends BiometricAuthenticator.AuthenticationCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
* @param errorCode An integer identifying the error message
* @param errString A human-readable error string that can be shown in UI
*/
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) { }
/**
* Called when a recoverable error has been encountered during authentication. The help
* string is provided to give the user guidance for what went wrong, such as
* "Sensor dirty, please clean it."
* @param helpCode An integer identifying the error message
* @param helpString A human-readable string that can be shown in UI
*/
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
/**
* Called when a fingerprint is recognized.
* @param result An object containing authentication-related data
*/
/*重點關注*/
public void onAuthenticationSucceeded(AuthenticationResult result) { }
/**
* Called when a fingerprint is valid but not recognized.
*/
@Override
public void onAuthenticationFailed() { }
/**
* Called when a fingerprint image has been acquired, but wasn't processed yet.
*
* @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants
* @hide
*/
@Override
public void onAuthenticationAcquired(int acquireInfo) {}
};
frameworks/base/packages/SystemUI/src/com/android/keyguard/
KeyguardUpdateMonitor.java
AuthenticationCallback
接口在KeyguardUpdateMonitor.java
中實現,用於監聽FingerprintService
中指紋的解鎖狀態
private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
= 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
public void onAuthenticationError(int errMsgId, CharSequence errString) {
handleFingerprintError(errMsgId, errString.toString());
}
@Override
public void onAuthenticationAcquired(int acquireInfo) {
handleFingerprintAcquired(acquireInfo);
}
};
handleFingerprintAuthenticated(result.getUserId());
private void handleFingerprintAuthenticated(int authUserId) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
try {
final int userId;
try {
userId = ActivityManager.getService().getCurrentUser().id;
} catch (RemoteException e) {
Log.e(TAG, "Failed to get current user id: ", e);
return;
}
if (userId != authUserId) {
Log.d(TAG, "Fingerprint authenticated for wrong user: " + authUserId);
return;
}
if (isFingerprintDisabled(userId)) {
Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
return;
}
/*重點關注*/
onFingerprintAuthenticated(userId);
} finally {
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
Trace.endSection();
}
onFingerprintAuthenticated(userId);
@VisibleForTesting
protected void onFingerprintAuthenticated(int userId) {
Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated");
mUserFingerprintAuthenticated.put(userId, true);
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT);
}
// Don't send cancel if authentication succeeds
mFingerprintCancelSignal = null;
for (int i = 0; i < mCallbacks.size(); i++) {
/*重點關注*/
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
/*重點關注*/
cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT);
}
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
BIOMETRIC_CONTINUE_DELAY_MS);
// Only authenticate fingerprint once when assistant is visible
mAssistantVisible = false;
Trace.endSection();
}
可以看到在onFingerprintAuthenticated()
方法中調用了KeyguardUpdateMonitorCallback
這個抽象類的onBiometricAuthenticated()
抽象方法,而BiometricUnlockController extends KeyguardUpdateMonitorCallback
,並註冊了回調mUpdateMonitor.registerCallback(this)
frameworks/base/packages/SystemUI/src/com/android/keyguard/
KeyguardUpdateMonitorCallback.java
/**
* Called when a biometric is recognized.
* @param userId the user id for which the biometric sample was authenticated
* @param biometricSourceType
*/
/*重點關注*/
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { }
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/
BiometricUnlockController.java
@Override
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
if (mUpdateMonitor.isGoingToSleep()) {
mPendingAuthenticatedUserId = userId;
mPendingAuthenticatedBioSourceType = biometricSourceType;
Trace.endSection();
return;
}
mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
.setType(MetricsEvent.TYPE_SUCCESS).setSubtype(toSubtype(biometricSourceType)));
/*重點關注*/
startWakeAndUnlock(calculateMode(biometricSourceType));
}
startWakeAndUnlock(calculateMode(biometricSourceType));
public void startWakeAndUnlock(int mode) {
// TODO(b/62444020): remove when this bug is fixed
Log.v(TAG, "startWakeAndUnlock(" + mode + ")");
boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
mMode = mode;
mHasScreenTurnedOnSinceAuthenticating = false;
if (mMode == MODE_WAKE_AND_UNLOCK_PULSING && pulsingOrAod()) {
// If we are waking the device up while we are pulsing the clock and the
// notifications would light up first, creating an unpleasant animation.
// Defer changing the screen brightness by forcing doze brightness on our window
// until the clock and the notifications are faded out.
mStatusBarWindowController.setForceDozeBrightness(true);
}
// During wake and unlock, we need to draw black before waking up to avoid abrupt
// brightness changes due to display state transitions.
boolean alwaysOnEnabled = DozeParameters.getInstance(mContext).getAlwaysOn();
boolean delayWakeUp = mode == MODE_WAKE_AND_UNLOCK && alwaysOnEnabled && mWakeUpDelay > 0;
Runnable wakeUp = ()-> {
if (!wasDeviceInteractive) {
if (DEBUG_BIO_WAKELOCK) {
Log.i(TAG, "bio wakelock: Authenticated, waking up...");
}
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"android.policy:BIOMETRIC");
}
if (delayWakeUp) {
/*重點關注*/
mKeyguardViewMediator.onWakeAndUnlocking();
}
Trace.beginSection("release wake-and-unlock");
releaseBiometricWakeLock();
Trace.endSection();
};
if (!delayWakeUp) {
wakeUp.run();
}
switch (mMode) {
case MODE_DISMISS_BOUNCER:
Trace.beginSection("MODE_DISMISS");
mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
false /* strongAuth */);
Trace.endSection();
break;
case MODE_UNLOCK:
case MODE_SHOW_BOUNCER:
Trace.beginSection("MODE_UNLOCK or MODE_SHOW_BOUNCER");
if (!wasDeviceInteractive) {
mPendingShowBouncer = true;
} else {
showBouncer();
}
Trace.endSection();
break;
case MODE_WAKE_AND_UNLOCK_FROM_DREAM:
case MODE_WAKE_AND_UNLOCK_PULSING:
case MODE_WAKE_AND_UNLOCK:
if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
mMediaManager.updateMediaMetaData(false /* metaDataChanged */,
true /* allowEnterAnimation */);
} else if (mMode == MODE_WAKE_AND_UNLOCK){
Trace.beginSection("MODE_WAKE_AND_UNLOCK");
} else {
Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM");
mUpdateMonitor.awakenFromDream();
}
mStatusBarWindowController.setStatusBarFocusable(false);
if (delayWakeUp) {
mHandler.postDelayed(wakeUp, mWakeUpDelay);
} else {
mKeyguardViewMediator.onWakeAndUnlocking();
}
if (mStatusBar.getNavigationBarView() != null) {
mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
}
Trace.endSection();
break;
case MODE_ONLY_WAKE:
case MODE_NONE:
break;
}
mStatusBar.notifyBiometricAuthModeChanged();
Trace.endSection();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/
KeyguardViewMediator.java
public void onWakeAndUnlocking() {
Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
mWakeAndUnlocking = true;
/*重點關注*/
keyguardDone();
Trace.endSection();
}
keyguardDone();
public void keyguardDone() {
Trace.beginSection("KeyguardViewMediator#keyguardDone");
if (DEBUG) Log.d(TAG, "keyguardDone()");
userActivity();
EventLog.writeEvent(70000, 2);
/*重點關注*/
Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
mHandler.sendMessage(msg);
Trace.endSection();
}
KEYGUARD_DONE
case KEYGUARD_DONE:
Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE");
/*重點關注*/
handleKeyguardDone();
Trace.endSection();
break;
handleKeyguardDone();
private void handleKeyguardDone() {
Trace.beginSection("KeyguardViewMediator#handleKeyguardDone");
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
mUiOffloadThread.submit(() -> {
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
}
});
if (DEBUG) Log.d(TAG, "handleKeyguardDone");
synchronized (this) {
resetKeyguardDonePendingLocked();
}
mUpdateMonitor.clearBiometricRecognized();
if (mGoingToSleep) {
Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
return;
}
if (mExitSecureCallback != null) {
try {
mExitSecureCallback.onKeyguardExitResult(true /* authenciated */);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onKeyguardExitResult()", e);
}
mExitSecureCallback = null;
// after succesfully exiting securely, no need to reshow
// the keyguard when they've released the lock
mExternallyEnabled = true;
mNeedToReshowWhenReenabled = false;
updateInputRestricted();
}
/*重點關注*/
handleHide();
Trace.endSection();
}
handleHide();
private void handleHide() {
Trace.beginSection("KeyguardViewMediator#handleHide");
// It's possible that the device was unlocked in a dream state. It's time to wake up.
if (mAodShowing) {
PowerManager pm = mContext.getSystemService(PowerManager.class);
pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:BOUNCER_DOZING");
}
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleHide");
if (mustNotUnlockCurrentUser()) {
// In split system user mode, we never unlock system user. The end user has to
// switch to another user.
// TODO: We should stop it early by disabling the swipe up flow. Right now swipe up
// still completes and makes the screen blank.
if (DEBUG) Log.d(TAG, "Split system user, quit unlocking.");
return;
}
mHiding = true;
if (mShowing && !mOccluded) {
mKeyguardGoingAwayRunnable.run();
} else {
/*重點關注*/
handleStartKeyguardExitAnimation(
SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
mHideAnimation.getDuration());
}
}
Trace.endSection();
}
handleStartKeyguardExitAnimation
private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+ " fadeoutDuration=" + fadeoutDuration);
synchronized (KeyguardViewMediator.this) {
if (!mHiding) {
// Tell ActivityManager that we canceled the keyguardExitAnimation.
setShowingLocked(mShowing, mAodShowing, true /* force */);
return;
}
mHiding = false;
if (mWakeAndUnlocking && mDrawnCallback != null) {
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
// the next draw from here so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
mStatusBarKeyguardViewManager.getViewRootImpl().setReportNextDraw();
notifyDrawn(mDrawnCallback);
mDrawnCallback = null;
}
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
playSounds(false);
}
mWakeAndUnlocking = false;
setShowingLocked(false, mAodShowing);
mDismissCallbackRegistry.notifyDismissSucceeded();
/*重點關注*/
mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
adjustStatusBarLocked();
sendUserPresentBroadcast();
}
Trace.endSection();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/
StatusBarKeyguardViewManager.java
/**
* Hides the keyguard view
*/
public void hide(long startTime, long fadeoutDuration) {
mShowing = false;
mKeyguardMonitor.notifyKeyguardState(
mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
launchPendingWakeupAction();
if (KeyguardUpdateMonitor.getInstance(mContext).needsSlowUnlockTransition()) {
fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED;
}
long uptimeMillis = SystemClock.uptimeMillis();
long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
if (mStatusBar.isInLaunchTransition() ) {
mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
@Override
public void run() {
mStatusBarWindowController.setKeyguardShowing(false);
mStatusBarWindowController.setKeyguardFadingAway(true);
hideBouncer(true /* destroyView */);
updateStates();
}
}, new Runnable() {
@Override
public void run() {
mStatusBar.hideKeyguard();
mStatusBarWindowController.setKeyguardFadingAway(false);
mViewMediatorCallback.keyguardGone();
executeAfterKeyguardGoneAction();
}
});
} else {
executeAfterKeyguardGoneAction();
boolean wakeUnlockPulsing =
mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
if (wakeUnlockPulsing) {
delay = 0;
fadeoutDuration = 240;
}
mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
mBiometricUnlockController.startKeyguardFadingAway();
/*重點關注*/
hideBouncer(true /* destroyView */);
if (wakeUnlockPulsing) {
mStatusBar.fadeKeyguardWhilePulsing();
wakeAndUnlockDejank();
} else {
boolean staying = mStatusBar.hideKeyguard();
if (!staying) {
mStatusBarWindowController.setKeyguardFadingAway(true);
// hide() will happen asynchronously and might arrive after the scrims
// were already hidden, this means that the transition callback won't
// be triggered anymore and StatusBarWindowController will be forever in
// the fadingAway state.
mStatusBar.updateScrimController();
wakeAndUnlockDejank();
} else {
mStatusBar.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
}
}
updateStates();
mStatusBarWindowController.setKeyguardShowing(false);
mViewMediatorCallback.keyguardGone();
}
StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
StatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN);
}
hideBouncer
private void hideBouncer(boolean destroyView) {
if (mBouncer == null) {
return;
}
/*重點關注*/
mBouncer.hide(destroyView);
cancelPendingWakeupAction();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/
KeyguardBouncer.java
public void hide(boolean destroyView) {
if (isShowing()) {
StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN);
mDismissCallbackRegistry.notifyDismissCancelled();
}
mIsScrimmed = false;
mFalsingManager.onBouncerHidden();
mCallback.onBouncerVisiblityChanged(false /* shown */);
cancelShowRunnable();
if (mKeyguardView != null) {
mKeyguardView.cancelDismissAction();
mKeyguardView.cleanUp();
}
mIsAnimatingAway = false;
if (mRoot != null) {
mRoot.setVisibility(View.INVISIBLE);
if (destroyView) {
// We have a ViewFlipper that unregisters a broadcast when being detached, which may
// be slow because of AM lock contention during unlocking. We can delay it a bit.
/*重點關注*/
mHandler.postDelayed(mRemoveViewRunnable, 50);
}
}
}
mRemoveViewRunnable
private final Runnable mRemoveViewRunnable = this::removeView;
removeView;
protected void removeView() {
if (mRoot != null && mRoot.getParent() == mContainer) {
/*重點關注*/
mContainer.removeView(mRoot);
mRoot = null;
}
}
至此鎖屏界面移除的邏輯基本clear