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