diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java index ef1970995df..83f23bdcf7a 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java +++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java @@ -107,7 +107,10 @@ public class BiometricEnrollActivity extends InstrumentedActivity { // 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 @@ public class BiometricEnrollActivity extends InstrumentedActivity { 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 @@ public class BiometricEnrollActivity extends InstrumentedActivity { 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 @@ public class BiometricEnrollActivity extends InstrumentedActivity { Log.d(TAG, "parentalOptionsRequired: " + mParentalOptionsRequired + ", skipReturnToParent: " + mSkipReturnToParent + + ", launchFaceEnrollFirst: " + mLaunchFaceEnrollFirst + ", isSetupWizard: " + isSetupWizard + ", isMultiSensor: " + isMultiSensor); @@ -356,7 +362,8 @@ public class BiometricEnrollActivity extends InstrumentedActivity { } 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 @@ public class BiometricEnrollActivity extends InstrumentedActivity { 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 @@ public class BiometricEnrollActivity extends InstrumentedActivity { // SetupFingerprintEnrollIntroduction/FingerprintEnrollmentActivity TransitionHelper.applyForwardTransition(this, TRANSITION_FADE_THROUGH); updateGatekeeperPasswordHandle(data); - if (mIsFingerprintEnrollable) { + if (mIsFingerprintEnrollable + && !(mIsFaceEnrollable && mLaunchFaceEnrollFirst)) { launchFingerprintOnlyEnroll(); } else { launchFaceOnlyEnroll(); @@ -548,7 +557,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity { } 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 @@ public class BiometricEnrollActivity extends InstrumentedActivity { mIsPreviousEnrollmentCanceled = resultCode != BiometricEnrollBase.RESULT_FINISHED; launchFaceOnlyEnroll(); + } else if (resultCode == Activity.RESULT_CANCELED && mIsFaceEnrollable + && mLaunchFaceEnrollFirst) { + launchFaceOnlyEnroll(); } else { notifySafetyIssueActionLaunchedIfNeeded(resultCode); finishOrLaunchHandToParent(resultCode); @@ -563,7 +575,14 @@ public class BiometricEnrollActivity extends InstrumentedActivity { 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 db6abc3c979..21b0fa03f06 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.LockPatternUtils; 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 class BiometricUtils { */ 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 eb28dfbe624..267b5bc4c21 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.Intents.intended; 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.FaceManager; 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 @@ public class BiometricEnrollActivityTest { 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) -> { @@ -162,6 +164,26 @@ public class BiometricEnrollActivityTest { } } + @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 scenario = + ActivityScenario.launch(intent)) { + intended(hasComponent(FaceEnroll.class.getName())); + } + } + @Test public void launchWithStrongBiometricAllowed_doNotEnrollWeak() throws Exception { assumeTrue(mHasFace || mHasFingerprint); @@ -184,13 +206,22 @@ public class BiometricEnrollActivityTest { } 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; }