Merge "Update bp subtitle base on modality" into udc-d1-dev

This commit is contained in:
Wenhui Yang
2023-07-14 20:55:22 +00:00
committed by Android (Google) Code Review
4 changed files with 120 additions and 42 deletions

View File

@@ -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>

View File

@@ -2566,6 +2566,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" />

View File

@@ -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

View File

@@ -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[] {
@@ -426,10 +437,57 @@ public class BiometricServiceTest {
eq(0 /* vendorCode */));
}
@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);