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 android.hardware.biometrics; 18 19 import static android.Manifest.permission.TEST_BIOMETRIC; 20 import static android.Manifest.permission.USE_BIOMETRIC; 21 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; 22 import static android.hardware.biometrics.BiometricManager.Authenticators; 23 24 import android.annotation.CallbackExecutor; 25 import android.annotation.IntDef; 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.annotation.RequiresPermission; 29 import android.annotation.TestApi; 30 import android.content.Context; 31 import android.content.DialogInterface; 32 import android.hardware.face.FaceManager; 33 import android.hardware.fingerprint.FingerprintManager; 34 import android.os.Binder; 35 import android.os.CancellationSignal; 36 import android.os.IBinder; 37 import android.os.Parcel; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.security.identity.IdentityCredential; 41 import android.security.identity.PresentationSession; 42 import android.security.keystore.KeyProperties; 43 import android.text.TextUtils; 44 import android.util.Log; 45 46 import com.android.internal.R; 47 import com.android.internal.util.FrameworkStatsLog; 48 49 import java.lang.annotation.Retention; 50 import java.lang.annotation.RetentionPolicy; 51 import java.security.Signature; 52 import java.util.List; 53 import java.util.concurrent.Executor; 54 55 import javax.crypto.Cipher; 56 import javax.crypto.Mac; 57 58 /** 59 * A class that manages a system-provided biometric dialog. 60 */ 61 public class BiometricPrompt implements BiometricAuthenticator, BiometricConstants { 62 63 private static final String TAG = "BiometricPrompt"; 64 65 /** 66 * Error/help message will show for this amount of time. 67 * For error messages, the dialog will also be dismissed after this amount of time. 68 * Error messages will be propagated back to the application via AuthenticationCallback 69 * after this amount of time. 70 * @hide 71 */ 72 public static final int HIDE_DIALOG_DELAY = 2000; // ms 73 74 /** 75 * @hide 76 */ 77 public static final int DISMISSED_REASON_BIOMETRIC_CONFIRMED = 1; 78 79 /** 80 * Dialog is done animating away after user clicked on the button set via 81 * {@link BiometricPrompt.Builder#setNegativeButton(CharSequence, Executor, 82 * DialogInterface.OnClickListener)}. 83 * @hide 84 */ 85 public static final int DISMISSED_REASON_NEGATIVE = 2; 86 87 /** 88 * @hide 89 */ 90 public static final int DISMISSED_REASON_USER_CANCEL = 3; 91 92 /** 93 * Authenticated, confirmation not required. Dialog animated away. 94 * @hide 95 */ 96 public static final int DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED = 4; 97 98 /** 99 * Error message shown on SystemUI. When BiometricService receives this, the UI is already 100 * gone. 101 * @hide 102 */ 103 public static final int DISMISSED_REASON_ERROR = 5; 104 105 /** 106 * Dialog dismissal requested by BiometricService. 107 * @hide 108 */ 109 public static final int DISMISSED_REASON_SERVER_REQUESTED = 6; 110 111 /** 112 * @hide 113 */ 114 public static final int DISMISSED_REASON_CREDENTIAL_CONFIRMED = 7; 115 116 /** 117 * @hide 118 */ 119 @IntDef({DISMISSED_REASON_BIOMETRIC_CONFIRMED, 120 DISMISSED_REASON_NEGATIVE, 121 DISMISSED_REASON_USER_CANCEL, 122 DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, 123 DISMISSED_REASON_ERROR, 124 DISMISSED_REASON_SERVER_REQUESTED, 125 DISMISSED_REASON_CREDENTIAL_CONFIRMED}) 126 @Retention(RetentionPolicy.SOURCE) 127 public @interface DismissedReason {} 128 129 private static class ButtonInfo { 130 Executor executor; 131 DialogInterface.OnClickListener listener; ButtonInfo(Executor ex, DialogInterface.OnClickListener l)132 ButtonInfo(Executor ex, DialogInterface.OnClickListener l) { 133 executor = ex; 134 listener = l; 135 } 136 } 137 138 /** 139 * A builder that collects arguments to be shown on the system-provided biometric dialog. 140 */ 141 public static class Builder { 142 private PromptInfo mPromptInfo; 143 private ButtonInfo mNegativeButtonInfo; 144 private Context mContext; 145 private IAuthService mService; 146 147 // LINT.IfChange 148 /** 149 * Creates a builder for a {@link BiometricPrompt} dialog. 150 * @param context The {@link Context} that will be used to build the prompt. 151 */ Builder(Context context)152 public Builder(Context context) { 153 mPromptInfo = new PromptInfo(); 154 mContext = context; 155 } 156 157 /** 158 * Required: Sets the title that will be shown on the prompt. 159 * @param title The title to display. 160 * @return This builder. 161 */ 162 @NonNull setTitle(@onNull CharSequence title)163 public Builder setTitle(@NonNull CharSequence title) { 164 mPromptInfo.setTitle(title); 165 return this; 166 } 167 168 /** 169 * Shows a default, modality-specific title for the prompt if the title would otherwise be 170 * null or empty. Currently for internal use only. 171 * @return This builder. 172 * @hide 173 */ 174 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 175 @NonNull setUseDefaultTitle()176 public Builder setUseDefaultTitle() { 177 mPromptInfo.setUseDefaultTitle(true); 178 return this; 179 } 180 181 /** 182 * Optional: Sets a subtitle that will be shown on the prompt. 183 * @param subtitle The subtitle to display. 184 * @return This builder. 185 */ 186 @NonNull setSubtitle(@onNull CharSequence subtitle)187 public Builder setSubtitle(@NonNull CharSequence subtitle) { 188 mPromptInfo.setSubtitle(subtitle); 189 return this; 190 } 191 192 /** 193 * Shows a default subtitle for the prompt if the subtitle would otherwise be 194 * null or empty. Currently for internal use only. 195 * @return This builder. 196 * @hide 197 */ 198 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 199 @NonNull setUseDefaultSubtitle()200 public Builder setUseDefaultSubtitle() { 201 mPromptInfo.setUseDefaultSubtitle(true); 202 return this; 203 } 204 205 /** 206 * Optional: Sets a description that will be shown on the prompt. 207 * @param description The description to display. 208 * @return This builder. 209 */ 210 @NonNull setDescription(@onNull CharSequence description)211 public Builder setDescription(@NonNull CharSequence description) { 212 mPromptInfo.setDescription(description); 213 return this; 214 } 215 216 /** 217 * @param service 218 * @return This builder. 219 * @hide 220 */ 221 @RequiresPermission(TEST_BIOMETRIC) 222 @NonNull setService(@onNull IAuthService service)223 public Builder setService(@NonNull IAuthService service) { 224 mService = service; 225 return this; 226 } 227 228 /** 229 * Sets an optional title, subtitle, and/or description that will override other text when 230 * the user is authenticating with PIN/pattern/password. Currently for internal use only. 231 * @return This builder. 232 * @hide 233 */ 234 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 235 @NonNull setTextForDeviceCredential( @ullable CharSequence title, @Nullable CharSequence subtitle, @Nullable CharSequence description)236 public Builder setTextForDeviceCredential( 237 @Nullable CharSequence title, 238 @Nullable CharSequence subtitle, 239 @Nullable CharSequence description) { 240 if (title != null) { 241 mPromptInfo.setDeviceCredentialTitle(title); 242 } 243 if (subtitle != null) { 244 mPromptInfo.setDeviceCredentialSubtitle(subtitle); 245 } 246 if (description != null) { 247 mPromptInfo.setDeviceCredentialDescription(description); 248 } 249 return this; 250 } 251 252 /** 253 * Required: Sets the text, executor, and click listener for the negative button on the 254 * prompt. This is typically a cancel button, but may be also used to show an alternative 255 * method for authentication, such as a screen that asks for a backup password. 256 * 257 * <p>Note that this setting is not required, and in fact is explicitly disallowed, if 258 * device credential authentication is enabled via {@link #setAllowedAuthenticators(int)} or 259 * {@link #setDeviceCredentialAllowed(boolean)}. 260 * 261 * @param text Text to be shown on the negative button for the prompt. 262 * @param executor Executor that will be used to run the on click callback. 263 * @param listener Listener containing a callback to be run when the button is pressed. 264 * @return This builder. 265 */ 266 @NonNull setNegativeButton(@onNull CharSequence text, @NonNull @CallbackExecutor Executor executor, @NonNull DialogInterface.OnClickListener listener)267 public Builder setNegativeButton(@NonNull CharSequence text, 268 @NonNull @CallbackExecutor Executor executor, 269 @NonNull DialogInterface.OnClickListener listener) { 270 if (TextUtils.isEmpty(text)) { 271 throw new IllegalArgumentException("Text must be set and non-empty"); 272 } 273 if (executor == null) { 274 throw new IllegalArgumentException("Executor must not be null"); 275 } 276 if (listener == null) { 277 throw new IllegalArgumentException("Listener must not be null"); 278 } 279 mPromptInfo.setNegativeButtonText(text); 280 mNegativeButtonInfo = new ButtonInfo(executor, listener); 281 return this; 282 } 283 284 /** 285 * Optional: Sets a hint to the system for whether to require user confirmation after 286 * authentication. For example, implicit modalities like face and iris are passive, meaning 287 * they don't require an explicit user action to complete authentication. If set to true, 288 * these modalities should require the user to take some action (e.g. press a button) 289 * before {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is 290 * called. Defaults to true. 291 * 292 * <p>A typical use case for not requiring confirmation would be for low-risk transactions, 293 * such as re-authenticating a recently authenticated application. A typical use case for 294 * requiring confirmation would be for authorizing a purchase. 295 * 296 * <p>Note that this just passes a hint to the system, which the system may then ignore. For 297 * example, a value of false may be ignored if the user has disabled implicit authentication 298 * in Settings, or if it does not apply to a particular modality (e.g. fingerprint). 299 * 300 * @param requireConfirmation true if explicit user confirmation should be required, or 301 * false otherwise. 302 * @return This builder. 303 */ 304 @NonNull setConfirmationRequired(boolean requireConfirmation)305 public Builder setConfirmationRequired(boolean requireConfirmation) { 306 mPromptInfo.setConfirmationRequested(requireConfirmation); 307 return this; 308 } 309 310 /** 311 * Optional: If enabled, the user will be given the option to authenticate with their device 312 * PIN, pattern, or password. Developers should first check {@link 313 * BiometricManager#canAuthenticate(int)} for {@link Authenticators#DEVICE_CREDENTIAL} 314 * before enabling. If the device is not secured with a credential, 315 * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} will be invoked 316 * with {@link BiometricPrompt#BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL}. Defaults to false. 317 * 318 * <p>Note that enabling this option replaces the negative button on the prompt with one 319 * that allows the user to authenticate with their device credential, making it an error to 320 * call {@link #setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. 321 * 322 * @param allowed true if the prompt should fall back to asking for the user's device 323 * credential (PIN/pattern/password), or false otherwise. 324 * @return This builder. 325 * 326 * @deprecated Replaced by {@link #setAllowedAuthenticators(int)}. 327 */ 328 @Deprecated 329 @NonNull setDeviceCredentialAllowed(boolean allowed)330 public Builder setDeviceCredentialAllowed(boolean allowed) { 331 mPromptInfo.setDeviceCredentialAllowed(allowed); 332 return this; 333 } 334 335 /** 336 * Optional: Specifies the type(s) of authenticators that may be invoked by 337 * {@link BiometricPrompt} to authenticate the user. Available authenticator types are 338 * defined in {@link Authenticators} and can be combined via bitwise OR. Defaults to: 339 * <ul> 340 * <li>{@link Authenticators#BIOMETRIC_WEAK} for non-crypto authentication, or</li> 341 * <li>{@link Authenticators#BIOMETRIC_STRONG} for crypto-based authentication.</li> 342 * </ul> 343 * 344 * <p>If this method is used and no authenticator of any of the specified types is available 345 * at the time <code>BiometricPrompt#authenticate(...)</code> is called, authentication will 346 * be canceled and {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} 347 * will be invoked with an appropriate error code. 348 * 349 * <p>This method should be preferred over {@link #setDeviceCredentialAllowed(boolean)} and 350 * overrides the latter if both are used. Using this method to enable device credential 351 * authentication (with {@link Authenticators#DEVICE_CREDENTIAL}) will replace the negative 352 * button on the prompt, making it an error to also call 353 * {@link #setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. 354 * 355 * <p>If unlocking cryptographic operation(s), it is the application's responsibility to 356 * request authentication with the proper set of authenticators (e.g. match the 357 * authenticators specified during key generation). 358 * 359 * @see android.security.keystore.KeyGenParameterSpec.Builder 360 * @see KeyProperties#AUTH_BIOMETRIC_STRONG 361 * @see KeyProperties#AUTH_DEVICE_CREDENTIAL 362 * 363 * @param authenticators A bit field representing all valid authenticator types that may be 364 * invoked by the prompt. 365 * @return This builder. 366 */ 367 @NonNull setAllowedAuthenticators(@uthenticators.Types int authenticators)368 public Builder setAllowedAuthenticators(@Authenticators.Types int authenticators) { 369 mPromptInfo.setAuthenticators(authenticators); 370 return this; 371 } 372 373 /** 374 * If non-empty, requests authentication to be performed only if the sensor is contained 375 * within the list. Note that the actual sensor presented to the user/test will meet all 376 * constraints specified within this builder. For example, on a device with the below 377 * configuration: 378 * 379 * SensorId: 1, Strength: BIOMETRIC_STRONG 380 * SensorId: 2, Strength: BIOMETRIC_WEAK 381 * 382 * If authentication is invoked with setAllowedAuthenticators(BIOMETRIC_STRONG) and 383 * setAllowedSensorIds(2), then no sensor will be eligible for authentication. 384 * 385 * @see {@link BiometricManager#getSensorProperties()} 386 * 387 * @param sensorIds Sensor IDs to constrain this authentication to. 388 * @return This builder 389 * @hide 390 */ 391 @TestApi 392 @NonNull 393 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setAllowedSensorIds(@onNull List<Integer> sensorIds)394 public Builder setAllowedSensorIds(@NonNull List<Integer> sensorIds) { 395 mPromptInfo.setAllowedSensorIds(sensorIds); 396 return this; 397 } 398 399 /** 400 * @param allow If true, allows authentication when the calling package is not in the 401 * foreground. This is set to false by default. 402 * @return This builder 403 * @hide 404 */ 405 @TestApi 406 @NonNull 407 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setAllowBackgroundAuthentication(boolean allow)408 public Builder setAllowBackgroundAuthentication(boolean allow) { 409 mPromptInfo.setAllowBackgroundAuthentication(allow); 410 return this; 411 } 412 413 /** 414 * If set check the Device Policy Manager for disabled biometrics. 415 * 416 * @param checkDevicePolicyManager 417 * @return This builder. 418 * @hide 419 */ 420 @NonNull 421 @RequiresPermission(anyOf = {USE_BIOMETRIC_INTERNAL}) setDisallowBiometricsIfPolicyExists(boolean checkDevicePolicyManager)422 public Builder setDisallowBiometricsIfPolicyExists(boolean checkDevicePolicyManager) { 423 mPromptInfo.setDisallowBiometricsIfPolicyExists(checkDevicePolicyManager); 424 return this; 425 } 426 427 /** 428 * If set, receive internal events via {@link AuthenticationCallback#onSystemEvent(int)} 429 * @param set 430 * @return This builder. 431 * @hide 432 */ 433 @NonNull 434 @RequiresPermission(anyOf = {USE_BIOMETRIC_INTERNAL}) setReceiveSystemEvents(boolean set)435 public Builder setReceiveSystemEvents(boolean set) { 436 mPromptInfo.setReceiveSystemEvents(set); 437 return this; 438 } 439 440 /** 441 * Flag to decide if authentication should ignore enrollment state. 442 * Defaults to false (not ignoring enrollment state) 443 * @param ignoreEnrollmentState 444 * @return This builder. 445 * @hide 446 */ 447 @NonNull 448 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setIgnoreEnrollmentState(boolean ignoreEnrollmentState)449 public Builder setIgnoreEnrollmentState(boolean ignoreEnrollmentState) { 450 mPromptInfo.setIgnoreEnrollmentState(ignoreEnrollmentState); 451 return this; 452 } 453 454 /** 455 * Set if BiometricPrompt is being used by the legacy fingerprint manager API. 456 * @param sensorId sensor id 457 * @return This builder. 458 * @hide 459 */ 460 @NonNull 461 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setIsForLegacyFingerprintManager(int sensorId)462 public Builder setIsForLegacyFingerprintManager(int sensorId) { 463 mPromptInfo.setIsForLegacyFingerprintManager(sensorId); 464 return this; 465 } 466 // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java) 467 468 /** 469 * Creates a {@link BiometricPrompt}. 470 * 471 * @return An instance of {@link BiometricPrompt}. 472 * 473 * @throws IllegalArgumentException If any required fields are unset, or if given any 474 * invalid combination of field values. 475 */ 476 @NonNull build()477 public BiometricPrompt build() { 478 final CharSequence title = mPromptInfo.getTitle(); 479 final CharSequence negative = mPromptInfo.getNegativeButtonText(); 480 final boolean useDefaultTitle = mPromptInfo.isUseDefaultTitle(); 481 final boolean deviceCredentialAllowed = mPromptInfo.isDeviceCredentialAllowed(); 482 final @Authenticators.Types int authenticators = mPromptInfo.getAuthenticators(); 483 final boolean willShowDeviceCredentialButton = deviceCredentialAllowed 484 || isCredentialAllowed(authenticators); 485 486 if (TextUtils.isEmpty(title) && !useDefaultTitle) { 487 throw new IllegalArgumentException("Title must be set and non-empty"); 488 } else if (TextUtils.isEmpty(negative) && !willShowDeviceCredentialButton) { 489 throw new IllegalArgumentException("Negative text must be set and non-empty"); 490 } else if (!TextUtils.isEmpty(negative) && willShowDeviceCredentialButton) { 491 throw new IllegalArgumentException("Can't have both negative button behavior" 492 + " and device credential enabled"); 493 } 494 mService = (mService == null) ? IAuthService.Stub.asInterface( 495 ServiceManager.getService(Context.AUTH_SERVICE)) : mService; 496 return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo, mService); 497 } 498 } 499 500 private class OnAuthenticationCancelListener implements CancellationSignal.OnCancelListener { 501 private final long mAuthRequestId; 502 OnAuthenticationCancelListener(long id)503 OnAuthenticationCancelListener(long id) { 504 mAuthRequestId = id; 505 } 506 507 @Override onCancel()508 public void onCancel() { 509 if (!mIsPromptShowing) { 510 Log.w(TAG, "BP is not showing"); 511 return; 512 } 513 Log.d(TAG, "Cancel BP authentication requested for: " + mAuthRequestId); 514 cancelAuthentication(mAuthRequestId); 515 } 516 } 517 518 private final IBinder mToken = new Binder(); 519 private final Context mContext; 520 private final IAuthService mService; 521 private final PromptInfo mPromptInfo; 522 private final ButtonInfo mNegativeButtonInfo; 523 524 private CryptoObject mCryptoObject; 525 private Executor mExecutor; 526 private AuthenticationCallback mAuthenticationCallback; 527 528 private final IBiometricServiceReceiver mBiometricServiceReceiver = 529 new IBiometricServiceReceiver.Stub() { 530 531 @Override 532 public void onAuthenticationSucceeded(@AuthenticationResultType int authenticationType) { 533 mExecutor.execute(() -> { 534 final AuthenticationResult result = 535 new AuthenticationResult(mCryptoObject, authenticationType); 536 mAuthenticationCallback.onAuthenticationSucceeded(result); 537 mIsPromptShowing = false; 538 }); 539 } 540 541 @Override 542 public void onAuthenticationFailed() { 543 mExecutor.execute(() -> { 544 mAuthenticationCallback.onAuthenticationFailed(); 545 }); 546 } 547 548 @Override 549 public void onError(@BiometricAuthenticator.Modality int modality, int error, 550 int vendorCode) { 551 552 String errorMessage = null; 553 switch (modality) { 554 case TYPE_FACE: 555 errorMessage = FaceManager.getErrorString(mContext, error, vendorCode); 556 break; 557 558 case TYPE_FINGERPRINT: 559 errorMessage = FingerprintManager.getErrorString(mContext, error, vendorCode); 560 break; 561 } 562 563 // Look for generic errors, as it may be a combination of modalities, or no modality 564 // (e.g. attempted biometric authentication without biometric sensors). 565 if (errorMessage == null) { 566 switch (error) { 567 case BIOMETRIC_ERROR_CANCELED: 568 errorMessage = mContext.getString(R.string.biometric_error_canceled); 569 break; 570 case BIOMETRIC_ERROR_USER_CANCELED: 571 errorMessage = mContext.getString(R.string.biometric_error_user_canceled); 572 break; 573 case BIOMETRIC_ERROR_HW_NOT_PRESENT: 574 errorMessage = mContext.getString(R.string.biometric_error_hw_unavailable); 575 break; 576 case BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL: 577 errorMessage = mContext.getString( 578 R.string.biometric_error_device_not_secured); 579 break; 580 default: 581 Log.e(TAG, "Unknown error, modality: " + modality 582 + " error: " + error 583 + " vendorCode: " + vendorCode); 584 errorMessage = mContext.getString(R.string.biometric_error_generic); 585 break; 586 } 587 } 588 589 final String stringToSend = errorMessage; 590 mExecutor.execute(() -> { 591 mAuthenticationCallback.onAuthenticationError(error, stringToSend); 592 mIsPromptShowing = false; 593 }); 594 } 595 596 @Override 597 public void onAcquired(int acquireInfo, String message) { 598 mExecutor.execute(() -> { 599 mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message); 600 }); 601 } 602 603 @Override 604 public void onDialogDismissed(int reason) { 605 // Check the reason and invoke OnClickListener(s) if necessary 606 if (reason == DISMISSED_REASON_NEGATIVE) { 607 mNegativeButtonInfo.executor.execute(() -> { 608 mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE); 609 mIsPromptShowing = false; 610 }); 611 } else { 612 mIsPromptShowing = false; 613 Log.e(TAG, "Unknown reason: " + reason); 614 } 615 } 616 617 @Override 618 public void onSystemEvent(int event) { 619 mExecutor.execute(() -> { 620 mAuthenticationCallback.onSystemEvent(event); 621 }); 622 } 623 }; 624 625 private boolean mIsPromptShowing; 626 BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo, IAuthService service)627 private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo, 628 IAuthService service) { 629 mContext = context; 630 mPromptInfo = promptInfo; 631 mNegativeButtonInfo = negativeButtonInfo; 632 mService = service; 633 mIsPromptShowing = false; 634 } 635 636 /** 637 * Gets the title for the prompt, as set by {@link Builder#setTitle(CharSequence)}. 638 * @return The title of the prompt, which is guaranteed to be non-null. 639 */ 640 @NonNull getTitle()641 public CharSequence getTitle() { 642 return mPromptInfo.getTitle(); 643 } 644 645 /** 646 * Whether to use a default modality-specific title. For internal use only. 647 * @return See {@link Builder#setUseDefaultTitle()}. 648 * @hide 649 */ 650 @RequiresPermission(USE_BIOMETRIC_INTERNAL) shouldUseDefaultTitle()651 public boolean shouldUseDefaultTitle() { 652 return mPromptInfo.isUseDefaultTitle(); 653 } 654 655 /** 656 * Gets the subtitle for the prompt, as set by {@link Builder#setSubtitle(CharSequence)}. 657 * @return The subtitle for the prompt, or null if the prompt has no subtitle. 658 */ 659 @Nullable getSubtitle()660 public CharSequence getSubtitle() { 661 return mPromptInfo.getSubtitle(); 662 } 663 664 /** 665 * Whether to use a default subtitle. For internal use only. 666 * @return See {@link Builder#setUseDefaultSubtitle()}. 667 * @hide 668 */ 669 @RequiresPermission(USE_BIOMETRIC_INTERNAL) shouldUseDefaultSubtitle()670 public boolean shouldUseDefaultSubtitle() { 671 return mPromptInfo.isUseDefaultSubtitle(); 672 } 673 674 /** 675 * Gets the description for the prompt, as set by {@link Builder#setDescription(CharSequence)}. 676 * @return The description for the prompt, or null if the prompt has no description. 677 */ 678 @Nullable getDescription()679 public CharSequence getDescription() { 680 return mPromptInfo.getDescription(); 681 } 682 683 /** 684 * Gets the negative button text for the prompt, as set by 685 * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. 686 * @return The negative button text for the prompt, or null if no negative button text was set. 687 */ 688 @Nullable getNegativeButtonText()689 public CharSequence getNegativeButtonText() { 690 return mPromptInfo.getNegativeButtonText(); 691 } 692 693 /** 694 * Determines if explicit user confirmation is required by the prompt, as set by 695 * {@link Builder#setConfirmationRequired(boolean)}. 696 * 697 * @return true if explicit user confirmation is required, or false otherwise. 698 */ isConfirmationRequired()699 public boolean isConfirmationRequired() { 700 return mPromptInfo.isConfirmationRequested(); 701 } 702 703 /** 704 * Gets the type(s) of authenticators that may be invoked by the prompt to authenticate the 705 * user, as set by {@link Builder#setAllowedAuthenticators(int)}. 706 * 707 * @return A bit field representing the type(s) of authenticators that may be invoked by the 708 * prompt (as defined by {@link Authenticators}), or 0 if this field was not set. 709 */ 710 @Nullable getAllowedAuthenticators()711 public int getAllowedAuthenticators() { 712 return mPromptInfo.getAuthenticators(); 713 } 714 715 /** 716 * @return The values set by {@link Builder#setAllowedSensorIds(List)} 717 * @hide 718 */ 719 @TestApi 720 @NonNull getAllowedSensorIds()721 public List<Integer> getAllowedSensorIds() { 722 return mPromptInfo.getAllowedSensorIds(); 723 } 724 725 /** 726 * @return The value set by {@link Builder#setAllowBackgroundAuthentication(boolean)} 727 * @hide 728 */ 729 @TestApi isAllowBackgroundAuthentication()730 public boolean isAllowBackgroundAuthentication() { 731 return mPromptInfo.isAllowBackgroundAuthentication(); 732 } 733 734 /** 735 * A wrapper class for the cryptographic operations supported by BiometricPrompt. 736 * 737 * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, 738 * {@link IdentityCredential}, and {@link PresentationSession}. 739 * 740 * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and 741 * time-based. This is specified during key creation via the timeout parameter of the 742 * {@code setUserAuthenticationParameters(int, int)} method of {@link 743 * android.security.keystore.KeyGenParameterSpec.Builder}. 744 * 745 * <p>CryptoObjects are used to unlock auth-per-use keys via 746 * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor, 747 * AuthenticationCallback)}, whereas time-based keys are unlocked for their specified duration 748 * any time the user authenticates with the specified authenticators (e.g. unlocking keyguard). 749 * If a time-based key is not available for use (i.e. none of the allowed authenticators have 750 * been unlocked recently), applications can prompt the user to authenticate via 751 * {@link BiometricPrompt#authenticate(CancellationSignal, Executor, AuthenticationCallback)} 752 * 753 * @see Builder#setAllowedAuthenticators(int) 754 */ 755 public static final class CryptoObject extends android.hardware.biometrics.CryptoObject { CryptoObject(@onNull Signature signature)756 public CryptoObject(@NonNull Signature signature) { 757 super(signature); 758 } 759 CryptoObject(@onNull Cipher cipher)760 public CryptoObject(@NonNull Cipher cipher) { 761 super(cipher); 762 } 763 CryptoObject(@onNull Mac mac)764 public CryptoObject(@NonNull Mac mac) { 765 super(mac); 766 } 767 768 /** 769 * Create from a {@link IdentityCredential} object. 770 * 771 * @param credential a {@link IdentityCredential} object. 772 * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}. 773 */ 774 @Deprecated CryptoObject(@onNull IdentityCredential credential)775 public CryptoObject(@NonNull IdentityCredential credential) { 776 super(credential); 777 } 778 CryptoObject(@onNull PresentationSession session)779 public CryptoObject(@NonNull PresentationSession session) { 780 super(session); 781 } 782 783 /** 784 * Get {@link Signature} object. 785 * @return {@link Signature} object or null if this doesn't contain one. 786 */ getSignature()787 public Signature getSignature() { 788 return super.getSignature(); 789 } 790 791 /** 792 * Get {@link Cipher} object. 793 * @return {@link Cipher} object or null if this doesn't contain one. 794 */ getCipher()795 public Cipher getCipher() { 796 return super.getCipher(); 797 } 798 799 /** 800 * Get {@link Mac} object. 801 * @return {@link Mac} object or null if this doesn't contain one. 802 */ getMac()803 public Mac getMac() { 804 return super.getMac(); 805 } 806 807 /** 808 * Get {@link IdentityCredential} object. 809 * @return {@link IdentityCredential} object or null if this doesn't contain one. 810 * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}. 811 */ 812 @Deprecated getIdentityCredential()813 public @Nullable IdentityCredential getIdentityCredential() { 814 return super.getIdentityCredential(); 815 } 816 817 /** 818 * Get {@link PresentationSession} object. 819 * @return {@link PresentationSession} object or null if this doesn't contain one. 820 */ getPresentationSession()821 public @Nullable PresentationSession getPresentationSession() { 822 return super.getPresentationSession(); 823 } 824 } 825 826 /** 827 * Authentication type reported by {@link AuthenticationResult} when the user authenticated by 828 * entering their device PIN, pattern, or password. 829 */ 830 public static final int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 1; 831 832 /** 833 * Authentication type reported by {@link AuthenticationResult} when the user authenticated by 834 * presenting some form of biometric (e.g. fingerprint or face). 835 */ 836 public static final int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 2; 837 838 /** 839 * An {@link IntDef} representing the type of auth, as reported by {@link AuthenticationResult}. 840 * @hide 841 */ 842 @Retention(RetentionPolicy.SOURCE) 843 @IntDef({AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL, AUTHENTICATION_RESULT_TYPE_BIOMETRIC}) 844 public @interface AuthenticationResultType { 845 } 846 847 /** 848 * Container for callback data from {@link #authenticate(CancellationSignal, Executor, 849 * AuthenticationCallback)} and {@link #authenticate(CryptoObject, CancellationSignal, Executor, 850 * AuthenticationCallback)}. 851 */ 852 public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult { 853 /** 854 * Authentication result 855 * @param crypto 856 * @param authenticationType 857 * @hide 858 */ AuthenticationResult(CryptoObject crypto, @AuthenticationResultType int authenticationType)859 public AuthenticationResult(CryptoObject crypto, 860 @AuthenticationResultType int authenticationType) { 861 // Identifier and userId is not used for BiometricPrompt. 862 super(crypto, authenticationType, null /* identifier */, 0 /* userId */); 863 } 864 865 /** 866 * Provides the crypto object associated with this transaction. 867 * @return The crypto object provided to {@link #authenticate(CryptoObject, 868 * CancellationSignal, Executor, AuthenticationCallback)} 869 */ getCryptoObject()870 public CryptoObject getCryptoObject() { 871 return (CryptoObject) super.getCryptoObject(); 872 } 873 874 /** 875 * Provides the type of authentication (e.g. device credential or biometric) that was 876 * requested from and successfully provided by the user. 877 * 878 * @return An integer value representing the authentication method used. 879 */ getAuthenticationType()880 public @AuthenticationResultType int getAuthenticationType() { 881 return super.getAuthenticationType(); 882 } 883 } 884 885 /** 886 * Callback structure provided to {@link BiometricPrompt#authenticate(CancellationSignal, 887 * Executor, AuthenticationCallback)} or {@link BiometricPrompt#authenticate(CryptoObject, 888 * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation 889 * of this for listening to authentication events. 890 */ 891 public abstract static class AuthenticationCallback extends 892 BiometricAuthenticator.AuthenticationCallback { 893 /** 894 * Called when an unrecoverable error has been encountered and the operation is complete. 895 * No further actions will be made on this object. 896 * @param errorCode An integer identifying the error message 897 * @param errString A human-readable error string that can be shown on an UI 898 */ 899 @Override onAuthenticationError(int errorCode, CharSequence errString)900 public void onAuthenticationError(int errorCode, CharSequence errString) {} 901 902 /** 903 * Called when a recoverable error has been encountered during authentication. The help 904 * string is provided to give the user guidance for what went wrong, such as "Sensor dirty, 905 * please clean it." 906 * @param helpCode An integer identifying the error message 907 * @param helpString A human-readable string that can be shown on an UI 908 */ 909 @Override onAuthenticationHelp(int helpCode, CharSequence helpString)910 public void onAuthenticationHelp(int helpCode, CharSequence helpString) {} 911 912 /** 913 * Called when a biometric is recognized. 914 * @param result An object containing authentication-related data 915 */ onAuthenticationSucceeded(AuthenticationResult result)916 public void onAuthenticationSucceeded(AuthenticationResult result) {} 917 918 /** 919 * Called when a biometric is valid but not recognized. 920 */ 921 @Override onAuthenticationFailed()922 public void onAuthenticationFailed() {} 923 924 /** 925 * Called when a biometric has been acquired, but hasn't been processed yet. 926 * @hide 927 */ 928 @Override onAuthenticationAcquired(int acquireInfo)929 public void onAuthenticationAcquired(int acquireInfo) {} 930 931 /** 932 * Receiver for internal system events. See {@link Builder#setReceiveSystemEvents(boolean)} 933 * @hide 934 */ onSystemEvent(int event)935 public void onSystemEvent(int event) {} 936 } 937 938 /** 939 * Authenticates for the given user. 940 * 941 * @param cancel An object that can be used to cancel authentication 942 * @param executor An executor to handle callback events 943 * @param callback An object to receive authentication events 944 * @param userId The user to authenticate 945 * 946 * @hide 947 */ 948 @RequiresPermission(USE_BIOMETRIC_INTERNAL) authenticateUser(@onNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId)949 public void authenticateUser(@NonNull CancellationSignal cancel, 950 @NonNull @CallbackExecutor Executor executor, 951 @NonNull AuthenticationCallback callback, 952 int userId) { 953 if (cancel == null) { 954 throw new IllegalArgumentException("Must supply a cancellation signal"); 955 } 956 if (executor == null) { 957 throw new IllegalArgumentException("Must supply an executor"); 958 } 959 if (callback == null) { 960 throw new IllegalArgumentException("Must supply a callback"); 961 } 962 963 authenticateInternal(0 /* operationId */, cancel, executor, callback, userId); 964 } 965 966 /** 967 * Authenticates for the given keystore operation. 968 * 969 * @param cancel An object that can be used to cancel authentication 970 * @param executor An executor to handle callback events 971 * @param callback An object to receive authentication events 972 * @param operationId The keystore operation associated with authentication 973 * 974 * @return A requestId that can be used to cancel this operation. 975 * 976 * @hide 977 */ 978 @RequiresPermission(USE_BIOMETRIC) authenticateForOperation( @onNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, long operationId)979 public long authenticateForOperation( 980 @NonNull CancellationSignal cancel, 981 @NonNull @CallbackExecutor Executor executor, 982 @NonNull AuthenticationCallback callback, 983 long operationId) { 984 if (cancel == null) { 985 throw new IllegalArgumentException("Must supply a cancellation signal"); 986 } 987 if (executor == null) { 988 throw new IllegalArgumentException("Must supply an executor"); 989 } 990 if (callback == null) { 991 throw new IllegalArgumentException("Must supply a callback"); 992 } 993 994 return authenticateInternal(operationId, cancel, executor, callback, mContext.getUserId()); 995 } 996 997 /** 998 * This call warms up the biometric hardware, displays a system-provided dialog, and starts 999 * scanning for a biometric. It terminates when {@link 1000 * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link 1001 * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)}, or when the user 1002 * dismisses the system-provided dialog, at which point the crypto object becomes invalid. This 1003 * operation can be canceled by using the provided cancel object. The application will receive 1004 * authentication errors through {@link AuthenticationCallback}, and button events through the 1005 * corresponding callback set in {@link Builder#setNegativeButton(CharSequence, Executor, 1006 * DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricPrompt} object, 1007 * and calling {@link BiometricPrompt#authenticate(CancellationSignal, Executor, 1008 * AuthenticationCallback)} while an existing authentication attempt is occurring will stop the 1009 * previous client and start a new authentication. The interrupted client will receive a 1010 * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int, 1011 * CharSequence)}. 1012 * 1013 * <p>Note: Applications generally should not cancel and start authentication in quick 1014 * succession. For example, to properly handle authentication across configuration changes, it's 1015 * recommended to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, 1016 * the application will not need to cancel/restart authentication during the configuration 1017 * change. 1018 * 1019 * <p>Per the Android CDD, only biometric authenticators that meet or exceed the requirements 1020 * for <strong>Strong</strong> are permitted to integrate with Keystore to perform related 1021 * cryptographic operations. Therefore, it is an error to call this method after explicitly 1022 * calling {@link Builder#setAllowedAuthenticators(int)} with any biometric strength other than 1023 * {@link Authenticators#BIOMETRIC_STRONG}. 1024 * 1025 * @throws IllegalArgumentException If any argument is null, or if the allowed biometric 1026 * authenticator strength is explicitly set to {@link Authenticators#BIOMETRIC_WEAK}. Prior to 1027 * {@link android.os.Build.VERSION_CODES#R}, this exception is also thrown if 1028 * {@link Builder#setDeviceCredentialAllowed(boolean)} was explicitly set to true. 1029 * 1030 * @param crypto A cryptographic operation to be unlocked after successful authentication. 1031 * @param cancel An object that can be used to cancel authentication. 1032 * @param executor An executor to handle callback events. 1033 * @param callback An object to receive authentication events. 1034 */ 1035 @RequiresPermission(USE_BIOMETRIC) authenticate(@onNull CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback)1036 public void authenticate(@NonNull CryptoObject crypto, 1037 @NonNull CancellationSignal cancel, 1038 @NonNull @CallbackExecutor Executor executor, 1039 @NonNull AuthenticationCallback callback) { 1040 1041 FrameworkStatsLog.write(FrameworkStatsLog.AUTH_PROMPT_AUTHENTICATE_INVOKED, 1042 true /* isCrypto */, 1043 mPromptInfo.isConfirmationRequested(), 1044 mPromptInfo.isDeviceCredentialAllowed(), 1045 mPromptInfo.getAuthenticators() != Authenticators.EMPTY_SET, 1046 mPromptInfo.getAuthenticators()); 1047 1048 if (crypto == null) { 1049 throw new IllegalArgumentException("Must supply a crypto object"); 1050 } 1051 if (cancel == null) { 1052 throw new IllegalArgumentException("Must supply a cancellation signal"); 1053 } 1054 if (executor == null) { 1055 throw new IllegalArgumentException("Must supply an executor"); 1056 } 1057 if (callback == null) { 1058 throw new IllegalArgumentException("Must supply a callback"); 1059 } 1060 1061 // Disallow explicitly setting any non-Strong biometric authenticator types. 1062 @Authenticators.Types int authenticators = mPromptInfo.getAuthenticators(); 1063 if (authenticators == Authenticators.EMPTY_SET) { 1064 authenticators = Authenticators.BIOMETRIC_STRONG; 1065 } 1066 final int biometricStrength = authenticators & Authenticators.BIOMETRIC_WEAK; 1067 if ((biometricStrength & ~Authenticators.BIOMETRIC_STRONG) != 0) { 1068 throw new IllegalArgumentException("Only Strong biometrics supported with crypto"); 1069 } 1070 1071 authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId()); 1072 } 1073 1074 /** 1075 * This call warms up the biometric hardware, displays a system-provided dialog, and starts 1076 * scanning for a biometric. It terminates when {@link 1077 * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link 1078 * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)} is called, or when 1079 * the user dismisses the system-provided dialog. This operation can be canceled by using the 1080 * provided cancel object. The application will receive authentication errors through {@link 1081 * AuthenticationCallback}, and button events through the corresponding callback set in {@link 1082 * Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. It is 1083 * safe to reuse the {@link BiometricPrompt} object, and calling {@link 1084 * BiometricPrompt#authenticate(CancellationSignal, Executor, AuthenticationCallback)} while 1085 * an existing authentication attempt is occurring will stop the previous client and start a new 1086 * authentication. The interrupted client will receive a cancelled notification through {@link 1087 * AuthenticationCallback#onAuthenticationError(int, CharSequence)}. 1088 * 1089 * <p>Note: Applications generally should not cancel and start authentication in quick 1090 * succession. For example, to properly handle authentication across configuration changes, it's 1091 * recommended to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, 1092 * the application will not need to cancel/restart authentication during the configuration 1093 * change. 1094 * 1095 * @throws IllegalArgumentException If any of the arguments are null. 1096 * 1097 * @param cancel An object that can be used to cancel authentication. 1098 * @param executor An executor to handle callback events. 1099 * @param callback An object to receive authentication events. 1100 */ 1101 @RequiresPermission(USE_BIOMETRIC) authenticate(@onNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback)1102 public void authenticate(@NonNull CancellationSignal cancel, 1103 @NonNull @CallbackExecutor Executor executor, 1104 @NonNull AuthenticationCallback callback) { 1105 1106 FrameworkStatsLog.write(FrameworkStatsLog.AUTH_PROMPT_AUTHENTICATE_INVOKED, 1107 false /* isCrypto */, 1108 mPromptInfo.isConfirmationRequested(), 1109 mPromptInfo.isDeviceCredentialAllowed(), 1110 mPromptInfo.getAuthenticators() != Authenticators.EMPTY_SET, 1111 mPromptInfo.getAuthenticators()); 1112 1113 if (cancel == null) { 1114 throw new IllegalArgumentException("Must supply a cancellation signal"); 1115 } 1116 if (executor == null) { 1117 throw new IllegalArgumentException("Must supply an executor"); 1118 } 1119 if (callback == null) { 1120 throw new IllegalArgumentException("Must supply a callback"); 1121 } 1122 authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId()); 1123 } 1124 cancelAuthentication(long requestId)1125 private void cancelAuthentication(long requestId) { 1126 if (mService != null) { 1127 try { 1128 mService.cancelAuthentication(mToken, mContext.getPackageName(), requestId); 1129 } catch (RemoteException e) { 1130 Log.e(TAG, "Unable to cancel authentication", e); 1131 } 1132 } 1133 } 1134 authenticateInternal( @ullable CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId)1135 private void authenticateInternal( 1136 @Nullable CryptoObject crypto, 1137 @NonNull CancellationSignal cancel, 1138 @NonNull @CallbackExecutor Executor executor, 1139 @NonNull AuthenticationCallback callback, 1140 int userId) { 1141 1142 mCryptoObject = crypto; 1143 final long operationId = crypto != null ? crypto.getOpId() : 0L; 1144 authenticateInternal(operationId, cancel, executor, callback, userId); 1145 } 1146 authenticateInternal( long operationId, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId)1147 private long authenticateInternal( 1148 long operationId, 1149 @NonNull CancellationSignal cancel, 1150 @NonNull @CallbackExecutor Executor executor, 1151 @NonNull AuthenticationCallback callback, 1152 int userId) { 1153 // Ensure we don't return the wrong crypto object as an auth result. 1154 if (mCryptoObject != null && mCryptoObject.getOpId() != operationId) { 1155 Log.w(TAG, "CryptoObject operation ID does not match argument; setting field to null"); 1156 mCryptoObject = null; 1157 } 1158 1159 try { 1160 if (cancel.isCanceled()) { 1161 Log.w(TAG, "Authentication already canceled"); 1162 return -1; 1163 } 1164 1165 mExecutor = executor; 1166 mAuthenticationCallback = callback; 1167 if (mIsPromptShowing) { 1168 final String stringToSend = mContext.getString(R.string.biometric_error_canceled); 1169 mExecutor.execute(() -> { 1170 mAuthenticationCallback.onAuthenticationError(BIOMETRIC_ERROR_CANCELED, 1171 stringToSend); 1172 }); 1173 return -1; 1174 } 1175 1176 final PromptInfo promptInfo; 1177 if (operationId != 0L) { 1178 // Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth. 1179 // Note that we use a new PromptInfo here so as to not overwrite the application's 1180 // preference, since it is possible that the same prompt configuration be used 1181 // without a crypto object later. 1182 Parcel parcel = Parcel.obtain(); 1183 mPromptInfo.writeToParcel(parcel, 0 /* flags */); 1184 parcel.setDataPosition(0); 1185 promptInfo = new PromptInfo(parcel); 1186 if (promptInfo.getAuthenticators() == Authenticators.EMPTY_SET) { 1187 promptInfo.setAuthenticators(Authenticators.BIOMETRIC_STRONG); 1188 } 1189 } else { 1190 promptInfo = mPromptInfo; 1191 } 1192 1193 final long authId = mService.authenticate(mToken, operationId, userId, 1194 mBiometricServiceReceiver, mContext.getPackageName(), promptInfo); 1195 cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); 1196 mIsPromptShowing = true; 1197 1198 return authId; 1199 } catch (RemoteException e) { 1200 Log.e(TAG, "Remote exception while authenticating", e); 1201 mExecutor.execute(() -> callback.onAuthenticationError( 1202 BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, 1203 mContext.getString(R.string.biometric_error_hw_unavailable))); 1204 return -1; 1205 } 1206 } 1207 isCredentialAllowed(@uthenticators.Types int allowedAuthenticators)1208 private static boolean isCredentialAllowed(@Authenticators.Types int allowedAuthenticators) { 1209 return (allowedAuthenticators & Authenticators.DEVICE_CREDENTIAL) != 0; 1210 } 1211 } 1212