Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f501b58d authored by Jim Miller's avatar Jim Miller
Browse files

Update and cleanup FingerprintManager API to new spec

Fixes bug 21040383

Change-Id: Ib97d486535869814d5b449cccb31670e8f412552
parent 4cf59747
Loading
Loading
Loading
Loading
+2 −13
Original line number Diff line number Diff line
@@ -13942,16 +13942,8 @@ package android.hardware.display {
package android.hardware.fingerprint {
  public final class Fingerprint implements android.os.Parcelable {
    ctor public Fingerprint(java.lang.CharSequence, int, int, long);
    method public int describeContents();
    method public java.lang.CharSequence getName();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.hardware.fingerprint.Fingerprint> CREATOR;
  }
  public class FingerprintManager {
    method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, int);
    method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
    method public boolean hasEnrolledFingerprints();
    method public boolean isHardwareDetected();
    field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
@@ -13960,14 +13952,12 @@ package android.hardware.fingerprint {
    field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
    field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
    field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
    field public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; // 0x3e8
    field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
    field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
    field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
    field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
    field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
    field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
    field public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000; // 0x3e8
  }
  public static abstract class FingerprintManager.AuthenticationCallback {
@@ -13979,11 +13969,10 @@ package android.hardware.fingerprint {
  }
  public static final class FingerprintManager.AuthenticationResult {
    ctor public FingerprintManager.AuthenticationResult(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.hardware.fingerprint.Fingerprint);
    method public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
  }
  public static class FingerprintManager.CryptoObject {
  public static final class FingerprintManager.CryptoObject {
    ctor public FingerprintManager.CryptoObject(java.security.Signature);
    ctor public FingerprintManager.CryptoObject(javax.crypto.Cipher);
    ctor public FingerprintManager.CryptoObject(javax.crypto.Mac);
+2 −13
Original line number Diff line number Diff line
@@ -14260,16 +14260,8 @@ package android.hardware.display {
package android.hardware.fingerprint {
  public final class Fingerprint implements android.os.Parcelable {
    ctor public Fingerprint(java.lang.CharSequence, int, int, long);
    method public int describeContents();
    method public java.lang.CharSequence getName();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.hardware.fingerprint.Fingerprint> CREATOR;
  }
  public class FingerprintManager {
    method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, int);
    method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
    method public boolean hasEnrolledFingerprints();
    method public boolean isHardwareDetected();
    field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
@@ -14278,14 +14270,12 @@ package android.hardware.fingerprint {
    field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
    field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
    field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
    field public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; // 0x3e8
    field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
    field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
    field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
    field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
    field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
    field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
    field public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000; // 0x3e8
  }
  public static abstract class FingerprintManager.AuthenticationCallback {
@@ -14297,11 +14287,10 @@ package android.hardware.fingerprint {
  }
  public static final class FingerprintManager.AuthenticationResult {
    ctor public FingerprintManager.AuthenticationResult(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.hardware.fingerprint.Fingerprint);
    method public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
  }
  public static class FingerprintManager.CryptoObject {
  public static final class FingerprintManager.CryptoObject {
    ctor public FingerprintManager.CryptoObject(java.security.Signature);
    ctor public FingerprintManager.CryptoObject(javax.crypto.Cipher);
    ctor public FingerprintManager.CryptoObject(javax.crypto.Mac);
+106 −77
Original line number Diff line number Diff line
@@ -18,32 +18,30 @@ package android.hardware.fingerprint;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.ActivityManagerNative;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
import android.security.keystore.AndroidKeyStoreProvider;
import android.util.Log;
import android.util.Slog;

import java.security.Signature;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.crypto.Cipher;
import javax.crypto.Mac;

import static android.Manifest.permission.USE_FINGERPRINT;
import static android.Manifest.permission.MANAGE_FINGERPRINT;

/**
 * A class that coordinates access to the fingerprint hardware.
 * <p>
@@ -57,9 +55,10 @@ public class FingerprintManager {
    private static final boolean DEBUG = true;
    private static final int MSG_ENROLL_RESULT = 100;
    private static final int MSG_ACQUIRED = 101;
    private static final int MSG_AUTHENTICATED = 102;
    private static final int MSG_ERROR = 103;
    private static final int MSG_REMOVED = 104;
    private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
    private static final int MSG_AUTHENTICATION_FAILED = 103;
    private static final int MSG_ERROR = 104;
    private static final int MSG_REMOVED = 105;

    //
    // Error messages from fingerprint hardware during initilization, enrollment, authentication or
@@ -112,6 +111,7 @@ public class FingerprintManager {
    /**
     * Hardware vendors may extend this list if there are conditions that do not fall under one of
     * the above categories. Vendors are responsible for providing error strings for these errors.
     * @hide
     */
    public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;

@@ -162,6 +162,7 @@ public class FingerprintManager {
    /**
     * Hardware vendors may extend this list if there are conditions that do not fall under one of
     * the above categories. Vendors are responsible for providing error strings for these errors.
     * @hide
     */
    public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;

@@ -173,6 +174,7 @@ public class FingerprintManager {
    private RemovalCallback mRemovalCallback;
    private CryptoObject mCryptoObject;
    private Fingerprint mRemovalFingerprint;
    private Handler mHandler;

    private class OnEnrollCancelListener implements OnCancelListener {
        @Override
@@ -198,72 +200,71 @@ public class FingerprintManager {
     * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
     * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
     */
    public static class CryptoObject {
    public static final class CryptoObject {

        public CryptoObject(@NonNull Signature signature) {
            mSignature = signature;
            mCipher = null;
            mMac = null;
            mCrypto = signature;
        }

        public CryptoObject(@NonNull Cipher cipher) {
            mCipher = cipher;
            mSignature = null;
            mMac = null;
            mCrypto = cipher;
        }

        public CryptoObject(@NonNull Mac mac) {
            mMac = mac;
            mCipher = null;
            mSignature = null;
            mCrypto = mac;
        }

        /**
         * Get {@link Signature} object.
         * @return {@link Signature} object or null if this doesn't contain one.
         */
        public Signature getSignature() { return mSignature; }
        public Signature getSignature() {
            return mCrypto instanceof Signature ? (Signature) mCrypto : null;
        }

        /**
         * Get {@link Cipher} object.
         * @return {@link Cipher} object or null if this doesn't contain one.
         */
        public Cipher getCipher() { return mCipher; }
        public Cipher getCipher() {
            return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
        }

        /**
         * Get {@link Mac} object.
         * @return {@link Mac} object or null if this doesn't contain one.
         */
        public Mac getMac() { return mMac; }
        public Mac getMac() {
            return mCrypto instanceof Mac ? (Mac) mCrypto : null;
        }

        /**
         * @hide
         * @return the opId associated with this object or 0 if none
         */
        public long getOpId() {
            if (mSignature != null) {
                return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mSignature);
            } else if (mCipher != null) {
                return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCipher);
            } else if (mMac != null) {
                return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mMac);
            }
            return 0;
            return mCrypto != null ?
                    AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
        }

        private final Signature mSignature;
        private final Cipher mCipher;
        private final Mac mMac;
        private final Object mCrypto;
    };

    /**
     * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
     *     CancellationSignal, AuthenticationCallback, int)}.
     *     CancellationSignal, int, AuthenticationCallback, Handler)}.
     */
    public static final class AuthenticationResult {
        private Fingerprint mFingerprint;
        private CryptoObject mCryptoObject;

        /**
         * Authentication result
         *
         * @param crypto the crypto object
         * @param fingerprint the recognized fingerprint data, if allowed.
         * @hide
         */
        public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint) {
            mCryptoObject = crypto;
            mFingerprint = fingerprint;
@@ -272,7 +273,7 @@ public class FingerprintManager {
        /**
         * Obtain the crypto object associated with this transaction
         * @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject,
         *     CancellationSignal, AuthenticationCallback, int)}.
         *     CancellationSignal, int, AuthenticationCallback, Handler)}.
         */
        public CryptoObject getCryptoObject() { return mCryptoObject; }

@@ -287,28 +288,28 @@ public class FingerprintManager {

    /**
     * Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject,
     * CancellationSignal, AuthenticationCallback, int)}. Users of {@link
     * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
     * FingerprintManager#authenticate(CryptoObject, CancellationSignal,
     * AuthenticationCallback, int) } must provide an implementation of this for listening to
     * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
     * fingerprint events.
     */
    public static abstract class AuthenticationCallback {
        /**
         * Called when an unrecoverable error has been encountered and the operation is complete.
         * No further callbacks will be made on this object.
         * @param errMsgId An integer identifying the error message
         * @param errorCode An integer identifying the error message
         * @param errString A human-readable error string that can be shown in UI
         */
        public void onAuthenticationError(int errMsgId, CharSequence errString) { }
        public void onAuthenticationError(int errorCode, CharSequence errString) { }

        /**
         * Called when a recoverable error has been encountered during authentication. The help
         * string is provided to give the user guidance for what went wrong, such as
         * "Sensor dirty, please clean it."
         * @param helpMsgId An integer identifying the error message
         * @param helpCode An integer identifying the error message
         * @param helpString A human-readable string that can be shown in UI
         */
        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }
        public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }

        /**
         * Called when a fingerprint is recognized.
@@ -326,7 +327,7 @@ public class FingerprintManager {
     * Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback,
     * CancellationSignal, int). Users of {@link #FingerprintManager()}
     * must provide an implementation of this to {@link FingerprintManager#enroll(long,
     * CancellationSignal, EnrollmentCallback, int) for listening to fingerprint events.
     * CancellationSignal, int, EnrollmentCallback) for listening to fingerprint events.
     *
     * @hide
     */
@@ -392,31 +393,35 @@ public class FingerprintManager {
     *
     * @param crypto object associated with the call or null if none required.
     * @param cancel an object that can be used to cancel authentication
     * @param callback an object to receive authentication events
     * @param flags optional flags; should be 0
     * @param callback an object to receive authentication events
     * @param handler an optional handler to handle callback events
     */
    @RequiresPermission(USE_FINGERPRINT)
    public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
            @NonNull AuthenticationCallback callback, int flags) {
        authenticate(crypto, cancel, callback, flags, UserHandle.myUserId());
            int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
        authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId());
    }

    /**
     * Request authentication of a crypto object. This call warms up the fingerprint hardware
     * and starts scanning for a fingerprint. It terminates when
     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
     * which point the object is no longer valid. The operation can be canceled by using the
     * provided cancel object.
     *
     * @param crypto object associated with the call or null if none required.
     * @param cancel an object that can be used to cancel authentication
     * @param callback an object to receive authentication events
     * @param flags optional flags; should be 0
     * @param userId the userId the fingerprint belongs to
     * Use the provided handler thread for events.
     * @param handler
     */
    private void useHandler(Handler handler) {
        if (handler != null) {
            mHandler = new MyHandler(handler.getLooper());
        } else if (mHandler.getLooper() != mContext.getMainLooper()){
            mHandler = new MyHandler(mContext.getMainLooper());
        }
    }

    /**
     * Per-user version
     * @hide
     */
    @RequiresPermission(USE_FINGERPRINT)
    public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
            @NonNull AuthenticationCallback callback, int flags, int userId) {
            int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
        if (callback == null) {
            throw new IllegalArgumentException("Must supply an authentication callback");
        }
@@ -431,6 +436,7 @@ public class FingerprintManager {
        }

        if (mService != null) try {
            useHandler(handler);
            mAuthenticationCallback = callback;
            mCryptoObject = crypto;
            long sessionId = crypto != null ? crypto.getOpId() : 0;
@@ -458,12 +464,13 @@ public class FingerprintManager {
     * @param token a unique token provided by a recent creation or verification of device
     * credentials (e.g. pin, pattern or password).
     * @param cancel an object that can be used to cancel enrollment
     * @param callback an object to receive enrollment events
     * @param flags optional flags
     * @param callback an object to receive enrollment events
     * @hide
     */
    public void enroll(byte [] token, CancellationSignal cancel, EnrollmentCallback callback,
            int flags) {
    @RequiresPermission(MANAGE_FINGERPRINT)
    public void enroll(byte [] token, CancellationSignal cancel, int flags,
            EnrollmentCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("Must supply an enrollment callback");
        }
@@ -496,6 +503,7 @@ public class FingerprintManager {
     * existing device credentials (e.g. pin/pattern/password).
     * @hide
     */
    @RequiresPermission(MANAGE_FINGERPRINT)
    public long preEnroll() {
        long result = 0;
        if (mService != null) try {
@@ -514,6 +522,7 @@ public class FingerprintManager {
     *
     * @hide
     */
    @RequiresPermission(MANAGE_FINGERPRINT)
    public void remove(Fingerprint fp, RemovalCallback callback) {
        if (mService != null) try {
            mRemovalCallback = callback;
@@ -535,6 +544,7 @@ public class FingerprintManager {
     *
     * @hide
     */
    @RequiresPermission(MANAGE_FINGERPRINT)
    public void rename(int fpId, String newName) {
        // Renames the given fpId
        if (mService != null) {
@@ -554,6 +564,7 @@ public class FingerprintManager {
     *
     * @hide
     */
    @RequiresPermission(USE_FINGERPRINT)
    public List<Fingerprint> getEnrolledFingerprints(int userId) {
        if (mService != null) try {
            return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
@@ -569,6 +580,7 @@ public class FingerprintManager {
     *
     * @hide
     */
    @RequiresPermission(USE_FINGERPRINT)
    public List<Fingerprint> getEnrolledFingerprints() {
        return getEnrolledFingerprints(UserHandle.myUserId());
    }
@@ -578,6 +590,7 @@ public class FingerprintManager {
     *
     * @return true if at least one fingerprint is enrolled, false otherwise
     */
    @RequiresPermission(USE_FINGERPRINT)
    public boolean hasEnrolledFingerprints() {
        if (mService != null) try {
            return mService.hasEnrolledFingerprints(UserHandle.myUserId(),
@@ -593,6 +606,7 @@ public class FingerprintManager {
     *
     * @return true if hardware is present and functional, false otherwise.
     */
    @RequiresPermission(USE_FINGERPRINT)
    public boolean isHardwareDetected() {
        if (mService != null) {
            try {
@@ -626,13 +640,15 @@ public class FingerprintManager {
        return 0;
    }

    private Handler mHandler;

    private class MyHandler extends Handler {
        private MyHandler(Context context) {
            super(context.getMainLooper());
        }

        private MyHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(android.os.Message msg) {
            switch(msg.what) {
                case MSG_ENROLL_RESULT:
@@ -641,8 +657,11 @@ public class FingerprintManager {
                case MSG_ACQUIRED:
                    sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */);
                    break;
                case MSG_AUTHENTICATED:
                    sendAuthenticatedResult((Fingerprint) msg.obj);
                case MSG_AUTHENTICATION_SUCCEEDED:
                    sendAuthenticatedSucceeded((Fingerprint) msg.obj);
                    break;
                case MSG_AUTHENTICATION_FAILED:
                    sendAuthenticatedFailed();
                    break;
                case MSG_ERROR:
                    sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */);
@@ -684,16 +703,17 @@ public class FingerprintManager {
            }
        }

        private void sendAuthenticatedResult(Fingerprint fp) {
        private void sendAuthenticatedSucceeded(Fingerprint fp) {
            if (mAuthenticationCallback != null) {
                if (fp.getFingerId() == 0) {
                    // Fingerprint template valid but doesn't match one in database
                    mAuthenticationCallback.onAuthenticationFailed();
                } else {
                final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
                mAuthenticationCallback.onAuthenticationSucceeded(result);
            }
        }

        private void sendAuthenticatedFailed() {
            if (mAuthenticationCallback != null) {
               mAuthenticationCallback.onAuthenticationFailed();
            }
        }

        private void sendAcquiredResult(long deviceId, int acquireInfo) {
@@ -809,24 +829,33 @@ public class FingerprintManager {

    private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {

        @Override // binder call
        public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
            mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
                    new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
        }

        @Override // binder call
        public void onAcquired(long deviceId, int acquireInfo) {
            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget();
        }

        public void onAuthenticated(long deviceId, int fingerId, int groupId) {
            mHandler.obtainMessage(MSG_AUTHENTICATED,
                    new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
        @Override // binder call
        public void onAuthenticationSucceeded(long deviceId, Fingerprint fp) {
            mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, fp).sendToTarget();
        }

        @Override // binder call
        public void onAuthenticationFailed(long deviceId) {
            mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();;
        }

        @Override // binder call
        public void onError(long deviceId, int error) {
            mHandler.obtainMessage(MSG_ERROR, error, 0, deviceId).sendToTarget();
        }

        @Override // binder call
        public void onRemoved(long deviceId, int fingerId, int groupId) {
            mHandler.obtainMessage(MSG_REMOVED, fingerId, groupId, deviceId).sendToTarget();
        }
+3 −1
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package android.hardware.fingerprint;

import android.hardware.fingerprint.Fingerprint;
import android.os.Bundle;
import android.os.UserHandle;

@@ -25,7 +26,8 @@ import android.os.UserHandle;
oneway interface IFingerprintServiceReceiver {
    void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
    void onAcquired(long deviceId, int acquiredInfo);
    void onAuthenticated(long deviceId, int fingerId, int groupId);
    void onAuthenticationSucceeded(long deviceId, in Fingerprint fp);
    void onAuthenticationFailed(long deviceId);
    void onError(long deviceId, int error);
    void onRemoved(long deviceId, int fingerId, int groupId);
}
+1 −1
Original line number Diff line number Diff line
@@ -197,7 +197,7 @@ of the two modes:
  reset (e.g. by a Device Admin).</li>
<li>User authentication is required for every use of the key. In this mode, a specific operation
  involving a specific key is authorized by the user. Currently, the only means of such
  authorization is fingerprint authentication: {@link android.hardware.fingerprint.FingerprintManager#authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, int) FingerprintManager.authenticate}.
  authorization is fingerprint authentication: {@link android.hardware.fingerprint.FingerprintManager#authenticate(CryptoObject, CancellationSignal, int, AuthenticationCallback, Handler) FingerprintManager.authenticate}.
  Such keys can only be generated or imported if at least one fingerprint is enrolled (see {@link android.hardware.fingerprint.FingerprintManager#hasEnrolledFingerprints() FingerprintManager.hasEnrolledFingerprints}).
  These keys become permanently invalidated once all fingerprints are unenrolled.</li>
</ul>
Loading