From c4ee2b83e5b129793bbd6f433b9f54519b4636fe Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 6 Mar 2025 08:36:57 +0000 Subject: [PATCH] Launch multiple biometric enrollment when no biometric is enrolled When a user clicks the biometric item in the Device unlock page and no biometric is enrolled: - Fingerint clicked: Launch fingerprint enroll then face enroll. - Face clicked: Launch face enroll then fingerprint enroll. Bug: 370940762 Test: atest FaceSafetySourceTest FingerprintSafetySourceTest Flag: com.android.settings.flags.biometrics_onboarding_education Change-Id: I874b96a75ec0c126ae1674bb6ab220a0a8533fcd --- .../safetycenter/BiometricSourcesUtils.java | 13 +++++++++ .../safetycenter/FaceSafetySource.java | 16 +++++++++-- .../safetycenter/FingerprintSafetySource.java | 8 +++++- .../safetycenter/FaceSafetySourceTest.java | 27 ++++++++++++++++++- .../FingerprintSafetySourceTest.java | 27 ++++++++++++++++++- 5 files changed, 86 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/safetycenter/BiometricSourcesUtils.java b/src/com/android/settings/safetycenter/BiometricSourcesUtils.java index 9cadbbb5fcf..786e3cf3865 100644 --- a/src/com/android/settings/safetycenter/BiometricSourcesUtils.java +++ b/src/com/android/settings/safetycenter/BiometricSourcesUtils.java @@ -19,11 +19,15 @@ package com.android.settings.safetycenter; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.safetycenter.SafetyEvent; import android.safetycenter.SafetySourceData; import android.safetycenter.SafetySourceIssue; import android.safetycenter.SafetySourceStatus; +import com.android.settings.Utils; + /** Static helpers for setting SafetyCenter data for biometric safety sources. */ public final class BiometricSourcesUtils { @@ -89,6 +93,15 @@ public final class BiometricSourcesUtils { .setSafetySourceData(context, safetySourceId, safetySourceData, safetyEvent); } + /** Check whether the multiple biometrics enrollment is needed. */ + public static boolean isMultipleBiometricsEnrollmentNeeded(Context context, int userId) { + FaceManager faceManager = Utils.getFaceManagerOrNull(context); + FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(context); + return Utils.isMultipleBiometricsSupported(context) + && !faceManager.hasEnrolledTemplates(userId) + && !fingerprintManager.hasEnrolledFingerprints(userId); + } + /** Helper method for creating a pending intent. */ public static PendingIntent createPendingIntent( Context context, Intent intent, int requestCode) { diff --git a/src/com/android/settings/safetycenter/FaceSafetySource.java b/src/com/android/settings/safetycenter/FaceSafetySource.java index 73b6df56803..c5bde6efb60 100644 --- a/src/com/android/settings/safetycenter/FaceSafetySource.java +++ b/src/com/android/settings/safetycenter/FaceSafetySource.java @@ -16,6 +16,7 @@ package com.android.settings.safetycenter; +import static com.android.settings.biometrics.BiometricEnrollActivity.EXTRA_LAUNCH_FACE_ENROLL_FIRST; import static com.android.settings.safetycenter.BiometricSourcesUtils.REQUEST_CODE_FACE_SETTING; import android.content.Context; @@ -27,6 +28,7 @@ import android.os.UserManager; import android.safetycenter.SafetyEvent; import com.android.settings.Utils; +import com.android.settings.biometrics.BiometricEnrollActivity; import com.android.settings.biometrics.BiometricNavigationUtils; import com.android.settings.biometrics.face.FaceStatusUtils; import com.android.settings.flags.Flags; @@ -73,6 +75,16 @@ public final class FaceSafetySource { Context profileParentContext = context.createContextAsUser(profileParentUserHandle, 0); if (Utils.hasFaceHardware(context)) { + boolean isMultipleBiometricsEnrollmentNeeded = + BiometricSourcesUtils.isMultipleBiometricsEnrollmentNeeded(context, userId); + String settingClassName = isMultipleBiometricsEnrollmentNeeded + ? BiometricEnrollActivity.class.getName() + : faceStatusUtils.getSettingsClassName(); + Bundle bundle = new Bundle(); + if (isMultipleBiometricsEnrollmentNeeded) { + // Launch face enrollment first then fingerprint enrollment. + bundle.putBoolean(EXTRA_LAUNCH_FACE_ENROLL_FIRST, true); + } RestrictedLockUtils.EnforcedAdmin disablingAdmin = faceStatusUtils.getDisablingAdmin(); BiometricSourcesUtils.setBiometricSafetySourceData( SAFETY_SOURCE_ID, @@ -84,9 +96,9 @@ public final class FaceSafetySource { biometricNavigationUtils .getBiometricSettingsIntent( context, - faceStatusUtils.getSettingsClassName(), + settingClassName, disablingAdmin, - Bundle.EMPTY) + bundle) .setIdentifier(Integer.toString(userId)), REQUEST_CODE_FACE_SETTING), disablingAdmin == null /* enabled */, diff --git a/src/com/android/settings/safetycenter/FingerprintSafetySource.java b/src/com/android/settings/safetycenter/FingerprintSafetySource.java index 62f218bd519..752fa69ba95 100644 --- a/src/com/android/settings/safetycenter/FingerprintSafetySource.java +++ b/src/com/android/settings/safetycenter/FingerprintSafetySource.java @@ -27,6 +27,7 @@ import android.os.UserManager; import android.safetycenter.SafetyEvent; import com.android.settings.Utils; +import com.android.settings.biometrics.BiometricEnrollActivity; import com.android.settings.biometrics.BiometricNavigationUtils; import com.android.settings.biometrics.fingerprint.FingerprintStatusUtils; import com.android.settings.flags.Flags; @@ -74,6 +75,11 @@ public final class FingerprintSafetySource { Context profileParentContext = context.createContextAsUser(profileParentUserHandle, 0); if (Utils.hasFingerprintHardware(context)) { + boolean isMultipleBiometricsEnrollmentNeeded = + BiometricSourcesUtils.isMultipleBiometricsEnrollmentNeeded(context, userId); + String settingClassName = isMultipleBiometricsEnrollmentNeeded + ? BiometricEnrollActivity.class.getName() + : fingerprintStatusUtils.getSettingsClassName(); RestrictedLockUtils.EnforcedAdmin disablingAdmin = fingerprintStatusUtils.getDisablingAdmin(); BiometricSourcesUtils.setBiometricSafetySourceData( @@ -86,7 +92,7 @@ public final class FingerprintSafetySource { biometricNavigationUtils .getBiometricSettingsIntent( context, - fingerprintStatusUtils.getSettingsClassName(), + settingClassName, disablingAdmin, Bundle.EMPTY) .setIdentifier(Integer.toString(userId)), diff --git a/tests/unit/src/com/android/settings/safetycenter/FaceSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/FaceSafetySourceTest.java index 9335ced149c..bd53e42eb74 100644 --- a/tests/unit/src/com/android/settings/safetycenter/FaceSafetySourceTest.java +++ b/tests/unit/src/com/android/settings/safetycenter/FaceSafetySourceTest.java @@ -37,6 +37,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; @@ -52,6 +53,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; import com.android.settings.Settings; +import com.android.settings.biometrics.BiometricEnrollActivity; import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal; import com.android.settings.flags.Flags; import com.android.settings.testutils.FakeFeatureFactory; @@ -83,6 +85,7 @@ public class FaceSafetySourceTest { @Mock private PackageManager mPackageManager; @Mock private DevicePolicyManager mDevicePolicyManager; @Mock private FaceManager mFaceManager; + @Mock private FingerprintManager mFingerprintManager; @Mock private LockPatternUtils mLockPatternUtils; @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; @Mock private SupervisionManager mSupervisionManager; @@ -97,6 +100,8 @@ public class FaceSafetySourceTest { when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE)) .thenReturn(mDevicePolicyManager); when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager); + when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE)) + .thenReturn(mFingerprintManager); when(mApplicationContext.getSystemService(Context.SUPERVISION_SERVICE)) .thenReturn(mSupervisionManager); FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); @@ -210,10 +215,12 @@ public class FaceSafetySourceTest { @Test @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) - public void setSafetySourceData_withFaceNotEnrolled_whenNotDisabledByAdmin_setsData() { + public void setSafetySourceData_onlyFaceNotEnrolled_whenNotDisabledByAdmin_setsData() { when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); when(mFaceManager.isHardwareDetected()).thenReturn(true); when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true); when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); @@ -224,6 +231,24 @@ public class FaceSafetySourceTest { FaceEnrollIntroductionInternal.class.getName()); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void setSafetySourceData_noBiometricEnrolled_whenNotDisabledByAdmin_setsData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceEnabledDataSetWithSingularSummary( + "security_settings_face_preference_title_new", + "security_settings_face_preference_summary_none_new", + BiometricEnrollActivity.class.getName()); + } + @Test @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) @DisableFlags(android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS) diff --git a/tests/unit/src/com/android/settings/safetycenter/FingerprintSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/FingerprintSafetySourceTest.java index 91e7953fd5f..33551885da2 100644 --- a/tests/unit/src/com/android/settings/safetycenter/FingerprintSafetySourceTest.java +++ b/tests/unit/src/com/android/settings/safetycenter/FingerprintSafetySourceTest.java @@ -36,6 +36,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.hardware.face.FaceManager; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.os.UserHandle; @@ -52,6 +53,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; +import com.android.settings.biometrics.BiometricEnrollActivity; import com.android.settings.biometrics.fingerprint.FingerprintSettings; import com.android.settings.flags.Flags; import com.android.settings.testutils.FakeFeatureFactory; @@ -87,6 +89,7 @@ public class FingerprintSafetySourceTest { @Mock private PackageManager mPackageManager; @Mock private DevicePolicyManager mDevicePolicyManager; @Mock private FingerprintManager mFingerprintManager; + @Mock private FaceManager mFaceManager; @Mock private LockPatternUtils mLockPatternUtils; @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; @Mock private SupervisionManager mSupervisionManager; @@ -100,6 +103,7 @@ public class FingerprintSafetySourceTest { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE)) .thenReturn(mFingerprintManager); + when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager); when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE)) .thenReturn(mDevicePolicyManager); when(mApplicationContext.getSystemService(Context.SUPERVISION_SERVICE)) @@ -227,10 +231,12 @@ public class FingerprintSafetySourceTest { @Test @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) - public void setSafetySourceData_withFingerprintNotEnrolled_whenNotDisabledByAdmin_setsData() { + public void setSafetySourceData_onlyFingerprintNotEnrolled_whenNotDisabledByAdmin_setsData() { when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); FingerprintSafetySource.setSafetySourceData( @@ -242,6 +248,25 @@ public class FingerprintSafetySourceTest { FingerprintSettings.class.getName()); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void setSafetySourceData_noBiometricEnrolled_whenNotDisabledByAdmin_setsData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceEnabledDataSetWithSingularSummary( + "security_settings_fingerprint", + "security_settings_fingerprint_preference_summary_none_new", + BiometricEnrollActivity.class.getName()); + } + @Test @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) @DisableFlags(android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS)