确定是否存在生物识别硬件以及用户是否已在 Android P 上注册生物识别

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

我被要求根据生物识别硬件的存在显示某些 UI 元素。对于 Android 23-27,我使用 FingerprintManager#isHardwareDetected()FingerprintManager#hasEnrolledFingerprints()。 Android 28.

中不推荐使用这两者

我知道我可以通过使用 BiometricPrompt#authenticate(...) 并在 BiometricPrompt.AuthenticationCallback#onAuthenticationError(int errorCode, ...) 方法中接收 BiometricPrompt#BIOMETRIC_ERROR_HW_NOT_PRESENTBiometricPrompt#BIOMETRIC_ERROR_NO_BIOMETRICS 来获取此信息。但这会导致 BiometricPrompt 在支持设备上显示,这是不可取的。使用 CancellationSignal 似乎也不是解决方案,因为我不知道何时取消提示。

有什么方法可以检测生物识别硬件的存在和用户注册吗?

遗憾的是 Google 将相关问题的状态更改为 "Won't Fix (Intended behavior)" 无法解决此问题。我现在更喜欢使用旧的已弃用 API。

但是对于那些想要使用较新的 API 的人来说,有一种 hacky/ugly 的方法来获得 hasEnrolledFingerprints() 模拟(代码适用于 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;

}

这基于以下 Android 密钥库文档声明:

  • 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.

请参阅此处的 "Require user authentication for key use" 部分 https://developer.android.com/training/articles/keystore

Google终于用AndroidQ

解决了这个问题

可以使用android.hardware.biometrics.BiometricManager#canAuthenticate()方法来判断是否可以使用生物识别。

该方法可用于确定是否存在生物识别硬件以及用户是否已注册。

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).

希望将其添加到 androidx.biometric:biometric 库中,以便它可以在所有设备上使用。

在此之前,@algrid 的解决方案用于确定生物识别注册。

如果存在指纹 reader,则可以使用以下内容来确定。

Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
            context.packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)

该方法 - 在使用程序包管理器验证设备上的指纹身份验证是否可用之前,检查用户是否为应用程序启用了生物识别身份验证权限。甚至它会检查用户是否已注册。

实施'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;
}

有 class 方法可用 FingerprintManagerCompat.from(this).isHardwareDetected androidx.core.hardware.fingerprint 包。

我为 Kotlin 写了这个方法:

fun checkForBiometrics() : Boolean{
    Log.d(TAG, "checkForBiometrics started")
    var canAuthenticate = true
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (Build.VERSION.SDK_INT < 29) {
            val keyguardManager : KeyguardManager = applicationContext.getSystemService(KEYGUARD_SERVICE) as KeyguardManager
            val packageManager : PackageManager   = applicationContext.packageManager
            if(!packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
                Log.w(TAG, "checkForBiometrics, Fingerprint Sensor not supported")
                canAuthenticate = false
            }
            if (!keyguardManager.isKeyguardSecure) {
                Log.w(TAG, "checkForBiometrics, Lock screen security not enabled in Settings")
                canAuthenticate = false
            }
        } else {
            val biometricManager : BiometricManager = this.getSystemService(BiometricManager::class.java)
            if(biometricManager.canAuthenticate() != BiometricManager.BIOMETRIC_SUCCESS){
                Log.w(TAG, "checkForBiometrics, biometrics not supported")
                canAuthenticate = false
            }
        }
    }else{
        canAuthenticate = false
    }
    Log.d(TAG, "checkForBiometrics ended, canAuthenticate=$canAuthenticate ")
    return canAuthenticate
}

此外,您必须在您的应用 gradle 文件上实施依赖项:

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

并使用最新的构建工具:

compileSdkVersion 29
buildToolsVersion "29.0.1"

对于那些不想等待支持库发布的人,你可以像这样使用夜间构建

repositories {
        maven {
            url "https://ci.android.com/builds/submitted/5795878/androidx_snapshot/latest/repository/"
        }
    }

implementation group: 'androidx.biometric', name: 'biometric', version: '1.0.0-SNAPSHOT'

从这里获取构建版本

https://ci.android.com/builds/branches/aosp-androidx-master-dev/

分支aosp-androidx-master-dev

显示来自 androidx-snapshot

的最新版本

AndroidX 生物识别库从版本 1.0.0-beta01 (androidx.biometric:biometric:1.0.0-beta01)

开始提供此类信息
BiometricManager.from(context).canAuthenticate()

其中returns其中之一

  • BIOMETRIC_SUCCESS
  • BIOMETRIC_ERROR_HW_UNAVAILABLE
  • BIOMETRIC_ERROR_NONE_ENROLLED
  • BIOMETRIC_ERROR_NO_HARDWARE

查看更新日志: https://developer.android.com/jetpack/androidx/releases/biometric#1.0.0-beta01

在我的生物识别中,我使用了这些和更多的检查来确保设备有能力并且启用了指纹。在 Kotlin 中,我创建了一个对象 class 并将其命名为实用程序。

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()
    }
}

然后在我的 bioManager class 中,我将条件语句放在一个名为 authenticate 的函数下,该函数实现了 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)
}

通过这种方式,您可以检查设备上硬件的可用性和指纹的存在,并让 OS 帮助您显示提示或不显示提示