Do not dismiss BiometricPrompt when "soft" errors are received
Some errors such as ERROR_TIMEOUT should not caues the BiometricPrompt
dialog to be dismissed. Updated plumbing to allow
onBiometricAuthenticated to pass a string indicating the failure reason
Fixes: 131240917
Test: BiometricPromptDemo
Change-Id: I63a6f1138a24fbc3736184efefc620dd5bb640dd
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 943c726..cfc32cf 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -153,7 +153,7 @@
void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
boolean requireConfirmation, int userId);
// Used to hide the dialog when a biometric is authenticated
- void onBiometricAuthenticated(boolean authenticated);
+ void onBiometricAuthenticated(boolean authenticated, String failureReason);
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
void onBiometricHelp(String message);
// Used to set a message - the dialog will dismiss after a certain amount of time
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 8f8e4d8..8ef6304 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -102,7 +102,7 @@
void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
boolean requireConfirmation, int userId);
// Used to hide the dialog when a biometric is authenticated
- void onBiometricAuthenticated(boolean authenticated);
+ void onBiometricAuthenticated(boolean authenticated, String failureReason);
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
void onBiometricHelp(String message);
// Used to set a message - the dialog will dismiss after a certain amount of time
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index 4028109..5860230 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -24,6 +24,7 @@
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
@@ -58,7 +59,7 @@
private boolean mDialogShowing;
private Callback mCallback = new Callback();
- private Handler mHandler = new Handler() {
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
@@ -66,15 +67,20 @@
handleShowDialog((SomeArgs) msg.obj, false /* skipAnimation */,
null /* savedState */);
break;
- case MSG_BIOMETRIC_AUTHENTICATED:
- handleBiometricAuthenticated((boolean) msg.obj);
+ case MSG_BIOMETRIC_AUTHENTICATED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleBiometricAuthenticated((boolean) args.arg1 /* authenticated */,
+ (String) args.arg2 /* failureReason */);
+ args.recycle();
break;
- case MSG_BIOMETRIC_HELP:
+ }
+ case MSG_BIOMETRIC_HELP: {
SomeArgs args = (SomeArgs) msg.obj;
handleBiometricHelp((String) args.arg1 /* message */,
(boolean) args.arg2 /* requireTryAgain */);
args.recycle();
break;
+ }
case MSG_BIOMETRIC_ERROR:
handleBiometricError((String) msg.obj);
break;
@@ -161,9 +167,14 @@
}
@Override
- public void onBiometricAuthenticated(boolean authenticated) {
- if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated);
- mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, authenticated).sendToTarget();
+ public void onBiometricAuthenticated(boolean authenticated, String failureReason) {
+ if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated
+ + " reason: " + failureReason);
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = authenticated;
+ args.arg2 = failureReason;
+ mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget();
}
@Override
@@ -230,7 +241,7 @@
mDialogShowing = true;
}
- private void handleBiometricAuthenticated(boolean authenticated) {
+ private void handleBiometricAuthenticated(boolean authenticated, String failureReason) {
if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated: " + authenticated);
if (authenticated) {
@@ -246,9 +257,7 @@
}, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
}
} else {
- handleBiometricHelp(mContext.getResources()
- .getString(com.android.internal.R.string.biometric_not_recognized),
- true /* requireTryAgain */);
+ handleBiometricHelp(failureReason, true /* requireTryAgain */);
mCurrentDialog.showTryAgainButton(true /* show */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index d584959..a688f36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -280,7 +280,7 @@
default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int type, boolean requireConfirmation, int userId) { }
- default void onBiometricAuthenticated(boolean authenticated) { }
+ default void onBiometricAuthenticated(boolean authenticated, String failureReason) { }
default void onBiometricHelp(String message) { }
default void onBiometricError(String error) { }
default void hideBiometricDialog() { }
@@ -760,9 +760,12 @@
}
@Override
- public void onBiometricAuthenticated(boolean authenticated) {
+ public void onBiometricAuthenticated(boolean authenticated, String failureReason) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, authenticated).sendToTarget();
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = authenticated;
+ args.arg2 = failureReason;
+ mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget();
}
}
@@ -1023,7 +1026,7 @@
mCallbacks.get(i).onRotationProposal(msg.arg1, msg.arg2 != 0);
}
break;
- case MSG_BIOMETRIC_SHOW:
+ case MSG_BIOMETRIC_SHOW: {
mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
mHandler.removeMessages(MSG_BIOMETRIC_HELP);
mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
@@ -1038,11 +1041,17 @@
}
someArgs.recycle();
break;
- case MSG_BIOMETRIC_AUTHENTICATED:
+ }
+ case MSG_BIOMETRIC_AUTHENTICATED: {
+ SomeArgs someArgs = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onBiometricAuthenticated((boolean) msg.obj);
+ mCallbacks.get(i).onBiometricAuthenticated(
+ (boolean) someArgs.arg1 /* authenticated */,
+ (String) someArgs.arg2 /* failureReason */);
}
+ someArgs.recycle();
break;
+ }
case MSG_BIOMETRIC_HELP:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onBiometricHelp((String) msg.obj);
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 91da7af..74b7221 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -104,6 +104,7 @@
public boolean onError(long deviceId, int error, int vendorCode) {
if (!shouldFrameworkHandleLockout()) {
switch (error) {
+ case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT:
case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
if (mStarted) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 4c59e60..6933ee8 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -306,11 +306,7 @@
}
case MSG_ON_AUTHENTICATION_FAILED: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleAuthenticationFailed(
- args.argi1 /* cookie */,
- (boolean) args.arg1 /* requireConfirmation */);
- args.recycle();
+ handleAuthenticationFailed((String) msg.obj /* failureReason */);
break;
}
@@ -567,19 +563,24 @@
@Override
public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
throws RemoteException {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = cookie;
- args.arg1 = requireConfirmation;
- mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, args).sendToTarget();
+ String failureReason = getContext().getString(R.string.biometric_not_recognized);
+ mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, failureReason).sendToTarget();
}
@Override
public void onError(int cookie, int error, String message) throws RemoteException {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = cookie;
- args.argi2 = error;
- args.arg1 = message;
- mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
+ // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are
+ // soft errors and we should allow the user to try authenticating again instead of
+ // dismissing BiometricPrompt.
+ if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
+ mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, message).sendToTarget();
+ } else {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = cookie;
+ args.argi2 = error;
+ args.arg1 = message;
+ mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
+ }
}
@Override
@@ -1151,13 +1152,13 @@
// Notify SysUI that the biometric has been authenticated. SysUI already knows
// the implicit/explicit state and will react accordingly.
- mStatusBarService.onBiometricAuthenticated(true);
+ mStatusBarService.onBiometricAuthenticated(true, null /* failureReason */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
}
- private void handleAuthenticationFailed(int cookie, boolean requireConfirmation) {
+ private void handleAuthenticationFailed(String failureReason) {
try {
// Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
// after user dismissed/canceled dialog).
@@ -1166,7 +1167,7 @@
return;
}
- mStatusBarService.onBiometricAuthenticated(false);
+ mStatusBarService.onBiometricAuthenticated(false, failureReason);
// TODO: This logic will need to be updated if BP is multi-modal
if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 9cbf00b..488ba73 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -610,11 +610,11 @@
}
@Override
- public void onBiometricAuthenticated(boolean authenticated) {
+ public void onBiometricAuthenticated(boolean authenticated, String failureReason) {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.onBiometricAuthenticated(authenticated);
+ mBar.onBiometricAuthenticated(authenticated, failureReason);
} catch (RemoteException ex) {
}
}