Fix feedback and animations for face to fingerprint fallback authentication.
Bug: 189164072 Test: atest com.android.systemui.biometrics com.android.server.biometrics Change-Id: I7fe8085dc4a4994c591bb4be20309ffb46bb06e9
This commit is contained in:
@@ -67,7 +67,8 @@ public interface BiometricAuthenticator {
|
||||
TYPE_NONE,
|
||||
TYPE_CREDENTIAL,
|
||||
TYPE_FINGERPRINT,
|
||||
TYPE_IRIS
|
||||
TYPE_IRIS,
|
||||
TYPE_FACE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Modality {}
|
||||
|
||||
@@ -143,17 +143,25 @@ oneway interface IStatusBar
|
||||
|
||||
void showShutdownUi(boolean isReboot, String reason);
|
||||
|
||||
// Used to show the authentication dialog (Biometrics, Device Credential)
|
||||
/**
|
||||
* Used to show the authentication dialog (Biometrics, Device Credential).
|
||||
*/
|
||||
void showAuthenticationDialog(in PromptInfo promptInfo, IBiometricSysuiReceiver sysuiReceiver,
|
||||
in int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId,
|
||||
String opPackageName, long operationId, int multiSensorConfig);
|
||||
// Used to notify the authentication dialog that a biometric has been authenticated
|
||||
/**
|
||||
* Used to notify the authentication dialog that a biometric has been authenticated.
|
||||
*/
|
||||
void onBiometricAuthenticated();
|
||||
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
|
||||
void onBiometricHelp(String message);
|
||||
// Used to show an error - the dialog will dismiss after a certain amount of time
|
||||
/**
|
||||
* Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc.
|
||||
*/
|
||||
void onBiometricHelp(int modality, String message);
|
||||
/** Used to show an error - the dialog will dismiss after a certain amount of time. */
|
||||
void onBiometricError(int modality, int error, int vendorCode);
|
||||
// Used to hide the authentication dialog, e.g. when the application cancels authentication
|
||||
/**
|
||||
* Used to hide the authentication dialog, e.g. when the application cancels authentication.
|
||||
*/
|
||||
void hideAuthenticationDialog();
|
||||
|
||||
/**
|
||||
|
||||
@@ -115,7 +115,7 @@ interface IStatusBarService
|
||||
// Used to notify the authentication dialog that a biometric has been authenticated
|
||||
void onBiometricAuthenticated();
|
||||
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
|
||||
void onBiometricHelp(String message);
|
||||
void onBiometricHelp(int modality, String message);
|
||||
// Used to show an error - the dialog will dismiss after a certain amount of time
|
||||
void onBiometricError(int modality, int error, int vendorCode);
|
||||
// Used to hide the authentication dialog, e.g. when the application cancels authentication
|
||||
|
||||
@@ -22,7 +22,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricAuthenticator;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
@@ -87,9 +87,11 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
|
||||
}
|
||||
}
|
||||
|
||||
@BiometricAuthenticator.Modality private int mActiveSensorType = TYPE_FACE;
|
||||
@Modality
|
||||
private int mActiveSensorType = TYPE_FACE;
|
||||
|
||||
@Nullable UdfpsDialogMeasureAdapter mUdfpsMeasureAdapter;
|
||||
@Nullable
|
||||
private UdfpsDialogMeasureAdapter mUdfpsMeasureAdapter;
|
||||
|
||||
public AuthBiometricFaceToFingerprintView(Context context) {
|
||||
super(context);
|
||||
@@ -125,6 +127,16 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed(
|
||||
@Modality int modality, @Nullable String failureReason) {
|
||||
if (modality == TYPE_FACE && mActiveSensorType == TYPE_FACE) {
|
||||
// switching from face -> fingerprint mode, suppress soft error messages
|
||||
failureReason = mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead);
|
||||
}
|
||||
super.onAuthenticationFailed(modality, failureReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
@BiometricState
|
||||
protected int getStateForAfterError() {
|
||||
@@ -155,7 +167,7 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(int newState) {
|
||||
public void updateState(@BiometricState int newState) {
|
||||
if (mState == STATE_HELP || mState == STATE_ERROR) {
|
||||
mActiveSensorType = TYPE_FINGERPRINT;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context;
|
||||
import android.graphics.drawable.Animatable2;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
@@ -29,6 +30,8 @@ import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.R;
|
||||
|
||||
@@ -206,7 +209,7 @@ public class AuthBiometricFaceView extends AuthBiometricView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed(String failureReason) {
|
||||
public void onAuthenticationFailed(@Modality int modality, @Nullable String failureReason) {
|
||||
if (getSize() == AuthDialog.SIZE_MEDIUM) {
|
||||
if (supportsManualRetry()) {
|
||||
mTryAgainButton.setVisibility(View.VISIBLE);
|
||||
@@ -216,7 +219,7 @@ public class AuthBiometricFaceView extends AuthBiometricView {
|
||||
|
||||
// Do this last since we want to know if the button is being animated (in the case of
|
||||
// small -> medium dialog)
|
||||
super.onAuthenticationFailed(failureReason);
|
||||
super.onAuthenticationFailed(modality, failureReason);
|
||||
}
|
||||
|
||||
private void resetErrorView() {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
@@ -24,6 +26,7 @@ import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.biometrics.BiometricPrompt;
|
||||
import android.hardware.biometrics.PromptInfo;
|
||||
import android.os.Bundle;
|
||||
@@ -542,12 +545,25 @@ public abstract class AuthBiometricView extends LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
public void onAuthenticationFailed(String failureReason) {
|
||||
/**
|
||||
* Notify the view that auth has failed.
|
||||
*
|
||||
* @param modality sensor modality that failed
|
||||
* @param failureReason message
|
||||
*/
|
||||
public void onAuthenticationFailed(
|
||||
@Modality int modality, @Nullable String failureReason) {
|
||||
showTemporaryMessage(failureReason, mResetErrorRunnable);
|
||||
updateState(STATE_ERROR);
|
||||
}
|
||||
|
||||
public void onError(String error) {
|
||||
/**
|
||||
* Notify the view that an error occurred.
|
||||
*
|
||||
* @param modality sensor modality that failed
|
||||
* @param error message
|
||||
*/
|
||||
public void onError(@Modality int modality, String error) {
|
||||
showTemporaryMessage(error, mResetErrorRunnable);
|
||||
updateState(STATE_ERROR);
|
||||
|
||||
@@ -556,11 +572,22 @@ public abstract class AuthBiometricView extends LinearLayout {
|
||||
}, mInjector.getDelayAfterError());
|
||||
}
|
||||
|
||||
public void onHelp(String help) {
|
||||
/**
|
||||
* Show a help message to the user.
|
||||
*
|
||||
* @param modality sensor modality
|
||||
* @param help message
|
||||
*/
|
||||
public void onHelp(@Modality int modality, String help) {
|
||||
if (mSize != AuthDialog.SIZE_MEDIUM) {
|
||||
Log.w(TAG, "Help received in size: " + mSize);
|
||||
return;
|
||||
}
|
||||
if (TextUtils.isEmpty(help)) {
|
||||
Log.w(TAG, "Ignoring blank help message");
|
||||
return;
|
||||
}
|
||||
|
||||
showTemporaryMessage(help, mResetHelpRunnable);
|
||||
updateState(STATE_HELP);
|
||||
}
|
||||
@@ -828,10 +855,10 @@ public abstract class AuthBiometricView extends LinearLayout {
|
||||
final String indicatorText =
|
||||
mSavedState.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING);
|
||||
if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_HELP_SHOWING)) {
|
||||
onHelp(indicatorText);
|
||||
onHelp(TYPE_NONE, indicatorText);
|
||||
} else if (mSavedState.getBoolean(
|
||||
AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)) {
|
||||
onAuthenticationFailed(indicatorText);
|
||||
onAuthenticationFailed(TYPE_NONE, indicatorText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.PromptInfo;
|
||||
import android.hardware.face.FaceSensorPropertiesInternal;
|
||||
@@ -598,18 +599,18 @@ public class AuthContainerView extends LinearLayout
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed(String failureReason) {
|
||||
mBiometricView.onAuthenticationFailed(failureReason);
|
||||
public void onAuthenticationFailed(@Modality int modality, String failureReason) {
|
||||
mBiometricView.onAuthenticationFailed(modality, failureReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHelp(String help) {
|
||||
mBiometricView.onHelp(help);
|
||||
public void onHelp(@Modality int modality, String help) {
|
||||
mBiometricView.onHelp(modality, help);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {
|
||||
mBiometricView.onError(error);
|
||||
public void onError(@Modality int modality, String error) {
|
||||
mBiometricView.onError(modality, error);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,8 +18,6 @@ package com.android.systemui.biometrics;
|
||||
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
import static android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -33,7 +31,10 @@ import android.content.IntentFilter;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
import android.hardware.biometrics.BiometricPrompt;
|
||||
import android.hardware.biometrics.IBiometricSysuiReceiver;
|
||||
import android.hardware.biometrics.PromptInfo;
|
||||
@@ -525,11 +526,11 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBiometricHelp(String message) {
|
||||
public void onBiometricHelp(@Modality int modality, String message) {
|
||||
if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
|
||||
|
||||
if (mCurrentDialog != null) {
|
||||
mCurrentDialog.onHelp(message);
|
||||
mCurrentDialog.onHelp(modality, message);
|
||||
} else {
|
||||
Log.w(TAG, "onBiometricHelp callback but dialog gone");
|
||||
}
|
||||
@@ -540,7 +541,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
return mUdfpsProps;
|
||||
}
|
||||
|
||||
private String getErrorString(int modality, int error, int vendorCode) {
|
||||
private String getErrorString(@Modality int modality, int error, int vendorCode) {
|
||||
switch (modality) {
|
||||
case TYPE_FACE:
|
||||
return FaceManager.getErrorString(mContext, error, vendorCode);
|
||||
@@ -559,7 +560,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
* example, KeyguardUpdateMonitor has its own {@link FingerprintManager.AuthenticationCallback}.
|
||||
*/
|
||||
@Override
|
||||
public void onBiometricError(int modality, int error, int vendorCode) {
|
||||
public void onBiometricError(@Modality int modality, int error, int vendorCode) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode));
|
||||
}
|
||||
@@ -580,11 +581,11 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
? mContext.getString(R.string.biometric_not_recognized)
|
||||
: getErrorString(modality, error, vendorCode);
|
||||
if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
|
||||
mCurrentDialog.onAuthenticationFailed(errorMessage);
|
||||
mCurrentDialog.onAuthenticationFailed(modality, errorMessage);
|
||||
} else {
|
||||
final String errorMessage = getErrorString(modality, error, vendorCode);
|
||||
if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
|
||||
mCurrentDialog.onError(errorMessage);
|
||||
mCurrentDialog.onError(modality, errorMessage);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "onBiometricError callback but dialog is gone");
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.biometrics;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@@ -112,21 +113,24 @@ public interface AuthDialog {
|
||||
|
||||
/**
|
||||
* Authentication failed (reject, timeout). Dialog stays showing.
|
||||
* @param failureReason
|
||||
* @param modality sensor modality that triggered the error
|
||||
* @param failureReason message
|
||||
*/
|
||||
void onAuthenticationFailed(String failureReason);
|
||||
void onAuthenticationFailed(@Modality int modality, String failureReason);
|
||||
|
||||
/**
|
||||
* Authentication rejected, or help message received.
|
||||
* @param help
|
||||
* @param modality sensor modality that triggered the help message
|
||||
* @param help message
|
||||
*/
|
||||
void onHelp(String help);
|
||||
void onHelp(@Modality int modality, String help);
|
||||
|
||||
/**
|
||||
* Authentication failed. Dialog going away.
|
||||
* @param error
|
||||
* @param modality sensor modality that triggered the error
|
||||
* @param error message
|
||||
*/
|
||||
void onError(String error);
|
||||
void onError(@Modality int modality, String error);
|
||||
|
||||
/**
|
||||
* Save the current state.
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar;
|
||||
|
||||
import static android.app.StatusBarManager.DISABLE2_NONE;
|
||||
import static android.app.StatusBarManager.DISABLE_NONE;
|
||||
import static android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
|
||||
import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
@@ -35,6 +34,8 @@ import android.app.StatusBarManager.WindowType;
|
||||
import android.app.StatusBarManager.WindowVisibleState;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
import android.hardware.biometrics.IBiometricSysuiReceiver;
|
||||
import android.hardware.biometrics.PromptInfo;
|
||||
import android.hardware.display.DisplayManager;
|
||||
@@ -293,13 +294,16 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
long operationId, @BiometricMultiSensorMode int multiSensorConfig) {
|
||||
}
|
||||
|
||||
/** @see IStatusBar#onBiometricAuthenticated() */
|
||||
default void onBiometricAuthenticated() {
|
||||
}
|
||||
|
||||
default void onBiometricHelp(String message) {
|
||||
/** @see IStatusBar#onBiometricHelp(String) */
|
||||
default void onBiometricHelp(@Modality int modality, String message) {
|
||||
}
|
||||
|
||||
default void onBiometricError(int modality, int error, int vendorCode) {
|
||||
/** @see IStatusBar#onBiometricError(int, int, int) */
|
||||
default void onBiometricError(@Modality int modality, int error, int vendorCode) {
|
||||
}
|
||||
|
||||
default void hideAuthenticationDialog() {
|
||||
@@ -891,9 +895,12 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBiometricHelp(String message) {
|
||||
public void onBiometricHelp(@Modality int modality, String message) {
|
||||
synchronized (mLock) {
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.argi1 = modality;
|
||||
args.arg1 = message;
|
||||
mHandler.obtainMessage(MSG_BIOMETRIC_HELP, args).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1318,11 +1325,16 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSG_BIOMETRIC_HELP:
|
||||
case MSG_BIOMETRIC_HELP: {
|
||||
SomeArgs someArgs = (SomeArgs) msg.obj;
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).onBiometricHelp((String) msg.obj);
|
||||
mCallbacks.get(i).onBiometricHelp(
|
||||
someArgs.argi1 /* modality */,
|
||||
(String) someArgs.arg1 /* message */);
|
||||
}
|
||||
someArgs.recycle();
|
||||
break;
|
||||
}
|
||||
case MSG_BIOMETRIC_ERROR: {
|
||||
SomeArgs someArgs = (SomeArgs) msg.obj;
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -31,6 +33,7 @@ import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -61,15 +64,14 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
|
||||
@Mock private TextView mIndicatorView;
|
||||
@Mock private ImageView mIconView;
|
||||
@Mock private View mIconHolderView;
|
||||
|
||||
@Mock private TextView mErrorView;
|
||||
@Mock private AuthBiometricFaceView.IconController mIconController;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mFaceToFpView = new TestableView(mContext);
|
||||
mFaceToFpView.mIconController = mock(AuthBiometricFaceView.IconController.class);
|
||||
mFaceToFpView.mIconController = mIconController;
|
||||
mFaceToFpView.setCallback(mCallback);
|
||||
|
||||
mFaceToFpView.mNegativeButton = mNegativeButton;
|
||||
@@ -77,8 +79,7 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
|
||||
mFaceToFpView.mUseCredentialButton = mUseCredentialButton;
|
||||
mFaceToFpView.mConfirmButton = mConfirmButton;
|
||||
mFaceToFpView.mTryAgainButton = mTryAgainButton;
|
||||
|
||||
mFaceToFpView.mIndicatorView = mErrorView;
|
||||
mFaceToFpView.mIndicatorView = mIndicatorView;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,7 +91,7 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testIconUpdatesState_whenDialogStateUpdated() {
|
||||
mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
|
||||
mFaceToFpView.onDialogAnimatedIn();
|
||||
verify(mFaceToFpView.mIconController)
|
||||
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
|
||||
|
||||
@@ -98,11 +99,13 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
|
||||
verify(mFaceToFpView.mIconController).updateState(
|
||||
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
|
||||
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED));
|
||||
|
||||
assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED, mFaceToFpView.mState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateUpdated_whenSwitchToFingerprint() {
|
||||
mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
|
||||
mFaceToFpView.onDialogAnimatedIn();
|
||||
verify(mFaceToFpView.mIconController)
|
||||
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
|
||||
|
||||
@@ -120,6 +123,17 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
|
||||
verify(mConfirmButton).setVisibility(eq(View.GONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModeUpdated_whenSwitchToFingerprint() {
|
||||
mFaceToFpView.onDialogAnimatedIn();
|
||||
mFaceToFpView.onAuthenticationFailed(TYPE_FACE, "no face");
|
||||
waitForIdleSync();
|
||||
|
||||
verify(mIndicatorView).setText(
|
||||
eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
|
||||
assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
|
||||
}
|
||||
|
||||
public class TestableView extends AuthBiometricFaceToFingerprintView {
|
||||
public TestableView(Context context) {
|
||||
super(context, null, new MockInjector());
|
||||
@@ -132,7 +146,7 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
|
||||
|
||||
@Override
|
||||
protected IconController createUdfpsIconController() {
|
||||
return mIconController;
|
||||
return AuthBiometricFaceToFingerprintViewTest.this.mIconController;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -199,10 +202,10 @@ public class AuthBiometricViewTest extends SysuiTestCase {
|
||||
public void testError_sendsActionError() {
|
||||
initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
|
||||
final String testError = "testError";
|
||||
mBiometricView.onError(testError);
|
||||
mBiometricView.onError(TYPE_FACE, testError);
|
||||
waitForIdleSync();
|
||||
|
||||
verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_ERROR);
|
||||
verify(mCallback).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR));
|
||||
assertEquals(AuthBiometricView.STATE_IDLE, mBiometricView.mState);
|
||||
}
|
||||
|
||||
@@ -239,6 +242,23 @@ public class AuthBiometricViewTest extends SysuiTestCase {
|
||||
verify(mCallback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoresUselessHelp() {
|
||||
initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
|
||||
|
||||
mBiometricView.onDialogAnimatedIn();
|
||||
waitForIdleSync();
|
||||
|
||||
assertEquals(AuthBiometricView.STATE_AUTHENTICATING, mBiometricView.mState);
|
||||
|
||||
mBiometricView.onHelp(TYPE_FINGERPRINT, "");
|
||||
waitForIdleSync();
|
||||
|
||||
verify(mIndicatorView, never()).setText(any());
|
||||
verify(mCallback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR));
|
||||
assertEquals(AuthBiometricView.STATE_AUTHENTICATING, mBiometricView.mState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestoresState() {
|
||||
final boolean requireConfirmation = true; // set/init from AuthController
|
||||
@@ -264,7 +284,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
|
||||
|
||||
final String failureMessage = "testFailureMessage";
|
||||
mBiometricView.setRequireConfirmation(requireConfirmation);
|
||||
mBiometricView.onAuthenticationFailed(failureMessage);
|
||||
mBiometricView.onAuthenticationFailed(TYPE_FACE, failureMessage);
|
||||
waitForIdleSync();
|
||||
|
||||
Bundle state = new Bundle();
|
||||
|
||||
@@ -252,52 +252,67 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void testOnAuthenticationFailedInvoked_whenBiometricRejected() {
|
||||
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
|
||||
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_NONE,
|
||||
final int modality = BiometricAuthenticator.TYPE_NONE;
|
||||
mAuthController.onBiometricError(modality,
|
||||
BiometricConstants.BIOMETRIC_PAUSED_REJECTED,
|
||||
0 /* vendorCode */);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(mDialog1).onAuthenticationFailed(captor.capture());
|
||||
ArgumentCaptor<Integer> modalityCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(mDialog1).onAuthenticationFailed(modalityCaptor.capture(), messageCaptor.capture());
|
||||
|
||||
assertEquals(captor.getValue(), mContext.getString(R.string.biometric_not_recognized));
|
||||
assertEquals(modalityCaptor.getValue().intValue(), modality);
|
||||
assertEquals(messageCaptor.getValue(),
|
||||
mContext.getString(R.string.biometric_not_recognized));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnAuthenticationFailedInvoked_whenBiometricTimedOut() {
|
||||
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
|
||||
final int modality = BiometricAuthenticator.TYPE_FACE;
|
||||
final int error = BiometricConstants.BIOMETRIC_ERROR_TIMEOUT;
|
||||
final int vendorCode = 0;
|
||||
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
|
||||
mAuthController.onBiometricError(modality, error, vendorCode);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(mDialog1).onAuthenticationFailed(captor.capture());
|
||||
ArgumentCaptor<Integer> modalityCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(mDialog1).onAuthenticationFailed(modalityCaptor.capture(), messageCaptor.capture());
|
||||
|
||||
assertEquals(captor.getValue(), FaceManager.getErrorString(mContext, error, vendorCode));
|
||||
assertEquals(modalityCaptor.getValue().intValue(), modality);
|
||||
assertEquals(messageCaptor.getValue(),
|
||||
FaceManager.getErrorString(mContext, error, vendorCode));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnHelpInvoked_whenSystemRequested() {
|
||||
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
|
||||
final int modality = BiometricAuthenticator.TYPE_IRIS;
|
||||
final String helpMessage = "help";
|
||||
mAuthController.onBiometricHelp(helpMessage);
|
||||
mAuthController.onBiometricHelp(modality, helpMessage);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(mDialog1).onHelp(captor.capture());
|
||||
ArgumentCaptor<Integer> modalityCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(mDialog1).onHelp(modalityCaptor.capture(), messageCaptor.capture());
|
||||
|
||||
assertEquals(captor.getValue(), helpMessage);
|
||||
assertEquals(modalityCaptor.getValue().intValue(), modality);
|
||||
assertEquals(messageCaptor.getValue(), helpMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnErrorInvoked_whenSystemRequested() {
|
||||
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
|
||||
final int modality = BiometricAuthenticator.TYPE_FACE;
|
||||
final int error = 1;
|
||||
final int vendorCode = 0;
|
||||
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
|
||||
mAuthController.onBiometricError(modality, error, vendorCode);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(mDialog1).onError(captor.capture());
|
||||
ArgumentCaptor<Integer> modalityCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(mDialog1).onError(modalityCaptor.capture(), messageCaptor.capture());
|
||||
|
||||
assertEquals(captor.getValue(), FaceManager.getErrorString(mContext, error, vendorCode));
|
||||
assertEquals(modalityCaptor.getValue().intValue(), modality);
|
||||
assertEquals(messageCaptor.getValue(),
|
||||
FaceManager.getErrorString(mContext, error, vendorCode));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -309,7 +324,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
when(mDialog1.isAllowDeviceCredentials()).thenReturn(true);
|
||||
|
||||
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
|
||||
verify(mDialog1, never()).onError(anyString());
|
||||
verify(mDialog1, never()).onError(anyInt(), anyString());
|
||||
verify(mDialog1).animateToCredentialUI();
|
||||
}
|
||||
|
||||
@@ -322,33 +337,37 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
when(mDialog1.isAllowDeviceCredentials()).thenReturn(true);
|
||||
|
||||
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
|
||||
verify(mDialog1, never()).onError(anyString());
|
||||
verify(mDialog1, never()).onError(anyInt(), anyString());
|
||||
verify(mDialog1).animateToCredentialUI();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrorLockout_whenCredentialNotAllowed_sendsOnError() {
|
||||
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
|
||||
final int modality = BiometricAuthenticator.TYPE_FACE;
|
||||
final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
|
||||
final int vendorCode = 0;
|
||||
|
||||
when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
|
||||
|
||||
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
|
||||
verify(mDialog1).onError(eq(FaceManager.getErrorString(mContext, error, vendorCode)));
|
||||
mAuthController.onBiometricError(modality, error, vendorCode);
|
||||
verify(mDialog1).onError(
|
||||
eq(modality), eq(FaceManager.getErrorString(mContext, error, vendorCode)));
|
||||
verify(mDialog1, never()).animateToCredentialUI();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrorLockoutPermanent_whenCredentialNotAllowed_sendsOnError() {
|
||||
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
|
||||
final int modality = BiometricAuthenticator.TYPE_FACE;
|
||||
final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
|
||||
final int vendorCode = 0;
|
||||
|
||||
when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
|
||||
|
||||
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
|
||||
verify(mDialog1).onError(eq(FaceManager.getErrorString(mContext, error, vendorCode)));
|
||||
mAuthController.onBiometricError(modality, error, vendorCode);
|
||||
verify(mDialog1).onError(
|
||||
eq(modality), eq(FaceManager.getErrorString(mContext, error, vendorCode)));
|
||||
verify(mDialog1, never()).animateToCredentialUI();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
|
||||
import static android.view.InsetsState.ITYPE_STATUS_BAR;
|
||||
@@ -444,10 +445,11 @@ public class CommandQueueTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testOnBiometricHelp() {
|
||||
String helpMessage = "test_help_message";
|
||||
mCommandQueue.onBiometricHelp(helpMessage);
|
||||
final int modality = TYPE_FACE;
|
||||
final String helpMessage = "test_help_message";
|
||||
mCommandQueue.onBiometricHelp(modality, helpMessage);
|
||||
waitForIdleSync();
|
||||
verify(mCallbacks).onBiometricHelp(eq(helpMessage));
|
||||
verify(mCallbacks).onBiometricHelp(eq(modality), eq(helpMessage));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -21,7 +21,6 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
|
||||
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT;
|
||||
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT;
|
||||
import static android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
|
||||
import static com.android.server.biometrics.BiometricServiceStateProto.MULTI_SENSOR_STATE_FACE_SCANNING;
|
||||
import static com.android.server.biometrics.BiometricServiceStateProto.MULTI_SENSOR_STATE_FP_SCANNING;
|
||||
@@ -44,8 +43,10 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricAuthenticator;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
import android.hardware.biometrics.BiometricPrompt;
|
||||
import android.hardware.biometrics.BiometricsProtoEnums;
|
||||
import android.hardware.biometrics.IBiometricSensorReceiver;
|
||||
@@ -498,7 +499,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
|
||||
}
|
||||
|
||||
try {
|
||||
mStatusBarService.onBiometricHelp(message);
|
||||
mStatusBarService.onBiometricHelp(sensorIdToModality(sensorId), message);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
}
|
||||
@@ -873,7 +874,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
|
||||
return modality;
|
||||
}
|
||||
|
||||
private @BiometricAuthenticator.Modality int sensorIdToModality(int sensorId) {
|
||||
private @Modality int sensorIdToModality(int sensorId) {
|
||||
for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
|
||||
if (sensorId == sensor.id) {
|
||||
return sensor.modality;
|
||||
@@ -884,7 +885,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
|
||||
}
|
||||
|
||||
private String getAcquiredMessageForSensor(int sensorId, int acquiredInfo, int vendorCode) {
|
||||
final @BiometricAuthenticator.Modality int modality = sensorIdToModality(sensorId);
|
||||
final @Modality int modality = sensorIdToModality(sensorId);
|
||||
switch (modality) {
|
||||
case BiometricAuthenticator.TYPE_FINGERPRINT:
|
||||
return FingerprintManager.getAcquiredString(mContext, acquiredInfo, vendorCode);
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.server.statusbar;
|
||||
|
||||
import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
|
||||
import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
|
||||
import static android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import android.Manifest;
|
||||
@@ -33,6 +32,8 @@ import android.compat.annotation.EnabledSince;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
import android.hardware.biometrics.IBiometricSysuiReceiver;
|
||||
import android.hardware.biometrics.PromptInfo;
|
||||
import android.hardware.display.DisplayManager;
|
||||
@@ -814,11 +815,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBiometricHelp(String message) {
|
||||
public void onBiometricHelp(@Modality int modality, String message) {
|
||||
enforceBiometricDialog();
|
||||
if (mBar != null) {
|
||||
try {
|
||||
mBar.onBiometricHelp(message);
|
||||
mBar.onBiometricHelp(modality, message);
|
||||
} catch (RemoteException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1108,7 +1108,8 @@ public class BiometricServiceTest {
|
||||
public void testAcquire_whenAuthenticating_sentToSystemUI() throws Exception {
|
||||
when(mContext.getResources().getString(anyInt())).thenReturn("test string");
|
||||
|
||||
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
|
||||
final int modality = BiometricAuthenticator.TYPE_FINGERPRINT;
|
||||
setupAuthForOnly(modality, Authenticators.BIOMETRIC_STRONG);
|
||||
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
|
||||
false /* requireConfirmation */, null /* authenticators */);
|
||||
|
||||
@@ -1121,7 +1122,7 @@ public class BiometricServiceTest {
|
||||
// Sends to SysUI and stays in authenticating state. We don't test that the correct
|
||||
// string is retrieved for now, but it's also very unlikely to break anyway.
|
||||
verify(mBiometricService.mStatusBarService)
|
||||
.onBiometricHelp(anyString());
|
||||
.onBiometricHelp(eq(modality), anyString());
|
||||
assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user