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.SET_BIOMETRIC_DIALOG_ADVANCED; 20 import static android.Manifest.permission.TEST_BIOMETRIC; 21 import static android.Manifest.permission.USE_BIOMETRIC; 22 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; 23 import static android.hardware.biometrics.BiometricManager.Authenticators; 24 import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; 25 import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT; 26 import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; 27 28 import android.annotation.CallbackExecutor; 29 import android.annotation.DrawableRes; 30 import android.annotation.FlaggedApi; 31 import android.annotation.IntDef; 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.annotation.RequiresPermission; 35 import android.annotation.TestApi; 36 import android.content.ComponentName; 37 import android.content.Context; 38 import android.content.DialogInterface; 39 import android.graphics.Bitmap; 40 import android.graphics.Canvas; 41 import android.graphics.drawable.BitmapDrawable; 42 import android.graphics.drawable.Drawable; 43 import android.hardware.face.FaceManager; 44 import android.hardware.fingerprint.FingerprintManager; 45 import android.os.Binder; 46 import android.os.CancellationSignal; 47 import android.os.IBinder; 48 import android.os.Parcel; 49 import android.os.RemoteException; 50 import android.os.ServiceManager; 51 import android.security.identity.IdentityCredential; 52 import android.security.identity.PresentationSession; 53 import android.security.keystore.KeyProperties; 54 import android.text.TextUtils; 55 import android.util.Log; 56 57 import com.android.internal.R; 58 import com.android.internal.annotations.VisibleForTesting; 59 import com.android.internal.util.FrameworkStatsLog; 60 61 import java.lang.annotation.Retention; 62 import java.lang.annotation.RetentionPolicy; 63 import java.security.Signature; 64 import java.util.List; 65 import java.util.concurrent.Executor; 66 67 import javax.crypto.Cipher; 68 import javax.crypto.KeyAgreement; 69 import javax.crypto.Mac; 70 71 /** 72 * A class that manages a system-provided biometric dialog. 73 */ 74 public class BiometricPrompt implements BiometricAuthenticator, BiometricConstants { 75 76 private static final String TAG = "BiometricPrompt"; 77 @VisibleForTesting 78 static final int MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER = 30; 79 80 /** 81 * Error/help message will show for this amount of time. 82 * For error messages, the dialog will also be dismissed after this amount of time. 83 * Error messages will be propagated back to the application via AuthenticationCallback 84 * after this amount of time. 85 * @hide 86 */ 87 public static final int HIDE_DIALOG_DELAY = 2000; // ms 88 89 /** 90 * @hide 91 */ 92 public static final int DISMISSED_REASON_BIOMETRIC_CONFIRMED = 1; 93 94 /** 95 * Dialog is done animating away after user clicked on the button set via 96 * {@link BiometricPrompt.Builder#setNegativeButton(CharSequence, Executor, 97 * DialogInterface.OnClickListener)}. 98 * @hide 99 */ 100 public static final int DISMISSED_REASON_NEGATIVE = 2; 101 102 /** 103 * @hide 104 */ 105 public static final int DISMISSED_REASON_USER_CANCEL = 3; 106 107 /** 108 * Authenticated, confirmation not required. Dialog animated away. 109 * @hide 110 */ 111 public static final int DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED = 4; 112 113 /** 114 * Error message shown on SystemUI. When BiometricService receives this, the UI is already 115 * gone. 116 * @hide 117 */ 118 public static final int DISMISSED_REASON_ERROR = 5; 119 120 /** 121 * Dialog dismissal requested by BiometricService. 122 * @hide 123 */ 124 public static final int DISMISSED_REASON_SERVER_REQUESTED = 6; 125 126 /** 127 * @hide 128 */ 129 public static final int DISMISSED_REASON_CREDENTIAL_CONFIRMED = 7; 130 131 /** 132 * Dialog is done animating away after user clicked on the button set via 133 * {@link PromptContentViewWithMoreOptionsButton.Builder#setMoreOptionsButtonListener(Executor, 134 * DialogInterface.OnClickListener)} )}. 135 * 136 * @hide 137 */ 138 public static final int DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS = 8; 139 140 /** 141 * Dialog dismissal due to the system being unable to retrieve a WindowManager instance required 142 * to show the dialog. 143 * @hide 144 */ 145 public static final int DISMISSED_REASON_ERROR_NO_WM = 9; 146 147 /** 148 * @hide 149 */ 150 @IntDef({DISMISSED_REASON_BIOMETRIC_CONFIRMED, 151 DISMISSED_REASON_NEGATIVE, 152 DISMISSED_REASON_USER_CANCEL, 153 DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, 154 DISMISSED_REASON_ERROR, 155 DISMISSED_REASON_SERVER_REQUESTED, 156 DISMISSED_REASON_CREDENTIAL_CONFIRMED, 157 DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS, 158 DISMISSED_REASON_ERROR_NO_WM}) 159 @Retention(RetentionPolicy.SOURCE) 160 public @interface DismissedReason {} 161 162 static class ButtonInfo { 163 Executor executor; 164 DialogInterface.OnClickListener listener; ButtonInfo(Executor ex, DialogInterface.OnClickListener l)165 ButtonInfo(Executor ex, DialogInterface.OnClickListener l) { 166 executor = ex; 167 listener = l; 168 } 169 } 170 171 /** 172 * A builder that collects arguments to be shown on the system-provided biometric dialog. 173 */ 174 public static class Builder { 175 private PromptInfo mPromptInfo; 176 private ButtonInfo mNegativeButtonInfo; 177 private ButtonInfo mContentViewMoreOptionsButtonInfo; 178 private Context mContext; 179 private IAuthService mService; 180 181 // LINT.IfChange 182 /** 183 * Creates a builder for a {@link BiometricPrompt} dialog. 184 * @param context The {@link Context} that will be used to build the prompt. 185 */ Builder(Context context)186 public Builder(Context context) { 187 mPromptInfo = new PromptInfo(); 188 mContext = context; 189 } 190 191 /** 192 * Optional: Sets the drawable resource of the logo that will be shown on the prompt. 193 * 194 * <p> Note that using this method is not recommended in most scenarios because the calling 195 * application's icon will be used by default. Setting the logo is intended for large 196 * bundled applications that perform a wide range of functions and need to show distinct 197 * icons for each function. 198 * 199 * @param logoRes A drawable resource of the logo that will be shown on the prompt. 200 * @return This builder. 201 */ 202 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) 203 @NonNull setLogoRes(@rawableRes int logoRes)204 public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) { 205 if (mPromptInfo.getLogoBitmap() != null) { 206 throw new IllegalStateException( 207 "Exclusively one of logo resource or logo bitmap can be set"); 208 } 209 if (logoRes != 0) { 210 mPromptInfo.setLogo(logoRes, 211 convertDrawableToBitmap(mContext.getDrawable(logoRes))); 212 } 213 return this; 214 } 215 216 /** 217 * Optional: Sets the bitmap drawable of the logo that will be shown on the prompt. 218 * 219 * <p> Note that using this method is not recommended in most scenarios because the calling 220 * application's icon will be used by default. Setting the logo is intended for large 221 * bundled applications that perform a wide range of functions and need to show distinct 222 * icons for each function. 223 * 224 * @param logoBitmap A bitmap drawable of the logo that will be shown on the prompt. 225 * @return This builder. 226 */ 227 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) 228 @NonNull setLogoBitmap(@onNull Bitmap logoBitmap)229 public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) { 230 if (mPromptInfo.getLogoRes() != 0) { 231 throw new IllegalStateException( 232 "Exclusively one of logo resource or logo bitmap can be set"); 233 } 234 mPromptInfo.setLogo(0, logoBitmap); 235 return this; 236 } 237 238 /** 239 * Optional: Sets logo description text that will be shown on the prompt. 240 * 241 * <p> Note that using this method is not recommended in most scenarios because the calling 242 * application's name will be used by default. Setting the logo description is intended for 243 * large bundled applications that perform a wide range of functions and need to show 244 * distinct description for each function. 245 * 246 * @param logoDescription The logo description text that will be shown on the prompt. 247 * @return This builder. 248 * @throws IllegalArgumentException If logo description is null. 249 */ 250 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) 251 @NonNull setLogoDescription(@onNull String logoDescription)252 public BiometricPrompt.Builder setLogoDescription(@NonNull String logoDescription) { 253 if (logoDescription == null || logoDescription.isEmpty()) { 254 throw new IllegalArgumentException("Logo description passed in can not be null"); 255 } 256 if (logoDescription.length() > MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER) { 257 Log.w(TAG, 258 "Logo description passed in exceeds" + MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER 259 + " character number and may be truncated."); 260 } 261 mPromptInfo.setLogoDescription(logoDescription); 262 return this; 263 } 264 265 /** 266 * Required: Sets the title that will be shown on the prompt. 267 * @param title The title to display. 268 * @return This builder. 269 */ 270 @NonNull setTitle(@onNull CharSequence title)271 public Builder setTitle(@NonNull CharSequence title) { 272 mPromptInfo.setTitle(title); 273 return this; 274 } 275 276 /** 277 * Shows a default, modality-specific title for the prompt if the title would otherwise be 278 * null or empty. Currently for internal use only. 279 * @return This builder. 280 * @hide 281 */ 282 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 283 @NonNull setUseDefaultTitle()284 public Builder setUseDefaultTitle() { 285 mPromptInfo.setUseDefaultTitle(true); 286 return this; 287 } 288 289 /** 290 * Optional: Sets a subtitle that will be shown on the prompt. 291 * @param subtitle The subtitle to display. 292 * @return This builder. 293 */ 294 @NonNull setSubtitle(@onNull CharSequence subtitle)295 public Builder setSubtitle(@NonNull CharSequence subtitle) { 296 mPromptInfo.setSubtitle(subtitle); 297 return this; 298 } 299 300 /** 301 * Shows a default subtitle for the prompt if the subtitle would otherwise be 302 * null or empty. Currently for internal use only. 303 * @return This builder. 304 * @hide 305 */ 306 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 307 @NonNull setUseDefaultSubtitle()308 public Builder setUseDefaultSubtitle() { 309 mPromptInfo.setUseDefaultSubtitle(true); 310 return this; 311 } 312 313 /** 314 * Optional: Sets a description that will be shown on the prompt. 315 * 316 * <p> Note that the description set by {@link Builder#setDescription(CharSequence)} will be 317 * overridden by {@link Builder#setContentView(PromptContentView)}. The view provided to 318 * {@link Builder#setContentView(PromptContentView)} will be used if both methods are 319 * called. 320 * 321 * @param description The description to display. 322 * @return This builder. 323 */ 324 @NonNull setDescription(@onNull CharSequence description)325 public Builder setDescription(@NonNull CharSequence description) { 326 mPromptInfo.setDescription(description); 327 return this; 328 } 329 330 /** 331 * Optional: Sets application customized content view that will be shown on the prompt. 332 * 333 * <p> Note that the description set by {@link Builder#setDescription(CharSequence)} will be 334 * overridden by {@link Builder#setContentView(PromptContentView)}. The view provided to 335 * {@link Builder#setContentView(PromptContentView)} will be used if both methods are 336 * called. 337 * 338 * @param view The customized view information. 339 * @return This builder. 340 */ 341 @NonNull setContentView(@onNull PromptContentView view)342 public BiometricPrompt.Builder setContentView(@NonNull PromptContentView view) { 343 mPromptInfo.setContentView(view); 344 345 if (mPromptInfo.isContentViewMoreOptionsButtonUsed()) { 346 mContentViewMoreOptionsButtonInfo = 347 ((PromptContentViewWithMoreOptionsButton) view).getButtonInfo(); 348 } 349 350 return this; 351 } 352 353 /** 354 * @param service 355 * @return This builder. 356 * @hide 357 */ 358 @RequiresPermission(TEST_BIOMETRIC) 359 @NonNull setService(@onNull IAuthService service)360 public Builder setService(@NonNull IAuthService service) { 361 mService = service; 362 return this; 363 } 364 365 /** 366 * Sets an optional title, subtitle, and/or description that will override other text when 367 * the user is authenticating with PIN/pattern/password. Currently for internal use only. 368 * @return This builder. 369 * @hide 370 */ 371 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 372 @NonNull setTextForDeviceCredential( @ullable CharSequence title, @Nullable CharSequence subtitle, @Nullable CharSequence description)373 public Builder setTextForDeviceCredential( 374 @Nullable CharSequence title, 375 @Nullable CharSequence subtitle, 376 @Nullable CharSequence description) { 377 if (title != null) { 378 mPromptInfo.setDeviceCredentialTitle(title); 379 } 380 if (subtitle != null) { 381 mPromptInfo.setDeviceCredentialSubtitle(subtitle); 382 } 383 if (description != null) { 384 mPromptInfo.setDeviceCredentialDescription(description); 385 } 386 return this; 387 } 388 389 /** 390 * Required: Sets the text, executor, and click listener for the negative button on the 391 * prompt. This is typically a cancel button, but may be also used to show an alternative 392 * method for authentication, such as a screen that asks for a backup password. 393 * 394 * <p>Note that this setting is not required, and in fact is explicitly disallowed, if 395 * device credential authentication is enabled via {@link #setAllowedAuthenticators(int)} or 396 * {@link #setDeviceCredentialAllowed(boolean)}. 397 * 398 * @param text Text to be shown on the negative button for the prompt. 399 * @param executor Executor that will be used to run the on click callback. 400 * @param listener Listener containing a callback to be run when the button is pressed. 401 * @return This builder. 402 */ 403 @NonNull setNegativeButton(@onNull CharSequence text, @NonNull @CallbackExecutor Executor executor, @NonNull DialogInterface.OnClickListener listener)404 public Builder setNegativeButton(@NonNull CharSequence text, 405 @NonNull @CallbackExecutor Executor executor, 406 @NonNull DialogInterface.OnClickListener listener) { 407 if (TextUtils.isEmpty(text)) { 408 throw new IllegalArgumentException("Text must be set and non-empty"); 409 } 410 if (executor == null) { 411 throw new IllegalArgumentException("Executor must not be null"); 412 } 413 if (listener == null) { 414 throw new IllegalArgumentException("Listener must not be null"); 415 } 416 mPromptInfo.setNegativeButtonText(text); 417 mNegativeButtonInfo = new ButtonInfo(executor, listener); 418 return this; 419 } 420 421 /** 422 * Optional: Sets a hint to the system for whether to require user confirmation after 423 * authentication. For example, implicit modalities like face and iris are passive, meaning 424 * they don't require an explicit user action to complete authentication. If set to true, 425 * these modalities should require the user to take some action (e.g. press a button) 426 * before {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is 427 * called. Defaults to true. 428 * 429 * <p>A typical use case for not requiring confirmation would be for low-risk transactions, 430 * such as re-authenticating a recently authenticated application. A typical use case for 431 * requiring confirmation would be for authorizing a purchase. 432 * 433 * <p>Note that this just passes a hint to the system, which the system may then ignore. For 434 * example, a value of false may be ignored if the user has disabled implicit authentication 435 * in Settings, or if it does not apply to a particular modality (e.g. fingerprint). 436 * 437 * @param requireConfirmation true if explicit user confirmation should be required, or 438 * false otherwise. 439 * @return This builder. 440 */ 441 @NonNull setConfirmationRequired(boolean requireConfirmation)442 public Builder setConfirmationRequired(boolean requireConfirmation) { 443 mPromptInfo.setConfirmationRequested(requireConfirmation); 444 return this; 445 } 446 447 /** 448 * Optional: If enabled, the user will be given the option to authenticate with their device 449 * PIN, pattern, or password. Developers should first check {@link 450 * BiometricManager#canAuthenticate(int)} for {@link Authenticators#DEVICE_CREDENTIAL} 451 * before enabling. If the device is not secured with a credential, 452 * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} will be invoked 453 * with {@link BiometricPrompt#BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL}. Defaults to false. 454 * 455 * <p>Note that enabling this option replaces the negative button on the prompt with one 456 * that allows the user to authenticate with their device credential, making it an error to 457 * call {@link #setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. 458 * 459 * @param allowed true if the prompt should fall back to asking for the user's device 460 * credential (PIN/pattern/password), or false otherwise. 461 * @return This builder. 462 * 463 * @deprecated Replaced by {@link #setAllowedAuthenticators(int)}. 464 */ 465 @Deprecated 466 @NonNull setDeviceCredentialAllowed(boolean allowed)467 public Builder setDeviceCredentialAllowed(boolean allowed) { 468 mPromptInfo.setDeviceCredentialAllowed(allowed); 469 return this; 470 } 471 472 /** 473 * Optional: Specifies the type(s) of authenticators that may be invoked by 474 * {@link BiometricPrompt} to authenticate the user. Available authenticator types are 475 * defined in {@link Authenticators} and can be combined via bitwise OR. Defaults to: 476 * <ul> 477 * <li>{@link Authenticators#BIOMETRIC_WEAK} for non-crypto authentication, or</li> 478 * <li>{@link Authenticators#BIOMETRIC_STRONG} for crypto-based authentication.</li> 479 * </ul> 480 * 481 * <p>If this method is used and no authenticator of any of the specified types is available 482 * at the time <code>BiometricPrompt#authenticate(...)</code> is called, authentication will 483 * be canceled and {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} 484 * will be invoked with an appropriate error code. 485 * 486 * <p>This method should be preferred over {@link #setDeviceCredentialAllowed(boolean)} and 487 * overrides the latter if both are used. Using this method to enable device credential 488 * authentication (with {@link Authenticators#DEVICE_CREDENTIAL}) will replace the negative 489 * button on the prompt, making it an error to also call 490 * {@link #setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. 491 * 492 * <p>If unlocking cryptographic operation(s), it is the application's responsibility to 493 * request authentication with the proper set of authenticators (e.g. match the 494 * authenticators specified during key generation). 495 * 496 * @see android.security.keystore.KeyGenParameterSpec.Builder 497 * @see KeyProperties#AUTH_BIOMETRIC_STRONG 498 * @see KeyProperties#AUTH_DEVICE_CREDENTIAL 499 * 500 * @param authenticators A bit field representing all valid authenticator types that may be 501 * invoked by the prompt. 502 * @return This builder. 503 */ 504 @NonNull setAllowedAuthenticators(@uthenticators.Types int authenticators)505 public Builder setAllowedAuthenticators(@Authenticators.Types int authenticators) { 506 mPromptInfo.setAuthenticators(authenticators); 507 return this; 508 } 509 510 /** 511 * If non-empty, requests authentication to be performed only if the sensor is contained 512 * within the list. Note that the actual sensor presented to the user/test will meet all 513 * constraints specified within this builder. For example, on a device with the below 514 * configuration: 515 * 516 * SensorId: 1, Strength: BIOMETRIC_STRONG 517 * SensorId: 2, Strength: BIOMETRIC_WEAK 518 * 519 * If authentication is invoked with setAllowedAuthenticators(BIOMETRIC_STRONG) and 520 * setAllowedSensorIds(2), then no sensor will be eligible for authentication. 521 * 522 * @see {@link BiometricManager#getSensorProperties()} 523 * 524 * @param sensorIds Sensor IDs to constrain this authentication to. 525 * @return This builder 526 * @hide 527 */ 528 @TestApi 529 @NonNull 530 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setAllowedSensorIds(@onNull List<Integer> sensorIds)531 public Builder setAllowedSensorIds(@NonNull List<Integer> sensorIds) { 532 mPromptInfo.setAllowedSensorIds(sensorIds); 533 return this; 534 } 535 536 /** 537 * @param allow If true, allows authentication when the calling package is not in the 538 * foreground. This is set to false by default. 539 * @return This builder 540 * @hide 541 */ 542 @TestApi 543 @NonNull 544 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setAllowBackgroundAuthentication(boolean allow)545 public Builder setAllowBackgroundAuthentication(boolean allow) { 546 mPromptInfo.setAllowBackgroundAuthentication(allow); 547 return this; 548 } 549 550 /** 551 * Remove {@link Builder#setAllowBackgroundAuthentication(boolean)} once 552 * FLAG_ALLOW_PRIVATE_PROFILE is enabled. 553 * 554 * @param allow If true, allows authentication when the calling package is not in the 555 * foreground. This is set to false by default. 556 * @param useParentProfileForDeviceCredential If true, uses parent profile for device 557 * credential IME request 558 * @return This builder 559 * @hide 560 */ 561 @FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE) 562 @TestApi 563 @NonNull 564 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setAllowBackgroundAuthentication(boolean allow, boolean useParentProfileForDeviceCredential)565 public Builder setAllowBackgroundAuthentication(boolean allow, 566 boolean useParentProfileForDeviceCredential) { 567 mPromptInfo.setAllowBackgroundAuthentication(allow); 568 mPromptInfo.setUseParentProfileForDeviceCredential(useParentProfileForDeviceCredential); 569 return this; 570 } 571 572 /** 573 * If set check the Device Policy Manager for disabled biometrics. 574 * 575 * @param checkDevicePolicyManager 576 * @return This builder. 577 * @hide 578 */ 579 @NonNull 580 @RequiresPermission(anyOf = {USE_BIOMETRIC_INTERNAL}) setDisallowBiometricsIfPolicyExists(boolean checkDevicePolicyManager)581 public Builder setDisallowBiometricsIfPolicyExists(boolean checkDevicePolicyManager) { 582 mPromptInfo.setDisallowBiometricsIfPolicyExists(checkDevicePolicyManager); 583 return this; 584 } 585 586 /** 587 * If set, receive internal events via {@link AuthenticationCallback#onSystemEvent(int)} 588 * @param set 589 * @return This builder. 590 * @hide 591 */ 592 @NonNull 593 @RequiresPermission(anyOf = {USE_BIOMETRIC_INTERNAL}) setReceiveSystemEvents(boolean set)594 public Builder setReceiveSystemEvents(boolean set) { 595 mPromptInfo.setReceiveSystemEvents(set); 596 return this; 597 } 598 599 /** 600 * Flag to decide if authentication should ignore enrollment state. 601 * Defaults to false (not ignoring enrollment state) 602 * @param ignoreEnrollmentState 603 * @return This builder. 604 * @hide 605 */ 606 @NonNull 607 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setIgnoreEnrollmentState(boolean ignoreEnrollmentState)608 public Builder setIgnoreEnrollmentState(boolean ignoreEnrollmentState) { 609 mPromptInfo.setIgnoreEnrollmentState(ignoreEnrollmentState); 610 return this; 611 } 612 613 /** 614 * Set if BiometricPrompt is being used by the legacy fingerprint manager API. 615 * @param sensorId sensor id 616 * @return This builder. 617 * @hide 618 */ 619 @NonNull 620 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setIsForLegacyFingerprintManager(int sensorId)621 public Builder setIsForLegacyFingerprintManager(int sensorId) { 622 mPromptInfo.setIsForLegacyFingerprintManager(sensorId); 623 return this; 624 } 625 626 /** 627 * Set if emergency call button should show, for example if biometrics are 628 * required to access the dialer app 629 * @param showEmergencyCallButton if true, shows emergency call button 630 * @return This builder. 631 * @hide 632 */ 633 @NonNull 634 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setShowEmergencyCallButton(boolean showEmergencyCallButton)635 public Builder setShowEmergencyCallButton(boolean showEmergencyCallButton) { 636 mPromptInfo.setShowEmergencyCallButton(showEmergencyCallButton); 637 return this; 638 } 639 640 /** 641 * Set caller's component name for getting logo icon/description. This should only be used 642 * by ConfirmDeviceCredentialActivity, see b/337082634 for more context. 643 * 644 * @param realCaller set the component name of real caller for 645 * ConfirmDeviceCredentialActivity. 646 * @return This builder. 647 * @hide 648 */ 649 @NonNull 650 @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}) setRealCallerForConfirmDeviceCredentialActivity(ComponentName realCaller)651 public Builder setRealCallerForConfirmDeviceCredentialActivity(ComponentName realCaller) { 652 mPromptInfo.setRealCallerForConfirmDeviceCredentialActivity(realCaller); 653 mPromptInfo.setClassNameIfItIsConfirmDeviceCredentialActivity( 654 mContext.getClass().getName()); 655 return this; 656 } 657 658 // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java) 659 660 /** 661 * Creates a {@link BiometricPrompt}. 662 * 663 * @return An instance of {@link BiometricPrompt}. 664 * 665 * @throws IllegalArgumentException If any required fields are unset, or if given any 666 * invalid combination of field values. 667 */ 668 @NonNull build()669 public BiometricPrompt build() { 670 final CharSequence title = mPromptInfo.getTitle(); 671 final CharSequence negative = mPromptInfo.getNegativeButtonText(); 672 final boolean useDefaultTitle = mPromptInfo.isUseDefaultTitle(); 673 final boolean deviceCredentialAllowed = mPromptInfo.isDeviceCredentialAllowed(); 674 final @Authenticators.Types int authenticators = mPromptInfo.getAuthenticators(); 675 final boolean willShowDeviceCredentialButton = deviceCredentialAllowed 676 || isCredentialAllowed(authenticators); 677 678 if (TextUtils.isEmpty(title) && !useDefaultTitle) { 679 throw new IllegalArgumentException("Title must be set and non-empty"); 680 } else if (TextUtils.isEmpty(negative) && !willShowDeviceCredentialButton) { 681 throw new IllegalArgumentException("Negative text must be set and non-empty"); 682 } else if (!TextUtils.isEmpty(negative) && willShowDeviceCredentialButton) { 683 throw new IllegalArgumentException("Can't have both negative button behavior" 684 + " and device credential enabled"); 685 } 686 mService = (mService == null) ? IAuthService.Stub.asInterface( 687 ServiceManager.getService(Context.AUTH_SERVICE)) : mService; 688 return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo, 689 mContentViewMoreOptionsButtonInfo, mService); 690 } 691 } 692 693 private class OnAuthenticationCancelListener implements CancellationSignal.OnCancelListener { 694 private final long mAuthRequestId; 695 OnAuthenticationCancelListener(long id)696 OnAuthenticationCancelListener(long id) { 697 mAuthRequestId = id; 698 } 699 700 @Override onCancel()701 public void onCancel() { 702 if (!mIsPromptShowing) { 703 Log.w(TAG, "BP is not showing"); 704 return; 705 } 706 Log.d(TAG, "Cancel BP authentication requested for: " + mAuthRequestId); 707 cancelAuthentication(mAuthRequestId); 708 } 709 } 710 711 private final IBinder mToken = new Binder(); 712 private final Context mContext; 713 private final IAuthService mService; 714 private final PromptInfo mPromptInfo; 715 private final ButtonInfo mNegativeButtonInfo; 716 private final ButtonInfo mContentViewMoreOptionsButtonInfo; 717 718 private CryptoObject mCryptoObject; 719 private Executor mExecutor; 720 private AuthenticationCallback mAuthenticationCallback; 721 722 private final IBiometricServiceReceiver mBiometricServiceReceiver = 723 new IBiometricServiceReceiver.Stub() { 724 725 @Override 726 public void onAuthenticationSucceeded(@AuthenticationResultType int authenticationType) { 727 mExecutor.execute(() -> { 728 final AuthenticationResult result = 729 new AuthenticationResult(mCryptoObject, authenticationType); 730 mAuthenticationCallback.onAuthenticationSucceeded(result); 731 mIsPromptShowing = false; 732 }); 733 } 734 735 @Override 736 public void onAuthenticationFailed() { 737 mExecutor.execute(() -> { 738 mAuthenticationCallback.onAuthenticationFailed(); 739 }); 740 } 741 742 @Override 743 public void onError(@BiometricAuthenticator.Modality int modality, int error, 744 int vendorCode) { 745 746 String errorMessage = null; 747 switch (modality) { 748 case TYPE_FACE: 749 errorMessage = FaceManager.getErrorString(mContext, error, vendorCode); 750 break; 751 752 case TYPE_FINGERPRINT: 753 errorMessage = FingerprintManager.getErrorString(mContext, error, vendorCode); 754 break; 755 } 756 757 // Look for generic errors, as it may be a combination of modalities, or no modality 758 // (e.g. attempted biometric authentication without biometric sensors). 759 if (errorMessage == null) { 760 switch (error) { 761 case BIOMETRIC_ERROR_CANCELED: 762 errorMessage = mContext.getString(R.string.biometric_error_canceled); 763 break; 764 case BIOMETRIC_ERROR_USER_CANCELED: 765 errorMessage = mContext.getString(R.string.biometric_error_user_canceled); 766 break; 767 case BIOMETRIC_ERROR_HW_NOT_PRESENT: 768 errorMessage = mContext.getString(R.string.biometric_error_hw_unavailable); 769 break; 770 case BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL: 771 errorMessage = mContext.getString( 772 R.string.biometric_error_device_not_secured); 773 break; 774 default: 775 Log.e(TAG, "Unknown error, modality: " + modality 776 + " error: " + error 777 + " vendorCode: " + vendorCode); 778 errorMessage = mContext.getString(R.string.biometric_error_generic); 779 break; 780 } 781 } 782 783 final String stringToSend = errorMessage; 784 mExecutor.execute(() -> { 785 mAuthenticationCallback.onAuthenticationError(error, stringToSend); 786 mIsPromptShowing = false; 787 }); 788 } 789 790 @Override 791 public void onAcquired(int acquireInfo, String message) { 792 mExecutor.execute(() -> { 793 mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message); 794 }); 795 } 796 797 @Override 798 public void onDialogDismissed(int reason) { 799 // Check the reason and invoke OnClickListener(s) if necessary 800 if (reason == DISMISSED_REASON_NEGATIVE) { 801 if (mNegativeButtonInfo != null) { 802 mNegativeButtonInfo.executor.execute(() -> { 803 mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE); 804 mIsPromptShowing = false; 805 }); 806 } else { 807 mAuthenticationCallback.onAuthenticationError(BIOMETRIC_ERROR_USER_CANCELED, 808 null /* errString */); 809 } 810 } else if (reason == DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS) { 811 if (mContentViewMoreOptionsButtonInfo != null) { 812 mContentViewMoreOptionsButtonInfo.executor.execute(() -> { 813 mContentViewMoreOptionsButtonInfo.listener.onClick(null, 814 DialogInterface.BUTTON_NEGATIVE); 815 }); 816 } 817 } else { 818 mIsPromptShowing = false; 819 Log.e(TAG, "Unknown reason: " + reason); 820 } 821 } 822 823 @Override 824 public void onSystemEvent(int event) { 825 mExecutor.execute(() -> { 826 mAuthenticationCallback.onSystemEvent(event); 827 }); 828 } 829 }; 830 831 private boolean mIsPromptShowing; 832 BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo, ButtonInfo contentViewMoreOptionsButtonInfo, IAuthService service)833 private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo, 834 ButtonInfo contentViewMoreOptionsButtonInfo, IAuthService service) { 835 mContext = context; 836 mPromptInfo = promptInfo; 837 mNegativeButtonInfo = negativeButtonInfo; 838 mContentViewMoreOptionsButtonInfo = contentViewMoreOptionsButtonInfo; 839 mService = service; 840 mIsPromptShowing = false; 841 } 842 843 /** 844 * Gets the drawable resource of the logo for the prompt, as set by 845 * {@link Builder#setLogoRes(int)}. Currently for system applications use only. 846 * 847 * @return The drawable resource of the logo, or 0 if the prompt has no logo resource set. 848 */ 849 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) 850 @DrawableRes getLogoRes()851 public int getLogoRes() { 852 return mPromptInfo.getLogoRes(); 853 } 854 855 /** 856 * Gets the logo bitmap for the prompt, as set by {@link Builder#setLogoBitmap(Bitmap)}. 857 * Currently for system applications use only. 858 * 859 * @return The logo bitmap of the prompt, or null if the prompt has no logo bitmap set. 860 */ 861 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) 862 @Nullable getLogoBitmap()863 public Bitmap getLogoBitmap() { 864 return mPromptInfo.getLogoBitmap(); 865 } 866 867 /** 868 * Gets the logo description for the prompt, as set by 869 * {@link Builder#setLogoDescription(String)}. 870 * Currently for system applications use only. 871 * 872 * @return The logo description of the prompt, or null if the prompt has no logo description 873 * set. 874 */ 875 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) 876 @Nullable getLogoDescription()877 public String getLogoDescription() { 878 return mPromptInfo.getLogoDescription(); 879 } 880 881 /** 882 * Gets the title for the prompt, as set by {@link Builder#setTitle(CharSequence)}. 883 * @return The title of the prompt, which is guaranteed to be non-null. 884 */ 885 @NonNull getTitle()886 public CharSequence getTitle() { 887 return mPromptInfo.getTitle(); 888 } 889 890 /** 891 * Whether to use a default modality-specific title. For internal use only. 892 * @return See {@link Builder#setUseDefaultTitle()}. 893 * @hide 894 */ 895 @RequiresPermission(USE_BIOMETRIC_INTERNAL) shouldUseDefaultTitle()896 public boolean shouldUseDefaultTitle() { 897 return mPromptInfo.isUseDefaultTitle(); 898 } 899 900 /** 901 * Gets the subtitle for the prompt, as set by {@link Builder#setSubtitle(CharSequence)}. 902 * @return The subtitle for the prompt, or null if the prompt has no subtitle. 903 */ 904 @Nullable getSubtitle()905 public CharSequence getSubtitle() { 906 return mPromptInfo.getSubtitle(); 907 } 908 909 /** 910 * Whether to use a default subtitle. For internal use only. 911 * @return See {@link Builder#setUseDefaultSubtitle()}. 912 * @hide 913 */ 914 @RequiresPermission(USE_BIOMETRIC_INTERNAL) shouldUseDefaultSubtitle()915 public boolean shouldUseDefaultSubtitle() { 916 return mPromptInfo.isUseDefaultSubtitle(); 917 } 918 919 /** 920 * Gets the description for the prompt, as set by {@link Builder#setDescription(CharSequence)}. 921 * @return The description for the prompt, or null if the prompt has no description. 922 */ 923 @Nullable getDescription()924 public CharSequence getDescription() { 925 return mPromptInfo.getDescription(); 926 } 927 928 /** 929 * Gets the content view for the prompt, as set by 930 * {@link Builder#setContentView(PromptContentView)}. 931 * 932 * @return The content view for the prompt, or null if the prompt has no content view. 933 */ 934 @Nullable getContentView()935 public PromptContentView getContentView() { 936 return mPromptInfo.getContentView(); 937 } 938 939 /** 940 * Gets the negative button text for the prompt, as set by 941 * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. 942 * @return The negative button text for the prompt, or null if no negative button text was set. 943 */ 944 @Nullable getNegativeButtonText()945 public CharSequence getNegativeButtonText() { 946 return mPromptInfo.getNegativeButtonText(); 947 } 948 949 /** 950 * Determines if explicit user confirmation is required by the prompt, as set by 951 * {@link Builder#setConfirmationRequired(boolean)}. 952 * 953 * @return true if explicit user confirmation is required, or false otherwise. 954 */ isConfirmationRequired()955 public boolean isConfirmationRequired() { 956 return mPromptInfo.isConfirmationRequested(); 957 } 958 959 /** 960 * Gets the type(s) of authenticators that may be invoked by the prompt to authenticate the 961 * user, as set by {@link Builder#setAllowedAuthenticators(int)}. 962 * 963 * @return A bit field representing the type(s) of authenticators that may be invoked by the 964 * prompt (as defined by {@link Authenticators}), or 0 if this field was not set. 965 */ 966 @Nullable getAllowedAuthenticators()967 public int getAllowedAuthenticators() { 968 return mPromptInfo.getAuthenticators(); 969 } 970 971 /** 972 * @return The values set by {@link Builder#setAllowedSensorIds(List)} 973 * @hide 974 */ 975 @TestApi 976 @NonNull getAllowedSensorIds()977 public List<Integer> getAllowedSensorIds() { 978 return mPromptInfo.getAllowedSensorIds(); 979 } 980 981 /** 982 * @return The value set by {@link Builder#setAllowBackgroundAuthentication(boolean)} 983 * @hide 984 */ 985 @TestApi isAllowBackgroundAuthentication()986 public boolean isAllowBackgroundAuthentication() { 987 return mPromptInfo.isAllowBackgroundAuthentication(); 988 } 989 990 /** 991 * A wrapper class for the cryptographic operations supported by BiometricPrompt. 992 * 993 * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, 994 * {@link IdentityCredential}, {@link PresentationSession} and {@link KeyAgreement}. 995 * 996 * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and 997 * time-based. This is specified during key creation via the timeout parameter of the 998 * {@code setUserAuthenticationParameters(int, int)} method of {@link 999 * android.security.keystore.KeyGenParameterSpec.Builder}. 1000 * 1001 * <p>CryptoObjects are used to unlock auth-per-use keys via 1002 * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor, 1003 * AuthenticationCallback)}, whereas time-based keys are unlocked for their specified duration 1004 * any time the user authenticates with the specified authenticators (e.g. unlocking keyguard). 1005 * If a time-based key is not available for use (i.e. none of the allowed authenticators have 1006 * been unlocked recently), applications can prompt the user to authenticate via 1007 * {@link BiometricPrompt#authenticate(CancellationSignal, Executor, AuthenticationCallback)} 1008 * 1009 * @see Builder#setAllowedAuthenticators(int) 1010 */ 1011 public static final class CryptoObject extends android.hardware.biometrics.CryptoObject { 1012 /** 1013 * Create from a {@link Signature} object. 1014 * 1015 * @param signature a {@link Signature} object. 1016 */ CryptoObject(@onNull Signature signature)1017 public CryptoObject(@NonNull Signature signature) { 1018 super(signature); 1019 } 1020 1021 /** 1022 * Create from a {@link Cipher} object. 1023 * 1024 * @param cipher a {@link Cipher} object. 1025 */ CryptoObject(@onNull Cipher cipher)1026 public CryptoObject(@NonNull Cipher cipher) { 1027 super(cipher); 1028 } 1029 1030 /** 1031 * Create from a {@link Mac} object. 1032 * 1033 * @param mac a {@link Mac} object. 1034 */ CryptoObject(@onNull Mac mac)1035 public CryptoObject(@NonNull Mac mac) { 1036 super(mac); 1037 } 1038 1039 /** 1040 * Create from a {@link IdentityCredential} object. 1041 * 1042 * @param credential a {@link IdentityCredential} object. 1043 * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}. 1044 */ 1045 @Deprecated CryptoObject(@onNull IdentityCredential credential)1046 public CryptoObject(@NonNull IdentityCredential credential) { 1047 super(credential); 1048 } 1049 1050 /** 1051 * Create from a {@link PresentationSession} object. 1052 * 1053 * @param session a {@link PresentationSession} object. 1054 */ CryptoObject(@onNull PresentationSession session)1055 public CryptoObject(@NonNull PresentationSession session) { 1056 super(session); 1057 } 1058 1059 /** 1060 * Create from a {@link KeyAgreement} object. 1061 * 1062 * @param keyAgreement a {@link KeyAgreement} object. 1063 */ 1064 @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) CryptoObject(@onNull KeyAgreement keyAgreement)1065 public CryptoObject(@NonNull KeyAgreement keyAgreement) { 1066 super(keyAgreement); 1067 } 1068 1069 /** 1070 * Create from an operation handle. 1071 * @see CryptoObject#getOperationHandle() 1072 * 1073 * @param operationHandle the operation handle associated with this object. 1074 */ 1075 @FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT) CryptoObject(long operationHandle)1076 public CryptoObject(long operationHandle) { 1077 super(operationHandle); 1078 } 1079 1080 /** 1081 * Get {@link Signature} object. 1082 * @return {@link Signature} object or null if this doesn't contain one. 1083 */ getSignature()1084 public @Nullable Signature getSignature() { 1085 return super.getSignature(); 1086 } 1087 1088 /** 1089 * Get {@link Cipher} object. 1090 * @return {@link Cipher} object or null if this doesn't contain one. 1091 */ getCipher()1092 public @Nullable Cipher getCipher() { 1093 return super.getCipher(); 1094 } 1095 1096 /** 1097 * Get {@link Mac} object. 1098 * @return {@link Mac} object or null if this doesn't contain one. 1099 */ getMac()1100 public @Nullable Mac getMac() { 1101 return super.getMac(); 1102 } 1103 1104 /** 1105 * Get {@link IdentityCredential} object. 1106 * @return {@link IdentityCredential} object or null if this doesn't contain one. 1107 * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}. 1108 */ 1109 @Deprecated getIdentityCredential()1110 public @Nullable IdentityCredential getIdentityCredential() { 1111 return super.getIdentityCredential(); 1112 } 1113 1114 /** 1115 * Get {@link PresentationSession} object. 1116 * @return {@link PresentationSession} object or null if this doesn't contain one. 1117 */ getPresentationSession()1118 public @Nullable PresentationSession getPresentationSession() { 1119 return super.getPresentationSession(); 1120 } 1121 1122 /** 1123 * Get {@link KeyAgreement} object. 1124 * @return {@link KeyAgreement} object or null if this doesn't contain one. 1125 */ 1126 @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) getKeyAgreement()1127 public @Nullable KeyAgreement getKeyAgreement() { 1128 return super.getKeyAgreement(); 1129 } 1130 1131 /** 1132 * Returns the {@code operationHandle} associated with this object or 0 if none. 1133 * The {@code operationHandle} is the underlying identifier associated with 1134 * the {@code CryptoObject}. 1135 * 1136 * <p> The {@code operationHandle} can be used to reconstruct a {@code CryptoObject} 1137 * instance. This is useful for any cross-process communication as the {@code CryptoObject} 1138 * class is not {@link android.os.Parcelable}. Hence, if the {@code CryptoObject} is 1139 * constructed in one process, and needs to be propagated to another process, 1140 * before calling the 1141 * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor, 1142 * AuthenticationCallback)} API in the second process, the recommendation is to retrieve the 1143 * {@code operationHandle} using this API, and then reconstruct the 1144 * {@code CryptoObject}using the constructor that takes in an {@code operationHandle}, and 1145 * pass that in to the {@code authenticate} API mentioned above. 1146 */ 1147 @FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT) getOperationHandle()1148 public long getOperationHandle() { 1149 return super.getOpId(); 1150 } 1151 } 1152 1153 /** 1154 * Authentication type reported by {@link AuthenticationResult} when the user authenticated by 1155 * entering their device PIN, pattern, or password. 1156 */ 1157 public static final int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 1; 1158 1159 /** 1160 * Authentication type reported by {@link AuthenticationResult} when the user authenticated by 1161 * presenting some form of biometric (e.g. fingerprint or face). 1162 */ 1163 public static final int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 2; 1164 1165 /** 1166 * An {@link IntDef} representing the type of auth, as reported by {@link AuthenticationResult}. 1167 * @hide 1168 */ 1169 @Retention(RetentionPolicy.SOURCE) 1170 @IntDef({AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL, AUTHENTICATION_RESULT_TYPE_BIOMETRIC}) 1171 public @interface AuthenticationResultType { 1172 } 1173 1174 /** 1175 * Container for callback data from {@link #authenticate(CancellationSignal, Executor, 1176 * AuthenticationCallback)} and {@link #authenticate(CryptoObject, CancellationSignal, Executor, 1177 * AuthenticationCallback)}. 1178 */ 1179 public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult { 1180 /** 1181 * Authentication result 1182 * @param crypto 1183 * @param authenticationType 1184 * @hide 1185 */ AuthenticationResult(CryptoObject crypto, @AuthenticationResultType int authenticationType)1186 public AuthenticationResult(CryptoObject crypto, 1187 @AuthenticationResultType int authenticationType) { 1188 // Identifier and userId is not used for BiometricPrompt. 1189 super(crypto, authenticationType, null /* identifier */, 0 /* userId */); 1190 } 1191 1192 /** 1193 * Provides the crypto object associated with this transaction. 1194 * @return The crypto object provided to {@link #authenticate(CryptoObject, 1195 * CancellationSignal, Executor, AuthenticationCallback)} 1196 */ getCryptoObject()1197 public CryptoObject getCryptoObject() { 1198 return (CryptoObject) super.getCryptoObject(); 1199 } 1200 1201 /** 1202 * Provides the type of authentication (e.g. device credential or biometric) that was 1203 * requested from and successfully provided by the user. 1204 * 1205 * @return An integer value representing the authentication method used. 1206 */ getAuthenticationType()1207 public @AuthenticationResultType int getAuthenticationType() { 1208 return super.getAuthenticationType(); 1209 } 1210 } 1211 1212 /** 1213 * Callback structure provided to {@link BiometricPrompt#authenticate(CancellationSignal, 1214 * Executor, AuthenticationCallback)} or {@link BiometricPrompt#authenticate(CryptoObject, 1215 * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation 1216 * of this for listening to authentication events. 1217 */ 1218 public abstract static class AuthenticationCallback extends 1219 BiometricAuthenticator.AuthenticationCallback { 1220 /** 1221 * Called when an unrecoverable error has been encountered and the operation is complete. 1222 * No further actions will be made on this object. 1223 * @param errorCode An integer identifying the error message 1224 * @param errString A human-readable error string that can be shown on an UI 1225 */ 1226 @Override onAuthenticationError(int errorCode, CharSequence errString)1227 public void onAuthenticationError(int errorCode, CharSequence errString) {} 1228 1229 /** 1230 * Called when a recoverable error has been encountered during authentication. The help 1231 * string is provided to give the user guidance for what went wrong, such as "Sensor dirty, 1232 * please clean it." 1233 * @param helpCode An integer identifying the error message 1234 * @param helpString A human-readable string that can be shown on an UI 1235 */ 1236 @Override onAuthenticationHelp(int helpCode, CharSequence helpString)1237 public void onAuthenticationHelp(int helpCode, CharSequence helpString) {} 1238 1239 /** 1240 * Called when a biometric is recognized. 1241 * @param result An object containing authentication-related data 1242 */ onAuthenticationSucceeded(AuthenticationResult result)1243 public void onAuthenticationSucceeded(AuthenticationResult result) {} 1244 1245 /** 1246 * Called when a biometric is valid but not recognized. 1247 */ 1248 @Override onAuthenticationFailed()1249 public void onAuthenticationFailed() {} 1250 1251 /** 1252 * Called when a biometric has been acquired, but hasn't been processed yet. 1253 * @hide 1254 */ 1255 @Override onAuthenticationAcquired(int acquireInfo)1256 public void onAuthenticationAcquired(int acquireInfo) {} 1257 1258 /** 1259 * Receiver for internal system events. See {@link Builder#setReceiveSystemEvents(boolean)} 1260 * @hide 1261 */ onSystemEvent(int event)1262 public void onSystemEvent(int event) {} 1263 } 1264 1265 /** 1266 * Authenticates for the given user. 1267 * 1268 * @param cancel An object that can be used to cancel authentication 1269 * @param executor An executor to handle callback events 1270 * @param callback An object to receive authentication events 1271 * @param userId The user to authenticate 1272 * 1273 * @hide 1274 */ 1275 @RequiresPermission(USE_BIOMETRIC_INTERNAL) authenticateUser(@onNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId)1276 public void authenticateUser(@NonNull CancellationSignal cancel, 1277 @NonNull @CallbackExecutor Executor executor, 1278 @NonNull AuthenticationCallback callback, 1279 int userId) { 1280 if (cancel == null) { 1281 throw new IllegalArgumentException("Must supply a cancellation signal"); 1282 } 1283 if (executor == null) { 1284 throw new IllegalArgumentException("Must supply an executor"); 1285 } 1286 if (callback == null) { 1287 throw new IllegalArgumentException("Must supply a callback"); 1288 } 1289 1290 authenticateInternal(0 /* operationId */, cancel, executor, callback, userId); 1291 } 1292 1293 /** 1294 * Authenticates for the given keystore operation. 1295 * 1296 * @param cancel An object that can be used to cancel authentication 1297 * @param executor An executor to handle callback events 1298 * @param callback An object to receive authentication events 1299 * @param operationId The keystore operation associated with authentication 1300 * 1301 * @return A requestId that can be used to cancel this operation. 1302 * 1303 * @hide 1304 */ 1305 @RequiresPermission(USE_BIOMETRIC) authenticateForOperation( @onNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, long operationId)1306 public long authenticateForOperation( 1307 @NonNull CancellationSignal cancel, 1308 @NonNull @CallbackExecutor Executor executor, 1309 @NonNull AuthenticationCallback callback, 1310 long operationId) { 1311 if (cancel == null) { 1312 throw new IllegalArgumentException("Must supply a cancellation signal"); 1313 } 1314 if (executor == null) { 1315 throw new IllegalArgumentException("Must supply an executor"); 1316 } 1317 if (callback == null) { 1318 throw new IllegalArgumentException("Must supply a callback"); 1319 } 1320 1321 return authenticateInternal(operationId, cancel, executor, callback, mContext.getUserId()); 1322 } 1323 1324 /** 1325 * This call warms up the biometric hardware, displays a system-provided dialog, and starts 1326 * scanning for a biometric. It terminates when {@link 1327 * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link 1328 * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)}, or when the user 1329 * dismisses the system-provided dialog, at which point the crypto object becomes invalid. This 1330 * operation can be canceled by using the provided cancel object. The application will receive 1331 * authentication errors through {@link AuthenticationCallback}, and button events through the 1332 * corresponding callback set in {@link Builder#setNegativeButton(CharSequence, Executor, 1333 * DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricPrompt} object, 1334 * and calling {@link BiometricPrompt#authenticate(CancellationSignal, Executor, 1335 * AuthenticationCallback)} while an existing authentication attempt is occurring will stop the 1336 * previous client and start a new authentication. The interrupted client will receive a 1337 * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int, 1338 * CharSequence)}. 1339 * 1340 * <p>Note: Applications generally should not cancel and start authentication in quick 1341 * succession. For example, to properly handle authentication across configuration changes, it's 1342 * recommended to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, 1343 * the application will not need to cancel/restart authentication during the configuration 1344 * change. 1345 * 1346 * <p>Per the Android CDD, only biometric authenticators that meet or exceed the requirements 1347 * for <strong>Strong</strong> are permitted to integrate with Keystore to perform related 1348 * cryptographic operations. Therefore, it is an error to call this method after explicitly 1349 * calling {@link Builder#setAllowedAuthenticators(int)} with any biometric strength other than 1350 * {@link Authenticators#BIOMETRIC_STRONG}. 1351 * 1352 * @throws IllegalArgumentException If any argument is null, or if the allowed biometric 1353 * authenticator strength is explicitly set to {@link Authenticators#BIOMETRIC_WEAK}. Prior to 1354 * {@link android.os.Build.VERSION_CODES#R}, this exception is also thrown if 1355 * {@link Builder#setDeviceCredentialAllowed(boolean)} was explicitly set to true. 1356 * 1357 * @param crypto A cryptographic operation to be unlocked after successful authentication. 1358 * @param cancel An object that can be used to cancel authentication. 1359 * @param executor An executor to handle callback events. 1360 * @param callback An object to receive authentication events. 1361 */ 1362 @RequiresPermission(USE_BIOMETRIC) authenticate(@onNull CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback)1363 public void authenticate(@NonNull CryptoObject crypto, 1364 @NonNull CancellationSignal cancel, 1365 @NonNull @CallbackExecutor Executor executor, 1366 @NonNull AuthenticationCallback callback) { 1367 1368 FrameworkStatsLog.write(FrameworkStatsLog.AUTH_PROMPT_AUTHENTICATE_INVOKED, 1369 true /* isCrypto */, 1370 mPromptInfo.isConfirmationRequested(), 1371 mPromptInfo.isDeviceCredentialAllowed(), 1372 mPromptInfo.getAuthenticators() != Authenticators.EMPTY_SET, 1373 mPromptInfo.getAuthenticators()); 1374 1375 if (crypto == null) { 1376 throw new IllegalArgumentException("Must supply a crypto object"); 1377 } 1378 if (cancel == null) { 1379 throw new IllegalArgumentException("Must supply a cancellation signal"); 1380 } 1381 if (executor == null) { 1382 throw new IllegalArgumentException("Must supply an executor"); 1383 } 1384 if (callback == null) { 1385 throw new IllegalArgumentException("Must supply a callback"); 1386 } 1387 1388 // Disallow explicitly setting any non-Strong biometric authenticator types. 1389 @Authenticators.Types int authenticators = mPromptInfo.getAuthenticators(); 1390 if (authenticators == Authenticators.EMPTY_SET) { 1391 authenticators = Authenticators.BIOMETRIC_STRONG; 1392 } 1393 final int biometricStrength = authenticators & Authenticators.BIOMETRIC_WEAK; 1394 if ((biometricStrength & ~Authenticators.BIOMETRIC_STRONG) != 0) { 1395 throw new IllegalArgumentException("Only Strong biometrics supported with crypto"); 1396 } 1397 1398 authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId()); 1399 } 1400 1401 /** 1402 * This call warms up the biometric hardware, displays a system-provided dialog, and starts 1403 * scanning for a biometric. It terminates when {@link 1404 * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link 1405 * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)} is called, or when 1406 * the user dismisses the system-provided dialog. This operation can be canceled by using the 1407 * provided cancel object. The application will receive authentication errors through {@link 1408 * AuthenticationCallback}, and button events through the corresponding callback set in {@link 1409 * Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. It is 1410 * safe to reuse the {@link BiometricPrompt} object, and calling {@link 1411 * BiometricPrompt#authenticate(CancellationSignal, Executor, AuthenticationCallback)} while 1412 * an existing authentication attempt is occurring will stop the previous client and start a new 1413 * authentication. The interrupted client will receive a cancelled notification through {@link 1414 * AuthenticationCallback#onAuthenticationError(int, CharSequence)}. 1415 * 1416 * <p>Note: Applications generally should not cancel and start authentication in quick 1417 * succession. For example, to properly handle authentication across configuration changes, it's 1418 * recommended to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, 1419 * the application will not need to cancel/restart authentication during the configuration 1420 * change. 1421 * 1422 * @throws IllegalArgumentException If any of the arguments are null. 1423 * 1424 * @param cancel An object that can be used to cancel authentication. 1425 * @param executor An executor to handle callback events. 1426 * @param callback An object to receive authentication events. 1427 */ 1428 @RequiresPermission(USE_BIOMETRIC) authenticate(@onNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback)1429 public void authenticate(@NonNull CancellationSignal cancel, 1430 @NonNull @CallbackExecutor Executor executor, 1431 @NonNull AuthenticationCallback callback) { 1432 1433 FrameworkStatsLog.write(FrameworkStatsLog.AUTH_PROMPT_AUTHENTICATE_INVOKED, 1434 false /* isCrypto */, 1435 mPromptInfo.isConfirmationRequested(), 1436 mPromptInfo.isDeviceCredentialAllowed(), 1437 mPromptInfo.getAuthenticators() != Authenticators.EMPTY_SET, 1438 mPromptInfo.getAuthenticators()); 1439 1440 if (cancel == null) { 1441 throw new IllegalArgumentException("Must supply a cancellation signal"); 1442 } 1443 if (executor == null) { 1444 throw new IllegalArgumentException("Must supply an executor"); 1445 } 1446 if (callback == null) { 1447 throw new IllegalArgumentException("Must supply a callback"); 1448 } 1449 authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId()); 1450 } 1451 cancelAuthentication(long requestId)1452 private void cancelAuthentication(long requestId) { 1453 if (mService != null) { 1454 try { 1455 mService.cancelAuthentication(mToken, mContext.getPackageName(), requestId); 1456 } catch (RemoteException e) { 1457 Log.e(TAG, "Unable to cancel authentication", e); 1458 } 1459 } 1460 } 1461 authenticateInternal( @ullable CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId)1462 private void authenticateInternal( 1463 @Nullable CryptoObject crypto, 1464 @NonNull CancellationSignal cancel, 1465 @NonNull @CallbackExecutor Executor executor, 1466 @NonNull AuthenticationCallback callback, 1467 int userId) { 1468 1469 mCryptoObject = crypto; 1470 final long operationId = crypto != null ? crypto.getOpId() : 0L; 1471 authenticateInternal(operationId, cancel, executor, callback, userId); 1472 } 1473 authenticateInternal( long operationId, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId)1474 private long authenticateInternal( 1475 long operationId, 1476 @NonNull CancellationSignal cancel, 1477 @NonNull @CallbackExecutor Executor executor, 1478 @NonNull AuthenticationCallback callback, 1479 int userId) { 1480 // Ensure we don't return the wrong crypto object as an auth result. 1481 if (mCryptoObject != null && mCryptoObject.getOpId() != operationId) { 1482 Log.w(TAG, "CryptoObject operation ID does not match argument; setting field to null"); 1483 mCryptoObject = null; 1484 } 1485 1486 try { 1487 if (cancel.isCanceled()) { 1488 Log.w(TAG, "Authentication already canceled"); 1489 return -1; 1490 } 1491 1492 mExecutor = executor; 1493 mAuthenticationCallback = callback; 1494 if (mIsPromptShowing) { 1495 final String stringToSend = mContext.getString(R.string.biometric_error_canceled); 1496 mExecutor.execute(() -> { 1497 mAuthenticationCallback.onAuthenticationError(BIOMETRIC_ERROR_CANCELED, 1498 stringToSend); 1499 }); 1500 return -1; 1501 } 1502 1503 final PromptInfo promptInfo; 1504 if (operationId != 0L) { 1505 // Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth. 1506 // Note that we use a new PromptInfo here so as to not overwrite the application's 1507 // preference, since it is possible that the same prompt configuration be used 1508 // without a crypto object later. 1509 Parcel parcel = Parcel.obtain(); 1510 mPromptInfo.writeToParcel(parcel, 0 /* flags */); 1511 parcel.setDataPosition(0); 1512 promptInfo = new PromptInfo(parcel); 1513 if (promptInfo.getAuthenticators() == Authenticators.EMPTY_SET) { 1514 promptInfo.setAuthenticators(Authenticators.BIOMETRIC_STRONG); 1515 } 1516 } else { 1517 promptInfo = mPromptInfo; 1518 } 1519 1520 final long authId = mService.authenticate(mToken, operationId, userId, 1521 mBiometricServiceReceiver, mContext.getPackageName(), promptInfo); 1522 cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); 1523 mIsPromptShowing = true; 1524 1525 return authId; 1526 } catch (RemoteException e) { 1527 Log.e(TAG, "Remote exception while authenticating", e); 1528 mExecutor.execute(() -> callback.onAuthenticationError( 1529 BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, 1530 mContext.getString(R.string.biometric_error_hw_unavailable))); 1531 return -1; 1532 } 1533 } 1534 isCredentialAllowed(@uthenticators.Types int allowedAuthenticators)1535 private static boolean isCredentialAllowed(@Authenticators.Types int allowedAuthenticators) { 1536 return (allowedAuthenticators & Authenticators.DEVICE_CREDENTIAL) != 0; 1537 } 1538 1539 /** Converts {@code drawable} to a {@link Bitmap}. */ convertDrawableToBitmap(Drawable drawable)1540 private static Bitmap convertDrawableToBitmap(Drawable drawable) { 1541 if (drawable == null) { 1542 return null; 1543 } 1544 1545 if (drawable instanceof BitmapDrawable) { 1546 return ((BitmapDrawable) drawable).getBitmap(); 1547 } 1548 1549 Bitmap bitmap; 1550 if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { 1551 bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 1552 // Single color bitmap will be created of 1x1 pixel 1553 } else { 1554 bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), 1555 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 1556 } 1557 1558 final Canvas canvas = new Canvas(bitmap); 1559 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 1560 drawable.draw(canvas); 1561 return bitmap; 1562 } 1563 } 1564