Android指纹录入与解锁源码解析

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值