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