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