Question: I'm asked to show certain UI elements depending on the presence of biometric hardware. For Android 23-27 I use `FingerprintManager#isHardwareDetected()` and `FingerprintManager#hasEnrolledFingerprints()`. Both of which are deprecated in Android 28.

I understand that I can get this information by using `BiometricPrompt#authenticate(...)` and receiving either `BiometricPrompt#BIOMETRIC_ERROR_HW_NOT_PRESENT` or `BiometricPrompt#BIOMETRIC_ERROR_NO_BIOMETRICS` in the `BiometricPrompt.AuthenticationCallback#onAuthenticationError(int errorCode, ...)` method. But this would lead to the `BiometricPrompt` being shown on supporting devices, which is undesirable. Using the `CancellationSignal` doesn't seem to be a solution either, since I wouldn't know when to cancel the prompt.

Is there any way to detect biometric hardware presence and user enrolment?

Answer: 
AndroidX biometric library started providing this kind of information from version `1.0.0-beta01` (`androidx.biometric:biometric:1.0.0-beta01`)

```
BiometricManager.from(context).canAuthenticate()

```

Which returns one of

* BIOMETRIC\_SUCCESS
* BIOMETRIC\_ERROR\_HW\_UNAVAILABLE
* BIOMETRIC\_ERROR\_NONE\_ENROLLED
* BIOMETRIC\_ERROR\_NO\_HARDWARE

**See changelog:**
<https://developer.android.com/jetpack/androidx/releases/biometric#1.0.0-beta01>
Sadly Google wouldn't solve this problem having changed the status of related issue to "Won't Fix (Intended behavior)". I prefer to use the old deprecated API for now.

But for those who want to use the newer API there's a hacky/ugly way to get a `hasEnrolledFingerprints()` analog (the code is for API23+):

```
public boolean isBiometryAvailable() {
    KeyStore keyStore;
    try {
        keyStore = KeyStore.getInstance("AndroidKeyStore");
    } catch (Exception e) {
        return false;
    }

    KeyGenerator keyGenerator;
    try {
        keyGenerator = KeyGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
    } catch (NoSuchAlgorithmException |
            NoSuchProviderException e) {
        return false;
    }

    if (keyGenerator == null || keyStore == null) {
        return false;
    }

    try {
        keyStore.load(null);
        keyGenerator.init(new
                KeyGenParameterSpec.Builder("dummy_key",
                KeyProperties.PURPOSE_ENCRYPT |
                        KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                .setUserAuthenticationRequired(true)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                .build());
    } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
            | CertificateException | IOException e) {
        return false;
    }
    return true;

}

```

This is based on the following Android keystore docs statement:

> 
> * User authentication authorizes a specific cryptographic operation associated with one key. In this mode, each operation involving such a key must be individually authorized by the user. Currently, the only means of such authorization is fingerprint authentication: FingerprintManager.authenticate. **Such keys can only be generated or imported if at least one fingerprint is enrolled** (see FingerprintManager.hasEnrolledFingerprints). These keys become permanently invalidated once a new fingerprint is enrolled or all fingerprints are unenrolled.
> 
> 
> 

See the "Require user authentication for key use" section here <https://developer.android.com/training/articles/keystore>