David Dong

David Dong

Java/C/C#/Python

Java/C/C#/Python

POST

Fingerprint implementation in android 8.0 and later

Since Android 8.0, Android has fully introduced the HIDL layer into the framework. The purpose is to separate vendor partition from system partition so that Android is capable to upgrade the framework through OTA without recompiling HAL. Correspondingly, the framework of fingerprint has also been reconstructed. This page will give an introduction about the difference in the fingerprint framework between android 7.0 (and early version) and android 8.0 (and later version).

After the study of the previous three articles,
Android Fingerprint Framework (1)
Android Fingerprint Framework (2)
Android Fingerprint Framework (3)

we have discussed the fingerprint framework on android 7.0 in previous blogs, here give a summary for anyone who has not read these articles yet.

fingerprint framework in Android 7.0

This diagram is the fingerprint framework on the android platform, which I have presented in another article and copied here.

framework

From the top layer, the fingerprint application will start the workflow and this is the fingerprint management entry defined by the Android system layer. In the framework internal, some tasks will be done to handle the request from the application.

  1. init.rc starts up the Fingerprintd process during the system boot-up. Fingerpringd then register IFingerprintDaemon remote service to ServiceManager.

  2. System Server will start fingerprint system service FingerprintService.
    SystemServer.java

mSystemServiceManager.startService(FingerprintService.class);
  1. FingerprintService calls the interface of Fingerprintd to communicate with Fingerprint HAL layer.
    FingerprintService.java
public IFingerprintDaemon getFingerprintDaemon() {
        if (mDaemon == null) {
            mDaemon = IFingerprintDaemon.Stub.
            asInterface(ServiceManager.getService(FINGERPRINTD));
            if (mDaemon != null) {
                try {
                    mDaemon.asBinder().linkToDeath(this, 0);
                    mDaemon.init(mDaemonCallback);
                    mHalDeviceId = mDaemon.openHal();
                    if (mHalDeviceId != 0) {
                        updateActiveGroup(ActivityManager.getCurrentUser(), null);
                    } else {
                        Slog.w(TAG, "Failed to open Fingerprint HAL!");
                        MetricsLogger.count(mContext,
                        "fingerprintd_openhal_error", 1);
                        mDaemon = null;
                    }
                } catch (RemoteException e) {
                    Slog.e(TAG, "Failed to open fingeprintd HAL", e);
                    mDaemon = null; // try again later!
                }
            } else {
                Slog.w(TAG, "fingerprint service not available");
            }
        }
        return mDaemon;
    }
  1. Fingerprintd calls FingerprintDaemonProxy function to open HAL.
    FingerprintDaemonProxy.cpp
int64_t FingerprintDaemonProxy::openHal() {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
    int err;
    const hw_module_t *hw_module = NULL;
    if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
        ALOGE("Can't open fingerprint HW Module, error: %d", err);
        return 0;
    }
    if (NULL == hw_module) {
        ALOGE("No valid fingerprint module");
        return 0;
    }

    mModule = reinterpret_cast<const fingerprint_module_t*>(hw_module);

    if (mModule->common.methods->open == NULL) {
        ALOGE("No valid open method");
        return 0;
    }

    hw_device_t *device = NULL;

    if (0 != (err = mModule->common.methods->open(hw_module, NULL, &device))) {
        ALOGE("Can't open fingerprint methods, error: %d", err);
        return 0;
    }

    if (kVersion != device->version) {
        ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
        // return 0; // FIXME
    }

    mDevice = reinterpret_cast<fingerprint_device_t*>(device);
    err = mDevice->set_notify(mDevice, hal_notify_callback);
    if (err < 0) {
        ALOGE("Failed in call to set_notify(), err=%d", err);
        return 0;
    }

    // Sanity check - remove
    if (mDevice->notify != hal_notify_callback) {
        ALOGE("NOTIFY not set properly: %p != %p", mDevice->notify, 
        hal_notify_callback);
    }

    ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
    return reinterpret_cast<int64_t>(mDevice); // This is just a handle
}
  1. The HAL code is at below android path normally.
/hardware/libhardware/include/hardware/fingerprint.h
/hardware/libhardware/modules/fingerprint.c

I drew a flow chart to help understand the whole flow more clearly.

workflow

The related source code and android path can be found in the below table. Android 7.0 (NOUGAT)

File Android Path
init.rc root/system/core/rootdir/init.rc
fingerprintd.cpp root/system/core/fingerprintd/fingerprintd.cpp
FingerprintDaemonProxy.h root/system/core/fingerprintd/
fingerprintdaemonproxy.cpp root/system/core/fingerprintd/fingerprintdaemonproxy.cpp
SystemServer.java root/frameworks/base/services/java/com/android/server/SystemServer.java
FingerprintService.java root/frameworks/base/services/core/
java/com/android/server/fingerprint/FingerprintService.java
hardware.h root/hardware/libhardware/include/hardware/hardware.h
hardware.c root/hardware/libhardware/hardware.c

fingerprint framework in Android 8.0

Above is the fingerprint framework of Android 7.0, however in Android 8.0 and later versions, Android has updated the framework and introduced a set of languages called HIDL to define the interface between framework and HAL.

Let’s see the difference.

hidl

Android 8.0 adds a sub-directory /interface in the /hardware directory, which includes all HIDL files for the hardware module.

Android 8.0 removed Fingerprintd, instead, FingerprintService accesses HAL by calling HIDL.

We can find the change in getFingerprintDaemon() method.

In Android 7.0
FingerprintService.java

mDaemon = IFingerprintDaemon.Stub.asInterface(ServiceManager.
getService(FINGERPRINTD));

While in Android 8.0, mDaemon is achieved from the service of IBiometricsFingerprint.
FingerprintService.java

mDaemon = IBiometricsFingerprint.getService();

IBiometricsFingerprint is a new fingerprint HIDL interface that was introduced on Android 8.0.
IBiometricsFingerprint.hal use HIDL language format defined a series of standard fingerprint operation interfaces. And biometricsfingerprint.cpp class realized the IBiometricsFingerprint interface.

We may notice that the IBiometricsFingerprint returns a service for caller, actually there is a file in the HIDL sub-directory:
android.hardware.biometrics.fingerprint@2.1-service.rc, which will start fps_hal service.
fingerprint@2.1-service.rc

 service 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

The files of the fingerprint HIDL related. hidl file

If we look at the Service.cpp, we will find the service actually will create a BiometricsFingerprint instance and register as service.

int main() {
    android::sp<IBiometricsFingerprint> bio = BiometricsFingerprint::getInstance();

    configureRpcThreadpool(1, true /*callerWillJoin*/);

    if (bio != nullptr) {
        bio->registerAsService();
    } else {
        ALOGE("Can't create instance of BiometricsFingerprint, nullptr");
    }

    joinRpcThreadpool();

    return 0; // should never get here
}

In the constructor of BiometricsFingerprint class, it calls openHal() to open HAL module.
BiometricsFingerprint.cpp

BiometricsFingerprint::BiometricsFingerprint() : mClientCallback(nullptr), 
mDevice(nullptr) {
    sInstance = this; // keep track of the most recent instance
    mDevice = openHal();
    if (!mDevice) {
        ALOGE("Can't open HAL module");
    }
}

Let’s check the openHal() function.
BiometricsFingerprint.cpp

fingerprint_device_t* BiometricsFingerprint::openHal() {
    int err;
    const hw_module_t *hw_mdl = nullptr;
    ALOGD("Opening fingerprint hal library...");
    if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_mdl))) {
        ALOGE("Can't open fingerprint HW Module, error: %d", err);
        return nullptr;
    }

    if (hw_mdl == nullptr) {
        ALOGE("No valid fingerprint module");
        return nullptr;
    }

    fingerprint_module_t const *module =
        reinterpret_cast<const fingerprint_module_t*>(hw_mdl);
    if (module->common.methods->open == nullptr) {
        ALOGE("No valid open method");
        return nullptr;
    }

    hw_device_t *device = nullptr;

    if (0 != (err = module->common.methods->open(hw_mdl, nullptr, &device))) {
        ALOGE("Can't open fingerprint methods, error: %d", err);
        return nullptr;
    }

    if (kVersion != device->version) {
        // enforce version on new devices because of HIDL@2.1 translation layer
        ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
        return nullptr;
    }

    fingerprint_device_t* fp_device =
        reinterpret_cast<fingerprint_device_t*>(device);

    if (0 != (err =
            fp_device->set_notify(fp_device, BiometricsFingerprint::notify))) {
        ALOGE("Can't register fingerprint module callback, error: %d", err);
        return nullptr;
    }

    return fp_device;
}

Have you found that the function realization is similar to the FingerprintDaemonProxy::openHal()? The native method is called and the HAL module is opened here. After access to the HAL, others are all same under the HAL layer.

So far, we can change the fingerprint framework of Android 8.0 as below.

fingerprint framework android8.0

Compare this flowchart carefully with the last flowchart above, we can find the difference.

The related source code and android path can be found in the below table

File Android Path
fingerprint@2.1-service root/hardware/interfaces/biometrics/fingerprint/2.1/default
service.cpp root/hardware/interfaces/biometrics/fingerprint/2.1/default
BiometricsFingerprint.h root/hardware/interfaces/biometrics/fingerprint/2.1/default
BiometricsFingerprint.cpp root/hardware/interfaces/biometrics/fingerprint/2.1/default
IBiometricsFingerprint.hal root/hardware/interfaces/biometrics/fingerprint/2.1
IBiometricsFingerprintClientCallback.hal root/hardware/interfaces/biometrics/fingerprint/2.1

Now, I think the main difference of the fingerprint framework on Android 8.0 has been introduced and if you have further questions, you can ask in the comment box, I will reply to you as soon as I can.


AndroidFingerprint

You may also like

further reading