1. BiometricPrompt类
这个类是应用APP做认证时直接访问的类。在Android API level 28的时候引入,同时早期版本中相应的FingerprintManager类被Deprecated,该类位于frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java。该类为生物识别提供了统一的接口,同时和之前的FingerprintManager相比这个类还提供了统一的用户界面。
1.1 实例化
内部类 BiometricPrompt.Builder用来收集将要显示的系统对话框的信息,可以看到可以设置对话款标题,为button设置监听回调listener等,根据这些信息build方法创建BiometricPrompt类。
/**
* A builder that collects arguments to be shown on the system-provided biometric dialog.
**/
public static class Builder {
private final Bundle mBundle;
private ButtonInfo mPositiveButtonInfo;
private ButtonInfo mNegativeButtonInfo;
private Context mContext;
/**
* Creates a builder for a biometric dialog.
* @param context
*/
public Builder(Context context) {
mBundle = new Bundle();
mContext = context;
}
/**
* Required: Set the title to display.
* @param title
* @return
*/
public Builder setTitle(@NonNull CharSequence title) {
mBundle.putCharSequence(KEY_TITLE, title);
return this;
}
/**
* Optional: Set the subtitle to display.
* @param subtitle
* @return
*/
public Builder setSubtitle(@NonNull CharSequence subtitle) {
mBundle.putCharSequence(KEY_SUBTITLE, subtitle);
return this;
}
/**
* Optional: Set the description to display.
* @param description
* @return
*/
public Builder setDescription(@NonNull CharSequence description) {
mBundle.putCharSequence(KEY_DESCRIPTION, description);
return this;
}
/**
* Optional: Set the text for the positive button. If not set, the positive button
* will not show.
* @param text
* @return
* @hide
*/
public Builder setPositiveButton(@NonNull CharSequence text,
@NonNull @CallbackExecutor Executor executor,
@NonNull DialogInterface.OnClickListener listener) {
if (TextUtils.isEmpty(text)) {
throw new IllegalArgumentException("Text must be set and non-empty");
}
if (executor == null) {
throw new IllegalArgumentException("Executor must not be null");
}
if (listener == null) {
throw new IllegalArgumentException("Listener must not be null");
}
mBundle.putCharSequence(KEY_POSITIVE_TEXT, text);
mPositiveButtonInfo = new ButtonInfo(executor, listener);
return this;
}
/**
* Required: Set the text for the negative button. This would typically be used as a
* "Cancel" button, but may be also used to show an alternative method for authentication,
* such as screen that asks for a backup password.
* @param text
* @return
*/
public Builder setNegativeButton(@NonNull CharSequence text,
@NonNull @CallbackExecutor Executor executor,
@NonNull DialogInterface.OnClickListener listener) {
if (TextUtils.isEmpty(text)) {
throw new IllegalArgumentException("Text must be set and non-empty");
}
if (executor == null) {
throw new IllegalArgumentException("Executor must not be null");
}
if (listener == null) {
throw new IllegalArgumentException("Listener must not be null");
}
mBundle.putCharSequence(KEY_NEGATIVE_TEXT, text);
mNegativeButtonInfo = new ButtonInfo(executor, listener);
return this;
}
/**
* Creates a {@link BiometricPrompt}.
* @return a {@link BiometricPrompt}
* @throws IllegalArgumentException if any of the required fields are not set.
*/
public BiometricPrompt build() {
final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
if (TextUtils.isEmpty(title)) {
throw new IllegalArgumentException("Title must be set and non-empty");
} else if (TextUtils.isEmpty(negative)) {
throw new IllegalArgumentException("Negative text must be set and non-empty");
}
return new BiometricPrompt(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
}
}
BioMetricPrompt构造函数,可以看到目前只拿到mFingerprintManger实例,后期应该会有更多的生物识别接口。
private BiometricPrompt(Context context, Bundle bundle,
ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
mBundle = bundle;
mPositiveButtonInfo = positiveButtonInfo;
mNegativeButtonInfo = negativeButtonInfo;
mFingerprintManager = context.getSystemService(FingerprintManager.class);
mPackageManager = context.getPackageManager();
}
比如实际的初始化一个例子
mBiometricPrompt = new BiometricPrompt.Builder(this)
.setTitle("FingerprintTest")
.setDescription("FingerprintTest")
.setNegativeButton("Cancel", getMainExecutor(), new
DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.i(TAG, "Cancel button clicked");
}
})
.build();
1.2 发起鉴权
公共方法authenticate重载了多次,最终的调用如下。这里仍然是调用了FingerprintManager中的authenticate,之前调用handlePreAuthenticationErrors确认是否有权限、是否硬件支持、是否有录入的指纹。
@RequiresPermission(USE_BIOMETRIC)
public void authenticate(@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback) {
if (handlePreAuthenticationErrors(callback, executor)) {
return;
}
mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
}
private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
Executor executor) {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
sendError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT, callback,
executor);
return true;
} else if (!mFingerprintManager.isHardwareDetected()) {
sendError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, callback,
executor);
return true;
} else if (!mFingerprintManager.hasEnrolledFingerprints()) {
sendError(BiometricPrompt.BIOMETRIC_ERROR_NO_BIOMETRICS, callback,
executor);
return true;
}
return false;
}
三个参数解释如下。这个函数会显示之前创建的对话框,调用指纹硬件进行扫描指纹。用户可以通过传入的cancel变量来取消鉴权过程;应用可以通过BiometricPrompt.AuthenticationCallback来得到认证的结果,或者通过前面setNegativeButton的回调来得到按键的event。
Parameters | |
---|---|
cancel | CancellationSignal : An object that can be used to cancel authentication This value must never be |
executor | Executor : An executor to handle callback events This value must never be Callback and listener events are dispatched through this |
callback | BiometricPrompt.AuthenticationCallback : An object to receive authentication events This value must never be |
比如下面的用法,加上前面的初始化,就是一个简单的应用。callback回调作为参数回调,通过复写该回调函数来得到认证结果。
mCancellationSignal = new CancellationSignal();
mCancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
@Override
public void onCancel() {
//handle cancel result
Log.i(TAG, "Canceled");
}
});
mAuthenticationCallback = new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
Log.i(TAG, "onAuthenticationError " + errString);
}
@Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
Log.i(TAG, "onAuthenticationSucceeded " + result.toString());
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
Log.i(TAG, "onAuthenticationFailed ");
}
};
mBiometricPrompt.authenticate(mCancellationSignal, getMainExecutor(), mAuthenticationCallback);
}
转自:https://blog.csdn.net/androidworkor/article/details/81101587
1.3 回调返回结果
1.3.1 回调类BiometricPrompt.AuthenticationCallback
用户可以自己复写对应的方法获取自己感兴趣结果。
void | onAuthenticationError(int errorCode, CharSequence errString) Called when an unrecoverable error has been encountered and the operation is complete. |
void | onAuthenticationFailed() Called when a biometric is valid but not recognized. |
void | onAuthenticationHelp(int helpCode, CharSequence helpString) Called when a recoverable error has been encountered during authentication. |
void | onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) Called when a biometric is recognized. |
void | onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) Called when a biometric is recognized. |
1.3.2 返回值
errorCode 和 errString
AuthenticationResult result有3个主要成员,可以通过方法得到mCryptoObject即当前该次鉴权的加密数据。 加密数据应该使用来做MAC的,anthenticate接口有一个实现可以用户提供CryptoObject,用户自己做鉴权?
private BiometricIdentifier mIdentifier;
private CryptoObject mCryptoObject;
private int mUserId;
2. FingerprintManager类
前面看到,目前在BiometricPrompt类都是直接调用FingerprintMangager的方法来实现。后面如果兼容有多种生物识别(如虹膜,人脸等),应该会增加更多的接口。
2.1 FingerprintManager实例化
在BiometricPrompt类中通过getSystemService得到FingerprintManager类。
FingerprintManager = context.getSystemService(FingerprintManager.class);
在frameworks/base/core/java/android/app/ContextImpl.java文件中实现getSystemService。
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
文件frameworks/base/core/java/android/app/SystemServiceRegistry.java中getSystemService的实现。
/**
* Gets a system service from a given context.
*/
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
SYSTEM_SERVICE_FETCHERS是一个HashMap,它以serviceName作为key值,serviceFetcher为value值,serviceFetcher是一个service模板。在SystemServiceRegistry中调用registerService将键值放到哈希数组中。
/**
* Statically registers a system service with the context.
* This method must be called during static initialization only.
*/
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
可以看到SystemServiceRegistry类中有一个static代码块,会调用很多registerService进行service注册(put哈希数组);我们找到FingerprintManger部分。可以看到createService直接返回FingerprintManager然后缓存在哈希数组中。FingerprintManager类实例传入的一个参数是通过ServiceManager来得到的getService,其实是从ServiceManager中拿到远程的服务,基于binder,client可以直接调用远程的服务。早前的Android版本并没有SystemServiceRegistry,而是在需要的时候通过getService去获取远程服务,加入SystemServiceRegistry之后先将远程服务都缓存起来,应该速度有提升。
registerService(Context.FINGERPRINT_SERVICE, FingerprintManager.class,
new CachedServiceFetcher<FingerprintManager>() {
@Override
public FingerprintManager createService(ContextImpl ctx) throws ServiceNotFoundException {
final IBinder binder;
if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
binder = ServiceManager.getServiceOrThrow(Context.FINGERPRINT_SERVICE);
} else {
binder = ServiceManager.getService(Context.FINGERPRINT_SERVICE);
}
IFingerprintService service = IFingerprintService.Stub.asInterface(binder);
return new FingerprintManager(ctx.getOuterContext(), service);
}});
回到Fingerprintmanger类,可以知道BiometricPrompt中得到的一个本地Fingerprintmanger实例,但是Fingerprintmanger实例中有一个mService对应远程的Service,来真正提供服务。
public FingerprintManager(Context context, IFingerprintService service) {
mContext = context;
mService = service;
if (mService == null) {
Slog.v(TAG, "FingerprintManagerService was null");
}
mHandler = new MyHandler(context);
}
2.2 FingerprintManager方法
相比前面的BiometricPrompt类FingerprintManager方法要多的多。我们知道生物(指纹)识别并不是只有识别,还有录入等。而对于用户APP来说,它只能使用authenticate接口(有些接口需要更多的权限)。因此可以按照FingerprintManager的名字来理解这个类,它是一个Java层的操作类,应用(包括用户级别的APP,系统级别的APP如settings)通过它和远程service交互。
authenticate方法
前面学习BiometricPrompt类的authenticate的时候知道它直接调用的是FingerprintManager类
mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
该方法的实现
public void authenticate(
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IBiometricPromptReceiver receiver,
@NonNull BiometricAuthenticator.AuthenticationCallback callback) {
if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
if (bundle == null) {
throw new IllegalArgumentException("Must supply a bundle");
}
if (executor == null) {
throw new IllegalArgumentException("Must supply an executor");
}
if (receiver == null) {
throw new IllegalArgumentException("Must supply a receiver");
}
if (callback == null) {
throw new IllegalArgumentException("Must supply a calback");
}
authenticate(mContext.getUserId(), null, cancel, bundle, executor, receiver, callback);
}
有3个工作:
- cancel设置listener,前面的章节学习到,应用app可以通过该cancel来取消当前的认证过程。所以进入到认证之前首先判断是否已经被cancel了,如果是则直接返回;如果没有则设置Listener,当应用app调用cancel的时候,listener被调用,我们跟踪listener可知,其实是调用远程的mService.cancelAuthentication(mToken, mContext.getOpPackageName()); 这里有一个疑问,应用app设置的listener这个时候还会被调用吗?后面分析cancel类。
- mService.authenticate(mToken, sessionId, userId, mServiceReceiver,0 /* flags */, mContext.getOpPackageName(), bundle, receiver);调用远程服务进行认证过程;这里设置了一个mServiceReceiver用来做远程service的回调。后面分析FingerprintService类再看这些参数。
- 如果认证返回失败(指那些能够立即返回失败的类型,因为认证需要硬件scan和算法处理,对于比如识别失败这些错误都是通过2中的receiver来返回结果),直接通过callback将结果返回到BiometricPrompt(应用app)。这里的excuter只是实现异步线程执行。
private void authenticate(int userId,
@Nullable android.hardware.biometrics.CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IBiometricPromptReceiver receiver,
@NonNull BiometricAuthenticator.AuthenticationCallback callback) {
mCryptoObject = crypto;
if (cancel.isCanceled()) {
Slog.w(TAG, "authentication already canceled");
return;
} else {
cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
}
if (mService != null) {
try {
mExecutor = executor;
mAuthenticationCallback = callback;
final long sessionId = crypto != null ? crypto.getOpId() : 0;
mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
0 /* flags */, mContext.getOpPackageName(), bundle, receiver);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception while authenticating", e);
mExecutor.execute(() -> {
callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
});
}
}
}
enroll方法
enroll方法和authenticate类似。只是它不能通过用户APP来调用,除非能够拿到权限。而且后面将看到在enroll前后还有pre/post-enroll等接口需要被调用。
@RequiresPermission(MANAGE_FINGERPRINT)
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(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
}
}
}
远程回调
如论是authenticate还是enroll等接口,传入到远程service的receiver都是mServiceReceiver。handler和excuter是为了多线程处理的目的,后面再深入学习,当前我们只简单看它们的接口,可以发现最终还是会通过前面的的callback回调到app(比如对autenticate来说它的callback保存在mAuthenticationCallback中,而在Handler或者excuter中还是通过该成员回调到上一层)
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 // binder call
public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
if (mExecutor != null) {
mExecutor.execute(() -> {
sendAcquiredResult(deviceId, acquireInfo, vendorCode);
});
} else {
mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode,
deviceId).sendToTarget();
}
}
@Override // binder call
public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
if (mExecutor != null) {
mExecutor.execute(() -> {
sendAuthenticatedSucceeded(fp, userId);
});
} else {
mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
}
}
@Override // binder call
public void onAuthenticationFailed(long deviceId) {
if (mExecutor != null) {
mExecutor.execute(() -> {
sendAuthenticatedFailed();
});
} else {
mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
}
}
@Override // binder call
public void onError(long deviceId, int error, int vendorCode) {
if (mExecutor != null) {
// BiometricPrompt case
if (error == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
|| error == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
// User tapped somewhere to cancel, or authentication was cancelled by the app
// or got kicked out. The prompt is already gone, so send the error immediately.
mExecutor.execute(() -> {
sendErrorResult(deviceId, error, vendorCode);
});
} else {
// User got an error that needs to be displayed on the dialog, post a delayed
// runnable on the FingerprintManager handler that sends the error message after
// FingerprintDialog.HIDE_DIALOG_DELAY to send the error to the application.
mHandler.postDelayed(() -> {
mExecutor.execute(() -> {
sendErrorResult(deviceId, error, vendorCode);
});
}, BiometricPrompt.HIDE_DIALOG_DELAY);
}
} else {
mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
}
}
@Override // binder call
public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
}
@Override // binder call
public void onEnumerated(long deviceId, int fingerId, int groupId, int remaining) {
// TODO: propagate remaining
mHandler.obtainMessage(MSG_ENUMERATED, fingerId, groupId, deviceId).sendToTarget();
}
};
}
3. FingerprintService类
这是一个远程service类,client端向ServiceManager获取(前面已经学习),server端向ServiceManager注册。在Android大部分系统服务都通过SystemServer向系统注册的,有一些需要处理繁重任务的Service会通过自己单独进程进行注册。
3.1 service注册和初始化
framework/base/services/java/com/android/server/SystemServer.java
private SystemServiceManager mSystemServiceManager;
...
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
traceBeginAndSlog("StartFingerprintSensor");
mSystemServiceManager.startService(FingerprintService.class);
traceEnd();
}
framework/base/services/core/java/com/android/server/SystemServiceManager.java
@SuppressWarnings("unchecked")
public SystemService startService(String className) {
final Class<SystemService> serviceClass;
try {
serviceClass = (Class<SystemService>)Class.forName(className);
} catch (ClassNotFoundException ex) {
Slog.i(TAG, "Starting " + className);
throw new RuntimeException("Failed to create service " + className
+ ": service class not found, usually indicates that the caller should "
+ "have called PackageManager.hasSystemFeature() to check whether the "
+ "feature is available on this device before trying to start the "
+ "services that implement it", ex);
}
return startService(serviceClass);
}
...
public void startService(@NonNull final SystemService service) {
// Register it.
mServices.add(service);
// Start it.
long time = SystemClock.elapsedRealtime();
try {
service.onStart();
} catch (RuntimeException ex) {
throw new RuntimeException("Failed to start service " + service.getClass().getName()
+ ": onStart threw an exception", ex);
}
warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
}
调用service.onstart(),我们回到framework/base/services/core/java/com/android/server/fingerprint/FingerprintService.java中。完成3件事情:
- 其中的publishBinderService会向ServiceManager注册系统服务,后面学习binder的时候再深入。这里需要知道注册的服务类是FingerprintServiceWrapper这个类
- getFingerprintDaemon()得到一个HAL的服务,并且保存在Pool中,这个远程的HAL服务由OEM实现。它和FingerprintService通信方式是HIDL
- 调用listenForUserSwitches监听用户切换,这里的用户切换指的是Android多用户,因为我们从前面看到参数中有不同的用户有不同的userID
public void onStart() {
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
listenForUserSwitches();
}
3.2 获取远程HAL service
调用IBiometricsFingerprint.getService获取远程服务,为什么调用接口的getService可以得到远程HAL服务,在HIDL中学习;当获得了HAL后面还会调用其Notify接口,做一些初始化的事?并且将mDaemonCallback设置回调,可以猜测这个回调用来接收HAL的信息返回,并且这个回调最终会调用上一层比如FingerprintManager的回调将信息最终上报上去。可以看到从上层来的处理基本都是通过这个mDaemon来处理,这里有一条Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");log,如果远程的HAL服务死掉,是可以重连的,mDaemon怎么会被设置为null? 有个serviceDie的回调...
public synchronized IBiometricsFingerprint getFingerprintDaemon() {
if (mDaemon == null) {
Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
try {
mDaemon = IBiometricsFingerprint.getService();
} catch (java.util.NoSuchElementException e) {
// Service doesn't exist or cannot be opened. Logged below.
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get biometric interface", e);
}
if (mDaemon == null) {
Slog.w(TAG, "fingerprint HIDL not available");
return null;
}
mDaemon.asBinder().linkToDeath(this, 0);
try {
mHalDeviceId = mDaemon.setNotify(mDaemonCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to open fingerprint HAL", e);
mDaemon = null; // try again later!
}
if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
if (mHalDeviceId != 0) {
loadAuthenticatorIds();
updateActiveGroup(ActivityManager.getCurrentUser(), null);
doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
} else {
Slog.w(TAG, "Failed to open Fingerprint HAL!");
MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
mDaemon = null;
}
}
return mDaemon;
}
3.3 基本的方法
我们已经知道在FingerprintService中,是通过mDaemon这个HAL服务来进行处理的,思路和前面FingerprintManger中类似。但这里有一个client的概念。看到wrapper中的authenticate方法
@Override // Binder call
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName, final Bundle bundle,
final IBiometricPromptReceiver dialogReceiver) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
final boolean restricted = isRestricted();
if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
callingUserId)) {
if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
// Get performance stats object for this user.
HashMap<Integer, PerformanceStats> pmap
= (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
PerformanceStats stats = pmap.get(mCurrentUserId);
if (stats == null) {
stats = new PerformanceStats();
pmap.put(mCurrentUserId, stats);
}
mPerformanceStats = stats;
startAuthentication(token, opId, callingUserId, groupId, receiver,
flags, restricted, opPackageName, bundle, dialogReceiver);
}
});
}
调用startAuthentication
private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted,
String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver) {
updateActiveGroup(groupId, opPackageName);
if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
receiver, mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
dialogReceiver, mStatusBarService) {
@Override
public void onStart() {
try {
mActivityManager.registerTaskStackListener(mTaskStackListener);
} catch (RemoteException e) {
Slog.e(TAG, "Could not register task stack listener", e);
}
}
@Override
public void onStop() {
try {
mActivityManager.unregisterTaskStackListener(mTaskStackListener);
} catch (RemoteException e) {
Slog.e(TAG, "Could not unregister task stack listener", e);
}
}
@Override
public int handleFailedAttempt() {
final int currentUser = ActivityManager.getCurrentUser();
mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
final int lockoutMode = getLockoutMode();
if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
mPerformanceStats.permanentLockout++;
} else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
mPerformanceStats.lockout++;
}
// Failing multiple times will continue to push out the lockout time
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
scheduleLockoutResetForUser(currentUser);
return lockoutMode;
}
return AuthenticationClient.LOCKOUT_NONE;
}
@Override
public void resetFailedAttempts() {
FingerprintService.this.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
ActivityManager.getCurrentUser());
}
@Override
public void notifyUserActivity() {
FingerprintService.this.userActivity();
}
@Override
public IBiometricsFingerprint getFingerprintDaemon() {
return FingerprintService.this.getFingerprintDaemon();
}
};
int lockoutMode = getLockoutMode();
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
Slog.v(TAG, "In lockout mode(" + lockoutMode +
") ; disallowing authentication");
int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
if (!client.onError(errorCode, 0 /* vendorCode */)) {
Slog.w(TAG, "Cannot send permanent lockout message to client");
}
return;
}
startClient(client, true /* initiatedByClient */);
}
可以看到是实际上是new一个AuthenticationClient然后调用它的start函数,并且如果当前已经有一个client的在运行话,需要先stop当前的client
private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
if (currentClient instanceof InternalEnumerateClient ||
currentClient instanceof InternalRemovalClient) {
// This condition means we're currently running internal diagnostics to
// remove extra fingerprints in the hardware and/or the software
// TODO: design an escape hatch in case client never finishes
if (newClient != null) {
Slog.w(TAG, "Internal cleanup in progress but trying to start client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
+ ", initiatedByClient = " + initiatedByClient);
}
}
else {
currentClient.stop(initiatedByClient);
}
mPendingClient = newClient;
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
} else if (newClient != null) {
mCurrentClient = newClient;
if (DEBUG) Slog.v(TAG, "starting client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
+ ", initiatedByClient = " + initiatedByClient);
notifyClientActiveCallbacks(true);
newClient.start();
}
}
不同的client的start函数不同,它通过实现一个父类ClientMonit来实现,比如对于authenticate来说,它的start实现如下
/**
* Start authentication
*/
@Override
public int start() {
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "start authentication: no fingerprint HAL!");
return ERROR_ESRCH;
}
onStart();
try {
final int result = daemon.authenticate(mOpId, getGroupId());
if (result != 0) {
Slog.w(TAG, "startAuthentication failed, result=" + result);
MetricsLogger.histogram(getContext(), "fingeprintd_auth_start_error", result);
onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
return result;
}
if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
// If authenticating with system dialog, show the dialog
if (mBundle != null) {
try {
mStatusBarService.showFingerprintDialog(mBundle, mDialogReceiver);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to show fingerprint dialog", e);
}
}
} catch (RemoteException e) {
Slog.e(TAG, "startAuthentication failed", e);
return ERROR_ESRCH;
}
return 0; // success
}
其他的接口方法类似,后面遇到问题再深入学习。
3.4 回调
前面在get HAL的service时介绍过,我们会把mDaemonCallback 设置到HAL,这个callback完成回调
private IBiometricsFingerprintClientCallback mDaemonCallback =
new IBiometricsFingerprintClientCallback.Stub() {
@Override
public void onEnrollResult(final long deviceId, final int fingerId, final int groupId,
final int remaining) {
mHandler.post(new Runnable() {
@Override
public void run() {
handleEnrollResult(deviceId, fingerId, groupId, remaining);
}
});
}
@Override
public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
mHandler.post(new Runnable() {
@Override
public void run() {
handleAcquired(deviceId, acquiredInfo, vendorCode);
}
});
}
@Override
public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
ArrayList<Byte> token) {
mHandler.post(new Runnable() {
@Override
public void run() {
handleAuthenticated(deviceId, fingerId, groupId, token);
}
});
}
@Override
public void onError(final long deviceId, final int error, final int vendorCode) {
mHandler.post(new Runnable() {
@Override
public void run() {
handleError(deviceId, error, vendorCode);
}
});
}
@Override
public void onRemoved(final long deviceId, final int fingerId, final int groupId, final int remaining) {
mHandler.post(new Runnable() {
@Override
public void run() {
handleRemoved(deviceId, fingerId, groupId, remaining);
}
});
}
@Override
public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
final int remaining) {
mHandler.post(new Runnable() {
@Override
public void run() {
handleEnumerate(deviceId, fingerId, groupId, remaining);
}
});
}
};
我们以authenticate为例,首先token操作是干啥?
protected void handleAuthenticated(long deviceId, int fingerId, int groupId,
ArrayList<Byte> token) {
ClientMonitor client = mCurrentClient;
if (fingerId != 0) {
// Ugh...
final byte[] byteToken = new byte[token.size()];
for (int i = 0; i < token.size(); i++) {
byteToken[i] = token.get(i);
}
// Send to Keystore
KeyStore.getInstance().addAuthToken(byteToken);
}
if (client != null && client.onAuthenticated(fingerId, groupId)) {
removeClient(client);
}
if (fingerId != 0) {
mPerformanceStats.accept++;
} else {
mPerformanceStats.reject++;
}
}
我们找到framework/base/services/core/java/com/android/server/fingerprint/AuthenticationClient.java中的onAuthenticated方法。如果出错了需要出错控制,学习handleFailedAttempt函数可以知道为什么我们的手机指纹识别失败几次之后需要等待60s等设定。最终通过reciever的接口进行上报,而这个reciever是client的mReceiver成员,这个成员真是调用接口如(authenticate)的时候从上层传下来的callback
public boolean onAuthenticated(int fingerId, int groupId) {
boolean result = false;
boolean authenticated = fingerId != 0;
// If the fingerprint dialog is showing, notify authentication succeeded
if (mBundle != null) {
try {
if (authenticated) {
mStatusBarService.onFingerprintAuthenticated();
} else {
mStatusBarService.onFingerprintHelp(getContext().getResources().getString(
com.android.internal.R.string.fingerprint_not_recognized));
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify Authenticated:", e);
}
}
IFingerprintServiceReceiver receiver = getReceiver();
if (receiver != null) {
try {
MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
authenticated);
if (!authenticated) {
receiver.onAuthenticationFailed(getHalDeviceId());
} else {
if (DEBUG) {
Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
+ ", id=" + fingerId + ", gp=" + groupId + ")");
}
Fingerprint fp = !getIsRestricted()
? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
: null;
receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Authenticated:", e);
result = true; // client failed
}
} else {
result = true; // client not listening
}
if (!authenticated) {
if (receiver != null) {
vibrateError();
}
// allow system-defined limit of number of attempts before giving up
int lockoutMode = handleFailedAttempt();
if (lockoutMode != LOCKOUT_NONE) {
try {
mInLockout = true;
Slog.w(TAG, "Forcing lockout (fp driver code should do this!), mode(" +
lockoutMode + ")");
stop(false);
int errorCode = lockoutMode == LOCKOUT_TIMED ?
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
// TODO: if the dialog is showing, this error should be delayed. On a similar
// note, AuthenticationClient should override onError and delay all other errors
// as well, if the dialog is showing
receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
// Send the lockout message to the system dialog
if (mBundle != null) {
mStatusBarService.onFingerprintError(
mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify lockout:", e);
}
}
result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
} else {
if (receiver != null) {
vibrateSuccess();
}
result |= true; // we have a valid fingerprint, done
resetFailedAttempts();
onStop();
}
return result;
}
到此,Java层的流程简单梳理完成,往下就是HAL层的逻辑了。实际机器上的HAL层都是由OEM来实现,在HAL层之下,我们知道对于Android上的设备来说,通常的处理流程是 HAL->Kernel->FW。因为生物(指纹)识别对安全的特殊要求,我们会发现kernel对于它来说仍然是数据传输的路径,而真正处理的过程需要在更安全的环境中,以ARM平台为例HAL->Kernel->TZ->FW
那对于OEM来说他们实现HAL也是有依据的,我们前面在获取HAL服务时知道,只需要调用IBiometricsFingerprint.getService就可以获得远程的HAL服务,这是由目前Android的HIDL来实现的,而指纹的HIDL接口定义在hardware/interfaces/biometrics/fingerprint目录中,OEM只需要按照HIDL手册实现这些接口。
4. HAL service
5. 遗留的问题
返回结果分析
API权限?
crypto
Token
Bundle
Executor
Handler
enroll(settings)
binder
hidl
SystemServer
ServiceManager
Content
Android 多用户
Slog.v等等log系统
Keystore
TZ
参考:https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.html