HIDL C++

Android 8 对 Android OS 的架构重新进行了设计,以在独立于设备的 Android 平台与特定于设备和供应商的代码之间定义清晰的接口。Android 已经以 HAL 接口的形式(在 hardware/libhardware 中定义为 C 头文件)定义了许多此类接口。HIDL 将这些 HAL 接口替换为带版本编号的稳定接口,它们可以是采用 C++(如下所述)或 Java 的客户端和服务器端 HIDL 接口。

本部分中的几页内容介绍了 HIDL 接口的 C++ 实现,其中详细说明了 hidl-gen 编译器基于 HIDL .hal 文件自动生成的文件,这些文件如何打包,以及如何将这些文件与使用它们的 C++ 代码集成。

客户端和服务器实现

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

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

在从 libhardware HAL 转换为 HIDL HAL 的过程中,HAL 实现成为服务器,而调用 HAL 的进程则成为客户端。默认实现可用于直通式 (passthrough) 和绑定式 (binderized) HAL,并可能会随着时间而发生变化:

图 1. 旧版 HAL 的发展历程。

创建 HAL 客户端

首先将 HAL 库添加到 makefile 中:

  • 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 在透传模式下运行,您必须具有 HIDL_FETCH_IModuleName 函数,并且该函数要位于 /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so 中,其中 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 的特定类的 sepolicyhal_<module> 属性(例如 hal_nfc))。您必须将此属性应用到运行特定 HAL 的守护程序(如果同一进程提供多个 HAL,可以将多个属性应用到该进程)。