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

Posted

技术标签:

【中文标题】确定是不是存在生物识别硬件以及用户是不是已在 Android P 上注册生物识别【英文标题】:Determine if biometric hardware is present and the user has enrolled biometrics on Android P确定是否存在生物识别硬件以及用户是否已在 Android P 上注册生物识别 【发布时间】:2018-11-30 19:09:01 【问题描述】:

我被要求根据生物识别硬件的存在显示某些 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 似乎也不是一个解决方案,因为我不知道何时取消提示。

有没有办法检测生物识别硬件的存在和用户注册?

【问题讨论】:

Android 错误跟踪器中的相应问题:issuetracker.google.com/issues/109826221 这有什么好运气吗? @Rahul 没有。Android 错误跟踪器中有一个更新:“您可以检查 PackageManager.FEATURE_FINGERPRINT,这是目前 BiometricPrompt 唯一支持的生物识别。”我还没试过。 【参考方案1】:

Google 终于用 Android Q 解决了这个问题

android.hardware.biometrics.BiometricManager#canAuthenticate() 方法可用于确定是否可以使用生物识别技术。

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

如果用户没有任何注册,则返回 BIOMETRIC_ERROR_NONE_ENROLLED,如果当前不支持/启用,则返回 BIOMETRIC_ERROR_HW_UNAVAILABLE。如果当前可以使用生物特征(已注册且可用),则返回 BIOMETRIC_SUCCESS。

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

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

以下可用于确定是否存在指纹读取器。

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

【讨论】:

这仅适用于 Android 10,对于 6 到 9,它会在面部、虹膜等注册时返回 HW_UNAVAILABLE(指纹除外) @olearyj234 你知道在没有指纹的情况下检测面部、虹膜...等的想法吗?【参考方案2】:

AndroidX 生物识别库从版本 1.0.0-beta01 (androidx.biometric:biometric:1.0.0-beta01) 开始提供此类信息

BiometricManager.from(context).canAuthenticate()

返回一个

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

【讨论】:

这仅适用于 Android 10,对于 6 到 9,它将为面部、虹膜等返回 HW_UNAVAILABLE,但指纹除外 库中实际上存在错误(应该在下一个版本中?)请参阅问题跟踪器issuetracker.google.com/issues/140427586 那个问题是针对 api issuetracker.google.com/issues/140398825(带有不同的错误代码) 可能是不同的问题【参考方案3】:

遗憾的是,Google 将相关问题的状态更改为“无法修复(预期行为)”,因此无法解决此问题。我现在更喜欢使用旧的已弃用 API。

但是对于那些想要使用更新的 API 的人来说,有一个 hacky/丑陋的方法来获得 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 密钥库文档声明:

用户身份验证授权与一个密钥关联的特定加密操作。在此模式下,涉及此类密钥的每个操作都必须由用户单独授权。目前,这种授权的唯一方式是指纹认证:FingerprintManager.authenticate。 只有在至少注册了一个指纹时才能生成或导入此类密钥(请参阅 FingerprintManager.hasEnrolledFingerprints)。一旦注册了新指纹或取消注册所有指纹,这些密钥将永久失效。

请参阅此处https://developer.android.com/training/articles/keystore 的“需要用户身份验证才能使用密钥”部分

【讨论】:

这不能用于检查生物特征,因为用户身份验证也适用于模式/pin/密码。 @slhddn 你试过了吗?关键是在此处使用KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT 的用户身份验证。 我看过这部分“一旦注册新指纹或取消注册所有指纹,这些密钥将永久失效”。但是,一旦注册了新的指纹,如果它们都失效了,你如何管理以前的密钥呢? 我认为正确的方法是捕获所有 keyGenerator.init() 和 cipher.init() 异常并实施适当的回退模式。关键是:有很多可能的情况和异常,您不能依赖像 FingerprintManager API 这样的检查。【参考方案4】:

我为 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"

【讨论】:

我无法在我的代码中导入 BiometricManager 我认为你必须在你的 gradle 应用文件上使用:compileSdkVersion 29, and buildToolsVersion "29.0.1"。 我无法导入 BiometricManager,然后我注意到我的目标是 28,所以我将我的 gradle 更新为 29【参考方案5】:

在我的生物识别中,我使用了这些和其他一些检查来确保设备能够正常工作并且启用了指纹。在 Kotlin 中,我创建了一个 Object 类并将其称为实用程序。

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 类中,我将条件语句放在一个名为 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)

这样您可以检查硬件的可用性和设备上是否存在指纹,并让操作系统帮助您显示提示或不显示提示

【讨论】:

【参考方案6】:

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

实现'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;

【讨论】:

【参考方案7】:

有可用的类方法FingerprintManagerCompat.from(this).isHardwareDetectedandroidx.core.hardware.fingerprint包。

【讨论】:

【参考方案8】:

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

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

的最新版本

【讨论】:

以上是关于确定是不是存在生物识别硬件以及用户是不是已在 Android P 上注册生物识别的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 selenium C# 识别页面上是不是存在浏览器警告框

megacli64 确定哪个硬盘坏了

如何使用 Lync SDK 通过电子邮件确定是不是存在有效的 Lync 用户?

我们如何确定 mongodb 中是不是已经存在用户名和电子邮件地址..?

使用 PHP 检查用户名和电子邮件是不是已经存在 [重复]

检查用户是不是已在 Swift 中注册本地通知时出错