diff options
4 files changed, 120 insertions, 42 deletions
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index f795bd7cc3fd..c120af347741 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1780,6 +1780,10 @@ <string name="biometric_dialog_default_title">Verify it\u2019s you</string> <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with a biometric (e.g. fingerprint or face). [CHAR LIMIT=70] --> <string name="biometric_dialog_default_subtitle">Use your biometric to continue</string> + <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with fingerprint. [CHAR LIMIT=70] --> + <string name="biometric_dialog_fingerprint_subtitle">Use your fingerprint to continue</string> + <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with face. [CHAR LIMIT=70] --> + <string name="biometric_dialog_face_subtitle">Use your face to continue</string> <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with a biometric (e.g. fingerprint or face) or their screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=90] --> <string name="biometric_or_screen_lock_dialog_default_subtitle">Use your biometric or screen lock to continue</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 59306a3a61ce..bffcf5f27ade 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2571,6 +2571,8 @@ <java-symbol type="string" name="biometric_or_screen_lock_app_setting_name" /> <java-symbol type="string" name="biometric_dialog_default_title" /> <java-symbol type="string" name="biometric_dialog_default_subtitle" /> + <java-symbol type="string" name="biometric_dialog_face_subtitle" /> + <java-symbol type="string" name="biometric_dialog_fingerprint_subtitle" /> <java-symbol type="string" name="biometric_or_screen_lock_dialog_default_subtitle" /> <java-symbol type="string" name="biometric_error_hw_unavailable" /> <java-symbol type="string" name="biometric_error_user_canceled" /> diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index e8ffe4feb458..279aaf9d3253 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -17,6 +17,8 @@ package com.android.server.biometrics; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; +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 com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE; @@ -369,7 +371,7 @@ public class BiometricService extends SystemService { public boolean getConfirmationAlwaysRequired(@BiometricAuthenticator.Modality int modality, int userId) { switch (modality) { - case BiometricAuthenticator.TYPE_FACE: + case TYPE_FACE: if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) { onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, @@ -567,22 +569,6 @@ public class BiometricService extends SystemService { Utils.combineAuthenticatorBundles(promptInfo); - // Set the default title if necessary. - if (promptInfo.isUseDefaultTitle()) { - if (TextUtils.isEmpty(promptInfo.getTitle())) { - promptInfo.setTitle(getContext() - .getString(R.string.biometric_dialog_default_title)); - } - } - - // Set the default subtitle if necessary. - if (promptInfo.isUseDefaultSubtitle()) { - if (TextUtils.isEmpty(promptInfo.getSubtitle())) { - promptInfo.setSubtitle(getContext() - .getString(R.string.biometric_dialog_default_subtitle)); - } - } - final long requestId = mRequestCounter.get(); mHandler.post(() -> handleAuthenticate( token, requestId, operationId, userId, receiver, opPackageName, promptInfo)); @@ -1302,6 +1288,33 @@ public class BiometricService extends SystemService { opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(), getContext(), mBiometricCameraManager); + // Set the default title if necessary. + if (promptInfo.isUseDefaultTitle()) { + if (TextUtils.isEmpty(promptInfo.getTitle())) { + promptInfo.setTitle(getContext() + .getString(R.string.biometric_dialog_default_title)); + } + } + + final int eligible = preAuthInfo.getEligibleModalities(); + final boolean hasEligibleFingerprintSensor = + (eligible & TYPE_FINGERPRINT) == TYPE_FINGERPRINT; + final boolean hasEligibleFaceSensor = (eligible & TYPE_FACE) == TYPE_FACE; + + // Set the subtitle according to the modality. + if (promptInfo.isUseDefaultSubtitle()) { + if (hasEligibleFingerprintSensor && hasEligibleFaceSensor) { + promptInfo.setSubtitle(getContext() + .getString(R.string.biometric_dialog_default_subtitle)); + } else if (hasEligibleFingerprintSensor) { + promptInfo.setSubtitle(getContext() + .getString(R.string.biometric_dialog_fingerprint_subtitle)); + } else if (hasEligibleFaceSensor) { + promptInfo.setSubtitle(getContext() + .getString(R.string.biometric_dialog_face_subtitle)); + } + } + final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus(); Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first 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 68217219e453..fc62e75b7d59 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -16,6 +16,7 @@ package com.android.server.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.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; @@ -113,6 +114,10 @@ public class BiometricServiceTest { private static final String ERROR_UNABLE_TO_PROCESS = "error_unable_to_process"; private static final String ERROR_USER_CANCELED = "error_user_canceled"; private static final String ERROR_LOCKOUT = "error_lockout"; + private static final String FACE_SUBTITLE = "face_subtitle"; + private static final String FINGERPRINT_SUBTITLE = "fingerprint_subtitle"; + private static final String DEFAULT_SUBTITLE = "default_subtitle"; + private static final String FINGERPRINT_ACQUIRED_SENSOR_DIRTY = "sensor_dirty"; @@ -191,6 +196,12 @@ public class BiometricServiceTest { .thenReturn(ERROR_NOT_RECOGNIZED); when(mResources.getString(R.string.biometric_error_user_canceled)) .thenReturn(ERROR_USER_CANCELED); + when(mContext.getString(R.string.biometric_dialog_face_subtitle)) + .thenReturn(FACE_SUBTITLE); + when(mContext.getString(R.string.biometric_dialog_fingerprint_subtitle)) + .thenReturn(FINGERPRINT_SUBTITLE); + when(mContext.getString(R.string.biometric_dialog_default_subtitle)) + .thenReturn(DEFAULT_SUBTITLE); when(mWindowManager.getDefaultDisplay()).thenReturn( new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY, @@ -211,7 +222,7 @@ public class BiometricServiceTest { @Test public void testClientBinderDied_whenPaused() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, true /* requireConfirmation */, null /* authenticators */); @@ -238,7 +249,7 @@ public class BiometricServiceTest { @Test public void testClientBinderDied_whenAuthenticating() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, true /* requireConfirmation */, null /* authenticators */); @@ -374,7 +385,7 @@ public class BiometricServiceTest { final int[] modalities = new int[] { TYPE_FINGERPRINT, - BiometricAuthenticator.TYPE_FACE, + TYPE_FACE, }; final int[] strengths = new int[] { @@ -427,9 +438,56 @@ public class BiometricServiceTest { } @Test + public void testAuthenticateFace_shouldShowSubtitleForFace() throws Exception { + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + + invokeAuthenticate(mBiometricService.mImpl, mReceiver1, + false /* requireConfirmation */, + null); + waitForIdle(); + + assertEquals(FACE_SUBTITLE, mBiometricService.mAuthSession.mPromptInfo.getSubtitle()); + } + + @Test + public void testAuthenticateFingerprint_shouldShowSubtitleForFingerprint() throws Exception { + setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG); + + invokeAuthenticate(mBiometricService.mImpl, mReceiver1, + false /* requireConfirmation */, + null); + waitForIdle(); + + assertEquals(FINGERPRINT_SUBTITLE, + mBiometricService.mAuthSession.mPromptInfo.getSubtitle()); + } + + @Test + public void testAuthenticateBothFpAndFace_shouldShowDefaultSubtitle() throws Exception { + final int[] modalities = new int[] { + TYPE_FINGERPRINT, + TYPE_FACE, + }; + + final int[] strengths = new int[] { + Authenticators.BIOMETRIC_WEAK, + Authenticators.BIOMETRIC_STRONG, + }; + + setupAuthForMultiple(modalities, strengths); + + invokeAuthenticate(mBiometricService.mImpl, mReceiver1, + false /* requireConfirmation */, + null); + waitForIdle(); + + assertEquals(DEFAULT_SUBTITLE, mBiometricService.mAuthSession.mPromptInfo.getSubtitle()); + } + + @Test public void testAuthenticateFace_respectsUserSetting() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); // Disabled in user settings receives onError when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false); @@ -568,7 +626,7 @@ public class BiometricServiceTest { @Test public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false); when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) .thenReturn(true); @@ -595,13 +653,13 @@ public class BiometricServiceTest { @Test public void testAuthenticate_happyPathWithConfirmation_strongBiometric() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); testAuthenticate_happyPathWithConfirmation(true /* isStrongBiometric */); } @Test public void testAuthenticate_happyPathWithConfirmation_weakBiometric() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_WEAK); testAuthenticate_happyPathWithConfirmation(false /* isStrongBiometric */); } @@ -637,7 +695,7 @@ public class BiometricServiceTest { @Test public void testAuthenticate_no_Biometrics_noCredential() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false); when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) .thenReturn(false); @@ -655,7 +713,7 @@ public class BiometricServiceTest { @Test public void testRejectFace_whenAuthenticating_notifiesSystemUIAndClient_thenPaused() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); @@ -663,7 +721,7 @@ public class BiometricServiceTest { waitForIdle(); verify(mBiometricService.mStatusBarService).onBiometricError( - eq(BiometricAuthenticator.TYPE_FACE), + eq(TYPE_FACE), eq(BiometricConstants.BIOMETRIC_PAUSED_REJECTED), eq(0 /* vendorCode */)); verify(mReceiver1).onAuthenticationFailed(); @@ -691,7 +749,7 @@ public class BiometricServiceTest { @Test public void testRequestAuthentication_whenAlreadyAuthenticating() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); @@ -700,7 +758,7 @@ public class BiometricServiceTest { waitForIdle(); verify(mReceiver1).onError( - eq(BiometricAuthenticator.TYPE_FACE), + eq(TYPE_FACE), eq(BiometricPrompt.BIOMETRIC_ERROR_CANCELED), eq(0) /* vendorCode */); verify(mBiometricService.mStatusBarService).hideAuthenticationDialog(eq(TEST_REQUEST_ID)); @@ -710,7 +768,7 @@ public class BiometricServiceTest { @Test public void testErrorHalTimeout_whenAuthenticating_entersPausedState() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); @@ -723,7 +781,7 @@ public class BiometricServiceTest { assertEquals(STATE_AUTH_PAUSED, mBiometricService.mAuthSession.getState()); verify(mBiometricService.mStatusBarService).onBiometricError( - eq(BiometricAuthenticator.TYPE_FACE), + eq(TYPE_FACE), eq(BiometricConstants.BIOMETRIC_ERROR_TIMEOUT), eq(0 /* vendorCode */)); // Timeout does not count as fail as per BiometricPrompt documentation. @@ -759,7 +817,7 @@ public class BiometricServiceTest { @Test public void testErrorFromHal_whenPaused_notifiesSystemUIAndClient() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); @@ -777,7 +835,7 @@ public class BiometricServiceTest { // Client receives error immediately verify(mReceiver1).onError( - eq(BiometricAuthenticator.TYPE_FACE), + eq(TYPE_FACE), eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), eq(0 /* vendorCode */)); // Dialog is hidden immediately @@ -926,7 +984,7 @@ public class BiometricServiceTest { int biometricPromptError) throws Exception { final int[] modalities = new int[] { TYPE_FINGERPRINT, - BiometricAuthenticator.TYPE_FACE, + TYPE_FACE, }; final int[] strengths = new int[] { @@ -1123,7 +1181,7 @@ public class BiometricServiceTest { @Test public void testDismissedReasonNegative_whilePaused_invokeHalCancel() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); @@ -1142,7 +1200,7 @@ public class BiometricServiceTest { @Test public void testDismissedReasonUserCancel_whilePaused_invokesHalCancel() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); @@ -1161,7 +1219,7 @@ public class BiometricServiceTest { @Test public void testDismissedReasonUserCancel_whenPendingConfirmation() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, true /* requireConfirmation */, null /* authenticators */); @@ -1175,7 +1233,7 @@ public class BiometricServiceTest { verify(mBiometricService.mSensors.get(0).impl) .cancelAuthenticationFromService(any(), any(), anyLong()); verify(mReceiver1).onError( - eq(BiometricAuthenticator.TYPE_FACE), + eq(TYPE_FACE), eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED), eq(0 /* vendorCode */)); verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class)); @@ -1296,7 +1354,7 @@ public class BiometricServiceTest { @Test public void testCanAuthenticate_whenBiometricsNotEnabledForApps() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false); when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) .thenReturn(true); @@ -1590,7 +1648,7 @@ public class BiometricServiceTest { @Test public void testWorkAuthentication_faceWorksIfNotDisabledByDevicePolicyManager() throws Exception { - setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); when(mDevicePolicyManager .getKeyguardDisabledFeatures(any() /* admin*/, anyInt() /* userHandle */)) .thenReturn(~DevicePolicyManager.KEYGUARD_DISABLE_FACE); @@ -1683,7 +1741,7 @@ public class BiometricServiceTest { mFingerprintAuthenticator); } - if ((modality & BiometricAuthenticator.TYPE_FACE) != 0) { + if ((modality & TYPE_FACE) != 0) { when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(enrolled); when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true); when(mFaceAuthenticator.getLockoutModeForUser(anyInt())) @@ -1715,7 +1773,7 @@ public class BiometricServiceTest { strength, mFingerprintAuthenticator); } - if ((modality & BiometricAuthenticator.TYPE_FACE) != 0) { + if ((modality & TYPE_FACE) != 0) { when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true); mBiometricService.mImpl.registerAuthenticator(SENSOR_ID_FACE, modality, @@ -1798,6 +1856,7 @@ public class BiometricServiceTest { boolean checkDevicePolicy) { final PromptInfo promptInfo = new PromptInfo(); promptInfo.setConfirmationRequested(requireConfirmation); + promptInfo.setUseDefaultSubtitle(true); if (authenticators != null) { promptInfo.setAuthenticators(authenticators); |