Writing in the front

Fingerprint recognition is no stranger to everyone. Most of the new Android phones now support facial recognition, and fingerprint recognition is a standard feature of the mainstream Android phones. These two functions can be said to have used good, is really convenient and fast.

However, you will find that the fingerprint recognition and facial recognition of these mobile phones only support the lock screen to unlock, and the huge number of apps for these two technologies can be said to be relatively few. Isn’t that a loss of APP experience?

Fortunately, the basic functionality of fingerprint recognition based on the Google API is not complicated.

Fingerprint identification compatibility and security issues

In addition to implementing the basic features of fingerprinting, I think there are two issues that developers need to focus on and selectively address: compatibility and security.

Why selective treatment?

First of all, compatibility. The FINGERPRINT API was opened up by Google in Android 6.0.

On systems below Android 6.0, some phone manufacturers support fingerprint recognition by themselves. If our APP is to be compatible with these devices, we need to integrate the fingerprint recognition SDK of the manufacturer, which is the biggest compatibility problem. However, there are very few devices under Android 6.0, and even fewer of them support fingerprint recognition, so I think it’s okay not to be compatible with it.

On systems above Android 6.0, there will be some compatibility problems due to the widespread customization of Android system and fingerprint identification module by manufacturers. This does not have what good method, need the developer to see move open move. Many developers have stepped on the pit, we can search online related articles to see.

Speaking of security, since the added fingerprint is stored on the phone and the Google API only returns true or false after verifying the fingerprint, it’s hard to trust the result unconditionally. Fingerprints can be hijacked and return false results, such as when a user’s phone is root or a custom device.

Fortunately, the probability of this happening is relatively low. If it’s not a transaction or a payment, it’s just something like “start the APP for fingerprint verification,” then the Google API will be fine.

As for the compatibility and security issues, this paper does not discuss much, so I recommend an article for you. Meanwhile, please pay attention to the processing methods of Alipay and Tencent mentioned in the article, and their open source situation:

How to implement Android fingerprint authentication elegantly in complex business scenarios?



Fingerprint API version evolution

In Android 6.0 (Android M Api23), open the Android system fingerprinting API, exists in the Android.. The hardware fingerprint packages, core classes is FingerprintManager, Provides a basic fingerprint identification function. Note that FingerprintManager is marked @deprecated on Android 9.0 (Android P Api28) and will be Deprecated.

. Later, in the android support. The v4. Hardware. The fingerprint packages and androidx core. The hardware. The fingerprint in the package, FingerprintManager has been upgraded to FingerprintManagerCompat with some enhancements and compatibility improvements, such as system version identification and fingerprint encryption. In fact, if you read the source code, you’ll see that its core functionality is implemented by calling FingerprintManager.

Then, in Android 9.0 (Android P Api 28), Google further enhanced BiometricPrompt by opening up a new Api, Exists in androidx. Biometric package and android. Hardware. Biometrics package, Google is explained in the developer documentation:

On devices running P and above, this will show a system-provided authentication prompt, using a device’s supported biometric (fingerprint, iris, face, etc).

In Android P and above, BiometricPrompt will display a system-provided verification prompt to support device-provided biometrics, including fingerprints, iris, faces, and more.

Currently, biometric APIS such as iris and face are not open, only fingerprint recognition is supported. However, fingerprint recognition has been unified, such as requiring the use of a unified FINGERPRINT recognition UI, not allowing developers to customize.

The key method of fingerprint identification is authenticate

This is the core method of fingerprint identification, which is used to pull up the fingerprint scanner for fingerprint identification.

Take the authenticate() method in FingerprintManagerCompat as an example, as defined in the developer documentation:

authenticate

authenticate parameters

Explain the parameters:

  • FingerprintManagerCompat.CryptoObject crypto

    The source code explains:

    /**     * A wrapper class for the crypto objects supported by FingerprintManager. Currently the     * framework supports {@link Signature} and {@link Cipher} objects.     */Copy the code

This is a wrapper class for a Cipher object that currently supports both Signature and Cipher encryption.

This object is used by the fingerprint scanner to determine the validity of the fingerprint authentication result. Android 6.0 is @nullable, but null is not recommended, and after Android 9.0 it will be @nonNULL.

A Cipher helper class is provided in my code, which can be used to create a Cipher object for reference.

  • int flags

    Optional flag, temporarily useless, pass 0. Only for Android 6.0.

  • CancellationSignal cancel

    This object is used to cancel the fingerprint scanner. For example, after the user clicks the “cancel” button or the “password verification” button on the recognition box, the scanner should be cancelled in time.

    If you don’t cancel it in time, the fingerprint scanner will keep scanning until it times out. This creates two problems:

    (1) electricity

    (2) Within the timeout period, the user will not be able to initiate fingerprint identification again.

    Also, this parameter is @nullable on Android 6.0 and @nonNULL after Android 9.0. For the above reasons, null is not recommended.

  • FingerprintManagerCompat.AuthenticationCallback callback

    The callback interface for fingerprint identification results is @nonnull, which declares the following methods:

    AuthenticationCallback

    Focus on the onAuthenticationError() and onAuthenticationHelp() methods, which are called back when a fingerprint exception occurs:

    (1) The errString and helpString parameters are specific exception information, such as finger movement too fast or verification failure. The exception information is provided by the system. As you can see, the system matches the exception information according to the language used by the application.

    If you are not sure about the exception message that the system matches the corresponding language, you can handle it according to errMsgId and helpMsgId.

    (2) The errMsgId and helpMsgId parameters are Id identifiers for an exception. There are a number of them. Please check the developer documentation. Note that there are differences between Android 6.0 and Android 9.0.

  • Handler handler

    Nullable: this is an @nullable parameter for Android 6.0, which tells the system to use this Handler’s Looper to process fingerprint identification messages. The default is to pass null to the main thread Looper.

  • Executor Executor (added, yes)

    . This parameter is the Android 9.0 Api BiometricPrompt authenticate () the parameter, is @ NonNull, role and last parameter Handler Handler, used to distribute callback events of fingerprint identification.

    When distributed through the main thread, Context#getMainExecutor() can be passed as a parameter;

    When distributed through a shared thread pool, AsyncTask#THREAD_POOL_EXECUTOR can be passed as an argument.

The practice of fingerprint identification

In the practice of fingerprint recognition function, I made it into an open source library and published it on Github, which can be relied on through Gradle. It is relatively simple to use, two or three lines of code, and then pass in a verification result monitor.

The following part of the implementation process is introduced. For detailed Api and source code, please go to Github.

Making address: https://github.com/ZuoHailong/BiometricPrompt

The sample

Android 6.0 fingerprint recognition frame, developer custom:

User-defined fingerprint identification frame

Android 9.0 Fingerprint Recognition frame, system provides:

System fingerprint identification frame

The text colors of fingerprint icon and cancel button are determined by the color value of attribute colorPrimary.


Fingerprint identification management class

FingerprintVerifyManager is fingerprint library entrance, manage the fingerprint identification, through FingerprintVerifyManager. Builder initialized for fingerprint identification.

In this class, the FingerprintManagerCompat or BiometricPrompt Api is invoked depending on the phone’s system version. BiometricPrompt can be turned on or off by default.

public FingerprintVerifyManager(Builder builder) {        IFingerprint fingerprint;        // >= Android P        if(AndrVersionUtil isAboveAndrP ()) {/ / on the Android P whether display system to provide the identification of a boxif (builder.enableAndroidP)                fingerprint = FingerprintAndrP.newInstance();            else                fingerprint = FingerprintAndrM.newInstance();        } else if(AndrVersionUtil isAboveAndrM ()) {/ / Android 6.0 = < Version fingerprint = FingerprintAndrM. NewInstance (); }else{// < Android 6.0, officially not open fingerprint identification, Do not deal with some models to support is builder. The callback. The onError (builder. Context. Get string (R.s tring. Biometricprompt_verify_error_below_m));return;        }        ……        fingerprint.authenticate(builder.context, bean, builder.callback);    }Copy the code

IFingerprint is the fingerprint identification interface. Both Android 6.0-compatible FingerprintAndrM and Android 9.0-compatible FingerprintAndrP implement this interface.

Public interface IFingerprint {/ * * * the initialization and the fingerprint identification * * @ param context * @ param verificationDialogStyleBean fingerprint identification frame style * @param callback notifydevelopers of fingerprint identification results */ void authenticate(Activity context, VerificationDialogStyleBean verificationDialogStyleBean, FingerprintCallback callback); }Copy the code

Realize fingerprint recognition based on Android 6.0

As mentioned above, FingerprintAndrM is a specific implementation class based on Android 6.0:

@RequiresApi(api = Build.VERSION_CODES.M)public class FingerprintAndrM implements IFingerprint { private final String TAG = FingerprintAndrM.class.getName(); private Activity context; private static FingerprintAndrM fingerprintAndrM; Private static FingerprintDialog FingerprintDialog; Private FingerprintCallback FingerprintCallback; Private CancellationSignal CancellationSignal; . / / the fingerprint encryption private static FingerprintManagerCompat CryptoObject CryptoObject; Private FingerprintManagerCompat FingerprintManagerCompat; @Override public void authenticate(Activity context, VerificationDialogStyleBean bean, FingerprintCallback callback) {// Check whether the fingerprint is availableif(! canAuthenticate(context, callback))return; this.context = context; this.fingerprintCallback = callback; / / Android 6.0 instantiation fingerprintManagerCompat = fingerprintManagerCompat fingerprint management. The from (context); CancellationSignal = new cancellationSignal (); cancellationSignal.setOnCancelListener(() -> fingerprintDialog.dismiss()); / / tuning up fingerprint verification fingerprintManagerCompat. Authenticate (cryptoObject, 0, cancellationSignal authenticationCallback, null); // Fingerprint verification box fingerprintDialog = FingerprintDialog.newInstance(context).setActionListener(dialogActionListener).setDialogStyle(bean); fingerprintDialog.show(context.getFragmentManager(), TAG); } public static FingerprintAndrMnewInstance() {        if (fingerprintAndrM == null) {            synchronized (FingerprintAndrM.class) {                if(fingerprintAndrM == null) { fingerprintAndrM = new FingerprintAndrM(); }}} // Fingerprint encryption, Cipher initialization in advance, Prevent the fingerprint authentication has not been initialized when finish the try {cryptoObject = new FingerprintManagerCompat. CryptoObject (new CipherHelper () createCipher ()); } catch (Exception e) { e.printStackTrace(); }returnfingerprintAndrM; } / fingerprint verification box button to monitor * * * * / private FingerprintDialog. OnDialogActionListener dialogActionListener = new FingerprintDialog.OnDialogActionListener() {        @Override        public void onUsepwd() {            if(fingerprintCallback ! = null) fingerprintCallback.onUsepwd(); } @Override public voidonCancle() {// Cancel fingerprint validation, notifying the callerif(fingerprintCallback ! = null) fingerprintCallback.onCancel(); } @Override public voidonDismiss() {// The fingerprint verification box disappearsif(cancellationSignal ! = null && ! cancellationSignal.isCanceled()) cancellationSignal.cancel(); }}; / * * * fingerprint verification results callback * / private FingerprintManagerCompat. AuthenticationCallback AuthenticationCallback = new FingerprintManagerCompat.AuthenticationCallback() {        @Override        public void onAuthenticationError(int errMsgId, CharSequence errString) {            super.onAuthenticationError(errMsgId, errString);            fingerprintDialog.setTip(errString.toString(), R.color.biometricprompt_color_FF5555);        }        @Override        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {            super.onAuthenticationHelp(helpMsgId, helpString);            fingerprintDialog.setTip(helpString.toString(), R.color.biometricprompt_color_FF5555);        }        @Override        public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {            super.onAuthenticationSucceeded(result);            fingerprintDialog.setTip(context.getString(R.string.biometricprompt_verify_success), R.color.biometricprompt_color_82C785);            fingerprintCallback.onSucceeded();            fingerprintDialog.dismiss();        }        @Override        public void onAuthenticationFailed() { super.onAuthenticationFailed(); fingerprintDialog.setTip(context.getString(R.string.biometricprompt_verify_failed), R.color.biometricprompt_color_FF5555); fingerprintCallback.onFailed(); }}; / * * the Android Q, Google provides Api BiometricManager. CanAuthenticate () is used to detect fingerprint hardware is available and whether to add the fingerprint * but has not yet open, marked"Stub"* */ private Boolean canAuthenticate(Context Context, FingerprintCallback FingerprintCallback) {/* * Whether the hardware supports fingerprint recognition * */if(! FingerprintManagerCompat.from(context).isHardwareDetected()) { fingerprintCallback.onError(FingerprintManager.FINGERPRINT_ERROR_HW_NOT_PRESENT, context.getString(R.string.biometricprompt_verify_error_no_hardware));return false; } // Whether fingerprints have been addedif(! FingerprintManagerCompat.from(context).hasEnrolledFingerprints()) { fingerprintCallback.onNoneEnrolled();return false;        }        return true;    }}Copy the code

CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal CancellationSignal

Fingerprint recognition based on Android 9.0

As mentioned above, FingerprintAndrP is a specific implementation class based on Android 9.0:

@RequiresApi(api = Build.VERSION_CODES.P)public class FingerprintAndrP implements IFingerprint { private static FingerprintAndrP fingerprintAndrP; Private FingerprintCallback FingerprintCallback; Private CancellationSignal CancellationSignal; . / / the fingerprint encryption private static BiometricPrompt CryptoObject CryptoObject; @Override public void authenticate(Activity context, VerificationDialogStyleBean verificationDialogStyleBean, FingerprintCallback callback) {// Check whether the fingerprint is availableif(! canAuthenticate(context, callback))return; this.fingerprintCallback = callback; /* * Initialize BiometricPrompt. // Build BiometricPrompt BiometricPrompt = builder.build(); CancellationSignal = new cancellationSignal (); cancellationSignal.setOnCancelListener(() -> { }); /* * Pull up the fingerprint authentication module and wait for validation * Executor:  * context.getMainExecutor() */ biometricPrompt.authenticate(cryptoObject, cancellationSignal, context.getMainExecutor(), authenticationCallback); } public static FingerprintAndrPnewInstance() {        if (fingerprintAndrP == null) {            synchronized (FingerprintAndrM.class) {                if(fingerprintAndrP == null) { fingerprintAndrP = new FingerprintAndrP(); }}} // Fingerprint encryption, Cipher initialization in advance, Prevent the fingerprint authentication has not been initialized when finish the try {cryptoObject = new BiometricPrompt. CryptoObject (new CipherHelper () createCipher ()); } catch (Exception e) { e.printStackTrace(); }returnfingerprintAndrP; } / certification results callback * * * * / private BiometricPrompt. AuthenticationCallback AuthenticationCallback = new BiometricPrompt.AuthenticationCallback() {        @Override        public void onAuthenticationError(int errorCode, CharSequence errString) {            super.onAuthenticationError(errorCode, errString);            if(fingerprintCallback ! = null) {if(errorCode = = 5) {/ / the user to cancel the fingerprint authentication, need not to throw the user information fingerprintCallback. OnCancel ();return;                }                fingerprintCallback.onError(errorCode, errString.toString());            }        }        @Override        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {            super.onAuthenticationHelp(helpCode, helpString);            if(fingerprintCallback ! = null) fingerprintCallback.onError(helpCode, helpString.toString());        }        @Override        public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {            super.onAuthenticationSucceeded(result);            if(fingerprintCallback ! = null) fingerprintCallback.onSucceeded(); } @Override public voidonAuthenticationFailed() {            super.onAuthenticationFailed();            if(fingerprintCallback ! = null) fingerprintCallback.onFailed(); }}; / * * the Android Q, Google provides Api BiometricManager. CanAuthenticate () is used to detect fingerprint hardware is available and whether to add the fingerprint * but has not yet open, marked"Stub"* */ private Boolean canAuthenticate(Context Context, FingerprintCallback FingerprintCallback) {/* * Whether the hardware supports fingerprint recognition * */if(! FingerprintManagerCompat.from(context).isHardwareDetected()) { fingerprintCallback.onError(FingerprintManager.FINGERPRINT_ERROR_HW_NOT_PRESENT, context.getString(R.string.biometricprompt_verify_error_no_hardware));return false; } // Whether fingerprints have been addedif(! FingerprintManagerCompat.from(context).hasEnrolledFingerprints()) { fingerprintCallback.onNoneEnrolled();return false;        }        return true;    }}Copy the code

There are two things developers should be aware of:

(1) Android 9.0 does not allow developers to customize the fingerprint frame, but the flexibility of the fingerprint frame provided by the system is somewhat uncertain. At the moment, for example, the system only allows a button in the recognition box that says “Cancel” and cannot say “Verify password,” and that says “Verify password” and cannot say “cancel. (Embarrassed face…)

(2) The fingerprint identification box provided by the system can only be located at the bottom of the interface, not in the middle. But on some phones (like the OPPO Reno), the fingerprint sensor is also at the bottom of the screen, and when pulled up, a fingerprint icon is displayed in the sensor’s place to tell the user where to point. However, the fingerprint identification frame provided by the system also has a fingerprint icon, and the two fingerprint ICONS overlap or are very close to each other. (Embarrassed face…)

Example:

Fingerprint icon overlap

EnableAndroidP (Boolean enableAndroidP) allows the caller to enable or disable the fingerprint identification box provided by Android 9.0. When off, Android 6.0’s fingerprint Api is used and a custom fingerprint box is used.


Fingerprint identification library making address: https://github.com/ZuoHailong/BiometricPrompt

This article is not easy, if you like this article, or helpful to you hope you more, like, forward, follow oh. The article will be updated continuously. Absolutely dry!!