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

相关推荐
_小马快跑_2 小时前
ConstraintLayout 中的ImageFilterView探索:处理图片圆角、亮度、饱和度、图片重叠等
android
IT-sec2 小时前
jquery-picture-cut 任意文件上传(CVE-2018-9208)
android·前端·javascript·安全·web安全·网络安全·jquery
xiaoduyyy3 小时前
【Android】RecyclerView回收复用机制
android
林北芒大果4 小时前
【Flutter】搭建Flutter开发环境,安卓开发
android·flutter
m0_748230215 小时前
MySQL 数据库连接池爆满问题排查与解决
android·数据库·mysql
SunshineBrother6 小时前
Flutter求职、面试20+面试官总结:Dart篇
android·前端·flutter
少年姜太公6 小时前
从0开始完全掌握JS中的原型
前端·javascript·程序员
鸿儒5176 小时前
利用adb工具安装卸载安卓平板(手机)软件
android·adb·智能手机
软件聚导航9 小时前
uniapp 安卓和ios震动方法,支持息屏和后台震动,ios和安卓均通过测试
android·ios·uni-app
zhangphil9 小时前
Android叠加双RecyclerView ScaleGestureDetector AnimatorSet动态放大缩小,Kotlin(1)
android·kotlin