确定Android P系统中是否存在生物识别硬件并且用户已经注册了生物识别。

50

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

是否有任何方法可以检测生物识别硬件的存在以及用户是否注册?


1
在Android错误跟踪器中对应的问题:https://issuetracker.google.com/issues/109826221 - sirius
你对这个有什么进展了吗? - Rahul
1
@Rahul 不是的。Android bug跟踪器中有一个更新:“您可以检查PackageManager.FEATURE_FINGERPRINT,这是目前BiometricPrompt支持的唯一生物识别技术。”我还没有尝试过。 - sirius
https://medium.com/@ghodasarabhaumik/android-fingerprint-enrolment-detection-detect-fingerprint-added-removed-68f8189766f9 - Bhaumik Ghodasara
8个回答

27

谷歌终于在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)

3
这只适用于安卓10,对于6到9版本,当面部、虹膜等被注册时(除了指纹),它会返回HW_UNAVAILABLE。 - James
1
@olearyj234,您知道在没有指纹情况下如何检测面部、虹膜等是否被注册的想法吗? - ppnfk

18

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用于面部、虹膜等,但指纹除外。 - James
这个库实际上存在一个错误(应该在下一个版本中修复?)请参见问题跟踪器 https://issuetracker.google.com/issues/140427586 - mlykotom
这个问题是针对API < 23的;我不确定@olearyj234提到的问题是什么,但可能是与三星特定的https://issuetracker.google.com/issues/140398825有关(具有不同的错误代码)。 - Gabor
可能是另一个问题。 - mlykotom

9
很遗憾,谷歌已经将相关问题的状态更改为“不修复(预期行为)”,无法解决此问题。我现在更喜欢使用旧的弃用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密钥库文档的陈述:
用户认证授权与一个密钥相关的特定加密操作。在此模式下,涉及此类密钥的每个操作都必须由用户单独授权。目前,这种授权的唯一方式是指纹认证:FingerprintManager.authenticate。只有至少注册了一个指纹(参见FingerprintManager.hasEnrolledFingerprints),才能生成或导入此类密钥。一旦注册了新指纹或所有指纹都被注销,这些密钥就会永久失效。
请参阅此处的“要求用户对密钥使用进行身份验证”部分https://developer.android.com/training/articles/keystore

这不能用来检查生物识别,因为用户认证也可以使用图案/密码/ PIN。 - slhddn
@slhddn你试过吗?这里的关键点是使用用户认证和KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT - algrid
我看到了这部分内容:“一旦注册了新的指纹或取消了所有指纹,这些密钥将永久失效”。但是如果所有密钥都失效了,那么在注册新指纹后如何管理以前的密钥呢? - stdout
我认为正确的方法是捕获所有keyGenerator.init()和cipher.init()异常,并实现适当的回退模式。关键在于:有太多可能的情况和异常,你不能依赖于像FingerprintManager API这样的检查。 - Mike76

6
我为 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。 - King of Masses
2
我认为你需要在gradle应用程序文件中使用:compileSdkVersion 29和buildToolsVersion“29.0.1”。 - markomoreno
我无法导入BiometricManager,然后我注意到我的目标是28,所以我将我的gradle更新为29。 - James

2

在我的生物识别技术中,我使用了这些以及其他一些检查来确保设备能够正常工作并启用指纹。在Kotlin中,我创建了一个Object类并将其命名为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()
    }
}

然后在我的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)
}

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


1
该方法-在使用包管理器验证设备上是否可用指纹身份验证之前,检查用户是否已为应用启用生物识别身份验证权限。甚至会检查用户是否已注册。
实现'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

有一个类方法可用:FingerprintManagerCompat.from(this).isHardwareDetected,位于androidx.core.hardware.fingerprint包中。


0

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

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的最新构建


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接