HIDL C++

Android 8 重新构建了 Android 操作系统,旨在为独立于设备的 Android 平台以及特定于设备和供应商的代码定义清晰的接口。Android 已经在 HAL 接口中定义了许多此类接口,这些接口在 hardware/libhardware 中定义为 C 标头。HIDL 用稳定、版本化的接口取代了这些 HAL 接口,这些接口可以是 C++ 中的客户端和服务器端 HIDL 接口(如下所述)或 Java

本节中的页面介绍了 HIDL 接口的 C++ 实现,包括有关 hidl-gen 编译器从 HIDL .hal 文件自动生成的文件、这些文件的打包方式以及如何将这些文件与使用它们的 C++ 代码集成在一起的详细信息。

客户端和服务器实现

HIDL 接口具有客户端和服务器实现

  • HIDL 接口的客户端是通过调用接口上的方法来使用该接口的代码。
  • 服务器是 HIDL 接口的实现,它接收来自客户端的调用并返回结果(如果需要)。

在从 libhardware HAL 过渡到 HIDL HAL 的过程中,HAL 实现变成了服务器,而调用 HAL 的进程变成了客户端。默认实现可以同时服务于直通式和绑定式 HAL,并且可以随时间变化

图 1. 传统 HAL 的开发过程。

创建 HAL 客户端

首先在 makefile 中包含 HAL 库

  • Make: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Soong: shared_libs: [ …, android.hardware.nfc@1.0 ]

接下来,包含 HAL 标头文件

#include <android/hardware/nfc/1.0/IFoo.h>

// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

创建 HAL 服务器

要创建 HAL 实现,您必须拥有代表您的 HAL 的 .hal 文件,并且已经使用 hidl-gen 上的 -Lmakefile-Landroidbp 为您的 HAL 生成了 makefile(./hardware/interfaces/update-makefiles.sh 为内部 HAL 文件执行此操作,并且是一个很好的参考)。从 libhardware 传输 HAL 时,您可以使用 c2hal 轻松完成大量工作。

要创建实现 HAL 所需的文件

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

为了使 HAL 在直通模式下工作,您必须在 /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so 中具有函数 HIDL_FETCH_IModuleName,其中 OPTIONAL_IDENTIFIER 是标识直通实现的字符串。上述命令自动满足直通模式要求,这些命令还会创建 android.hardware.nfc@1.0-impl 目标,但可以使用任何扩展名。例如,android.hardware.nfc@1.0-impl-foo 使用 -foo 来区分自身。

如果 HAL 是另一个 HAL 的次要版本或扩展,则应使用基本 HAL 来命名此二进制文件。例如,android.hardware.graphics.mapper@2.1 实现仍应位于名为 android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER) 的二进制文件中。通常,此处的 OPTIONAL_IDENTIFIER 将包括实际的 HAL 版本。通过这样命名二进制文件,2.0 客户端可以直接检索它,而 2.1 客户端可以向上转换实现。

接下来,用功能填充存根并设置守护程序。示例守护程序代码(支持直通)

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

defaultPassthroughServiceImplementation 为提供的 -impl 库调用 dlopen(),并将其作为绑定式服务提供。示例守护程序代码(用于纯绑定式服务)

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool never exceeds
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

此守护程序通常位于 $PACKAGE + "-service-suffix" 中(例如,android.hardware.nfc@1.0-service),但它可能位于任何位置。特定类 HAL 的 sepolicy 是属性 hal_<module>(例如,hal_nfc)。此属性必须应用于运行特定 HAL 的守护程序(如果同一进程服务于多个 HAL,则可以将多个属性应用于它)。