Determine if biometric hardware is present and the user has enrolled biometrics on Android P

前端 未结 8 1170
半阙折子戏
半阙折子戏 2020-12-13 05:50

I\'m asked to show certain UI elements depending on the presence of biometric hardware. For Android 23-27 I use FingerprintManager#isHardwareDetected() and

相关标签:
8条回答
  • 2020-12-13 06:31

    There is class method available FingerprintManagerCompat.from(this).isHardwareDetected androidx.core.hardware.fingerprint package.

    0 讨论(0)
  • 2020-12-13 06:38

    Google finally solved this problem with Android Q

    The android.hardware.biometrics.BiometricManager#canAuthenticate() method can be used to determine if biometrics can be used.

    The method can be used to determine if biometric hardware is present and if the user is enrolled or not.

    Returns BIOMETRIC_ERROR_NONE_ENROLLED if the user does not have any enrolled, or BIOMETRIC_ERROR_HW_UNAVAILABLE if none are currently supported/enabled. Returns BIOMETRIC_SUCCESS if a biometric can currently be used (enrolled and available).

    Hopefully this is added to the androidx.biometric:biometric library, so it can be used on all devices.

    Until then the solution by @algrid works to determine biometrics enrollment.

    And the following can be used to determine, if a fingerprint reader is present.

    Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
                context.packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
    
    0 讨论(0)
  • 2020-12-13 06:40

    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

    0 讨论(0)
  • 2020-12-13 06:42

    In my biometrics, I used these and a few more checks to make sure that the device was capable and the fingerprint was enabled. In Kotlin I created an Object class and called it utilities.

    object BiometricUtilities {
        fun isBiometricPromptEnabled(): Boolean {
            return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
        }
        fun isSdkVersionSupported(): Boolean {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
        }
        fun isHardwareSupported(context: Context): Boolean {
            val fingerprintManager = FingerprintManagerCompat.from(context)
            return fingerprintManager.isHardwareDetected
        }
        fun isFingerprintAvailable(context: Context): Boolean {
            val fingerprintManager = FingerprintManagerCompat.from(context)
            return fingerprintManager.hasEnrolledFingerprints()
        }
    }
    

    Then in my bioManager class, I placed the conditional statements under a function called authenticate that implements BiometricCallback

    fun authenticate(biometricCallback: BiometricCallback) {
    
        if (!BiometricUtilities.isHardwareSupported(context)) {
            biometricCallback.onBiometricAuthenticationNotSupported()
            return
        }
    
        if (!BiometricUtilities.isFingerprintAvailable(context)) {
            val intent = Intent(Settings.ACTION_SECURITY_SETTINGS)
               biometricCallback.onBiometricAuthenticationNotAvailable()
            return
        }
    
        displayBiometricDialog(biometricCallback)
    }
    

    This way you can check for availability of hardware and presence of fingerprint on the device and let the OS help you to display a prompt or not

    0 讨论(0)
  • 2020-12-13 06:45

    The method - checks that the user has biometric authentication permission enabled for the app before using the package manager to verify that fingerprint authentication is available on the device. And even it will check if the user is enrolled or not.

    implementation 'androidx.biometric:biometric:1.0.0-alpha03'

    private Boolean checkBiometricSupport() {
    
        KeyguardManager keyguardManager =
                (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
    
        PackageManager packageManager = this.getPackageManager();
    
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            notifyUser("This Android version does not support fingerprint authentication.");
            return false;
        }
    
        if(!packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
        {
            notifyUser("Fingerprint Sensor not supported");
            return false;
        }
    
        if (!keyguardManager.isKeyguardSecure()) {
            notifyUser("Lock screen security not enabled in Settings");
    
            return false;
        }
    
        if (ActivityCompat.checkSelfPermission(this,
                Manifest.permission.USE_BIOMETRIC) !=
                PackageManager.PERMISSION_GRANTED) {
            notifyUser("Fingerprint authentication permission not enabled");
    
            return false;
        }
    
        return true;
    }
    
    0 讨论(0)
  • 2020-12-13 06:49

    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

    0 讨论(0)
提交回复
热议问题