diff options
| author | 2023-07-10 17:50:56 +0000 | |
|---|---|---|
| committer | 2023-07-11 17:58:17 +0000 | |
| commit | 1fa4a8c913c80e680da69d4f231f0d831f843b9b (patch) | |
| tree | b83ee3ee9cfe9fb4cf6b8c505c6fc8748aea6523 | |
| parent | c9f03886f715480c4973c5d6aead70936f3fc7c8 (diff) | |
Update bp subtitle base on modality
Change the subtile of biometric prompt to reflect modality.
Test: Manual (see bug)
Test: atest BiometricServiceTest
Fixes: 283151438
Change-Id: I290148e47cf6daf0821df1da10907124848266bc
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 85e9792fc99b..196d8d01d505 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2570,6 +2570,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 1fa97a3cb97f..643e46ff1b52 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; @@ -368,7 +370,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, @@ -566,22 +568,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)); @@ -1301,6 +1287,33 @@ public class BiometricService extends SystemService { opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(), getContext(), mBiometricSensorPrivacy); + // 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 41f7dbcb0ff5..f5cc897453d5 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"; @@ -188,6 +193,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, @@ -208,7 +219,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 */); @@ -235,7 +246,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 */); @@ -371,7 +382,7 @@ public class BiometricServiceTest { final int[] modalities = new int[] { TYPE_FINGERPRINT, - BiometricAuthenticator.TYPE_FACE, + TYPE_FACE, }; final int[] strengths = new int[] { @@ -424,9 +435,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); @@ -565,7 +623,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); @@ -592,13 +650,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 */); } @@ -634,7 +692,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); @@ -652,7 +710,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 */); @@ -660,7 +718,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(); @@ -688,7 +746,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 */); @@ -697,7 +755,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)); @@ -707,7 +765,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 */); @@ -720,7 +778,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. @@ -756,7 +814,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 */); @@ -774,7 +832,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 @@ -923,7 +981,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[] { @@ -1120,7 +1178,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 */); @@ -1139,7 +1197,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 */); @@ -1158,7 +1216,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 */); @@ -1172,7 +1230,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)); @@ -1293,7 +1351,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); @@ -1587,7 +1645,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); @@ -1680,7 +1738,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())) @@ -1712,7 +1770,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, @@ -1795,6 +1853,7 @@ public class BiometricServiceTest { boolean checkDevicePolicy) { final PromptInfo promptInfo = new PromptInfo(); promptInfo.setConfirmationRequested(requireConfirmation); + promptInfo.setUseDefaultSubtitle(true); if (authenticators != null) { promptInfo.setAuthenticators(authenticators); |