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