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;
}