1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.settings.biometrics; 18 19 import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL; 20 import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED; 21 22 import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED; 23 import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED; 24 25 import android.annotation.NonNull; 26 import android.app.admin.DevicePolicyManager; 27 import android.app.settings.SettingsEnums; 28 import android.content.Intent; 29 import android.content.pm.PackageManager; 30 import android.content.res.Resources; 31 import android.hardware.biometrics.BiometricAuthenticator; 32 import android.hardware.biometrics.BiometricManager; 33 import android.hardware.biometrics.BiometricManager.Authenticators; 34 import android.hardware.biometrics.BiometricManager.BiometricError; 35 import android.hardware.face.FaceManager; 36 import android.hardware.face.FaceSensorPropertiesInternal; 37 import android.hardware.fingerprint.FingerprintManager; 38 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; 39 import android.os.Bundle; 40 import android.os.UserHandle; 41 import android.os.UserManager; 42 import android.util.Log; 43 44 import androidx.annotation.Nullable; 45 46 import com.android.internal.util.FrameworkStatsLog; 47 import com.android.internal.widget.LockPatternUtils; 48 import com.android.settings.R; 49 import com.android.settings.SetupWizardUtils; 50 import com.android.settings.core.InstrumentedActivity; 51 import com.android.settings.password.ChooseLockGeneric; 52 import com.android.settings.password.ChooseLockPattern; 53 import com.android.settings.password.ChooseLockSettingsHelper; 54 55 import com.google.android.setupcompat.util.WizardManagerHelper; 56 57 import java.util.List; 58 59 /** 60 * Trampoline activity launched by the {@code android.settings.BIOMETRIC_ENROLL} action which 61 * shows the user an appropriate enrollment flow depending on the device's biometric hardware. 62 * This activity must only allow enrollment of biometrics that can be used by 63 * {@link android.hardware.biometrics.BiometricPrompt}. 64 */ 65 public class BiometricEnrollActivity extends InstrumentedActivity { 66 67 private static final String TAG = "BiometricEnrollActivity"; 68 69 private static final int REQUEST_CHOOSE_LOCK = 1; 70 private static final int REQUEST_CONFIRM_LOCK = 2; 71 // prompt for parental consent options 72 private static final int REQUEST_CHOOSE_OPTIONS = 3; 73 // prompt hand phone back to parent after enrollment 74 private static final int REQUEST_HANDOFF_PARENT = 4; 75 private static final int REQUEST_SINGLE_ENROLL = 5; 76 77 public static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP; 78 79 // Intent extra. If true, biometric enrollment should skip introductory screens. Currently 80 // this only applies to fingerprint. 81 public static final String EXTRA_SKIP_INTRO = "skip_intro"; 82 83 // Intent extra. If true, parental consent will be requested before user enrollment. 84 public static final String EXTRA_REQUIRE_PARENTAL_CONSENT = "require_consent"; 85 86 // Intent extra. If true, the screen asking the user to return the device to their parent will 87 // be skipped after enrollment. 88 public static final String EXTRA_SKIP_RETURN_TO_PARENT = "skip_return_to_parent"; 89 90 // If EXTRA_REQUIRE_PARENTAL_CONSENT was used to start the activity then the result 91 // intent will include this extra containing a bundle of the form: 92 // "modality" -> consented (boolean). 93 public static final String EXTRA_PARENTAL_CONSENT_STATUS = "consent_status"; 94 95 private static final String SAVED_STATE_CONFIRMING_CREDENTIALS = "confirming_credentials"; 96 private static final String SAVED_STATE_ENROLL_ACTION_LOGGED = "enroll_action_logged"; 97 private static final String SAVED_STATE_PARENTAL_OPTIONS = "enroll_preferences"; 98 private static final String SAVED_STATE_GK_PW_HANDLE = "gk_pw_handle"; 99 100 public static final class InternalActivity extends BiometricEnrollActivity {} 101 102 private int mUserId = UserHandle.myUserId(); 103 private boolean mConfirmingCredentials; 104 private boolean mIsEnrollActionLogged; 105 private boolean mHasFeatureFace = false; 106 private boolean mHasFeatureFingerprint = false; 107 private boolean mIsFaceEnrollable = false; 108 private boolean mIsFingerprintEnrollable = false; 109 private boolean mParentalOptionsRequired = false; 110 private boolean mSkipReturnToParent = false; 111 private Bundle mParentalOptions; 112 @Nullable private Long mGkPwHandle; 113 @Nullable private ParentalConsentHelper mParentalConsentHelper; 114 @Nullable private MultiBiometricEnrollHelper mMultiBiometricEnrollHelper; 115 116 @Override onCreate(@ullable Bundle savedInstanceState)117 public void onCreate(@Nullable Bundle savedInstanceState) { 118 super.onCreate(savedInstanceState); 119 120 final Intent intent = getIntent(); 121 122 if (this instanceof InternalActivity) { 123 mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); 124 if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) { 125 mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(getIntent()); 126 } 127 } 128 129 if (savedInstanceState != null) { 130 mConfirmingCredentials = savedInstanceState.getBoolean( 131 SAVED_STATE_CONFIRMING_CREDENTIALS, false); 132 mIsEnrollActionLogged = savedInstanceState.getBoolean( 133 SAVED_STATE_ENROLL_ACTION_LOGGED, false); 134 mParentalOptions = savedInstanceState.getBundle(SAVED_STATE_PARENTAL_OPTIONS); 135 if (savedInstanceState.containsKey(SAVED_STATE_GK_PW_HANDLE)) { 136 mGkPwHandle = savedInstanceState.getLong(SAVED_STATE_GK_PW_HANDLE); 137 } 138 } 139 140 // Log a framework stats event if this activity was launched via intent action. 141 if (!mIsEnrollActionLogged && ACTION_BIOMETRIC_ENROLL.equals(intent.getAction())) { 142 mIsEnrollActionLogged = true; 143 144 // Get the current status for each authenticator type. 145 @BiometricError final int strongBiometricStatus; 146 @BiometricError final int weakBiometricStatus; 147 @BiometricError final int deviceCredentialStatus; 148 final BiometricManager bm = getSystemService(BiometricManager.class); 149 if (bm != null) { 150 strongBiometricStatus = bm.canAuthenticate(Authenticators.BIOMETRIC_STRONG); 151 weakBiometricStatus = bm.canAuthenticate(Authenticators.BIOMETRIC_WEAK); 152 deviceCredentialStatus = bm.canAuthenticate(Authenticators.DEVICE_CREDENTIAL); 153 } else { 154 strongBiometricStatus = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; 155 weakBiometricStatus = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; 156 deviceCredentialStatus = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; 157 } 158 159 FrameworkStatsLog.write(FrameworkStatsLog.AUTH_ENROLL_ACTION_INVOKED, 160 strongBiometricStatus == BiometricManager.BIOMETRIC_SUCCESS, 161 weakBiometricStatus == BiometricManager.BIOMETRIC_SUCCESS, 162 deviceCredentialStatus == BiometricManager.BIOMETRIC_SUCCESS, 163 intent.hasExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED), 164 intent.getIntExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, 0)); 165 } 166 167 // Put the theme in the intent so it gets propagated to other activities in the flow 168 if (intent.getStringExtra(WizardManagerHelper.EXTRA_THEME) == null) { 169 intent.putExtra( 170 WizardManagerHelper.EXTRA_THEME, 171 SetupWizardUtils.getThemeString(intent)); 172 } 173 174 final PackageManager pm = getApplicationContext().getPackageManager(); 175 mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); 176 mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE); 177 178 // determine what can be enrolled 179 final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent()); 180 181 if (mHasFeatureFace) { 182 final FaceManager faceManager = getSystemService(FaceManager.class); 183 final List<FaceSensorPropertiesInternal> faceProperties = 184 faceManager.getSensorPropertiesInternal(); 185 if (!faceProperties.isEmpty()) { 186 final int maxEnrolls = 187 isSetupWizard ? 1 : faceProperties.get(0).maxEnrollmentsPerUser; 188 mIsFaceEnrollable = 189 faceManager.getEnrolledFaces(mUserId).size() < maxEnrolls; 190 } 191 } 192 if (mHasFeatureFingerprint) { 193 final FingerprintManager fpManager = getSystemService(FingerprintManager.class); 194 final List<FingerprintSensorPropertiesInternal> fpProperties = 195 fpManager.getSensorPropertiesInternal(); 196 if (!fpProperties.isEmpty()) { 197 final int maxEnrolls = 198 isSetupWizard ? 1 : fpProperties.get(0).maxEnrollmentsPerUser; 199 mIsFingerprintEnrollable = 200 fpManager.getEnrolledFingerprints(mUserId).size() < maxEnrolls; 201 } 202 } 203 204 mParentalOptionsRequired = intent.getBooleanExtra(EXTRA_REQUIRE_PARENTAL_CONSENT, false); 205 mSkipReturnToParent = intent.getBooleanExtra(EXTRA_SKIP_RETURN_TO_PARENT, false); 206 207 Log.d(TAG, "parentalOptionsRequired: " + mParentalOptionsRequired 208 + ", skipReturnToParent: " + mSkipReturnToParent 209 + ", isSetupWizard: " + isSetupWizard); 210 211 // TODO(b/195128094): remove this restriction 212 // Consent can only be recorded when this activity is launched directly from the kids 213 // module. This can be removed when there is a way to notify consent status out of band. 214 if (isSetupWizard && mParentalOptionsRequired) { 215 Log.w(TAG, "Enrollment with parental consent is not supported when launched " 216 + " directly from SuW - skipping enrollment"); 217 setResult(RESULT_SKIP); 218 finish(); 219 return; 220 } 221 222 // Only allow the consent flow to happen once when running from setup wizard. 223 // This isn't common and should only happen if setup wizard is not completed normally 224 // due to a restart, etc. 225 // This check should probably remain even if b/195128094 is fixed to prevent SuW from 226 // restarting the process once it has been fully completed at least one time. 227 if (isSetupWizard && mParentalOptionsRequired) { 228 final boolean consentAlreadyManaged = ParentalControlsUtils.parentConsentRequired(this, 229 BiometricAuthenticator.TYPE_FACE | BiometricAuthenticator.TYPE_FINGERPRINT) 230 != null; 231 if (consentAlreadyManaged) { 232 Log.w(TAG, "Consent was already setup - skipping enrollment"); 233 setResult(RESULT_SKIP); 234 finish(); 235 return; 236 } 237 } 238 239 // start enrollment process if we haven't bailed out yet 240 if (mParentalOptionsRequired && mParentalOptions == null) { 241 mParentalConsentHelper = new ParentalConsentHelper( 242 mIsFaceEnrollable, mIsFingerprintEnrollable, mGkPwHandle); 243 setOrConfirmCredentialsNow(); 244 } else { 245 startEnroll(); 246 } 247 } 248 249 private void startEnroll() { 250 // Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL. 251 final int authenticators = getIntent().getIntExtra( 252 EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK); 253 Log.d(TAG, "Authenticators: " + authenticators); 254 255 startEnrollWith(authenticators, WizardManagerHelper.isAnySetupWizard(getIntent())); 256 } 257 258 private void startEnrollWith(@Authenticators.Types int authenticators, boolean setupWizard) { 259 // If the caller is not setup wizard, and the user has something enrolled, finish. 260 if (!setupWizard) { 261 final BiometricManager bm = getSystemService(BiometricManager.class); 262 final @BiometricError int result = bm.canAuthenticate(authenticators); 263 if (result != BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) { 264 Log.e(TAG, "Unexpected result (has enrollments): " + result); 265 finish(); 266 return; 267 } 268 } 269 270 boolean canUseFace = mHasFeatureFace; 271 boolean canUseFingerprint = mHasFeatureFingerprint; 272 if (mParentalOptionsRequired) { 273 if (mParentalOptions == null) { 274 throw new IllegalStateException("consent options required, but not set"); 275 } 276 canUseFace = canUseFace 277 && ParentalConsentHelper.hasFaceConsent(mParentalOptions); 278 canUseFingerprint = canUseFingerprint 279 && ParentalConsentHelper.hasFingerprintConsent(mParentalOptions); 280 } 281 282 // This will need to be updated if the device has sensors other than BIOMETRIC_STRONG 283 if (!setupWizard && authenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) { 284 launchCredentialOnlyEnroll(); 285 finish(); 286 } else if (canUseFace && canUseFingerprint) { 287 if (mGkPwHandle != null) { 288 launchFaceAndFingerprintEnroll(); 289 } else { 290 setOrConfirmCredentialsNow(); 291 } 292 } else if (canUseFingerprint) { 293 launchFingerprintOnlyEnroll(); 294 } else if (canUseFace) { 295 launchFaceOnlyEnroll(); 296 } else { // no modalities available 297 if (mParentalOptionsRequired) { 298 Log.d(TAG, "No consent for any modality: skipping enrollment"); 299 setResult(RESULT_OK, newResultIntent()); 300 } else { 301 Log.e(TAG, "Unknown state, finishing (was SUW: " + setupWizard + ")"); 302 } 303 finish(); 304 } 305 } 306 307 @Override 308 protected void onSaveInstanceState(@NonNull Bundle outState) { 309 super.onSaveInstanceState(outState); 310 outState.putBoolean(SAVED_STATE_CONFIRMING_CREDENTIALS, mConfirmingCredentials); 311 outState.putBoolean(SAVED_STATE_ENROLL_ACTION_LOGGED, mIsEnrollActionLogged); 312 if (mParentalOptions != null) { 313 outState.putBundle(SAVED_STATE_PARENTAL_OPTIONS, mParentalOptions); 314 } 315 if (mGkPwHandle != null) { 316 outState.putLong(SAVED_STATE_GK_PW_HANDLE, mGkPwHandle); 317 } 318 } 319 320 @Override 321 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 322 super.onActivityResult(requestCode, resultCode, data); 323 324 // single enrollment is handled entirely by the launched activity 325 // this handles multi enroll or if parental consent is required 326 if (mParentalConsentHelper != null) { 327 handleOnActivityResultWhileConsenting(requestCode, resultCode, data); 328 } else { 329 handleOnActivityResultWhileEnrolling(requestCode, resultCode, data); 330 } 331 } 332 333 // handles responses while parental consent is pending 334 private void handleOnActivityResultWhileConsenting( 335 int requestCode, int resultCode, Intent data) { 336 overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); 337 338 switch (requestCode) { 339 case REQUEST_CHOOSE_LOCK: 340 case REQUEST_CONFIRM_LOCK: 341 mConfirmingCredentials = false; 342 if (isSuccessfulConfirmOrChooseCredential(requestCode, resultCode)) { 343 updateGatekeeperPasswordHandle(data); 344 if (!mParentalConsentHelper.launchNext(this, REQUEST_CHOOSE_OPTIONS)) { 345 Log.e(TAG, "Nothing to prompt for consent (no modalities enabled)!"); 346 finish(); 347 } 348 } else { 349 Log.d(TAG, "Unknown result for set/choose lock: " + resultCode); 350 setResult(resultCode); 351 finish(); 352 } 353 break; 354 case REQUEST_CHOOSE_OPTIONS: 355 if (resultCode == RESULT_CONSENT_GRANTED || resultCode == RESULT_CONSENT_DENIED) { 356 final boolean isStillPrompting = mParentalConsentHelper.launchNext( 357 this, REQUEST_CHOOSE_OPTIONS, resultCode, data); 358 if (!isStillPrompting) { 359 Log.d(TAG, "Enrollment consent options set, starting enrollment"); 360 mParentalOptions = mParentalConsentHelper.getConsentResult(); 361 mParentalConsentHelper = null; 362 startEnroll(); 363 } 364 } else { 365 Log.d(TAG, "Unknown or cancelled parental consent"); 366 setResult(RESULT_CANCELED); 367 finish(); 368 } 369 break; 370 default: 371 Log.w(TAG, "Unknown consenting requestCode: " + requestCode + ", finishing"); 372 finish(); 373 } 374 } 375 376 // handles responses while multi biometric enrollment is pending 377 private void handleOnActivityResultWhileEnrolling( 378 int requestCode, int resultCode, Intent data) { 379 if (requestCode == REQUEST_HANDOFF_PARENT) { 380 Log.d(TAG, "Enrollment complete, requesting handoff, result: " + resultCode); 381 setResult(RESULT_OK, newResultIntent()); 382 finish(); 383 } else if (mMultiBiometricEnrollHelper == null) { 384 overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); 385 386 switch (requestCode) { 387 case REQUEST_CHOOSE_LOCK: 388 case REQUEST_CONFIRM_LOCK: 389 mConfirmingCredentials = false; 390 final boolean isOk = 391 isSuccessfulConfirmOrChooseCredential(requestCode, resultCode); 392 // single modality enrollment requests confirmation directly 393 // via BiometricEnrollBase#onCreate and should never get here 394 if (isOk && mHasFeatureFace && mHasFeatureFingerprint) { 395 updateGatekeeperPasswordHandle(data); 396 launchFaceAndFingerprintEnroll(); 397 } else { 398 Log.d(TAG, "Unknown result for set/choose lock: " + resultCode); 399 setResult(resultCode); 400 finish(); 401 } 402 break; 403 case REQUEST_SINGLE_ENROLL: 404 finishOrLaunchHandToParent(resultCode); 405 break; 406 default: 407 Log.w(TAG, "Unknown enrolling requestCode: " + requestCode + ", finishing"); 408 finish(); 409 } 410 } else { 411 Log.d(TAG, "RequestCode: " + requestCode + " resultCode: " + resultCode); 412 BiometricUtils.removeGatekeeperPasswordHandle(this, mGkPwHandle); 413 finishOrLaunchHandToParent(resultCode); 414 } 415 } 416 417 private void finishOrLaunchHandToParent(int resultCode) { 418 if (mParentalOptionsRequired) { 419 if (!mSkipReturnToParent) { 420 launchHandoffToParent(); 421 } else { 422 setResult(RESULT_OK, newResultIntent()); 423 finish(); 424 } 425 } else { 426 setResult(resultCode); 427 finish(); 428 } 429 } 430 431 private Intent newResultIntent() { 432 final Intent intent = new Intent(); 433 final Bundle consentStatus = mParentalOptions.deepCopy(); 434 intent.putExtra(EXTRA_PARENTAL_CONSENT_STATUS, consentStatus); 435 Log.v(TAG, "Result consent status: " + consentStatus); 436 return intent; 437 } 438 439 private static boolean isSuccessfulConfirmOrChooseCredential(int requestCode, int resultCode) { 440 final boolean okChoose = requestCode == REQUEST_CHOOSE_LOCK 441 && resultCode == ChooseLockPattern.RESULT_FINISHED; 442 final boolean okConfirm = requestCode == REQUEST_CONFIRM_LOCK 443 && resultCode == RESULT_OK; 444 return okChoose || okConfirm; 445 } 446 447 @Override 448 protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) { 449 final int newResid = SetupWizardUtils.getTheme(this, getIntent()); 450 theme.applyStyle(R.style.SetupWizardPartnerResource, true); 451 super.onApplyThemeResource(theme, newResid, first); 452 } 453 454 private void setOrConfirmCredentialsNow() { 455 if (!mConfirmingCredentials) { 456 mConfirmingCredentials = true; 457 if (!userHasPassword(mUserId)) { 458 launchChooseLock(); 459 } else { 460 launchConfirmLock(); 461 } 462 } 463 } 464 465 private void updateGatekeeperPasswordHandle(@NonNull Intent data) { 466 mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data); 467 if (mParentalConsentHelper != null) { 468 mParentalConsentHelper.updateGatekeeperHandle(data); 469 } 470 } 471 472 private boolean userHasPassword(int userId) { 473 final UserManager userManager = getSystemService(UserManager.class); 474 final int passwordQuality = new LockPatternUtils(this) 475 .getActivePasswordQuality(userManager.getCredentialOwnerProfile(userId)); 476 return passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 477 } 478 479 private void launchChooseLock() { 480 Log.d(TAG, "launchChooseLock"); 481 482 Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent()); 483 intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true); 484 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true); 485 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true); 486 487 if (mUserId != UserHandle.USER_NULL) { 488 intent.putExtra(Intent.EXTRA_USER_ID, mUserId); 489 } 490 startActivityForResult(intent, REQUEST_CHOOSE_LOCK); 491 } 492 493 private void launchConfirmLock() { 494 Log.d(TAG, "launchConfirmLock"); 495 496 final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this); 497 builder.setRequestCode(REQUEST_CONFIRM_LOCK) 498 .setRequestGatekeeperPasswordHandle(true) 499 .setForegroundOnly(true) 500 .setReturnCredentials(true); 501 if (mUserId != UserHandle.USER_NULL) { 502 builder.setUserId(mUserId); 503 } 504 final boolean launched = builder.show(); 505 if (!launched) { 506 // This shouldn't happen, as we should only end up at this step if a lock thingy is 507 // already set. 508 finish(); 509 } 510 } 511 512 // This should only be used to launch enrollment for single-sensor devices. 513 private void launchSingleSensorEnrollActivity(@NonNull Intent intent, int requestCode) { 514 byte[] hardwareAuthToken = null; 515 if (this instanceof InternalActivity) { 516 hardwareAuthToken = getIntent().getByteArrayExtra( 517 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN); 518 } 519 BiometricUtils.launchEnrollForResult(this, intent, requestCode, hardwareAuthToken, 520 mGkPwHandle, mUserId); 521 } 522 523 private void launchCredentialOnlyEnroll() { 524 final Intent intent; 525 // If only device credential was specified, ask the user to only set that up. 526 intent = new Intent(this, ChooseLockGeneric.class); 527 intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true); 528 launchSingleSensorEnrollActivity(intent, 0 /* requestCode */); 529 } 530 531 private void launchFingerprintOnlyEnroll() { 532 final Intent intent; 533 // ChooseLockGeneric can request to start fingerprint enroll bypassing the intro screen. 534 if (getIntent().getBooleanExtra(EXTRA_SKIP_INTRO, false) 535 && this instanceof InternalActivity) { 536 intent = BiometricUtils.getFingerprintFindSensorIntent(this, getIntent()); 537 } else { 538 intent = BiometricUtils.getFingerprintIntroIntent(this, getIntent()); 539 } 540 launchSingleSensorEnrollActivity(intent, REQUEST_SINGLE_ENROLL); 541 } 542 543 private void launchFaceOnlyEnroll() { 544 final Intent intent = BiometricUtils.getFaceIntroIntent(this, getIntent()); 545 launchSingleSensorEnrollActivity(intent, REQUEST_SINGLE_ENROLL); 546 } 547 548 private void launchFaceAndFingerprintEnroll() { 549 mMultiBiometricEnrollHelper = new MultiBiometricEnrollHelper(this, mUserId, 550 mIsFaceEnrollable, mIsFingerprintEnrollable, mGkPwHandle); 551 mMultiBiometricEnrollHelper.startNextStep(); 552 } 553 554 private void launchHandoffToParent() { 555 final Intent intent = BiometricUtils.getHandoffToParentIntent(this, getIntent()); 556 startActivityForResult(intent, REQUEST_HANDOFF_PARENT); 557 } 558 559 @Override 560 public int getMetricsCategory() { 561 return SettingsEnums.BIOMETRIC_ENROLL_ACTIVITY; 562 } 563 } 564