diff options
13 files changed, 229 insertions, 120 deletions
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 77c7ce8cf5c4..b9c2fd532d0f 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -145,8 +145,8 @@ oneway interface IStatusBar // Used to show the authentication dialog (Biometrics, Device Credential) void showAuthenticationDialog(in PromptInfo promptInfo, IBiometricSysuiReceiver sysuiReceiver, - int biometricModality, boolean requireConfirmation, int userId, String opPackageName, - long operationId); + in int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId, + String opPackageName, long operationId); // 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 diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index bcbbf6e98dae..bb0fd7fb8c23 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -106,8 +106,9 @@ interface IStatusBarService // Used to show the authentication dialog (Biometrics, Device Credential) void showAuthenticationDialog(in PromptInfo promptInfo, IBiometricSysuiReceiver sysuiReceiver, - int biometricModality, boolean requireConfirmation, int userId, String opPackageName, - long operationId); + in int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, + int userId, String opPackageName,long operationId); + // 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 diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index ab4025f7ef9d..24ab6355c2bd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -24,6 +24,9 @@ import android.graphics.PixelFormat; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.PromptInfo; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.fingerprint.FingerprintSensorProperties; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -51,6 +54,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; /** * Top level container/controller for the BiometricPrompt UI. @@ -76,6 +80,8 @@ public class AuthContainerView extends LinearLayout final Config mConfig; final int mEffectiveUserId; + @Nullable private final List<FingerprintSensorPropertiesInternal> mFpProps; + @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps; private final Handler mHandler; private final Injector mInjector; private final IBinder mWindowToken = new Binder(); @@ -111,7 +117,8 @@ public class AuthContainerView extends LinearLayout boolean mRequireConfirmation; int mUserId; String mOpPackageName; - @BiometricAuthenticator.Modality int mModalityMask; + int[] mSensorIds; + boolean mCredentialAllowed; boolean mSkipIntro; long mOperationId; } @@ -159,9 +166,12 @@ public class AuthContainerView extends LinearLayout return this; } - public AuthContainerView build(@BiometricAuthenticator.Modality int modalityMask) { - mConfig.mModalityMask = modalityMask; - return new AuthContainerView(mConfig, new Injector()); + public AuthContainerView build(int[] sensorIds, boolean credentialAllowed, + @Nullable List<FingerprintSensorPropertiesInternal> fpProps, + @Nullable List<FaceSensorPropertiesInternal> faceProps) { + mConfig.mSensorIds = sensorIds; + mConfig.mCredentialAllowed = credentialAllowed; + return new AuthContainerView(mConfig, new Injector(), fpProps, faceProps); } } @@ -242,11 +252,15 @@ public class AuthContainerView extends LinearLayout } @VisibleForTesting - AuthContainerView(Config config, Injector injector) { + AuthContainerView(Config config, Injector injector, + @Nullable List<FingerprintSensorPropertiesInternal> fpProps, + @Nullable List<FaceSensorPropertiesInternal> faceProps) { super(config.mContext); mConfig = config; mInjector = injector; + mFpProps = fpProps; + mFaceProps = faceProps; mEffectiveUserId = mInjector.getUserManager(mContext) .getCredentialOwnerProfile(mConfig.mUserId); @@ -269,24 +283,29 @@ public class AuthContainerView extends LinearLayout // Inflate biometric view only if necessary. if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) { - final @BiometricAuthenticator.Modality int biometricModality = - config.mModalityMask & ~BiometricAuthenticator.TYPE_CREDENTIAL; - - switch (biometricModality) { - case BiometricAuthenticator.TYPE_FINGERPRINT: + if (config.mSensorIds.length == 1) { + final int singleSensorAuthId = config.mSensorIds[0]; + if (Utils.containsSensorId(mFpProps, singleSensorAuthId)) { mBiometricView = (AuthBiometricFingerprintView) factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false); - break; - case BiometricAuthenticator.TYPE_FACE: + } else if (Utils.containsSensorId(mFaceProps, singleSensorAuthId)) { mBiometricView = (AuthBiometricFaceView) factory.inflate(R.layout.auth_biometric_face_view, null, false); - break; - default: - Log.e(TAG, "Unsupported biometric modality: " + biometricModality); + } else { + // Unknown sensorId + Log.e(TAG, "Unknown sensorId: " + singleSensorAuthId); mBiometricView = null; mBackgroundView = null; mBiometricScrollView = null; return; + } + } else { + // The UI currently only supports authentication with a single sensor. + Log.e(TAG, "Unsupported sensor array, length: " + config.mSensorIds.length); + mBiometricView = null; + mBackgroundView = null; + mBiometricScrollView = null; + return; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index a4aeb8a9a94d..874c73baf146 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -29,12 +29,12 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; @@ -74,8 +74,12 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private final StatusBarStateController mStatusBarStateController; private final IActivityTaskManager mActivityTaskManager; @Nullable private final FingerprintManager mFingerprintManager; + @Nullable private final FaceManager mFaceManager; private final Provider<UdfpsController> mUdfpsControllerFactory; + @Nullable private final List<FingerprintSensorPropertiesInternal> mFpProps; + @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps; + // TODO: These should just be saved from onSaveState private SomeArgs mCurrentDialogArgs; @VisibleForTesting @@ -285,14 +289,20 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, StatusBarStateController statusBarStateController, IActivityTaskManager activityTaskManager, @Nullable FingerprintManager fingerprintManager, + @Nullable FaceManager faceManager, Provider<UdfpsController> udfpsControllerFactory) { super(context); mCommandQueue = commandQueue; mStatusBarStateController = statusBarStateController; mActivityTaskManager = activityTaskManager; mFingerprintManager = fingerprintManager; + mFaceManager = faceManager; mUdfpsControllerFactory = udfpsControllerFactory; + mFpProps = mFingerprintManager != null ? mFingerprintManager.getSensorPropertiesInternal() + : null; + mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null; + IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); @@ -326,24 +336,30 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Override public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver, - @BiometricAuthenticator.Modality int biometricModality, boolean requireConfirmation, + int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId, String opPackageName, long operationId) { @Authenticators.Types final int authenticators = promptInfo.getAuthenticators(); if (DEBUG) { + StringBuilder ids = new StringBuilder(); + for (int sensorId : sensorIds) { + ids.append(sensorId).append(" "); + } Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators - + ", biometricModality: " + biometricModality + + ", sensorIds: " + ids.toString() + + ", credentialAllowed: " + credentialAllowed + ", requireConfirmation: " + requireConfirmation + ", operationId: " + operationId); } SomeArgs args = SomeArgs.obtain(); args.arg1 = promptInfo; args.arg2 = receiver; - args.argi1 = biometricModality; - args.arg3 = requireConfirmation; - args.argi2 = userId; - args.arg4 = opPackageName; - args.arg5 = operationId; + args.arg3 = sensorIds; + args.arg4 = credentialAllowed; + args.arg5 = requireConfirmation; + args.argi1 = userId; + args.arg6 = opPackageName; + args.arg7 = operationId; boolean skipAnimation = false; if (mCurrentDialog != null) { @@ -467,25 +483,28 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) { mCurrentDialogArgs = args; - final @BiometricAuthenticator.Modality int type = args.argi1; + final PromptInfo promptInfo = (PromptInfo) args.arg1; - final boolean requireConfirmation = (boolean) args.arg3; - final int userId = args.argi2; - final String opPackageName = (String) args.arg4; - final long operationId = (long) args.arg5; + final int[] sensorIds = (int[]) args.arg3; + final boolean credentialAllowed = (boolean) args.arg4; + final boolean requireConfirmation = (boolean) args.arg5; + final int userId = args.argi1; + final String opPackageName = (String) args.arg6; + final long operationId = (long) args.arg7; // Create a new dialog but do not replace the current one yet. final AuthDialog newDialog = buildDialog( promptInfo, requireConfirmation, userId, - type, + sensorIds, + credentialAllowed, opPackageName, skipAnimation, operationId); if (newDialog == null) { - Log.e(TAG, "Unsupported type: " + type); + Log.e(TAG, "Unsupported type configuration"); return; } @@ -493,8 +512,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, Log.d(TAG, "userId: " + userId + " savedState: " + savedState + " mCurrentDialog: " + mCurrentDialog - + " newDialog: " + newDialog - + " type: " + type); + + " newDialog: " + newDialog); } if (mCurrentDialog != null) { @@ -550,7 +568,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation, - int userId, @BiometricAuthenticator.Modality int type, String opPackageName, + int userId, int[] sensorIds, boolean credentialAllowed, String opPackageName, boolean skipIntro, long operationId) { return new AuthContainerView.Builder(mContext) .setCallback(this) @@ -560,6 +578,6 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, .setOpPackageName(opPackageName) .setSkipIntro(skipIntro) .setOperationId(operationId) - .build(type); + .build(sensorIds, credentialAllowed, mFpProps, mFaceProps); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java index 2e9afa58987a..fd5e85a953ad 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java @@ -20,9 +20,11 @@ import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; import android.annotation.IntDef; +import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.hardware.biometrics.PromptInfo; +import android.hardware.biometrics.SensorPropertiesInternal; import android.os.UserManager; import android.util.DisplayMetrics; import android.view.ViewGroup; @@ -33,6 +35,7 @@ import com.android.internal.widget.LockPatternUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; public class Utils { @@ -98,4 +101,19 @@ public class Utils { final UserManager userManager = context.getSystemService(UserManager.class); return userManager.isManagedProfile(userId); } + + static boolean containsSensorId(@Nullable List<? extends SensorPropertiesInternal> properties, + int sensorId) { + if (properties == null) { + return false; + } + + for (SensorPropertiesInternal prop : properties) { + if (prop.sensorId == sensorId) { + return true; + } + } + + return false; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index dcee9fa9e648..5b3763e66307 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -284,7 +284,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver, - @BiometricAuthenticator.Modality int biometricModality, + int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId, String opPackageName, long operationId) { } default void onBiometricAuthenticated() { } @@ -829,17 +829,18 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< @Override public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver, - @BiometricAuthenticator.Modality int biometricModality, boolean requireConfirmation, + int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId, String opPackageName, long operationId) { synchronized (mLock) { SomeArgs args = SomeArgs.obtain(); args.arg1 = promptInfo; args.arg2 = receiver; - args.argi1 = biometricModality; - args.arg3 = requireConfirmation; - args.argi2 = userId; - args.arg4 = opPackageName; - args.arg5 = operationId; + args.arg3 = sensorIds; // + args.arg4 = credentialAllowed; // + args.arg5 = requireConfirmation; + args.argi1 = userId; + args.arg6 = opPackageName; + args.arg7 = operationId; mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args) .sendToTarget(); } @@ -1264,11 +1265,12 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< mCallbacks.get(i).showAuthenticationDialog( (PromptInfo) someArgs.arg1, (IBiometricSysuiReceiver) someArgs.arg2, - someArgs.argi1 /* biometricModality */, - (boolean) someArgs.arg3 /* requireConfirmation */, - someArgs.argi2 /* userId */, - (String) someArgs.arg4 /* opPackageName */, - (long) someArgs.arg5 /* operationId */); + (int[]) someArgs.arg3 /* sensorIds */, + (boolean) someArgs.arg4 /* credentialAllowed */, + (boolean) someArgs.arg5 /* requireConfirmation */, + someArgs.argi1 /* userId */, + (String) someArgs.arg6 /* opPackageName */, + (long) someArgs.arg7 /* operationId */); } someArgs.recycle(); break; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java index b60fa4f7ea67..777db952591e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java @@ -32,10 +32,15 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.PromptInfo; +import android.hardware.biometrics.SensorProperties; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.fingerprint.FingerprintSensorProperties; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.IBinder; import android.os.UserManager; import android.test.suitebuilder.annotation.SmallTest; @@ -58,6 +63,9 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest @@ -222,18 +230,28 @@ public class AuthContainerViewTest extends SysuiTestCase { AuthContainerView.Config config = new AuthContainerView.Config(); config.mContext = mContext; config.mCallback = mCallback; - config.mModalityMask |= BiometricAuthenticator.TYPE_FINGERPRINT; + config.mSensorIds = new int[] {0}; + config.mCredentialAllowed = false; PromptInfo promptInfo = new PromptInfo(); promptInfo.setAuthenticators(authenticators); config.mPromptInfo = promptInfo; - mAuthContainer = new TestableAuthContainer(config); + final List<FingerprintSensorPropertiesInternal> fpProps = new ArrayList<>(); + fpProps.add(new FingerprintSensorPropertiesInternal(0, + SensorProperties.STRENGTH_STRONG, + 5 /* maxEnrollmentsPerUser */, + FingerprintSensorProperties.TYPE_REAR, + false /* resetLockoutRequiresHardwareAuthToken */)); + mAuthContainer = new TestableAuthContainer(config, fpProps, null /* faceProps */); } private class TestableAuthContainer extends AuthContainerView { - TestableAuthContainer(AuthContainerView.Config config) { - super(config, new MockInjector()); + TestableAuthContainer(AuthContainerView.Config config, + @Nullable List<FingerprintSensorPropertiesInternal> fpProps, + @Nullable List<FaceSensorPropertiesInternal> faceProps) { + + super(config, new MockInjector(), fpProps, faceProps); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index e0420ca38f45..0186d7394ecb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -97,6 +97,8 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private FingerprintManager mFingerprintManager; @Mock + private FaceManager mFaceManager; + @Mock private UdfpsController mUdfpsController; private TestableAuthController mAuthController; @@ -132,7 +134,7 @@ public class AuthControllerTest extends SysuiTestCase { when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); mAuthController = new TestableAuthController(context, mCommandQueue, - mStatusBarStateController, mActivityTaskManager, mFingerprintManager, + mStatusBarStateController, mActivityTaskManager, mFingerprintManager, mFaceManager, () -> mUdfpsController); mAuthController.start(); @@ -142,7 +144,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, null /* credentialAttestation */); verify(mReceiver).onDialogDismissed( @@ -152,7 +154,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE, null /* credentialAttestation */); verify(mReceiver).onDialogDismissed( @@ -162,7 +164,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE, null /* credentialAttestation */); verify(mReceiver).onDialogDismissed( @@ -172,7 +174,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED, null /* credentialAttestation */); verify(mReceiver).onDialogDismissed( @@ -182,7 +184,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonError_whenDismissedByError() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR, null /* credentialAttestation */); verify(mReceiver).onDialogDismissed( @@ -192,7 +194,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER, null /* credentialAttestation */); verify(mReceiver).onDialogDismissed( @@ -203,7 +205,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final byte[] credentialAttestation = generateRandomHAT(); @@ -217,22 +219,21 @@ public class AuthControllerTest extends SysuiTestCase { // Statusbar tests @Test - public void testShowInvoked_whenSystemRequested() - throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + public void testShowInvoked_whenSystemRequested() { + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); verify(mDialog1).show(any(), any()); } @Test public void testOnAuthenticationSucceededInvoked_whenSystemRequested() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onBiometricAuthenticated(); verify(mDialog1).onAuthenticationSucceeded(); } @Test public void testOnAuthenticationFailedInvoked_whenBiometricRejected() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onBiometricError(BiometricAuthenticator.TYPE_NONE, BiometricConstants.BIOMETRIC_PAUSED_REJECTED, 0 /* vendorCode */); @@ -245,7 +246,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testOnAuthenticationFailedInvoked_whenBiometricTimedOut() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final int error = BiometricConstants.BIOMETRIC_ERROR_TIMEOUT; final int vendorCode = 0; mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); @@ -258,7 +259,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testOnHelpInvoked_whenSystemRequested() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final String helpMessage = "help"; mAuthController.onBiometricHelp(helpMessage); @@ -269,8 +270,8 @@ public class AuthControllerTest extends SysuiTestCase { } @Test - public void testOnErrorInvoked_whenSystemRequested() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + public void testOnErrorInvoked_whenSystemRequested() { + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final int error = 1; final int vendorCode = 0; mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); @@ -283,7 +284,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testErrorLockout_whenCredentialAllowed_AnimatesToCredentialUI() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT; final int vendorCode = 0; @@ -296,7 +297,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testErrorLockoutPermanent_whenCredentialAllowed_AnimatesToCredentialUI() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; final int vendorCode = 0; @@ -309,7 +310,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testErrorLockout_whenCredentialNotAllowed_sendsOnError() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT; final int vendorCode = 0; @@ -322,7 +323,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testErrorLockoutPermanent_whenCredentialNotAllowed_sendsOnError() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; final int vendorCode = 0; @@ -335,7 +336,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testHideAuthenticationDialog_invokesDismissFromSystemServer() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.hideAuthenticationDialog(); verify(mDialog1).dismissFromSystemServer(); @@ -355,7 +356,7 @@ public class AuthControllerTest extends SysuiTestCase { // 1) Credential is confirmed // 2) Client cancels authentication - showDialog(Authenticators.DEVICE_CREDENTIAL, BiometricPrompt.TYPE_NONE); + showDialog(new int[0] /* sensorIds */, true /* credentialAllowed */); verify(mDialog1).show(any(), any()); final byte[] credentialAttestation = generateRandomHAT(); @@ -371,10 +372,10 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); verify(mDialog1).show(any(), any()); - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); // First dialog should be dismissed without animation verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */); @@ -385,7 +386,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testConfigurationPersists_whenOnConfigurationChanged() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); verify(mDialog1).show(any(), any()); // Return that the UI is in "showing" state @@ -415,8 +416,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testConfigurationPersists_whenBiometricFallbackToCredential() { - showDialog(Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK, - BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, true /* credentialAllowed */); verify(mDialog1).show(any(), any()); // Pretend that the UI is now showing device credential UI. @@ -440,7 +440,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>(); ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); @@ -462,7 +462,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, null /* credentialAttestation */); mAuthController.onTryAgainPressed(); @@ -470,7 +470,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, null /* credentialAttestation */); mAuthController.onDeviceCredentialPressed(); @@ -478,7 +478,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testActionCloseSystemDialogs_dismissesDialogIfShowing() throws Exception { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); mAuthController.mBroadcastReceiver.onReceive(mContext, intent); waitForIdleSync(); @@ -500,14 +500,14 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testOnBiometricAuthenticated_OnCancelAodInterrupt() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FINGERPRINT); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onBiometricAuthenticated(); verify(mUdfpsController).onCancelAodInterrupt(); } @Test public void testOnBiometricError_OnCancelAodInterrupt() { - showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FINGERPRINT); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onBiometricError(0, 0, 0); verify(mUdfpsController).onCancelAodInterrupt(); } @@ -538,17 +538,18 @@ public class AuthControllerTest extends SysuiTestCase { // Helpers - private void showDialog(int authenticators, int biometricModality) { - mAuthController.showAuthenticationDialog(createTestPromptInfo(authenticators), + private void showDialog(int[] sensorIds, boolean credentialAllowed) { + mAuthController.showAuthenticationDialog(createTestPromptInfo(), mReceiver /* receiver */, - biometricModality, + sensorIds, + credentialAllowed, true /* requireConfirmation */, 0 /* userId */, "testPackage", 0 /* operationId */); } - private PromptInfo createTestPromptInfo(int authenticators) { + private PromptInfo createTestPromptInfo() { PromptInfo promptInfo = new PromptInfo(); promptInfo.setTitle("Title"); @@ -560,8 +561,6 @@ public class AuthControllerTest extends SysuiTestCase { // by user settings, and should be tested in BiometricService. promptInfo.setConfirmationRequested(true); - promptInfo.setAuthenticators(authenticators); - return promptInfo; } @@ -580,15 +579,16 @@ public class AuthControllerTest extends SysuiTestCase { StatusBarStateController statusBarStateController, IActivityTaskManager activityTaskManager, FingerprintManager fingerprintManager, + FaceManager faceManager, Provider<UdfpsController> udfpsControllerFactory) { super(context, commandQueue, statusBarStateController, activityTaskManager, - fingerprintManager, udfpsControllerFactory); + fingerprintManager, faceManager, udfpsControllerFactory); } @Override protected AuthDialog buildDialog(PromptInfo promptInfo, - boolean requireConfirmation, int userId, int type, String opPackageName, - boolean skipIntro, long operationId) { + boolean requireConfirmation, int userId, int[] sensorIds, boolean credentialAllowed, + String opPackageName, boolean skipIntro, long operationId) { mLastBiometricPromptInfo = promptInfo; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 8617a8326283..d2d57087485c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import android.content.ComponentName; import android.graphics.Rect; +import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.os.Bundle; import android.view.WindowInsetsController.Appearance; @@ -409,13 +410,20 @@ public class CommandQueueTest extends SysuiTestCase { @Test public void testShowAuthenticationDialog() { PromptInfo promptInfo = new PromptInfo(); - String packageName = "test"; + final IBiometricSysuiReceiver receiver = mock(IBiometricSysuiReceiver.class); + final int[] sensorIds = {1, 2}; + final boolean credentialAllowed = true; + final boolean requireConfirmation = true; + final int userId = 10; + final String packageName = "test"; final long operationId = 1; - mCommandQueue.showAuthenticationDialog(promptInfo, null /* receiver */, 1, true, 3, - packageName, operationId); + + mCommandQueue.showAuthenticationDialog(promptInfo, receiver, sensorIds, + credentialAllowed, requireConfirmation , userId, packageName, operationId); waitForIdleSync(); - verify(mCallbacks).showAuthenticationDialog(eq(promptInfo), eq(null), eq(1), eq(true), - eq(3), eq(packageName), eq(operationId)); + verify(mCallbacks).showAuthenticationDialog(eq(promptInfo), eq(receiver), eq(sensorIds), + eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(packageName), + eq(operationId)); } @Test diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 15dc95673790..3c18cd4ed231 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -44,6 +44,8 @@ import com.android.internal.util.FrameworkStatsLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; import java.util.Random; /** @@ -241,7 +243,8 @@ public final class AuthSession implements IBinder.DeathRecipient { mStatusBarService.showAuthenticationDialog( mPromptInfo, mSysuiReceiver, - 0 /* biometricModality */, + new int[0] /* sensorIds */, + true /* credentialAllowed */, false /* requireConfirmation */, mUserId, mOpPackageName, @@ -271,11 +274,16 @@ public final class AuthSession implements IBinder.DeathRecipient { try { // If any sensor requires confirmation, request it to be shown. final boolean requireConfirmation = isConfirmationRequiredByAnyEligibleSensor(); - final @BiometricAuthenticator.Modality int modality = - getEligibleModalities(); + + final int[] sensorIds = new int[mPreAuthInfo.eligibleSensors.size()]; + for (int i = 0; i < mPreAuthInfo.eligibleSensors.size(); i++) { + sensorIds[i] = mPreAuthInfo.eligibleSensors.get(i).id; + } + mStatusBarService.showAuthenticationDialog(mPromptInfo, mSysuiReceiver, - modality, + sensorIds, + mPreAuthInfo.shouldShowCredential(), requireConfirmation, mUserId, mOpPackageName, @@ -369,7 +377,8 @@ public final class AuthSession implements IBinder.DeathRecipient { mStatusBarService.showAuthenticationDialog( mPromptInfo, mSysuiReceiver, - 0 /* biometricModality */, + new int[0] /* sensorIds */, + true /* credentialAllowed */, false /* requireConfirmation */, mUserId, mOpPackageName, diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index 08a617134221..6905b3da9bc4 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -32,7 +32,6 @@ import android.os.RemoteException; import android.util.Pair; import android.util.Slog; -import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.sensors.LockoutTracker; import java.lang.annotation.Retention; @@ -356,6 +355,13 @@ class PreAuthInfo { } /** + * @return true if SystemUI should show the credential UI. + */ + boolean shouldShowCredential() { + return credentialRequested && credentialAvailable; + } + + /** * @return bitmask representing the modalities that are running or could be running for the * current session. */ diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 55cb7f32e1b7..fb47ebbcaa07 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -706,12 +706,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D @Override public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver, - @BiometricAuthenticator.Modality int biometricModality, boolean requireConfirmation, + int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId, String opPackageName, long operationId) { enforceBiometricDialog(); if (mBar != null) { try { - mBar.showAuthenticationDialog(promptInfo, receiver, biometricModality, + mBar.showAuthenticationDialog(promptInfo, receiver, sensorIds, credentialAllowed, requireConfirmation, userId, opPackageName, operationId); } catch (RemoteException ex) { } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index f0be9f1d3213..3b6059406785 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -262,7 +262,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService).showAuthenticationDialog( eq(mBiometricService.mCurrentAuthSession.mPromptInfo), any(IBiometricSysuiReceiver.class), - eq(0), + AdditionalMatchers.aryEq(new int[0]) /* sensorIds */, + eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, eq(TEST_PACKAGE_NAME), @@ -345,7 +346,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService).showAuthenticationDialog( eq(mBiometricService.mCurrentAuthSession.mPromptInfo), any(IBiometricSysuiReceiver.class), - eq(BiometricAuthenticator.TYPE_FACE), + AdditionalMatchers.aryEq(new int[] {SENSOR_ID_FACE}), + eq(false) /* credentialAllowed */, eq(false) /* requireConfirmation */, anyInt() /* userId */, eq(TEST_PACKAGE_NAME), @@ -477,7 +479,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService).showAuthenticationDialog( eq(mBiometricService.mCurrentAuthSession.mPromptInfo), any(IBiometricSysuiReceiver.class), - eq(BiometricAuthenticator.TYPE_FINGERPRINT), + any(), + eq(false) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, eq(TEST_PACKAGE_NAME), @@ -530,7 +533,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService).showAuthenticationDialog( eq(mBiometricService.mCurrentAuthSession.mPromptInfo), any(IBiometricSysuiReceiver.class), - eq(0 /* biometricModality */), + AdditionalMatchers.aryEq(new int[0]) /* sensorIds */, + eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, eq(TEST_PACKAGE_NAME), @@ -696,7 +700,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService, never()).showAuthenticationDialog( any(PromptInfo.class), any(IBiometricSysuiReceiver.class), - anyInt(), + any() /* sensorIds */, + anyBoolean() /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, anyString(), @@ -796,7 +801,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService).showAuthenticationDialog( eq(mBiometricService.mCurrentAuthSession.mPromptInfo), any(IBiometricSysuiReceiver.class), - eq(0 /* biometricModality */), + AdditionalMatchers.aryEq(new int[0]) /* sensorIds */, + eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, eq(TEST_PACKAGE_NAME), @@ -874,7 +880,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService).showAuthenticationDialog( eq(mBiometricService.mCurrentAuthSession.mPromptInfo), any(IBiometricSysuiReceiver.class), - eq(0 /* biometricModality */), + AdditionalMatchers.aryEq(new int[0]) /* sensorIds */, + eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, eq(TEST_PACKAGE_NAME), @@ -1383,7 +1390,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService).showAuthenticationDialog( eq(mBiometricService.mCurrentAuthSession.mPromptInfo), any(IBiometricSysuiReceiver.class), - eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */), + AdditionalMatchers.aryEq(new int[] {testId}), + eq(false) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, eq(TEST_PACKAGE_NAME), @@ -1403,7 +1411,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService).showAuthenticationDialog( eq(mBiometricService.mCurrentAuthSession.mPromptInfo), any(IBiometricSysuiReceiver.class), - eq(BiometricAuthenticator.TYPE_NONE /* biometricModality */), + AdditionalMatchers.aryEq(new int[0]) /* sensorIds */, + eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, eq(TEST_PACKAGE_NAME), @@ -1426,7 +1435,8 @@ public class BiometricServiceTest { verify(mBiometricService.mStatusBarService).showAuthenticationDialog( eq(mBiometricService.mCurrentAuthSession.mPromptInfo), any(IBiometricSysuiReceiver.class), - eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */), + AdditionalMatchers.aryEq(new int[] {testId}) /* sensorIds */, + eq(false) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, eq(TEST_PACKAGE_NAME), |