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
This commit is contained in:
Kevin Chyn
2019-04-24 12:39:40 -07:00
parent f5f1236add
commit e674e85497
7 changed files with 58 additions and 38 deletions

View File

@@ -153,7 +153,7 @@ oneway interface IStatusBar
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

View File

@@ -102,7 +102,7 @@ interface IStatusBarService
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

View File

@@ -24,6 +24,7 @@ import android.hardware.biometrics.BiometricPrompt;
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 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
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 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
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 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
}
@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 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
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 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
}, 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 */);
}
}

View File

@@ -280,7 +280,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
@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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
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);

View File

@@ -104,6 +104,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
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) {

View File

@@ -306,11 +306,7 @@ public class BiometricService extends SystemService {
}
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 @@ public class BiometricService extends SystemService {
@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 @@ public class BiometricService extends SystemService {
// 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 @@ public class BiometricService extends SystemService {
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) {

View File

@@ -610,11 +610,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@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) {
}
}