Android R于近期发布,针对Android Q BiometricPrompt暴露的一些bug,R中给出了答案。
一起来看看吧
之前我们已经报了BiometricPrompt于Android Q的重要bug,见《BiometricPrompt之四 - BiometricDialogView无法消失》。
首先看视频:
再看看android R log:
04-18 12:47:53.832 914 914 D BiometricService: Package: com.android.settings Authenticator ID: 0 Modality: 2 Reported Modality: 2 Status: 1
04-18 12:47:53.832 914 914 D BiometricService: Creating auth session. Modality: 2, cookie: 847382688, authenticators: 33023
04-18 12:47:53.836 914 914 V FingerprintService: startAuthentication(com.android.settings)
04-18 12:47:53.836 914 914 V FingerprintService: Returning cookie: 847382688
04-18 12:47:53.837 914 914 D BiometricService: Matched cookie: 847382688, 0 remaining
04-18 12:47:53.837 914 914 V FingerprintService: starting client AuthenticationClientImpl(com.android.settings) targetUserId: 0 currentUserId: 0 cookie: 847382688/847382688
04-18 12:47:53.837 755 861 D android.hardware.biometrics.fingerprint@2.1-service: BiometricsFingerprint::authenticate(opid, gid)
04-18 12:47:53.965 914 914 W FingerprintService: client com.android.settings is authenticating...
04-18 12:47:53.966 914 914 W Looper : Slow dispatch took 130ms main h=com.android.server.biometrics.BiometricService$1 c=null m=8
04-18 12:47:53.968 2569 2569 D BiometricPrompt/AuthController: showAuthenticationDialog, authenticators: 33023, biometricModality: 2, requireConfirmation: false, operationId: 0
04-18 12:47:53.985 2569 2569 D BiometricPrompt/AuthController: userId: 0 savedState: null mCurrentDialog: null newDialog: com.android.systemui.biometrics.AuthContainerView{5905139 VFE...... .F....I. 0,0-0,0} type: 2
04-18 12:47:54.007 2569 2569 V BiometricPrompt/AuthBiometricView: newState: 1
04-18 12:47:54.019 2569 2569 V BiometricPrompt/AuthBiometricView: Current size: 0 New size: 2
04-18 12:47:54.026 604 2071 E BufferQueueCore: [BiometricPrompt#0](id:25c00000047,api:1,p:2569,c:604) Slot 1 is in mActiveBuffers but has no buffer
04-18 12:47:54.303 2569 2569 V BiometricPrompt/AuthBiometricView: newState: 2
//Menu pressed
04-18 12:48:13.855 2569 2569 W BiometricPrompt/AuthController: ACTION_CLOSE_SYSTEM_DIALOGS received
04-18 12:48:13.865 914 914 V FingerprintService: Stopping client com.android.settings, fromClient: false
04-18 12:48:13.976 755 861 D android.hardware.biometrics.fingerprint@2.1-service: onError(5)
04-18 12:48:13.977 914 914 W FingerprintService: client com.android.settings is no longer authenticating
04-18 12:48:13.977 914 914 W Looper : Slow dispatch took 113ms main h=com.android.server.biometrics.BiometricServiceBase$H c=com.android.server.biometrics.-$$Lambda$BiometricServiceBase$B1PDNz5plOtQUbeZgXMkI_dh_yQ@5ebc01d m=0
04-18 12:48:13.977 914 914 V FingerprintService: handleError(client=com.android.settings, error = 5)
04-18 12:48:13.977 914 914 V BiometricStats: Error latency: -1
04-18 12:48:13.977 914 914 W BiometricStats: found a negative latency : -1
04-18 12:48:13.978 914 914 V FingerprintService: Done with client: com.android.settings
04-18 12:48:13.978 914 914 D BiometricService: handleOnError: 5 cookie: 847382688
04-18 12:48:13.979 914 914 E BiometricService: Unknown cookie: 847382688
04-18 12:48:14.244 2569 2569 D BiometricPrompt/AuthContainerView: pendingCallback: null
04-18 12:48:14.296 914 1016 W InputDispatcher: channel '18f1ba9 BiometricPrompt (server)' ~ Consumer closed input channel or an error occurred. events=0x9
04-18 12:48:14.296 914 1016 E InputDispatcher: channel '18f1ba9 BiometricPrompt (server)' ~ Channel is unrecoverably broken and will be disposed!
04-18 12:48:14.306 914 1663 W InputDispatcher: Attempted to unregister already unregistered input channel '18f1ba9 BiometricPrompt (server)'
指纹验证成功的log:
指纹验证成功的log:
04-18 12:47:48.626 755 907 D android.hardware.biometrics.fingerprint@2.1-service: onAcquired(0)
04-18 12:47:48.626 755 907 D android.hardware.biometrics.fingerprint@2.1-service: onAcquired(6)
04-18 12:47:48.628 914 914 V FingerprintService: Acquired: 0 0
04-18 12:47:48.633 914 914 V FingerprintService: Acquired: 6 2
04-18 12:47:48.634 914 914 W FingerprintManager: Invalid acquired message: 6, 2
04-18 12:47:48.634 914 914 W BiometricService: Ignoring null message: 0
04-18 12:47:48.635 914 914 W BiometricService: Ignoring null message: 6
04-18 12:47:48.700 755 907 D android.hardware.biometrics.fingerprint@2.1-service: onAcquired(0)
04-18 12:47:48.700 755 907 D android.hardware.biometrics.fingerprint@2.1-service: onAuthenticated(fid=1153064581, gid=0)
04-18 12:47:48.701 914 914 V FingerprintService: Acquired: 0 0
04-18 12:47:48.702 914 914 V BiometricStats: Authentication latency: -1
04-18 12:47:48.702 914 914 W BiometricStats: found a negative latency : -1
04-18 12:47:48.703 914 914 V FingerprintService: onAuthenticated(true), ID:1153064581, Owner: com.android.settings, isBP: true, listener: com.android.server.biometrics.fingerprint.FingerprintService$BiometricPromptServiceListenerImpl@4317d4, requireConfirmation: false, user: 0
04-18 12:47:48.709 914 914 V FingerprintService: Done with client: com.android.settings
04-18 12:47:48.709 914 914 W BiometricService: Ignoring null message: 0
04-18 12:47:48.715 2569 2569 V BiometricPrompt/AuthBiometricView: newState: 6
04-18 12:47:48.767 755 907 I chatty : uid=1000(system) fingerprint@2.1 identical 1 line
04-18 12:47:48.948 755 907 D android.hardware.biometrics.fingerprint@2.1-service: onAcquired(6)
04-18 12:47:49.116 2569 2569 D BiometricPrompt/AuthContainerView: pendingCallback: 4
04-18 12:47:49.117 2569 2569 D BiometricPrompt/AuthController: onDialogDismissed: 4
04-18 12:47:49.146 914 1016 W InputDispatcher: channel '68b0a19 BiometricPrompt (server)' ~ Consumer closed input channel or an error occurred. events=0x9
04-18 12:47:49.146 914 1016 E InputDispatcher: channel '68b0a19 BiometricPrompt (server)' ~ Channel is unrecoverably broken and will be disposed!
04-18 12:47:49.146 914 2439 W InputDispatcher: Attempted to unregister already unregistered input channel '68b0a19 BiometricPrompt (server)'
再看看源码:
lenovo@HZCS13:~/coding/r/frameworks/base/packages/SystemUI$ ll src/com/android/systemui/biometrics/
total 164
drwxr-xr-x 2 lenovo lenov 4096 Apr 18 18:18 ./
drwxr-xr-x 49 lenovo lenov 4096 Apr 18 18:18 ../
-rw-r--r-- 1 lenovo lenov 8325 Apr 18 18:18 AuthBiometricFaceView.java
-rw-r--r-- 1 lenovo lenov 4453 Apr 18 18:18 AuthBiometricFingerprintView.java
-rw-r--r-- 1 lenovo lenov 29073 Apr 18 18:18 AuthBiometricView.java
-rw-r--r-- 1 lenovo lenov 23530 Apr 18 18:18 AuthContainerView.java
-rw-r--r-- 1 lenovo lenov 18094 Apr 18 18:18 AuthController.java
-rw-r--r-- 1 lenovo lenov 4519 Apr 18 18:18 AuthCredentialPasswordView.java
-rw-r--r-- 1 lenovo lenov 3483 Apr 18 18:18 AuthCredentialPatternView.java
-rw-r--r-- 1 lenovo lenov 18594 Apr 18 18:18 AuthCredentialView.java
-rw-r--r-- 1 lenovo lenov 2219 Apr 18 18:18 AuthDialogCallback.java
-rw-r--r-- 1 lenovo lenov 3964 Apr 18 18:18 AuthDialog.java
-rw-r--r-- 1 lenovo lenov 6547 Apr 18 18:18 AuthPanelController.java
-rw-r--r-- 1 lenovo lenov 115 Apr 18 18:18 OWNERS
-rw-r--r-- 1 lenovo lenov 4316 Apr 18 18:18 Utils.java
有代码可见,框架在SystemUI部分也给出了一些调整,不再叫BiometricDialogView, 而改成了AuthBiometricView。
SystemUI端实现AuthBiometricView相关类图
由上可知,其实现不仅支持生物特征验证还支持强密码(例如:Pattern)验证。
前面提到Android Q中遇到Menu键不消失问题,现直接SystemUI内部AuthController注册监听Intent.ACTION_CLOSE_SYSTEM_DIALOGS广播的方式dismiss。
不再需要BiometricService发送消息来HideDialog。
@VisibleForTesting
AuthController(Context context, CommandQueue commandQueue, Injector injector) {
super(context);
mCommandQueue = commandQueue;
mInjector = injector;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.registerReceiver(mBroadcastReceiver, filter);
}
@VisibleForTesting
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mCurrentDialog != null
&& Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
Log.w(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received");
mCurrentDialog.dismissWithoutCallback(true /* animate */);
mCurrentDialog = null;
try {
if (mReceiver != null) {
mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
null /* credentialAttestation */);
mReceiver = null;
}
} catch (RemoteException e) {
Log.e(TAG, "Remote exception", e);
}
}
}
};
由SystemUI发送dismiss消息到BiometricService去cancelAuthentication()取消指纹监听,同时往上发送onAuthenticationError(error, errorMessage)消息到3rd app。释放当前Session: mCurrentAuthSession。
FingerprintService取消指纹监听后,会往BiometricService上报Error。
private void handleOnDismissed(int reason, @Nullable byte[] credentialAttestation) {
if (mCurrentAuthSession == null) {
Slog.e(TAG, "onDismissed: " + reason + ", auth session null");
return;
}
logDialogDismissed(reason);
try {
switch (reason) {
case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
mKeyStore.addAuthToken(credentialAttestation);
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
if (mCurrentAuthSession.mTokenEscrow != null) {
mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
}
mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(
Utils.getAuthenticationTypeForResult(reason));
break;
case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
// Cancel authentication. Skip the token/package check since we are cancelling
// from system server. The interface is permission protected so this is fine.
cancelInternal(null /* token */, null /* package */,
mCurrentAuthSession.mCallingUid, mCurrentAuthSession.mCallingPid,
mCurrentAuthSession.mCallingUserId, false /* fromClient */);
break;
case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
mCurrentAuthSession.mClientReceiver.onError(
mCurrentAuthSession.mModality,
BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
0 /* vendorCode */
);
// Cancel authentication. Skip the token/package check since we are cancelling
// from system server. The interface is permission protected so this is fine.
cancelInternal(null /* token */, null /* package */, Binder.getCallingUid(),
Binder.getCallingPid(), UserHandle.getCallingUserId(),
false /* fromClient */);
break;
case BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED:
case BiometricPrompt.DISMISSED_REASON_ERROR:
mCurrentAuthSession.mClientReceiver.onError(
mCurrentAuthSession.mModality,
mCurrentAuthSession.mErrorEscrow,
mCurrentAuthSession.mVendorCodeEscrow
);
break;
default:
Slog.w(TAG, "Unhandled reason: " + reason);
break;
}
// Dialog is gone, auth session is done.
mCurrentAuthSession = null;
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
}