Support the ability to enroll face unlock first

Add a new EXTRA value to indicate whehter the face enrollment should be
launched first.

Bug: 370940762
Test: atest BiometricEnrollActivityTest
Flag: com.android.settings.flags.biometrics_onboarding_education
Change-Id: I7c85212c7fbcc6fe9dd53a26515412623c80ecbf
diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
index ef19709..83f23bd 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
@@ -107,7 +107,10 @@
     // intent will include this extra containing a bundle of the form:
     // "modality" -> consented (boolean).
     public static final String EXTRA_PARENTAL_CONSENT_STATUS = "consent_status";
-
+    // Whether the face enrollment should be launched first when there are multiple biometrics
+    // supported.
+    public static final String EXTRA_LAUNCH_FACE_ENROLL_FIRST =
+            "launch_face_enroll_first";
     private static final String SAVED_STATE_CONFIRMING_CREDENTIALS = "confirming_credentials";
     private static final String SAVED_STATE_IS_SINGLE_ENROLLING =
             "is_single_enrolling";
@@ -130,6 +133,7 @@
     private boolean mIsFingerprintEnrollable = false;
     private boolean mParentalOptionsRequired = false;
     private boolean mSkipReturnToParent = false;
+    private boolean mLaunchFaceEnrollFirst = false;
     private Bundle mParentalOptions;
     @Nullable private Long mGkPwHandle;
     @Nullable private ParentalConsentHelper mParentalConsentHelper;
@@ -214,6 +218,7 @@
 
         mParentalOptionsRequired = intent.getBooleanExtra(EXTRA_REQUIRE_PARENTAL_CONSENT, false);
         mSkipReturnToParent = intent.getBooleanExtra(EXTRA_SKIP_RETURN_TO_PARENT, false);
+        mLaunchFaceEnrollFirst = intent.getBooleanExtra(EXTRA_LAUNCH_FACE_ENROLL_FIRST, false);
 
         // determine what can be enrolled
         final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
@@ -221,6 +226,7 @@
 
         Log.d(TAG, "parentalOptionsRequired: " + mParentalOptionsRequired
                 + ", skipReturnToParent: " + mSkipReturnToParent
+                + ", launchFaceEnrollFirst: " + mLaunchFaceEnrollFirst
                 + ", isSetupWizard: " + isSetupWizard
                 + ", isMultiSensor: " + isMultiSensor);
 
@@ -356,7 +362,8 @@
         } else if (canUseFace || canUseFingerprint) {
             if (mGkPwHandle == null) {
                 setOrConfirmCredentialsNow();
-            } else if (canUseFingerprint && mIsFingerprintEnrollable) {
+            } else if (canUseFingerprint && mIsFingerprintEnrollable
+                    && !(canUseFace && mIsFaceEnrollable && mLaunchFaceEnrollFirst)) {
                 launchFingerprintOnlyEnroll();
             } else if (canUseFace && mIsFaceEnrollable) {
                 launchFaceOnlyEnroll();
@@ -510,7 +517,8 @@
             int requestCode, int resultCode, Intent data) {
 
         Log.d(TAG, "handleOnActivityResultWhileEnrolling, request = " + requestCode + ""
-                + ", resultCode = " + resultCode);
+                + ", resultCode = " + resultCode + ", launchFaceEnrollFirst="
+                + mLaunchFaceEnrollFirst);
         switch (requestCode) {
             case REQUEST_HANDOFF_PARENT:
                 setResult(RESULT_OK, newResultIntent());
@@ -526,7 +534,8 @@
                     // SetupFingerprintEnrollIntroduction/FingerprintEnrollmentActivity
                     TransitionHelper.applyForwardTransition(this, TRANSITION_FADE_THROUGH);
                     updateGatekeeperPasswordHandle(data);
-                    if (mIsFingerprintEnrollable) {
+                    if (mIsFingerprintEnrollable
+                            && !(mIsFaceEnrollable && mLaunchFaceEnrollFirst)) {
                         launchFingerprintOnlyEnroll();
                     } else {
                         launchFaceOnlyEnroll();
@@ -548,7 +557,7 @@
                 }
                 if ((resultCode == BiometricEnrollBase.RESULT_SKIP
                         || resultCode == BiometricEnrollBase.RESULT_FINISHED)
-                        && mIsFaceEnrollable) {
+                        && mIsFaceEnrollable && !mLaunchFaceEnrollFirst) {
                     // Apply forward animation during the transition from
                     // SetupFingerprintEnroll*/FingerprintEnrollmentActivity to
                     // SetupFaceEnrollIntroduction
@@ -556,6 +565,9 @@
                     mIsPreviousEnrollmentCanceled =
                             resultCode != BiometricEnrollBase.RESULT_FINISHED;
                     launchFaceOnlyEnroll();
+                } else if (resultCode == Activity.RESULT_CANCELED && mIsFaceEnrollable
+                        && mLaunchFaceEnrollFirst) {
+                    launchFaceOnlyEnroll();
                 } else {
                     notifySafetyIssueActionLaunchedIfNeeded(resultCode);
                     finishOrLaunchHandToParent(resultCode);
@@ -563,7 +575,14 @@
                 break;
             case REQUEST_SINGLE_ENROLL_FACE:
                 mIsSingleEnrolling = false;
-                if (resultCode == Activity.RESULT_CANCELED && mIsFingerprintEnrollable) {
+                if ((resultCode == BiometricEnrollBase.RESULT_SKIP
+                        || resultCode == BiometricEnrollBase.RESULT_FINISHED)
+                        && mIsFingerprintEnrollable && mLaunchFaceEnrollFirst) {
+                    mIsPreviousEnrollmentCanceled =
+                            resultCode != BiometricEnrollBase.RESULT_FINISHED;
+                    launchFingerprintOnlyEnroll();
+                } else if (resultCode == Activity.RESULT_CANCELED && mIsFingerprintEnrollable
+                        && !mLaunchFaceEnrollFirst) {
                     mIsPreviousEnrollmentCanceled = true;
                     launchFingerprintOnlyEnroll();
                 } else {
diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java
index db6abc3..21b0fa0 100644
--- a/src/com/android/settings/biometrics/BiometricUtils.java
+++ b/src/com/android/settings/biometrics/BiometricUtils.java
@@ -43,6 +43,7 @@
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.settings.R;
 import com.android.settings.SetupWizardUtils;
+import com.android.settings.biometrics.face.FaceEnroll;
 import com.android.settings.biometrics.fingerprint.FingerprintEnroll;
 import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
 import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
@@ -282,9 +283,7 @@
      */
     public static Intent getFaceIntroIntent(@NonNull Context context,
             @NonNull Intent activityIntent) {
-        final Intent intent = new Intent(context,
-                FeatureFactory.getFeatureFactory().getFaceFeatureProvider()
-                        .getEnrollActivityClassProvider().getNext());
+        final Intent intent = new Intent(context, FaceEnroll.class);
         WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
         return intent;
     }
diff --git a/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java b/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java
index eb28dfb..267b5bc 100644
--- a/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java
+++ b/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java
@@ -23,6 +23,7 @@
 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
 
+import static com.android.settings.biometrics.BiometricEnrollActivity.EXTRA_LAUNCH_FACE_ENROLL_FIRST;
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS;
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE;
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
@@ -39,6 +40,7 @@
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
 
@@ -145,7 +147,7 @@
         assumeTrue(mHasFace || mHasFingerprint);
 
         setPin();
-        final Intent intent = getIntent(true /* useInternal */);
+        final Intent intent = getIntent(true /* useInternal */, null);
         LockPatternChecker.verifyCredential(new LockPatternUtils(mContext),
                 LockscreenCredential.createPin(TEST_PIN), UserHandle.myUserId(),
                 LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, (response, timeoutMs) -> {
@@ -163,6 +165,26 @@
     }
 
     @Test
+    public void launchWithPinAndPwHandle_confirmsPin_firstEnrollmentIsFace() throws Exception {
+        assumeTrue(mHasFace && mHasFingerprint);
+
+        setPin();
+        final Intent intent = getFaceEnrollFirstIntent();
+        LockPatternChecker.verifyCredential(new LockPatternUtils(mContext),
+                LockscreenCredential.createPin(TEST_PIN), UserHandle.myUserId(),
+                LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, (response, timeoutMs) -> {
+                    assertThat(response.containsGatekeeperPasswordHandle()).isTrue();
+                    intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
+                            response.getGatekeeperPasswordHandle());
+                }).get();
+
+        try (ActivityScenario<BiometricEnrollActivity> scenario =
+                     ActivityScenario.launch(intent)) {
+            intended(hasComponent(FaceEnroll.class.getName()));
+        }
+    }
+
+    @Test
     public void launchWithStrongBiometricAllowed_doNotEnrollWeak() throws Exception {
         assumeTrue(mHasFace || mHasFingerprint);
 
@@ -184,13 +206,22 @@
     }
 
     private Intent getIntent() {
-        return getIntent(false /* useInternal */);
+        return getIntent(false /* useInternal */, null);
     }
 
-    private Intent getIntent(boolean useInternal) {
+    private Intent getFaceEnrollFirstIntent() {
+        final Bundle bundle = new Bundle();
+        bundle.putBoolean(EXTRA_LAUNCH_FACE_ENROLL_FIRST, true);
+        return getIntent(true /* useInternal */, bundle);
+    }
+
+    private Intent getIntent(boolean useInternal, Bundle bundle) {
         final Intent intent = new Intent(mContext, useInternal
                 ? BiometricEnrollActivity.InternalActivity.class : BiometricEnrollActivity.class);
         intent.setAction(ACTION_BIOMETRIC_ENROLL);
+        if (bundle != null && !bundle.isEmpty()) {
+            intent.putExtras(bundle);
+        }
         return intent;
     }