Responding to Authentication Callbacks

The fingerprint scanner runs in the background on its own thread, and when it is finished it will report the results of the scan by invoking one method of FingerprintManager.AuthenticationCallback on the UI thread. An Android application must provide its own handler which extends this abstract class, implementing all the following methods:

  • OnAuthenticationError(int errorCode, ICharSequence errString) – Called when there is an unrecoverable error. There is nothing more an application or user can do to correct the situation except possibly try again.
  • OnAuthenticationFailed() – This method is invoked when a fingerprint has been detected but not recognized by the device.
  • OnAuthenticationHelp(int helpMsgId, ICharSequence helpString) – Called when there is a recoverable error, such as the finger being swiped to fast over the scanner.
  • OnAuthenticationSucceeded(FingerprintManagerCompati.AuthenticationResult result) – This is called when a fingerprint has been recognized.

If a CryptoObject was used when calling Authenticate, it is recommended to call Cipher.DoFinal in OnAuthenticationSuccessful. DoFinal will throw an exception if the cipher was tampered with or improperly initialized, indicating that the result of the fingerprint scanner may have been tampered with outside of the application.

Note

It is recommended to keep the callback class relatively light weight and free of application specific logic. The callbacks should act as a "traffic cop" between the Android application and the results from the fingerprint scanner.

A Sample Authentication Callback Handler

The following class is an example of a minimal FingerprintManager.AuthenticationCallback implementation:

class MyAuthCallbackSample : FingerprintManagerCompat.AuthenticationCallback
{
    // Can be any byte array, keep unique to application.
    static readonly byte[] SECRET_BYTES = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    // The TAG can be any string, this one is for demonstration.
    static readonly string TAG = "X:" + typeof (SimpleAuthCallbacks).Name;

    public MyAuthCallbackSample()
    {
    }

    public override void OnAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result)
    {
        if (result.CryptoObject.Cipher != null) 
        {
            try
            {
                // Calling DoFinal on the Cipher ensures that the encryption worked.
                byte[] doFinalResult = result.CryptoObject.Cipher.DoFinal(SECRET_BYTES);
    
                // No errors occurred, trust the results.              
            }
            catch (BadPaddingException bpe)
            {
                // Can't really trust the results.
                Log.Error(TAG, "Failed to encrypt the data with the generated key." + bpe);
            }
            catch (IllegalBlockSizeException ibse)
            {
                // Can't really trust the results.
                Log.Error(TAG, "Failed to encrypt the data with the generated key." + ibse);
            }
        }
        else
        {
            // No cipher used, assume that everything went well and trust the results.
        }
    }

    public override void OnAuthenticationError(int errMsgId, ICharSequence errString)
    {
        // Report the error to the user. Note that if the user canceled the scan,
        // this method will be called and the errMsgId will be FingerprintState.ErrorCanceled.
    }

    public override void OnAuthenticationFailed()
    {
        // Tell the user that the fingerprint was not recognized.
    }

    public override void OnAuthenticationHelp(int helpMsgId, ICharSequence helpString)
    {
        // Notify the user that the scan failed and display the provided hint.
    }
}

OnAuthenticationSucceeded checks to see if a Cipher was provided to FingerprintManager when Authentication was invoked. If so, the DoFinal method is called on the cipher. This closes the Cipher, restoring it to its original state. If there was a problem with the cipher, then DoFinal will throw an exception and the authentication attempt should be considered to have failed.

The OnAuthenticationError and OnAuthenticationHelp callbacks each receive an integer indicating what the problem was. The following section explains each of the possible help or error codes. The two callbacks serve similar purposes – to inform the application that fingerprint authentication has failed. How they differ is in severity. OnAuthenticationHelp is a user recoverable error, such as swiping the fingerprint too fast; OnAuthenticationError is more a severe error, such as a damaged fingerprint scanner.

Note that OnAuthenticationError will be invoked when the fingerprint scan is cancelled via the CancellationSignal.Cancel() message. The errMsgId parameter will have the value of 5 (FingerprintState.ErrorCanceled). Depending on the requirements, an implementation of the AuthenticationCallbacks may treat this situation differently than the other errors.

OnAuthenticationFailed is invoked when the fingerprint was successfully scanned but did not match any fingerprint enrolled with the device.

Help Codes and Error Message Ids

A list and description of the error codes and help codes may be found in the Android SDK documentation for the FingerprintManager class. Xamarin.Android represents these values with the Android.Hardware.Fingerprints.FingerprintState enum:

  • AcquiredGood – (value 0) The image acquired was good.

  • AcquiredImagerDirty – (value 3) The fingerprint image was too noisy due to suspected or detected dirt on the sensor. For example, it's reasonable to return this after multiple AcquiredInsufficient or actual detection of dirt on the sensor (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor when this is returned.

  • AcquiredInsufficient – (value 2) The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or a possibly dirty sensor (See AcquiredImagerDirty.

  • AcquiredPartial – (value 1) Only a partial fingerprint image was detected. During enrollment, the user should be informed on what needs to happen to resolve this problem, e.g., “press firmly on sensor.”

  • AcquiredTooFast – (value 5) The fingerprint image was incomplete due to quick motion. While mostly appropriate for linear array sensors, this could also happen if the finger was moved during acquisition. The user should be asked to move the finger slower (linear) or leave the finger on the sensor longer.

  • AcquiredToSlow – (value 4) The fingerprint image was unreadable due to lack of motion. This is most appropriate for linear array sensors that require a swipe motion.

  • ErrorCanceled – (value 5) The operation was canceled because the fingerprint sensor is unavailable. For example, this may happen when the user is switched, the device is locked, or another pending operation prevents or disables it.

  • ErrorHwUnavailable – (value 1) The hardware is unavailable. Try again later.

  • ErrorLockout – (value 7) The operation was canceled because the API is locked out due to too many attempts.

  • ErrorNoSpace – (value 4) Error state returned for operations like enrollment; the operation cannot be completed because there's not enough storage remaining to complete the operation.

  • ErrorTimeout – (value 3) Error state returned when the current request has been running too long. This is intended to prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is platform and sensor-specific, but is generally about 30 seconds.

  • ErrorUnableToProcess – (value 2) Error state returned when the sensor was unable to process the current image.