1 指纹的启动流程
我们直接从kernel启动开始看。
1.1 指纹驱动的启动
Linux内核开始启动,初始化各种软硬件环境,加载驱动程序,挂载根文件系统,在系统文件中寻找init.rc文件,并启动init进程。Kernel中,加载指纹驱动,根据传入的dts信息创建设备节点,注册设备。
1.2 hal服务启动
然后在Init启动后,初始化和启动属性服务,并且启动Zygote进程。然后找到android.hardware.biometrics.fingerprint@2.1-service.rc,启动android.hardware.biometrics.fingerprint@2.1-service,会去open fingerprint.deault.so,等待与上层通信。
service vendor.fps_hal /vendor/bin/hw/android.hardware.biometrics.fingerprint@2.1-service
# "class hal" causes a race condition on some devices due to files created
# in /data. As a workaround, postpone startup until later in boot once
# /data is mounted.
class late_start
user system
group system input uhid
task_profiles ServiceCapacityLow
会使位于系统vendor/bin/hw下的android.hardware.biometrics.fingerprint@2.1-service(以下简称2.1 bin)开机自启动,启动后会注册2.1 service,该bin服务对应的代码在:hardware/interfaces/biometrics/fingerprint/2.1/default/service.cpp。
int main() {
android::sp<IBiometricsFingerprint> bio = BiometricsFingerprint::getInstance();
configureRpcThreadpool(1, true /*callerWillJoin*/);
if (bio != nullptr) {
if (::android::OK != bio->registerAsService()) {
return 1;
}
} else {
ALOGE("Can't create instance of BiometricsFingerprint, nullptr");
}
joinRpcThreadpool();
return 0; // should never get here
}
整个注册过程只有两步,首先实例化传入的 IBiometricsFingerprint 接口对象,然后通过 registerAsService 将服务注册到 hwservicemanager。
这里BiometricsFingerprint后面会分析。
1.3 Fingerprintservice启动
等待Zygote进程启动,创建java虚拟机并为java虚拟机注册JNI方法,创建服务器端Socket,启动SystemServer进程。
SystemServer进程启动,启动Binder线程池和SystemServiceManager,并且启动各种系统服务。会启动Fingerprintservice。
//framework/base/services/java/com/android/server/SystemServer.java
private void run() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
...
startOtherServices(t);
...
}
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
final boolean hasFeatureFingerprint =
mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
if (hasFeatureFingerprint) {
t.traceBegin("StartFingerprintSensor");
final FingerprintService fingerprintService =
mSystemServiceManager.startService(FingerprintService.class);
t.traceEnd();
}
}
再来看SystemServiceManager#startService
//framework/base/services/core/java/com/android/server/SystemServiceManager.java
public SystemService startService(String className) {
final Class<SystemService> serviceClass = loadClassFromLoader(className,
this.getClass().getClassLoader());
return startService(serviceClass);
}
private static Class<SystemService> loadClassFromLoader(String className,
ClassLoader classLoader) {
try {
return (Class<SystemService>) Class.forName(className, true, classLoader);
}
}
public <T extends SystemService> T startService(Class<T> serviceClass) {
final T service;
try {
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);
}
...
startService(service);
return service;
}
public void startService(@NonNull final SystemService service) {
// Check if already started
String className = service.getClass().getName();
if (mServiceClassnames.contains(className)) {
Slog.i(TAG, "Not starting an already started service " + className);
return;
}
mServiceClassnames.add(className);
// 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");
}
可以看到会反射创建这个类的构造方法并把它添加到services中,接着执行这个类的onStart方法.
因此,我们来看FingerprintService的onStart方法。
@Override
public void onStart() {
publishBinderService(Context.FINGERPRINT_SERVICE, mServiceWrapper);
}
最终调用到了SystemService#publishBinderService
//framework/base/services/core/java/com/android/server/SystemService.java
protected final void publishBinderService(String name, IBinder service,
boolean allowIsolated, int dumpPriority) {
ServiceManager.addService(name, service, allowIsolated, dumpPriority);
}
注册的服务接口
/** Receives the incoming binder calls from FingerprintManager. */
final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() {
@Override // Binder call
public long enroll() {
}
@Override // Binder call
public long authenticate(){
}
}
这样就可以提供指纹服务给客户端。
1.4 总结
具有系统权限的client下发注册命令->FingerprintManager收到命令->FingerprintService收到命令->(2.1 service)BiometricsFingerprint收到命令->(fingerprint.default.so)Fingerprint.cpp收到命令->指纹CA收到命令->指纹TA收到命令->SPI采集数据\算法进行注册等
2 指纹录入
指纹录入的入口在Settings中,其重要负责指纹录入一些UI的加载和一些录入动画的逻辑。
2.1 指纹录入
先来看录入的入口类FingerprintEnrollEnrolling
。
public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling
- public abstract class BiometricsEnrollEnrolling extends BiometricEnrollBase
- public abstract class BiometricEnrollBase extends InstrumentedActivity
- public abstract class InstrumentedActivity extends ObservableActivity
- public class ObservableActivity extends FragmentActivity
- androidx.fragment.app.FragmentActivity;
从继承关系可以看到FingerprintEnrollEnrolling与人脸基本是一样的。
//packages/apps/Settings/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
/**
* Activity which handles the actual enrolling for fingerprint.
*/
public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
}
同样的,FingerprintEnrollEnrolling也是一个activity。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final FingerprintManager fingerprintManager =
getSystemService(FingerprintManager.class);
final List<FingerprintSensorPropertiesInternal> props =
fingerprintManager.getSensorPropertiesInternal();
mCanAssumeUdfps = props.size() == 1 && props.get(0).isAnyUdfpsType();
if (mCanAssumeUdfps) {
if (BiometricUtils.isReverseLandscape(getApplicationContext())) {
setContentView(R.layout.udfps_enroll_enrolling_land);
} else {
setContentView(R.layout.udfps_enroll_enrolling);
}
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
} else {
setContentView(R.layout.fingerprint_enroll_enrolling);
setDescriptionText(
R.string.security_settings_fingerprint_enroll_start_message);
}
....
}
我们看这里的startEnrollment
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
if (mCanAssumeUdfps) {
startEnrollment();
}
mAnimationCancelled = false;
startIconAnimation();
}
这里调到了FingerprintEnrollSidecar#startEnrollment录入的方法
@Override
protected void startEnrollment() {
super.startEnrollment();
if (mToken == null) {
Log.e(TAG, "Null hardware auth token for enroll");
onEnrollmentError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
getString(R.string.fingerprint_intro_error_unknown));
return;
}
mFingerprintManager.enroll(
mToken, mEnrollmentCancel, mUserId, mEnrollmentCallback,
mEnrollReason);
}
startEnrollment
里面直接调用FingerprintManager#enroll。我们先看看这个mEnrollmentCallback有啥。
private FingerprintManager.EnrollmentCallback mEnrollmentCallback
= new FingerprintManager.EnrollmentCallback() {
@Override
public void onEnrollmentProgress(int remaining) {
FingerprintEnrollSidecar.super.onEnrollmentProgress(remaining);
}
@Override
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
FingerprintEnrollSidecar.super.onEnrollmentHelp(helpMsgId, helpString);
}
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {
FingerprintEnrollSidecar.super.onEnrollmentError(errMsgId, errString);
}
};
这里主要是录入进度、提示信息、录入错误等回调。
再来看frameworks/base/core/java/android/hardware/fingerprint/FingerprintManager.java的enrol,到这里就正式进入系统流程了。
/**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
* {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
* {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
* which point the object is no longer valid. The operation can be canceled by using the
* provided cancel object.
* @param token a unique token provided by a recent creation or verification of device
* credentials (e.g. pin, pattern or password).
* @param cancel an object that can be used to cancel enrollment
* @param flags optional flags
* @param userId the user to whom this fingerprint will belong to
* @param callback an object to receive enrollment events
* @hide
*/
@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(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */));
}
}
}
这里直接调用的FaceService#enroll
,继续往下看。
/**
* Receives the incoming binder calls from FingerprintManager.
*/
@Override // Binder call
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 */, ENROLL_TIMEOUT_SEC) {
@Override
public boolean shouldVibrate() {
return true;
}
@Override
protected int statsModality() {
return FingerprintService.this.statsModality();
}
};
enrollInternal(client, userId);
}
这里先检查有没有MANAGE_FINGERPRINT的权限,然后向构建了EnrollClientImpl,我们知道后面接收录入进度全靠这个类了。
这里重点是enrollInternal。
//framework/base/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
/**
* Calls from the Manager. These are still on the calling binder's thread.
*/
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 */);
});
}
enrollInternal首先检查指纹录入的数量是否达到限制。
@Override
protected boolean hasReachedEnrollmentLimit(int userId) {
final int limit = getContext().getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
final int enrolled = FingerprintService.this.getEnrolledTemplates(userId).size();
if (enrolled >= limit) {
Slog.w(TAG, "Too many fingerprints registered");
return true;
}
return false;
}
对比的方法是将res配置的数量和已经录入的进行对比。
<!-- For performance and storage reasons, limit the number of fingerprints per user -->
<integer name="config_fingerprintMaxTemplatesPerUser">5</integer>
从res可以看到,对于指纹来说,这个最大配置个数为5。
最后从binder线程,切换到主线程中执行startClient。
/**
* Calls the HAL to switch states to the new task. If there's already a current task,
* it calls cancel() and sets mPendingClient to begin when the current task finishes
* ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
*
* @param newClient the new client that wants to connect
* @param initiatedByClient true for authenticate, remove and enroll
*/
@VisibleForTesting
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());
}
}
这里如果已经有task,就先cancel调当前的,然后将newClient设置到mPendingClient,并设置一个3s的延迟消息来延迟执行mPendingClient,最后执行startCurrentClient。
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() + ")"
+ " targetUserId: " + mCurrentClient.getTargetUserId()
+ " currentUserId: " + mCurrentUserId
+ " cookie: " + cookie + "/" + mCurrentClient.getCookie());
if (cookie != mCurrentClient.getCookie()) {
Slog.e(getTag(), "Mismatched cookie");
return;
}
int status = mCurrentClient.start();
if (status == 0) {
notifyClientActiveCallbacks(true);
} else {
mCurrentClient.onError(getHalDeviceId(), BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
removeClient(mCurrentClient);
}
}
还是把mCurrentClient的对象传进去了。然后是 mCurrentClient.start()。
//frameworks/base/services/core/java/com/android/server/biometrics/EnrollClient.java
@Override
public int start() {
mEnrollmentStartTimeMs = System.currentTimeMillis();
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(), mTimeoutSec,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
}
EnrollClient#start方法会通过getDaemonWrapper().enroll调用底层,调用底层的指纹库,底层库返回结果后会调用onEnrollResult来反馈结果receiver,再往上层反馈。这就是指纹的录制流程。
2.2 录入进度
在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();
}
}
private class MyHandler extends Handler {
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_ENROLL_RESULT:
sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
break;
}
}
}
private void sendEnrollResult(Fingerprint fp, int remaining) {
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentProgress(remaining);
}
}
在前面FingerprintEnrollEnrolling类中的onEnrollmentProgressChange(int steps, int remaining)更新录入进度的方法中用通过传递进来的remaining来获取实际的进度;最后当进度条达到最大值时打开录入结束界launchFinish(mToken)
///packages/apps/Settings/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
updateProgress(true /* animate */);
updateTitleAndDescription();
clearError();
animateFlash();
...
}
这里更新了进度条、背景图、提示语等,具体的就不看了。
2.3 Android 12流程
上面的流程中,Android高版本上有一些差异,我们从FingerprintService开始。
@Override // Binder call
public void enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
final int userId, final IFingerprintServiceReceiver receiver,
final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
return;
}
provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
receiver, opPackageName, enrollReason, mFingerprintStateCallback);
}
这里provider.second.scheduleEnroll我们一层一层拆解,先看getSingleProvider
@Nullable
private Pair<Integer, ServiceProvider> getSingleProvider() {
final List<FingerprintSensorPropertiesInternal> properties =
getSensorProperties();
if (properties.isEmpty()) {
Slog.e(TAG, "No providers found");
return null;
}
// Theoretically we can just return the first provider,
// but maybe this is easier to understand.
final int sensorId = properties.get(0).sensorId;
for (ServiceProvider provider : mServiceProviders) {
if (provider.containsSensor(sensorId)) {
return new Pair<>(sensorId, provider);
}
}
Slog.e(TAG, "Provider not found");
return null;
}
可以看到,getSingleProvider是一个Pair<Integer, ServiceProvider>,所以provider.second就是去取Pair的第二个元素,也就是ServiceProvider。getSingleProvider就是去找到匹配的ServiceProvider。它是从mServiceProviders中去找的,那么mServiceProviders是什么时候赋值的呢?
private void addHidlProviders(List<FingerprintSensorPropertiesInternal> hidlSensors) {
for (FingerprintSensorPropertiesInternal hidlSensor : hidlSensors) {
final Fingerprint21 fingerprint21;
if ((Build.IS_USERDEBUG || Build.IS_ENG)
&& getContext().getResources().getBoolean(R.bool.allow_test_udfps)
&& Settings.Secure.getIntForUser(getContext().getContentResolver(),
Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
UserHandle.USER_CURRENT) != 0