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