第一部分、Fingerprint HIDL
在配有指纹传感器的设备上,用户可以注册一个或多个指纹,并使用这些指纹来解锁设备以及执行其他任务。Android 会利用 Fingerprint HIDL(硬件接口定义语言)连接到供应商专用库和指纹硬件(例如指纹传感器)。
要实现Fingerprint HIDL,你必须在某个供应商专用库中实现 IBiometricsFingerprint.hal
指纹匹配条件
设备的指纹传感器通常处于闲置状态。不过,为了响应对authenticate 或 enroll 的调用,指纹传感器会监听触摸操作(并且屏幕可能会在用户触摸传感器时被唤醒)。指纹匹配的概要流程包括以下步骤:
1.用户将手指放在传感器上。
2.供应商专用库会根据当前已经注册指纹模板集来判断是否匹配。
3.匹配结果会传递到FingerprintService。
指纹匹配架构
Fingerprint HAL会与以下组件交互 :
· BiometricManager 会在应用进程中与应用直接交互,每个应用都有一个 IBiometricsFringerprint.hal 实例。
· FingerprintService 在系统进程中运行,可处理与 Fingerprint HAL 之间的通信。
· Fingerprint HAL 是 IBiometricsFringerprint HIDL 接口 C/C++实现。它包含可与设备专与硬件进行通信的供应商专用库。
· Keystore API 和 KeyMaster 组件提供由硬件支持的加密功能,以便在安全环境(例如可信执行环境( TEE ))中安全地存储密钥。
第二部分、指纹模块流程分析
要实现Fingerprint HIDL,你必须在某个供应商专用库中实现IBiometricsFingerprint.hal.
IBiometricsFingerprint.hal中主要包含以下主要方法:enroll();preEnroll();getAuthenticatorld();cancel();enumerate();remove();setActiveGroup();authenticate();setNotify();postEnroll()。
指纹模块中的录入,匹配,移除是三个大部分;
一、指纹录入
指纹录入的入口在Settings中的FingerprintEnrollEnrolling.java类中
在这个类中没有看到明显的录入的方法,只有一些UI的加载和一些录入动画的逻辑。
在此类中的updateProgress(boolean animate)更新录入进度的方法中用
int progress = getProgress(
mSidecar.getEnrollmentSteps(), mSidecar.getEnrollmentRemaining());
来获取实际的进度;
此类中没有mSidecar的获取方法,但是此类继承自BiometricsEnrolling,我们去他的父类中找找
BiometricsEnrollEnrolling.java
在此类中我们可以看到mSidecar的获取逻辑
public void startEnrollment() {
mSidecar = (BiometricEnrollSidecar) getSupportFragmentManager()
.findFragmentByTag(TAG_SIDECAR);
if (mSidecar == null) {
mSidecar = getSidecar();/*重点关注*/
getSupportFragmentManager().beginTransaction().add(mSidecar, TAG_SIDECAR)
.commitAllowingStateLoss();
}
mSidecar.setListener(this);
}
通过BiometricEnrollSider强转类型或是getSider()方法,而getSider()在此类中是一个抽象方法,具体在子类中实现,具体在他的子类中也是返回了一个继承自BiometricEnrollSidecar的FingerprintEnrollSider对象
FingerprintEnrollSidecar.java
在此类中我们终于发现了核心代码在该类的startEnrollment()方法中使用mFingerprintManager.enroll(mToken,mEnrollmentCancel,0,mUserId,mEnrollmentCallback);来监听录入过程中的onEnrollmentProgress,onEnrollmentHelp,onEnrollmentError的录入状态
而FingerprintEnrollSidecar的父类是BiometricEnrollSidecar,该类是继承自InstrumentedFragment,所以说明该类为一个Fragment,该类的生命周期也遵循Fragment;
在BiometricEnrollSidecar的onStart方法中有执行startEnrollment()
frameworks/base/core/java/android/hardware/fingerprint/
FingerprintManager.java
从调用FingerprintManager.enroll方法开始录入的
public void enroll(byte [] token, CancellationSignal cancel, int flags,
int userId, EnrollmentCallback callback) {
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentUserId();
}
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
if (cancel != null) {
if (cancel.isCanceled()) {
Slog.w(TAG, "enrollment already canceled");
return;
} else {
cancel.setOnCancelListener(new OnEnrollCancelListener());
}
}
if (mService != null) try {
mEnrollmentCallback = callback;
/*重点关注*/
mService.enroll(mToken, token, userId, mServiceReceiver, flags,
mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */));
}
}
}
从FingerprintManager的enroll方法可以看出主要是通过调用mService.enroll开始进行录入的,而这个mService也就是传进来的
FIngerprintManager(Context context, IFingerprintService){
...
mService = service;
...
}
的实现类的参数。
frameworks /base /services/ core/ java com/ android /server /biometrics/ fingerprint /
FingerprintService.java
在FingerprintService中注册了IFingerprintService
private final class FingerprintServiceWrapper extends IFingerPrintService.Stub实现了enroll方法
public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
checkPermission(MANAGE_FINGERPRINT);
final boolean restricted = isRestricted();
final int groupId = userId; // default group for fingerprint enrollment
final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */) {
@Override
public boolean shouldVibrate() {
return true;
}
@Override
protected int statsModality() {
return FingerprintService.this.statsModality();
}
};
/*重点关注*/
enrollInternal(client, userId);
}
在FingerprintService的父类BiometricServiceBase中
frameworks/base/services/core/java/com/android/server/biometrics/
BiometricServiceBase.java
protected void enrollInternal(EnrollClientImpl client, int userId) {
if (hasReachedEnrollmentLimit(userId)) {
return;
}
// Group ID is arbitrarily set to parent profile user ID. It just represents
// the default biometrics for the user.
if (!isCurrentUserOrProfile(userId)) {
return;
}
mHandler.post(() -> {
/*重点关注*/
startClient(client, true /* initiatedByClient */);
});
}
startClient(client, true /* initiatedByClient */);
private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
// This check only matters for FingerprintService, since enumerate may call back
// multiple times.
if (currentClient instanceof InternalEnumerateClient
|| currentClient instanceof InternalRemovalClient) {
// This condition means we're currently running internal diagnostics to
// remove extra templates in the hardware and/or the software
// TODO: design an escape hatch in case client never finishes
if (newClient != null) {
Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
+ ", initiatedByClient = " + initiatedByClient);
}
} else {
currentClient.stop(initiatedByClient);
// Only post the reset runnable for non-cleanup clients. Cleanup clients should
// never be forcibly stopped since they ensure synchronization between HAL and
// framework. Thus, we should instead just start the pending client once cleanup
// finishes instead of using the reset runnable.
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
}
mPendingClient = newClient;
} else if (newClient != null) {
// For BiometricPrompt clients, do not start until
// <Biometric>Service#startPreparedClient is called. BiometricService waits until all
// modalities are ready before initiating authentication.
if (newClient instanceof AuthenticationClient) {
AuthenticationClient client = (AuthenticationClient) newClient;
if (client.isBiometricPrompt()) {
if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
mCurrentClient = newClient;
if (mBiometricService == null) {
mBiometricService = IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE));
}
try {
mBiometricService.onReadyForAuthentication(client.getCookie(),
client.getRequireConfirmation(), client.getTargetUserId());
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception", e);
}
return;
}
}
// We are not a BiometricPrompt client, start the client immediately
mCurrentClient = newClient;
/*重点关注*/
startCurrentClient(mCurrentClient.getCookie());
}
}
在此将EnrollClient的对象传进去
startCurrentClient(mCurrentClient.getCookie());
protected void startCurrentClient(int cookie) {
if (mCurrentClient == null) {
Slog.e(getTag(), "Trying to start null client!");
return;
}
if (DEBUG) Slog.v(getTag(), "starting client "
+ mCurrentClient.getClass().getSuperclass().getSimpleName()
+ "(" + mCurrentClient.getOwnerString() + ")"
+ " cookie: " + cookie + "/" + mCurrentClient.getCookie());
if (cookie != mCurrentClient.getCookie()) {
Slog.e(getTag(), "Mismatched cookie");
return;
}
notifyClientActiveCallbacks(true);
/*重点关注*/
mCurrentClient.start();
}
frameworks/base/services/core/java/com/android/server/biometrics/
EnrollClient.java
@Override
public int start() {
mEnrollmentStartTimeMs = System.currentTimeMillis();
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
final ArrayList<Integer> disabledFeatures = new ArrayList<>();
for (int i = 0; i < mDisabledFeatures.length; i++) {
disabledFeatures.add(mDisabledFeatures[i]);
}
/*重点关注*/
final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout,
disabledFeatures);
if (result != 0) {
Slog.w(getLogTag(), "startEnroll failed, result=" + result);
mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result);
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
return result;
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "startEnroll failed", e);
}
return 0; // success
}
start 方法会调用fingerprintd,调用底层的指纹库,底层库返回结果后会调用onEnrollResult来反馈结果receiver,再往上层反馈。这就是指纹的录制流程。在onEnrollResult中当remaining等于0的时候完成录制,调用addBiometricForUser。
FingerprintManager.java中注册了IFingerprintServiceReceiver,实现onEnrollResult方法发送 MSG_ENROLL_RESULT
frameworks/base/core/java/android/hardware/fingerprint/
FingerprintManager.java
private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
@Override // binder call
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
/*重点关注*/
mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
}
.......
};
}
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_ENROLL_RESULT<

本文深入解析了Android系统中指纹识别的全过程,从指纹录入、匹配到屏幕解锁的详细流程及关键代码路径,揭示了FingerprintHIDL接口如何连接硬件与软件,实现指纹功能。
最低0.47元/天 解锁文章
2342

被折叠的 条评论
为什么被折叠?



