Files
packages_apps_Settings/src/com/android/settings/biometrics/BiometricEnrollActivity.java
Milton Wu e4fe606939 Fix enrolled pages not hide during SUW
Update mIsFaceEnrollable and mIsFingerprintEnrollable in
BiometricEnrollActivity, and use them to determine the activity flow.

Bug: 270102158
Test: Manually test following cases
      1. Complete fp and face in suw, then back to "Google Service" page
	 to make sure
	 a. Fingeprint introduction page isn't shown
	 b. Face introduction page isn't shown
	 c. ConfirmCredential page is shown
      2. Complete fp in suw, then press back to previous page in Face
	 introduction page. And check Fingerpirnt isn't shown.
Change-Id: Ibc74128f6776128d8a0f4cab014b58057ac0f844
2023-04-13 14:57:37 +08:00

700 lines
33 KiB
Java

/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.biometrics;
import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
import static com.google.android.setupdesign.transition.TransitionHelper.TRANSITION_FADE_THROUGH;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricManager.BiometricError;
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.os.UserManager;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockPattern;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.transition.TransitionHelper;
import java.util.List;
/**
* Trampoline activity launched by the {@code android.settings.BIOMETRIC_ENROLL} action which
* shows the user an appropriate enrollment flow depending on the device's biometric hardware.
* This activity must only allow enrollment of biometrics that can be used by
* {@link android.hardware.biometrics.BiometricPrompt}.
*/
public class BiometricEnrollActivity extends InstrumentedActivity {
private static final String TAG = "BiometricEnrollActivity";
private static final int REQUEST_CHOOSE_LOCK = 1;
private static final int REQUEST_CONFIRM_LOCK = 2;
// prompt for parental consent options
private static final int REQUEST_CHOOSE_OPTIONS = 3;
// prompt hand phone back to parent after enrollment
private static final int REQUEST_HANDOFF_PARENT = 4;
private static final int REQUEST_SINGLE_ENROLL_FINGERPRINT = 5;
private static final int REQUEST_SINGLE_ENROLL_FACE = 6;
public static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP;
// Intent extra. If true, biometric enrollment should skip introductory screens. Currently
// this only applies to fingerprint.
public static final String EXTRA_SKIP_INTRO = "skip_intro";
// Intent extra. If true, parental consent will be requested before user enrollment.
public static final String EXTRA_REQUIRE_PARENTAL_CONSENT = "require_consent";
// Intent extra. If true, the screen asking the user to return the device to their parent will
// be skipped after enrollment.
public static final String EXTRA_SKIP_RETURN_TO_PARENT = "skip_return_to_parent";
// If EXTRA_REQUIRE_PARENTAL_CONSENT was used to start the activity then the result
// intent will include this extra containing a bundle of the form:
// "modality" -> consented (boolean).
public static final String EXTRA_PARENTAL_CONSENT_STATUS = "consent_status";
private static final String SAVED_STATE_CONFIRMING_CREDENTIALS = "confirming_credentials";
private static final String SAVED_STATE_IS_SINGLE_ENROLLING =
"is_single_enrolling";
private static final String SAVED_STATE_PASS_THROUGH_EXTRAS_FROM_CHOSEN_LOCK_IN_SUW =
"pass_through_extras_from_chosen_lock_in_suw";
private static final String SAVED_STATE_ENROLL_ACTION_LOGGED = "enroll_action_logged";
private static final String SAVED_STATE_PARENTAL_OPTIONS = "enroll_preferences";
private static final String SAVED_STATE_GK_PW_HANDLE = "gk_pw_handle";
public static final class InternalActivity extends BiometricEnrollActivity {}
private int mUserId = UserHandle.myUserId();
private boolean mConfirmingCredentials;
private boolean mIsSingleEnrolling;
private Bundle mPassThroughExtrasFromChosenLockInSuw = null;
private boolean mIsEnrollActionLogged;
private boolean mHasFeatureFace = false;
private boolean mHasFeatureFingerprint = false;
private boolean mIsFaceEnrollable = false;
private boolean mIsFingerprintEnrollable = false;
private boolean mParentalOptionsRequired = false;
private boolean mSkipReturnToParent = false;
private Bundle mParentalOptions;
@Nullable private Long mGkPwHandle;
@Nullable private ParentalConsentHelper mParentalConsentHelper;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
if (this instanceof InternalActivity) {
mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(getIntent());
}
} else if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(getIntent());
}
}
if (savedInstanceState != null) {
mConfirmingCredentials = savedInstanceState.getBoolean(
SAVED_STATE_CONFIRMING_CREDENTIALS, false);
mIsSingleEnrolling = savedInstanceState.getBoolean(
SAVED_STATE_IS_SINGLE_ENROLLING, false);
mPassThroughExtrasFromChosenLockInSuw = savedInstanceState.getBundle(
SAVED_STATE_PASS_THROUGH_EXTRAS_FROM_CHOSEN_LOCK_IN_SUW);
mIsEnrollActionLogged = savedInstanceState.getBoolean(
SAVED_STATE_ENROLL_ACTION_LOGGED, false);
mParentalOptions = savedInstanceState.getBundle(SAVED_STATE_PARENTAL_OPTIONS);
if (savedInstanceState.containsKey(SAVED_STATE_GK_PW_HANDLE)) {
mGkPwHandle = savedInstanceState.getLong(SAVED_STATE_GK_PW_HANDLE);
}
}
// Log a framework stats event if this activity was launched via intent action.
if (!mIsEnrollActionLogged && ACTION_BIOMETRIC_ENROLL.equals(intent.getAction())) {
mIsEnrollActionLogged = true;
// Get the current status for each authenticator type.
@BiometricError final int strongBiometricStatus;
@BiometricError final int weakBiometricStatus;
@BiometricError final int deviceCredentialStatus;
final BiometricManager bm = getSystemService(BiometricManager.class);
if (bm != null) {
strongBiometricStatus = bm.canAuthenticate(Authenticators.BIOMETRIC_STRONG);
weakBiometricStatus = bm.canAuthenticate(Authenticators.BIOMETRIC_WEAK);
deviceCredentialStatus = bm.canAuthenticate(Authenticators.DEVICE_CREDENTIAL);
} else {
strongBiometricStatus = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
weakBiometricStatus = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
deviceCredentialStatus = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
}
FrameworkStatsLog.write(FrameworkStatsLog.AUTH_ENROLL_ACTION_INVOKED,
strongBiometricStatus == BiometricManager.BIOMETRIC_SUCCESS,
weakBiometricStatus == BiometricManager.BIOMETRIC_SUCCESS,
deviceCredentialStatus == BiometricManager.BIOMETRIC_SUCCESS,
intent.hasExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED),
intent.getIntExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, 0));
}
// Put the theme in the intent so it gets propagated to other activities in the flow
if (intent.getStringExtra(WizardManagerHelper.EXTRA_THEME) == null) {
intent.putExtra(
WizardManagerHelper.EXTRA_THEME,
SetupWizardUtils.getThemeString(intent));
}
final PackageManager pm = getApplicationContext().getPackageManager();
mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
// Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
final int authenticators = getIntent().getIntExtra(
EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
Log.d(TAG, "Authenticators: " + authenticators);
mParentalOptionsRequired = intent.getBooleanExtra(EXTRA_REQUIRE_PARENTAL_CONSENT, false);
mSkipReturnToParent = intent.getBooleanExtra(EXTRA_SKIP_RETURN_TO_PARENT, false);
// determine what can be enrolled
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
final boolean isMultiSensor = mHasFeatureFace && mHasFeatureFingerprint;
Log.d(TAG, "parentalOptionsRequired: " + mParentalOptionsRequired
+ ", skipReturnToParent: " + mSkipReturnToParent
+ ", isSetupWizard: " + isSetupWizard
+ ", isMultiSensor: " + isMultiSensor);
if (mHasFeatureFace) {
final FaceManager faceManager = getSystemService(FaceManager.class);
final List<FaceSensorPropertiesInternal> faceProperties =
faceManager.getSensorPropertiesInternal();
final int maxFacesEnrollableIfSUW = getApplicationContext().getResources()
.getInteger(R.integer.suw_max_faces_enrollable);
if (!faceProperties.isEmpty()) {
final FaceSensorPropertiesInternal props = faceProperties.get(0);
final int maxEnrolls =
isSetupWizard ? maxFacesEnrollableIfSUW : props.maxEnrollmentsPerUser;
mIsFaceEnrollable =
faceManager.getEnrolledFaces(mUserId).size() < maxEnrolls;
final boolean parentalConsent = isSetupWizard || (mParentalOptionsRequired
&& !WizardManagerHelper.isUserSetupComplete(this));
if (parentalConsent && isMultiSensor && mIsFaceEnrollable) {
// Exclude face enrollment from setup wizard if feature config not supported
// in setup wizard flow, we still allow user enroll faces through settings.
mIsFaceEnrollable = FeatureFactory.getFactory(getApplicationContext())
.getFaceFeatureProvider()
.isSetupWizardSupported(getApplicationContext());
Log.d(TAG, "config_suw_support_face_enroll: " + mIsFaceEnrollable);
}
}
}
updateFingerprintEnrollable(isSetupWizard);
// TODO(b/195128094): remove this restriction
// Consent can only be recorded when this activity is launched directly from the kids
// module. This can be removed when there is a way to notify consent status out of band.
if (isSetupWizard && mParentalOptionsRequired) {
Log.w(TAG, "Enrollment with parental consent is not supported when launched "
+ " directly from SuW - skipping enrollment");
setResult(RESULT_SKIP);
finish();
return;
}
// Only allow the consent flow to happen once when running from setup wizard.
// This isn't common and should only happen if setup wizard is not completed normally
// due to a restart, etc.
// This check should probably remain even if b/195128094 is fixed to prevent SuW from
// restarting the process once it has been fully completed at least one time.
if (isSetupWizard && mParentalOptionsRequired) {
final boolean consentAlreadyManaged = ParentalControlsUtils.parentConsentRequired(this,
BiometricAuthenticator.TYPE_FACE | BiometricAuthenticator.TYPE_FINGERPRINT)
!= null;
if (consentAlreadyManaged) {
Log.w(TAG, "Consent was already setup - skipping enrollment");
setResult(RESULT_SKIP);
finish();
return;
}
}
if (mParentalOptionsRequired && mParentalOptions == null) {
mParentalConsentHelper = new ParentalConsentHelper(mGkPwHandle);
setOrConfirmCredentialsNow();
} else {
// Start enrollment process if we haven't bailed out yet
startEnrollWith(authenticators, isSetupWizard);
}
}
private void updateFingerprintEnrollable(boolean isSetupWizard) {
if (mHasFeatureFingerprint) {
final FingerprintManager fpManager = getSystemService(FingerprintManager.class);
final List<FingerprintSensorPropertiesInternal> fpProperties =
fpManager.getSensorPropertiesInternal();
final int maxFingerprintsEnrollableIfSUW = getApplicationContext().getResources()
.getInteger(R.integer.suw_max_fingerprints_enrollable);
if (!fpProperties.isEmpty()) {
final int maxEnrolls =
isSetupWizard ? maxFingerprintsEnrollableIfSUW
: fpProperties.get(0).maxEnrollmentsPerUser;
mIsFingerprintEnrollable =
fpManager.getEnrolledFingerprints(mUserId).size() < maxEnrolls;
}
}
}
private void startEnrollWith(@Authenticators.Types int authenticators, boolean setupWizard) {
// If the caller is not setup wizard, and the user has something enrolled, finish.
// Allow parental consent flow to skip this check, since one modality could be consented
// and another non-consented. This can also happen if the restriction is applied when
// enrollments already exists.
if (!setupWizard && !mParentalOptionsRequired) {
final BiometricManager bm = getSystemService(BiometricManager.class);
final @BiometricError int result = bm.canAuthenticate(authenticators);
if (result != BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
Log.e(TAG, "Unexpected result (has enrollments): " + result);
finish();
return;
}
}
boolean canUseFace = mHasFeatureFace;
boolean canUseFingerprint = mHasFeatureFingerprint;
if (mParentalOptionsRequired) {
if (mParentalOptions == null) {
throw new IllegalStateException("consent options required, but not set");
}
canUseFace = canUseFace
&& ParentalConsentHelper.hasFaceConsent(mParentalOptions);
canUseFingerprint = canUseFingerprint
&& ParentalConsentHelper.hasFingerprintConsent(mParentalOptions);
}
// This will need to be updated if the device has sensors other than BIOMETRIC_STRONG
if (!setupWizard && authenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
launchCredentialOnlyEnroll();
finish();
} else if (canUseFace || canUseFingerprint) {
if (mGkPwHandle == null) {
setOrConfirmCredentialsNow();
} else if (canUseFingerprint) {
launchFingerprintOnlyEnroll();
} else {
launchFaceOnlyEnroll();
}
} else { // no modalities available
if (mParentalOptionsRequired) {
Log.d(TAG, "No consent for any modality: skipping enrollment");
setResult(RESULT_OK, newResultIntent());
} else {
Log.e(TAG, "Unknown state, finishing (was SUW: " + setupWizard + ")");
}
finish();
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVED_STATE_CONFIRMING_CREDENTIALS, mConfirmingCredentials);
outState.putBoolean(SAVED_STATE_IS_SINGLE_ENROLLING, mIsSingleEnrolling);
outState.putBundle(SAVED_STATE_PASS_THROUGH_EXTRAS_FROM_CHOSEN_LOCK_IN_SUW,
mPassThroughExtrasFromChosenLockInSuw);
outState.putBoolean(SAVED_STATE_ENROLL_ACTION_LOGGED, mIsEnrollActionLogged);
if (mParentalOptions != null) {
outState.putBundle(SAVED_STATE_PARENTAL_OPTIONS, mParentalOptions);
}
if (mGkPwHandle != null) {
outState.putLong(SAVED_STATE_GK_PW_HANDLE, mGkPwHandle);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (isSuccessfulChooseCredential(requestCode, resultCode)
&& data != null && data.getExtras() != null && data.getExtras().size() > 0
&& WizardManagerHelper.isAnySetupWizard(getIntent())) {
mPassThroughExtrasFromChosenLockInSuw = data.getExtras();
}
Log.d(TAG,
"onActivityResult(requestCode=" + requestCode + ", resultCode=" + resultCode + ")");
// single enrollment is handled entirely by the launched activity
// this handles multi enroll or if parental consent is required
if (mParentalConsentHelper != null) {
// Lazily retrieve the values from ParentalControlUtils, since the value may not be
// ready in onCreate.
final boolean faceConsentRequired = ParentalControlsUtils
.parentConsentRequired(this, BiometricAuthenticator.TYPE_FACE) != null;
final boolean fpConsentRequired = ParentalControlsUtils
.parentConsentRequired(this, BiometricAuthenticator.TYPE_FINGERPRINT) != null;
final boolean requestFaceConsent = faceConsentRequired
&& mHasFeatureFace
&& mIsFaceEnrollable;
final boolean requestFpConsent = fpConsentRequired && mHasFeatureFingerprint;
Log.d(TAG, "faceConsentRequired: " + faceConsentRequired
+ ", fpConsentRequired: " + fpConsentRequired
+ ", hasFeatureFace: " + mHasFeatureFace
+ ", hasFeatureFingerprint: " + mHasFeatureFingerprint
+ ", faceEnrollable: " + mIsFaceEnrollable
+ ", fpEnrollable: " + mIsFingerprintEnrollable);
mParentalConsentHelper.setConsentRequirement(requestFaceConsent, requestFpConsent);
handleOnActivityResultWhileConsenting(requestCode, resultCode, data);
} else {
handleOnActivityResultWhileEnrolling(requestCode, resultCode, data);
}
}
// handles responses while parental consent is pending
private void handleOnActivityResultWhileConsenting(
int requestCode, int resultCode, Intent data) {
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
switch (requestCode) {
case REQUEST_CHOOSE_LOCK:
case REQUEST_CONFIRM_LOCK:
mConfirmingCredentials = false;
if (isSuccessfulConfirmOrChooseCredential(requestCode, resultCode)) {
updateGatekeeperPasswordHandle(data);
if (!mParentalConsentHelper.launchNext(this, REQUEST_CHOOSE_OPTIONS)) {
Log.e(TAG, "Nothing to prompt for consent (no modalities enabled)!");
finish();
}
} else {
Log.d(TAG, "Unknown result for set/choose lock: " + resultCode);
setResult(resultCode);
finish();
}
break;
case REQUEST_CHOOSE_OPTIONS:
if (resultCode == RESULT_CONSENT_GRANTED || resultCode == RESULT_CONSENT_DENIED) {
final boolean isStillPrompting = mParentalConsentHelper.launchNext(
this, REQUEST_CHOOSE_OPTIONS, resultCode, data);
if (!isStillPrompting) {
mParentalOptions = mParentalConsentHelper.getConsentResult();
mParentalConsentHelper = null;
Log.d(TAG, "Enrollment consent options set, starting enrollment: "
+ mParentalOptions);
// Note that we start enrollment with CONVENIENCE instead of the default
// of WEAK in startEnroll(), since we want to allow enrollment for any
// sensor as long as it has been consented for. We should eventually
// clean up this logic and do something like pass in the parental consent
// result, so that we can request enrollment for specific sensors, but
// that's quite a large and risky change to the startEnrollWith() logic.
startEnrollWith(Authenticators.BIOMETRIC_CONVENIENCE,
WizardManagerHelper.isAnySetupWizard(getIntent()));
}
} else {
Log.d(TAG, "Unknown or cancelled parental consent");
setResult(RESULT_CANCELED, newResultIntent());
finish();
}
break;
default:
Log.w(TAG, "Unknown consenting requestCode: " + requestCode + ", finishing");
finish();
}
}
// handles responses while multi biometric enrollment is pending
private void handleOnActivityResultWhileEnrolling(
int requestCode, int resultCode, Intent data) {
Log.d(TAG, "handleOnActivityResultWhileEnrolling, request = " + requestCode + ""
+ ", resultCode = " + resultCode);
switch (requestCode) {
case REQUEST_HANDOFF_PARENT:
setResult(RESULT_OK, newResultIntent());
finish();
break;
case REQUEST_CHOOSE_LOCK:
case REQUEST_CONFIRM_LOCK:
mConfirmingCredentials = false;
final boolean isOk =
isSuccessfulConfirmOrChooseCredential(requestCode, resultCode);
if (isOk && (mIsFaceEnrollable || mIsFingerprintEnrollable)) {
// Apply forward animation during the transition from ChooseLock/ConfirmLock to
// SetupFingerprintEnrollIntroduction/FingerprintEnrollmentActivity
TransitionHelper.applyForwardTransition(this, TRANSITION_FADE_THROUGH);
updateGatekeeperPasswordHandle(data);
if (mIsFingerprintEnrollable) {
launchFingerprintOnlyEnroll();
} else {
launchFaceOnlyEnroll();
}
} else {
Log.d(TAG, "Unknown result for set/choose lock: " + resultCode);
setResult(resultCode, newResultIntent());
finish();
}
break;
case REQUEST_SINGLE_ENROLL_FINGERPRINT:
mIsSingleEnrolling = false;
if (resultCode == BiometricEnrollBase.RESULT_FINISHED) {
// FingerprintEnrollIntroduction's visibility is determined by
// mIsFingerprintEnrollable. Keep this value up-to-date after a successful
// enrollment.
updateFingerprintEnrollable(WizardManagerHelper.isAnySetupWizard(getIntent()));
}
if ((resultCode == BiometricEnrollBase.RESULT_SKIP
|| resultCode == BiometricEnrollBase.RESULT_FINISHED)
&& mIsFaceEnrollable) {
// Apply forward animation during the transition from
// SetupFingerprintEnroll*/FingerprintEnrollmentActivity to
// SetupFaceEnrollIntroduction
TransitionHelper.applyForwardTransition(this, TRANSITION_FADE_THROUGH);
launchFaceOnlyEnroll();
} else {
finishOrLaunchHandToParent(resultCode);
}
break;
case REQUEST_SINGLE_ENROLL_FACE:
mIsSingleEnrolling = false;
if (resultCode == Activity.RESULT_CANCELED && mIsFingerprintEnrollable) {
launchFingerprintOnlyEnroll();
} else {
finishOrLaunchHandToParent(resultCode);
}
break;
default:
Log.w(TAG, "Unknown enrolling requestCode: " + requestCode + ", finishing");
finish();
}
}
@Override
public void finish() {
if (mGkPwHandle != null) {
// When launched as InternalActivity, the mGkPwHandle was gotten from intent extra
// instead of requesting from the user. Do not remove the mGkPwHandle in service side
// for this case because the caller activity may still need it and will be responsible
// for removing it.
if (!(this instanceof InternalActivity)) {
BiometricUtils.removeGatekeeperPasswordHandle(this, mGkPwHandle);
}
}
super.finish();
}
private void finishOrLaunchHandToParent(int resultCode) {
if (mParentalOptionsRequired) {
if (!mSkipReturnToParent) {
launchHandoffToParent();
} else {
setResult(RESULT_OK, newResultIntent());
finish();
}
} else {
setResult(resultCode, newResultIntent());
finish();
}
}
@NonNull
private Intent newResultIntent() {
final Intent intent = new Intent();
if (mParentalOptionsRequired && mParentalOptions != null) {
final Bundle consentStatus = mParentalOptions.deepCopy();
intent.putExtra(EXTRA_PARENTAL_CONSENT_STATUS, consentStatus);
Log.v(TAG, "Result consent status: " + consentStatus);
}
if (mPassThroughExtrasFromChosenLockInSuw != null) {
intent.putExtras(mPassThroughExtrasFromChosenLockInSuw);
}
return intent;
}
private static boolean isSuccessfulConfirmOrChooseCredential(int requestCode, int resultCode) {
return isSuccessfulChooseCredential(requestCode, resultCode)
|| isSuccessfulConfirmCredential(requestCode, resultCode);
}
private static boolean isSuccessfulChooseCredential(int requestCode, int resultCode) {
return requestCode == REQUEST_CHOOSE_LOCK
&& resultCode == ChooseLockPattern.RESULT_FINISHED;
}
private static boolean isSuccessfulConfirmCredential(int requestCode, int resultCode) {
return requestCode == REQUEST_CONFIRM_LOCK && resultCode == RESULT_OK;
}
@Override
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
final int newResid = SetupWizardUtils.getTheme(this, getIntent());
theme.applyStyle(R.style.SetupWizardPartnerResource, true);
super.onApplyThemeResource(theme, newResid, first);
}
private void setOrConfirmCredentialsNow() {
if (!mConfirmingCredentials) {
mConfirmingCredentials = true;
if (!userHasPassword(mUserId)) {
launchChooseLock();
} else {
launchConfirmLock();
}
}
}
private void updateGatekeeperPasswordHandle(@NonNull Intent data) {
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
if (mParentalConsentHelper != null) {
mParentalConsentHelper.updateGatekeeperHandle(data);
}
}
private boolean userHasPassword(int userId) {
final UserManager userManager = getSystemService(UserManager.class);
final int passwordQuality = new LockPatternUtils(this)
.getActivePasswordQuality(userManager.getCredentialOwnerProfile(userId));
return passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
private void launchChooseLock() {
Log.d(TAG, "launchChooseLock");
Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent());
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
if (mHasFeatureFingerprint && mHasFeatureFace) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true);
} else if (mHasFeatureFace) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, true);
} else if (mHasFeatureFingerprint) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
}
if (mUserId != UserHandle.USER_NULL) {
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
}
startActivityForResult(intent, REQUEST_CHOOSE_LOCK);
}
private void launchConfirmLock() {
Log.d(TAG, "launchConfirmLock");
final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this);
builder.setRequestCode(REQUEST_CONFIRM_LOCK)
.setRequestGatekeeperPasswordHandle(true)
.setForegroundOnly(true)
.setReturnCredentials(true);
if (mUserId != UserHandle.USER_NULL) {
builder.setUserId(mUserId);
}
final boolean launched = builder.show();
if (!launched) {
// This shouldn't happen, as we should only end up at this step if a lock thingy is
// already set.
finish();
}
}
// This should only be used to launch enrollment for single-sensor devices.
private void launchSingleSensorEnrollActivity(@NonNull Intent intent, int requestCode) {
byte[] hardwareAuthToken = null;
if (this instanceof InternalActivity) {
hardwareAuthToken = getIntent().getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
}
BiometricUtils.launchEnrollForResult(this, intent, requestCode, hardwareAuthToken,
mGkPwHandle, mUserId);
}
private void launchCredentialOnlyEnroll() {
final Intent intent;
// If only device credential was specified, ask the user to only set that up.
intent = new Intent(this, ChooseLockGeneric.class);
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true);
launchSingleSensorEnrollActivity(intent, 0 /* requestCode */);
}
private void launchFingerprintOnlyEnroll() {
if (!mIsSingleEnrolling) {
mIsSingleEnrolling = true;
final Intent intent;
// ChooseLockGeneric can request to start fingerprint enroll bypassing the intro screen.
if (getIntent().getBooleanExtra(EXTRA_SKIP_INTRO, false)
&& this instanceof InternalActivity) {
intent = BiometricUtils.getFingerprintFindSensorIntent(this, getIntent());
} else {
intent = BiometricUtils.getFingerprintIntroIntent(this, getIntent());
}
launchSingleSensorEnrollActivity(intent, REQUEST_SINGLE_ENROLL_FINGERPRINT);
}
}
private void launchFaceOnlyEnroll() {
if (!mIsSingleEnrolling) {
mIsSingleEnrolling = true;
final Intent intent = BiometricUtils.getFaceIntroIntent(this, getIntent());
launchSingleSensorEnrollActivity(intent, REQUEST_SINGLE_ENROLL_FACE);
}
}
private void launchHandoffToParent() {
final Intent intent = BiometricUtils.getHandoffToParentIntent(this, getIntent());
startActivityForResult(intent, REQUEST_HANDOFF_PARENT);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.BIOMETRIC_ENROLL_ACTIVITY;
}
}