Android获取手机已录入指纹信息列表

序言

Android SDK是不开放给普通开发者系统已录入的指纹信息列表的,那当我们需要获取手机系统中已录入指纹信息的时候,应该怎么做呢?

正常的Android指纹识别怎么做

旧版写法 使用FingerprintManager

Java 复制代码
private class AuthenticationCallback extends FingerprintManager.AuthenticationCallback {
    @Override
    public void onAuthenticationError(int errorCode, CharSequence errString) {
        super.onAuthenticationError(errorCode, errString);
        Toast.makeText(MainActivity.this, "认证错误:" + errString, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
        super.onAuthenticationHelp(helpCode, helpString);
        Toast.makeText(MainActivity.this, "认证帮助:" + helpString, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
        super.onAuthenticationSucceeded(result);
        Toast.makeText(MainActivity.this, "认证成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onAuthenticationFailed() {
        super.onAuthenticationFailed();
        Toast.makeText(MainActivity.this, "认证失败", Toast.LENGTH_SHORT).show();
    }
}

新版写法 使用BiometricManager

java 复制代码
BiometricPrompt.AuthenticationCallback callback = new BiometricPrompt.AuthenticationCallback() {
    @Override
    public void onAuthenticationError(int errorCode, CharSequence errString) {
        super.onAuthenticationError(errorCode, errString);
        // 处理认证错误
    }

    @Override
    public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
        super.onAuthenticationSucceeded(result);
        // 认证成功
        // 在这里可以执行登录逻辑
    }

    @Override
    public void onAuthenticationFailed() {
        super.onAuthenticationFailed();
        // 认证失败
    }
};

对比发现,新的onAuthenticationSucceeded认证成功后返回参数result对象为 BiometricPrompt.AuthenticationResult ,而以前的写法是返回的FingerprintManager.AuthenticationResult

先查看旧版中 FingerprintManager.AuthenticationResult 源码,可以看到类已经@Deprecated,让我们使用新版的android.hardware.biometrics.BiometricPrompt.AuthenticationResult,这里面有个属性是Fingerprint类,看着很像是存放指纹信息的内容

java 复制代码
/**
 * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
 *     CancellationSignal, int, AuthenticationCallback, Handler)}.
 * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationResult}
 */
@Deprecated
public static class AuthenticationResult {
    private Fingerprint mFingerprint;
    private CryptoObject mCryptoObject;
    private int mUserId;
    private boolean mIsStrongBiometric;

    /**
     * Authentication result
     *
     * @param crypto the crypto object
     * @param fingerprint the recognized fingerprint data, if allowed.
     * @hide
     */
    public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId,
            boolean isStrongBiometric) {
        mCryptoObject = crypto;
        mFingerprint = fingerprint;
        mUserId = userId;
        mIsStrongBiometric = isStrongBiometric;
    }

    /**
     * Obtain the crypto object associated with this transaction
     * @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject,
     *     CancellationSignal, int, AuthenticationCallback, Handler)}.
     */
    public CryptoObject getCryptoObject() { return mCryptoObject; }

    /**
     * Obtain the Fingerprint associated with this operation. Applications are strongly
     * discouraged from associating specific fingers with specific applications or operations.
     *
     * @hide
     */
    @UnsupportedAppUsage
    public Fingerprint getFingerprint() { return mFingerprint; }

    /**
     * Obtain the userId for which this fingerprint was authenticated.
     * @hide
     */
    public int getUserId() { return mUserId; }

    /**
     * Check whether the strength of the fingerprint modality associated with this operation is
     * strong (i.e. not weak or convenience).
     * @hide
     */
    public boolean isStrongBiometric() {
        return mIsStrongBiometric;
    }
}

查看Fingerprint 类源码,Fingerprint 继承于BiometricAuthenticator.Identifier ,这个好像有点眼熟啊,和新版的BiometricPrompt 很像啊,我们先放着,去看看新版BiometricPrompt.AuthenticationResult的源码

java 复制代码
/**
 * Container for fingerprint metadata.
 * @hide
 */
public final class Fingerprint extends BiometricAuthenticator.Identifier {
    private int mGroupId;

    public Fingerprint(CharSequence name, int groupId, int fingerId, long deviceId) {
        super(name, fingerId, deviceId);
        mGroupId = groupId;
    }

    public Fingerprint(CharSequence name, int fingerId, long deviceId) {
        super(name, fingerId, deviceId);
    }

    private Fingerprint(Parcel in) {
        super(in.readString(), in.readInt(), in.readLong());
        mGroupId = in.readInt();
    }

    /**
     * Gets the group id specified when the fingerprint was enrolled.
     * @return group id for the set of fingerprints this one belongs to.
     */
    public int getGroupId() {
        return mGroupId;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeString(getName().toString());
        out.writeInt(getBiometricId());
        out.writeLong(getDeviceId());
        out.writeInt(mGroupId);
    }

    public static final @android.annotation.NonNull Parcelable.Creator<Fingerprint> CREATOR
            = new Parcelable.Creator<Fingerprint>() {
        public Fingerprint createFromParcel(Parcel in) {
            return new Fingerprint(in);
        }

        public Fingerprint[] newArray(int size) {
            return new Fingerprint[size];
        }
    };
}

新版 BiometricPrompt.AuthenticationResult 源码

java 复制代码
public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult

从代码来看BiometricPrompt.AuthenticationResult 是继承BiometricAuthenticator.AuthenticationResult 的,这个就和上面的Fingerprint 继承于BiometricAuthenticator.Identifier 联系上了啊,一家人整整齐齐了的,那么继续看BiometricAuthenticator.AuthenticationResult类的源码

java 复制代码
/**
 * Container for callback data from {@link BiometricAuthenticator#authenticate(
 * CancellationSignal, Executor, AuthenticationCallback)} and
 * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
 * AuthenticationCallback)}
 */
class AuthenticationResult {
    private Identifier mIdentifier;
    private CryptoObject mCryptoObject;
    private @AuthenticationResultType int mAuthenticationType;
    private int mUserId;

    /**
     * @hide
     */
    public AuthenticationResult() { }

    /**
     * Authentication result
     * @param crypto
     * @param authenticationType
     * @param identifier
     * @param userId
     * @hide
     */
    public AuthenticationResult(CryptoObject crypto,
            @AuthenticationResultType int authenticationType, Identifier identifier,
            int userId) {
        mCryptoObject = crypto;
        mAuthenticationType = authenticationType;
        mIdentifier = identifier;
        mUserId = userId;
    }

    /**
     * Provides the crypto object associated with this transaction.
     * @return The crypto object provided to {@link BiometricPrompt#authenticate(
     * BiometricPrompt.CryptoObject, CancellationSignal, Executor,
     * BiometricPrompt.AuthenticationCallback)}
     */
    public CryptoObject getCryptoObject() {
        return mCryptoObject;
    }

    /**
     * Provides the type of authentication (e.g. device credential or biometric) that was
     * requested from and successfully provided by the user.
     *
     * @return An integer value representing the authentication method used.
     */
    public @AuthenticationResultType int getAuthenticationType() {
        return mAuthenticationType;
    }

    /**
     * Obtain the biometric identifier associated with this operation. Applications are strongly
     * discouraged from associating specific identifiers with specific applications or
     * operations.
     * @hide
     */
    public Identifier getId() {
        return mIdentifier;
    }

    /**
     * Obtain the userId for which this biometric was authenticated.
     * @hide
     */
    public int getUserId() {
        return mUserId;
    }
};

BiometricPrompt.AuthenticationResult 类中有Identifier 这个类诶,那这个类和老版本的Fingerprint是一样的作用啊,那仔细再查看下Identifier类的内容

java 复制代码
/**
 * Container for biometric data
 * @hide
 */
abstract class Identifier implements Parcelable {
    private CharSequence mName;
    private int mBiometricId;
    private long mDeviceId; // physical device this is associated with

    public Identifier() {}

    public Identifier(CharSequence name, int biometricId, long deviceId) {
        mName = name;
        mBiometricId = biometricId;
        mDeviceId = deviceId;
    }

    /**
     * Gets the human-readable name for the given biometric.
     * @return name given to the biometric
     */
    public CharSequence getName() {
        return mName;
    }

    /**
     * Gets the device-specific biometric id.  Used by Settings to map a name to a specific
     * biometric template.
     */
    public int getBiometricId() {
        return mBiometricId;
    }

    /**
     * Device this biometric belongs to.
     */
    public long getDeviceId() {
        return mDeviceId;
    }

    public void setName(CharSequence name) {
        mName = name;
    }

    public void setDeviceId(long deviceId) {
        mDeviceId = deviceId;
    }
}

mBiometricId 生物识别id这个属性, 好像是找到了,但是这个要怎么获取呢?

我们再找找有没有Fingerprint 列表的获取方式,find一下List<Fingerprint>或者 List<BiometricAuthenticator.Identifier>

还真找到了,在 FingerprintManager.java 中有一个方法返回List<Fingerprint>,这个注释看着也像是获取指纹列表啊

java 复制代码
/**
 * Obtain the list of enrolled fingerprints templates.
 * @return list of current fingerprint items
 *
 * @hide
 */
@RequiresPermission(USE_FINGERPRINT)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<Fingerprint> getEnrolledFingerprints(int userId) {
    if (mService != null) try {
            return mService.getEnrolledFingerprints(
                    userId, mContext.getOpPackageName(), mContext.getAttributionTag());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
    return null;
}

Android获取手机已录入指纹信息列表-解决方案

我们看到都是@hide,Android隐藏接口,我们要想访问的话,需要使用反射+绕过Android禁止访问隐藏api的方式,我直接用了第三方库FreeReflection这个库,实测在Android13上也没有问题

java 复制代码
@SuppressLint("WrongConstant") FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
try {
    Class clz = Class.forName("android.hardware.fingerprint.FingerprintManager");
    Method method = clz.getDeclaredMethod("getEnrolledFingerprints");
    method.setAccessible(true);
    Object obj = method.invoke(fingerprintManager, (Object[]) null);
    if (obj != null) {
        Log.i(TAG, "FingerprintInfo: " + gson.toJson(obj));
    }
} catch (Exception e) {
    Log.i(TAG, ""+e.getMessage());
    return null;
}
return null;

打印出来的信息就是 [{"mGroupId":0,"mBiometricId":-123,"mDeviceId":213232,"mName":"手指 1"},{"mGroupId":0,"mBiometricId":123,"mDeviceId":213232,"mName":"手指 2"}]

对于Android 9以下的手机这里是返回的是,老版的Fingerprint类的内容,

我查了下2015-04-02提交的Fingerprint类的代码内容如下

java 复制代码
/**
 * Container for fingerprint metadata.
 * @hide
 */
public final class Fingerprint implements Parcelable {
    private CharSequence mName;
    private int mGroupId;
    private int mFingerId;
    private long mDeviceId; // physical device this is associated with

    public Fingerprint(CharSequence name, int groupId, int fingerId, long deviceId) {
        mName = name;
        mGroupId = groupId;
        mFingerId = fingerId;
        mDeviceId = deviceId;
    }

    private Fingerprint(Parcel in) {
        mName = in.readString();
        mGroupId = in.readInt();
        mFingerId = in.readInt();
        mDeviceId = in.readLong();
    }

    /**
     * Gets the human-readable name for the given fingerprint.
     * @return name given to finger
     */
    public CharSequence getName() { return mName; }

    /**
     * Gets the device-specific finger id.  Used by Settings to map a name to a specific
     * fingerprint template.
     * @return device-specific id for this finger
     * @hide
     */
    public int getFingerId() { return mFingerId; }

    /**
     * Gets the group id specified when the fingerprint was enrolled.
     * @return group id for the set of fingerprints this one belongs to.
     * @hide
     */
    public int getGroupId() { return mGroupId; }

    /**
     * Device this fingerprint belongs to.
     * @hide
     */
    public long getDeviceId() { return mDeviceId; }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeString(mName.toString());
        out.writeInt(mGroupId);
        out.writeInt(mFingerId);
        out.writeLong(mDeviceId);
    }

    public static final Parcelable.Creator<Fingerprint> CREATOR
            = new Parcelable.Creator<Fingerprint>() {
        public Fingerprint createFromParcel(Parcel in) {
            return new Fingerprint(in);
        }

        public Fingerprint[] newArray(int size) {
            return new Fingerprint[size];
        }
    };
};

老版的Fingerprint 类,还没有继承继承于BiometricAuthenticator.Identifier,因此,指纹id的信息用的是mFingerId属性来存储的。所以,在适配上,我们只要对mFingerId和mBiometricId都做处理,就可以把本机的指纹信息保存下来了。

总结

Andriod官方是不建议使用隐藏方法的,因此,除非是我们开发的需求,确实要比对不同手指的指纹信息,否则我们用不着去做,获取本机指纹信息的操作

相关推荐
太空漫步112 小时前
android社畜模拟器
android
海绵宝宝_5 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
凯文的内存6 小时前
android 定制mtp连接外设的设备名称
android·media·mtp·mtpserver
天若子6 小时前
Android今日头条的屏幕适配方案
android
林的快手8 小时前
伪类选择器
android·前端·css·chrome·ajax·html·json
望佑8 小时前
Tmp detached view should be removed from RecyclerView before it can be recycled
android
无限大69 小时前
算法精讲--动态规划四步法则
java·算法·程序员
xvch10 小时前
Kotlin 2.1.0 入门教程(二十四)泛型、泛型约束、绝对非空类型、下划线运算符
android·kotlin
人民的石头14 小时前
Android系统开发 给system/app传包报错
android
yujunlong391914 小时前
android,flutter 混合开发,通信,传参
android·flutter·混合开发·enginegroup