生物解锁--指纹录入流程

直接从FingerprintManager.java类开始讲起。

首先从enroll方法作为入口

/**
     * 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 userId the user to whom this fingerprint will belong to
     * @param callback an object to receive enrollment events
     * @param shouldLogMetrics a flag that indicates if enrollment failure/success metrics
     * should be logged.
     * @hide
     */
    @RequiresPermission(MANAGE_FINGERPRINT)
    public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
            EnrollmentCallback callback, @EnrollReason int enrollReason) {
        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, hardwareAuthToken, userId, mServiceReceiver,
                        mContext.getOpPackageName(), enrollReason);
            } catch (RemoteException e) {
                Slog.w(TAG, "Remote exception in enroll: ", e);
                // 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 */));
            }
        }
    }

见第37行,变量mService

下面看看变量mService对象是哪个?通过souce insight 工具搜索

有这个定义private IFingerprintService mService;

然后继续往下找在哪儿定义的

public FingerprintManager(Context context, IFingerprintService service) {
        mContext = context;
        mService = service;
        if (mService == null) {
            Slog.v(TAG, "FingerprintService was null");
        }
        mHandler = new MyHandler(context);
    }

在其他地方初始化 FingerprintManager的时候传进来的,然后在KeyguardUpdate类中找到以下代码

if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
            mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
        }

显然mFpm是个系统服务,继续往下找。搜到类SystemServieRegistry.java

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);
            }});

mService变量实际上就是通过aidl进行访问。

service 就是继承IFingerprintService的FingerprintService类。

就是说,上面的mService 就是 要到FingerprintService类中去寻找对象实例了。

然后来到FingerprintService类,查找对应的enroll方法

mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
                        mContext.getOpPackageName(), enrollReason);

看看FingerprintService.java

 @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);
        }

 第13行的部分,provider.second 的详细分析,见下面博客

生物解锁--指纹服务注册流程_liujun3512159的博客-CSDN博客

return provider.second.scheduleEnroll方法调用,实际上就是调用Fingerprint21对象实例。注意 这里的second的用法,可以参照pair使用规则。

然后继续看Fingerprint21.java类的scheduleEnroll方法。

    @Override
    public void scheduleEnroll(int sensorId, @NonNull IBinder token,
            @NonNull byte[] hardwareAuthToken, int userId,
            @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
            @FingerprintManager.EnrollReason int enrollReason) {
        mHandler.post(() -> {
            scheduleUpdateActiveUserWithoutHandler(userId);

            final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
                    mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
                    hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
                    ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
                    mSidefpsController, enrollReason);
            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
                @Override
                public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                    mFingerprintStateCallback.onClientStarted(clientMonitor);
                }

                @Override
                public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                        boolean success) {
                    mFingerprintStateCallback.onClientFinished(clientMonitor, success);
                    if (success) {
                        // Update authenticatorIds
                        scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
                                true /* force */);
                    }
                }
            });
        });
    }

见第14行,实际上是调用类BiometricScheduler下的方法scheduleClientMonitor

public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor,
            @Nullable BaseClientMonitor.Callback clientCallback) {
        // If the incoming operation should interrupt preceding clients, mark any interruptable
        // pending clients as canceling. Once they reach the head of the queue, the scheduler will
        // send ERROR_CANCELED and skip the operation.
        if (clientMonitor.interruptsPrecedingClients()) {
            for (Operation operation : mPendingOperations) {
                if (operation.mClientMonitor instanceof Interruptable
                        && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
                    Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
                            + operation.mClientMonitor);
                    operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
                }
            }
        }
 
        mPendingOperations.add(new Operation(clientMonitor, clientCallback));
        Slog.d(getTag(), "[Added] " + clientMonitor
                + ", new queue size: " + mPendingOperations.size());
 
        // If the new operation should interrupt preceding clients, and if the current operation is
        // cancellable, start the cancellation process.
        if (clientMonitor.interruptsPrecedingClients()
                && mCurrentOperation != null
                && mCurrentOperation.mClientMonitor instanceof Interruptable
                && mCurrentOperation.mState == Operation.STATE_STARTED) {
            Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
            cancelInternal(mCurrentOperation);
        }
 
        startNextOperationIfIdle();
    }

第17行,很重要,后面会说到

第31行,继续往下


protected void startNextOperationIfIdle() {
        if (mCurrentOperation != null) {
            Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation);
            return;
        }
        if (mPendingOperations.isEmpty()) {
            Slog.d(getTag(), "No operations, returning to idle");
            return;
        }

        mCurrentOperation = mPendingOperations.poll();
        final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor;
        Slog.d(getTag(), "[Polled] " + mCurrentOperation);

        // If the operation at the front of the queue has been marked for cancellation, send
        // ERROR_CANCELED. No need to start this client.
        if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
            Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
            if (!(currentClient instanceof Interruptable)) {
                throw new IllegalStateException("Mis-implemented client or scheduler, "
                        + "trying to cancel non-interruptable operation: " + mCurrentOperation);
            }

            final Interruptable interruptable = (Interruptable) currentClient;
            interruptable.cancelWithoutStarting(getInternalCallback());
            // Now we wait for the client to send its FinishCallback, which kicks off the next
            // operation.
            return;
        }

        if (mGestureAvailabilityDispatcher != null
                && mCurrentOperation.mClientMonitor instanceof AcquisitionClient) {
            mGestureAvailabilityDispatcher.markSensorActive(
                    mCurrentOperation.mClientMonitor.getSensorId(),
                    true /* active */);
        }

        // Not all operations start immediately. BiometricPrompt waits for its operation
        // to arrive at the head of the queue, before pinging it to start.
        final boolean shouldStartNow = currentClient.getCookie() == 0;
        if (shouldStartNow) {
            if (mCurrentOperation.isUnstartableHalOperation()) {
                final HalClientMonitor<?> halClientMonitor =
                        (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
                // Note down current length of queue
                final int pendingOperationsLength = mPendingOperations.size();
                final Operation lastOperation = mPendingOperations.peekLast();
                Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation
                        + ". Last pending operation: " + lastOperation);

                // For current operations, 1) unableToStart, which notifies the caller-side, then
                // 2) notify operation's callback, to notify applicable system service that the
                // operation failed.
                halClientMonitor.unableToStart();
                if (mCurrentOperation.mClientCallback != null) {
                    mCurrentOperation.mClientCallback.onClientFinished(
                            mCurrentOperation.mClientMonitor, false /* success */);
                }

                // Then for each operation currently in the pending queue at the time of this
                // failure, do the same as above. Otherwise, it's possible that something like
                // setActiveUser fails, but then authenticate (for the wrong user) is invoked.
                for (int i = 0; i < pendingOperationsLength; i++) {
                    final Operation operation = mPendingOperations.pollFirst();
                    if (operation == null) {
                        Slog.e(getTag(), "Null operation, index: " + i
                                + ", expected length: " + pendingOperationsLength);
                        break;
                    }
                    if (operation.isHalOperation()) {
                        ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart();
                    }
                    if (operation.mClientCallback != null) {
                        operation.mClientCallback.onClientFinished(operation.mClientMonitor,
                                false /* success */);
                    }
                    Slog.w(getTag(), "[Aborted Operation] " + operation);
                }

                // It's possible that during cleanup a new set of operations came in. We can try to
                // run these. A single request from the manager layer to the service layer may
                // actually be multiple operations (i.e. updateActiveUser + authenticate).
                mCurrentOperation = null;
                startNextOperationIfIdle();
            } else {
                Slog.d(getTag(), "[Starting] " + mCurrentOperation);
                currentClient.start(getInternalCallback());
                mCurrentOperation.mState = Operation.STATE_STARTED;
            }
        } else {
            try {
                mBiometricService.onReadyForAuthentication(currentClient.getCookie());
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
            }
            Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
            mCurrentOperation.mState = Operation.STATE_WAITING_FOR_COOKIE;
        }
    }

第88行代码,需要先弄清楚currentClient变量。

首先是

final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor;

然后继续追踪mCurrentOperation。然后搜索到前面的代码中

mPendingOperations.add(new Operation(clientMonitor, clientCallback));

  Operation(
                @NonNull BaseClientMonitor clientMonitor,
                @Nullable BaseClientMonitor.Callback callback
        ) {
            this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
        }

 很容易分析出clientMonitor 就是mCurrentOperation.mClientMonitor 对象实例

那继续追踪clientMonitor,在方法scheduleClientMonitor中传递进来的。

搜索 mScheduler.scheduleClientMonitor ,发现在Fingerprint21.java类的scheduleEnroll方法定义的。见文章开头部分。

最终FingerprintEnrollClient 对象实例就是clientMonitor ,也就是mClientMonitor。

所以,第88行的

currentClient.start(getInternalCallback());

实际上是调用的FingerprintEnrollClient对象下的start方法。

然后在其父类中找到其方法

 @Override
    public void start(@NonNull Callback callback) {
        super.start(callback);

        if (hasReachedEnrollmentLimit()) {
            Slog.e(TAG, "Reached enrollment limit");
            callback.onClientFinished(this, false /* success */);
            return;
        }

        mEnrollmentStartTimeMs = System.currentTimeMillis();
        startHalOperation();
    }

第12行,startHalOperation方法,实际上是调用的FingerprintEnrollClient下的。

 @Override
    protected void startHalOperation() {
        mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this);

        BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
        try {
            mCancellationSignal = getFreshDaemon().enroll(
                    HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken));
        } catch (RemoteException e) {
            Slog.e(TAG, "Remote exception when requesting enroll", e);
            onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
                    0 /* vendorCode */);
            mCallback.onClientFinished(this, false /* success */);
        }
    }

第7行,实际上就是跨进程调用远程对象BiometricsFingerprint.cpp下的方法enroll方法。

Return<RequestStatus> BiometricsFingerprint::enroll(const hidl_array<uint8_t, 69>& hat,
        uint32_t gid, uint32_t timeoutSec) {
    const hw_auth_token_t* authToken =
        reinterpret_cast<const hw_auth_token_t*>(hat.data());
    return ErrorFilter(mDevice->enroll(mDevice, authToken, gid, timeoutSec));
}

到这里,指纹录入流程,已经关联到指纹模组了,下面再分析,指纹录入(可以说是指纹按压)的流程。

下面的部分,是我猜测的,这段代码,我感觉应该是hal层或者说硬件厂商指纹接受录入的指纹,然后回调到这里的,然后有这个地方层层上报到最上层,告诉用户,正在录入指纹。

BiometricsFingerprint.cpp

void BiometricsFingerprint::notify(const fingerprint_msg_t *msg) {
    BiometricsFingerprint* thisPtr = static_cast<BiometricsFingerprint*>(
            BiometricsFingerprint::getInstance());
    std::lock_guard<std::mutex> lock(thisPtr->mClientCallbackMutex);
    if (thisPtr == nullptr || thisPtr->mClientCallback == nullptr) {
        ALOGE("Receiving callbacks before the client callback is registered.");
        return;
    }
    const uint64_t devId = reinterpret_cast<uint64_t>(thisPtr->mDevice);
    switch (msg->type) {
        case FINGERPRINT_ERROR: {
                int32_t vendorCode = 0;
                FingerprintError result = VendorErrorFilter(msg->data.error, &vendorCode);
                ALOGD("onError(%d)", result);
                if (!thisPtr->mClientCallback->onError(devId, result, vendorCode).isOk()) {
                    ALOGE("failed to invoke fingerprint onError callback");
                }
            }
            break;
        case FINGERPRINT_ACQUIRED: {
                int32_t vendorCode = 0;
                FingerprintAcquiredInfo result =
                    VendorAcquiredFilter(msg->data.acquired.acquired_info, &vendorCode);
                ALOGD("onAcquired(%d)", result);
                if (!thisPtr->mClientCallback->onAcquired(devId, result, vendorCode).isOk()) {
                    ALOGE("failed to invoke fingerprint onAcquired callback");
                }
            }
            break;
        case FINGERPRINT_TEMPLATE_ENROLLING:
            ALOGD("onEnrollResult(fid=%d, gid=%d, rem=%d)",
                msg->data.enroll.finger.fid,
                msg->data.enroll.finger.gid,
                msg->data.enroll.samples_remaining);
            if (!thisPtr->mClientCallback->onEnrollResult(devId,
                    msg->data.enroll.finger.fid,
                    msg->data.enroll.finger.gid,
                    msg->data.enroll.samples_remaining).isOk()) {
                ALOGE("failed to invoke fingerprint onEnrollResult callback");
            }
            break;
        case FINGERPRINT_TEMPLATE_REMOVED:
            ALOGD("onRemove(fid=%d, gid=%d, rem=%d)",
                msg->data.removed.finger.fid,
                msg->data.removed.finger.gid,
                msg->data.removed.remaining_templates);
            if (!thisPtr->mClientCallback->onRemoved(devId,
                    msg->data.removed.finger.fid,
                    msg->data.removed.finger.gid,
                    msg->data.removed.remaining_templates).isOk()) {
                ALOGE("failed to invoke fingerprint onRemoved callback");
            }
            break;
        case FINGERPRINT_AUTHENTICATED:
            if (msg->data.authenticated.finger.fid != 0) {
                ALOGD("onAuthenticated(fid=%d, gid=%d)",
                    msg->data.authenticated.finger.fid,
                    msg->data.authenticated.finger.gid);
                const uint8_t* hat =
                    reinterpret_cast<const uint8_t *>(&msg->data.authenticated.hat);
                const hidl_vec<uint8_t> token(
                    std::vector<uint8_t>(hat, hat + sizeof(msg->data.authenticated.hat)));
                if (!thisPtr->mClientCallback->onAuthenticated(devId,
                        msg->data.authenticated.finger.fid,
                        msg->data.authenticated.finger.gid,
                        token).isOk()) {
                    ALOGE("failed to invoke fingerprint onAuthenticated callback");
                }
            } else {
                // Not a recognized fingerprint
                if (!thisPtr->mClientCallback->onAuthenticated(devId,
                        msg->data.authenticated.finger.fid,
                        msg->data.authenticated.finger.gid,
                        hidl_vec<uint8_t>()).isOk()) {
                    ALOGE("failed to invoke fingerprint onAuthenticated callback");
                }
            }
            break;
        case FINGERPRINT_TEMPLATE_ENUMERATING:
            ALOGD("onEnumerate(fid=%d, gid=%d, rem=%d)",
                msg->data.enumerated.finger.fid,
                msg->data.enumerated.finger.gid,
                msg->data.enumerated.remaining_templates);
            if (!thisPtr->mClientCallback->onEnumerate(devId,
                    msg->data.enumerated.finger.fid,
                    msg->data.enumerated.finger.gid,
                    msg->data.enumerated.remaining_templates).isOk()) {
                ALOGE("failed to invoke fingerprint onEnumerate callback");
            }
            break;
    }
}

见第35行,!thisPtr->mClientCallback->onEnrollResult 

这里的mClientCallback,在下面的博客中,有详细的介绍。直接直接给出结果,实际上就是HalResultController类对象。

指纹设别流程_liujun3512159的博客-CSDN博客

直接看源码

public static class HalResultController extends IBiometricsFingerprintClientCallback.Stub {
 
        /**
         * Interface to sends results to the HalResultController's owner.
         */
        public interface Callback {
            /**
             * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
             */
            void onHardwareUnavailable();
        }
 
        private final int mSensorId;
        @NonNull private final Context mContext;
        @NonNull final Handler mHandler;
        @NonNull final BiometricScheduler mScheduler;
        @Nullable private Callback mCallback;
 
        HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
                @NonNull BiometricScheduler scheduler) {
            mSensorId = sensorId;
            mContext = context;
            mHandler = handler;
            mScheduler = scheduler;
        }
 
        public void setCallback(@Nullable Callback callback) {
            mCallback = callback;
        }
 
        @Override
        public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
            mHandler.post(() -> {
                final BaseClientMonitor client = mScheduler.getCurrentClient();
                if (!(client instanceof FingerprintEnrollClient)) {
                    Slog.e(TAG, "onEnrollResult for non-enroll client: "
                            + Utils.getClientName(client));
                    return;
                }
 
                final int currentUserId = client.getTargetUserId();
                final CharSequence name = FingerprintUtils.getLegacyInstance(mSensorId)
                        .getUniqueName(mContext, currentUserId);
                final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
 
                final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
                enrollClient.onEnrollResult(fingerprint, remaining);
            });
        }
 
        @Override
        public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
            onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
        }
 
        @Override
        public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
            mHandler.post(() -> {
                final BaseClientMonitor client = mScheduler.getCurrentClient();
                if (!(client instanceof AcquisitionClient)) {
                    Slog.e(TAG, "onAcquired for non-acquisition client: "
                            + Utils.getClientName(client));
                    return;
                }
 
                final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
                acquisitionClient.onAcquired(acquiredInfo, vendorCode);
            });
        }
 
        @Override
        public void onAuthenticated(long deviceId, int fingerId, int groupId,
                ArrayList<Byte> token) {
            mHandler.post(() -> {
                final BaseClientMonitor client = mScheduler.getCurrentClient();
                if (!(client instanceof AuthenticationConsumer)) {
                    Slog.e(TAG, "onAuthenticated for non-authentication consumer: "
                            + Utils.getClientName(client));
                    return;
                }
 
                final AuthenticationConsumer authenticationConsumer =
                        (AuthenticationConsumer) client;
                final boolean authenticated = fingerId != 0;
                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
                authenticationConsumer.onAuthenticated(fp, authenticated, token);
            });
        }
 
        @Override
        public void onError(long deviceId, int error, int vendorCode) {
            mHandler.post(() -> {
                final BaseClientMonitor client = mScheduler.getCurrentClient();
                Slog.d(TAG, "handleError"
                        + ", client: " + Utils.getClientName(client)
                        + ", error: " + error
                        + ", vendorCode: " + vendorCode);
                if (!(client instanceof ErrorConsumer)) {
                    Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(client));
                    return;
                }
 
                final ErrorConsumer errorConsumer = (ErrorConsumer) client;
                errorConsumer.onError(error, vendorCode);
 
                if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
                    Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
                    if (mCallback != null) {
                        mCallback.onHardwareUnavailable();
                    }
                }
            });
        }
 
        @Override
        public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
            mHandler.post(() -> {
                Slog.d(TAG, "Removed, fingerId: " + fingerId + ", remaining: " + remaining);
                final BaseClientMonitor client = mScheduler.getCurrentClient();
                if (!(client instanceof RemovalConsumer)) {
                    Slog.e(TAG, "onRemoved for non-removal consumer: "
                            + Utils.getClientName(client));
                    return;
                }
 
                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
                final RemovalConsumer removalConsumer = (RemovalConsumer) client;
                removalConsumer.onRemoved(fp, remaining);
            });
        }
 
        @Override
        public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
            mHandler.post(() -> {
                final BaseClientMonitor client = mScheduler.getCurrentClient();
                if (!(client instanceof EnumerateConsumer)) {
                    Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
                            + Utils.getClientName(client));
                    return;
                }
 
                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
                final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
                enumerateConsumer.onEnumerationResult(fp, remaining);
            });
        }
    }

在第45行,其实这里的enrollClient是在前面的方法scheduleEnroll中传进来的,实际上就是FingerprintEnrollClient

继续追踪 FingerprintEnrollClient下的onEnrollResult 方法

@Override
    public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
        super.onEnrollResult(identifier, remaining);

        mSensorOverlays.ifUdfps(
                controller -> controller.onEnrollmentProgress(getSensorId(), remaining));

        if (remaining == 0) {
            mSensorOverlays.hide(getSensorId());
        }
    }

分析到这里,基本上结束,注意第3行,调用uper.onEnrollResult(identifier, remaining);

其实现逻辑在方法scheduleEnroll中,见最上面。

第6行的

controller.onEnrollmentProgress 就是录入指纹的进度条方法了。抽空也补充下。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liujun3512159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值