Android学习笔记1 - Fingerprint authenticate流程梳理

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
cancelCancellationSignal: An object that can be used to cancel authentication

This value must never be null.

executorExecutor: An executor to handle callback events

This value must never be null.

Callback and listener events are dispatched through this Executor, providing an easy way to control which thread is used. To dispatch events through the main thread of your application, you can use Context.getMainExecutor(). To dispatch events through a shared thread pool, you can use AsyncTask.THREAD_POOL_EXECUTOR.

callbackBiometricPrompt.AuthenticationCallback: An object to receive authentication events

This value must never be null.

    比如下面的用法,加上前面的初始化,就是一个简单的应用。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

        用户可以自己复写对应的方法获取自己感兴趣结果。

voidonAuthenticationError(int errorCode, CharSequence errString)

Called when an unrecoverable error has been encountered and the operation is complete.

voidonAuthenticationFailed()

Called when a biometric is valid but not recognized.

voidonAuthenticationHelp(int helpCode, CharSequence helpString)

Called when a recoverable error has been encountered during authentication.

voidonAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result)

Called when a biometric is recognized.

voidonAuthenticationSucceeded(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个工作:

  1. cancel设置listener,前面的章节学习到,应用app可以通过该cancel来取消当前的认证过程。所以进入到认证之前首先判断是否已经被cancel了,如果是则直接返回;如果没有则设置Listener,当应用app调用cancel的时候,listener被调用,我们跟踪listener可知,其实是调用远程的mService.cancelAuthentication(mToken, mContext.getOpPackageName()); 这里有一个疑问,应用app设置的listener这个时候还会被调用吗?后面分析cancel类。
  2. mService.authenticate(mToken, sessionId, userId, mServiceReceiver,0 /* flags */, mContext.getOpPackageName(), bundle, receiver);调用远程服务进行认证过程;这里设置了一个mServiceReceiver用来做远程service的回调。后面分析FingerprintService类再看这些参数。
  3. 如果认证返回失败(指那些能够立即返回失败的类型,因为认证需要硬件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件事情:

  1. 其中的publishBinderService会向ServiceManager注册系统服务,后面学习binder的时候再深入。这里需要知道注册的服务类是FingerprintServiceWrapper这个类
  2. getFingerprintDaemon()得到一个HAL的服务,并且保存在Pool中,这个远程的HAL服务由OEM实现。它和FingerprintService通信方式是HIDL
  3. 调用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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值