HIDL HAL 服务创建全流程

核心目标是:定义一个接口,实现它,并将其编译成一个可执行文件。这个文件在系统启动时运行,将我们的服务注册到 hwservicemanager,使得其他进程可以发现并调用它

整个流程涉及两个主要代码位置:

  1. 接口定义层 (hardware/interfaces/): 定义 .hal 文件,生成所有项目都会用到的通用代码
  2. 服务实现层 (vendor/ 或 device/): 编写服务的具体业务逻辑和启动入口
步骤 一: 定义接口(创建 .hal 文件)

目的:创建一份“合同”,明确规定服务提供哪些方法

  1. 创建目录结构
    包名决定了目录结构。假设我们的包名是 vendor.mycompany.hardware.demo@1.0
    # 在AOSP源码根目录下操作
    mkdir -p hardware/interfaces/demo/1.0/default/
    ○ hardware/interfaces/: 存放HIDL接口的标准位置
    ○ demo/: 我们的模块名称
    ○ 1.0/: 主版本号
    ○ default/: 通常在这里存放服务的实现和启动配置(但我们先专注于接口)
  2. 编写接口文件 IDemo.hal
    vim hardware/interfaces/demo/1.0/IDemo.hal
    文件内容
    package vendor.mycompany.hardware.demo@1.0;
    
    interface IDemo {
        // 方法:加法
        add(int32_t a, int32_t b) generates (int32_t result);
        // 方法:返回问候语
        getHello(string name) generates (string result);
    };
    此文件作用:这是最根本的接口定义。HIDL编译器 (hidl-gen) 将读取这个文件来生成C++和Java的代码框架
步骤 二: 生成编译配置并编译接口

目的:告诉构建系统如何编译我们的接口,并实际生成代码

  1. 创建接口的 Android.bp 文件
    vim hardware/interfaces/demo/1.0/Android.bp

    文件内容

    hidl_interface {
        name: "vendor.mycompany.hardware.demo@1.0",
        root: "vendor.mycompany.hardware", // 包名的根
        srcs: ["IDemo.hal"],              // 源文件
        interfaces: [
            "android.hidl.base@1.0"       // 所有HIDL接口都需要继承的基类
        ],
        gen_java: true,                   // 也生成Java代码(可选)
    }

    此文件作用hidl_interface 是一个特殊的模块类型。当构建系统处理它时,它会调用 hidl-gen 工具,根据 IDemo.hal 生成对应的C++头文件/源文件

  2. 编译接口模块

    m vendor.mycompany.hardware.demo@1.0

    此命令作用:执行后,构建系统会生成代码,输出路径通常在 out/soong/.intermediates/hardware/interfaces/demo/1.0/。你会看到生成的文件,例如:
    ○ vendor/mycompany/hardware/demo/1.0/IDemo.h (C++头文件)
    ○ vendor/mycompany/hardware/demo/1.0/IDemo.cpp (C++源文件)
    ○ 这些生成的文件包含了纯虚类 IDemo、用于客户端调用的 Proxy 类和服务端需要继承的 Stub 类

步骤 三: 实现服务(编写服务代码)

目的:创建真正的服务可执行文件。我们通常在 vendor/ 目录下进行实现,以将厂商实现与Android通用接口分离

  1. 创建实现目录
    mkdir -p vendor/mycompany/hardware/demo/implementation/
    cd vendor/mycompany/hardware/demo/implementation/
  2. 编写实现文件 Demo.cpp

    #include <log/log.h>
    #include "vendor/mycompany/hardware/demo/1.0/IDemo.h"
    
    using ::vendor::mycompany::hardware::demo::V1_0::IDemo;
    using ::android::hardware::hidl_string;
    using ::android::hardware::Return;
    using ::android::hardware::Void;
    using ::android::sp;
    
    // 继承自生成的 Stub 类,并实现所有纯虚函数
    struct Demo : public IDemo::Stub {
        Return<int32_t> add(int32_t a, int32_t b) override {
            ALOGD("Adding %d and %d", a, b);
            return a + b;
        }
    
        Return<void> getHello(const hidl_string& name, getHello_cb _hidl_cb) override {
            ALOGD("Getting hello for %s", name.c_str());
            hidl_string result = "Hello, " + name;
            _hidl_cb(result); // 通过回调函数返回结果
            return Void();
        }
    };

    此文件作用:包含服务的具体业务逻辑。Demo 类就是我们的HAL实现体

  3. 编写服务入口文件 service.cpp

    #include <hidl/LegacySupport.h>
    #include "vendor/mycompany/hardware/demo/1.0/IDemo.h"
    
    using ::vendor::mycompany::hardware::demo::V1_0::IDemo;
    using ::android::hardware::defaultPassthroughServiceImplementation;
    using ::android::hardware::configureRpcThreadpool;
    using ::android::hardware::joinRpcThreadpool;
    using ::android::sp;
    
    int main() {
        // 配置线程池,服务端需要
        configureRpcThreadpool(1, true /* callerWillJoin */);
    
        // 创建服务实例
        sp<IDemo> demoService = new Demo();
    
        // 将服务注册到 hwservicemanager,使其可被其他进程发现
        android::status_t status = demoService->registerAsService();
        if (status != android::OK) {
            ALOGE("Could not register service.");
            return -1;
        }
    
        ALOGD("Demo service is ready.");
        // 将线程加入线程池,进入循环,等待客户端调用
        joinRpcThreadpool();
        // 正常情况下不会从 joinRpcThreadpool() 返回
        return 1;
    }

    此文件作用:包含 main() 函数,是服务的启动入口。它负责创建我们的实现对象 (Demo) 并将其注册到系统服务管理器

步骤 四: 编译服务(创建二进制的 Android.bp

目的:将 Demo.cpp 和 service.cpp 编译成一个可执行文件

在 vendor/mycompany/hardware/demo/implementation/ 目录下创建 Android.bp

cc_binary {
    name: "vendor.mycompany.hardware.demo@1.0-service", // 最终生成的可执行文件名称
    relative_install_path: "hw", // 安装到 /vendor/bin/hw/
    vendor: true,                // 这是一个vendor模块
    init_rc: ["vendor.mycompany.hardware.demo@1.0-service.rc"], // 关联的init脚本
    srcs: ["Demo.cpp", "service.cpp"], // 要编译的源文件
    shared_libs: [
        "liblog", // 用于 ALOGD
        "libhidlbase", // HIDL基础库
        "libutils",
        "vendor.mycompany.hardware.demo@1.0", // 链接步骤2中生成的接口库!!!
    ],
}

此文件作用:指导构建系统如何编译我们的服务。最关键的是 shared_libs 中的 vendor.mycompany.hardware.demo@1.0,它让我们的实现代码能够链接到步骤2中生成的 IDemo.h 头文件和相关的桩代码

现在执行 m vendor.mycompany.hardware.demo@1.0-service 就会编译出可执行文件

步骤 五: 注册和启动(让系统知道这个服务)

目的:确保系统启动时能自动运行我们的服务

  1. 创建Init RC文件
    vim vendor/mycompany/hardware/demo/implementation/vendor.mycompany.hardware.demo@1.0-service.rc

    文件内容

    service vendor.demo_hal_service /vendor/bin/hw/vendor.mycompany.hardware.demo@1.0-service
        class hal
        user system
        group system

    此文件作用:Init进程(第一个进程)会解析这个文件。它定义了一个名为 vendor.demo_hal_service 的服务,并指定其可执行文件路径。class hal 表示它将在 hal 类服务启动时被启动

  2. 将服务加入产品配置
    在你的设备产品Makefile (如 device/your_company/your_device/device.mk) 中添加:

    PRODUCT_PACKAGES += \
        vendor.mycompany.hardware.demo@1.0-service

    此操作作用:告诉构建系统,在为你的设备制作镜像时,需要将 vendor.mycompany.hardware.demo@1.0-service 这个模块打包进去

流程总结与文件作用
步骤 文件路径 作用 编译命令/结果
1. 定义 hardware/interfaces/demo/1.0/IDemo.hal 接口合同。定义方法 -
2. 生成 hardware/interfaces/demo/1.0/Android.bp 接口编译指令。指导生成代码 m vendor.mycompany.hardware.demo@1.0
(生成的) out/.../IDemo.h C++桩代码。包含 IDemoStubProxy 类 (生成)
3. 实现 vendor/.../implementation/Demo.cpp 业务逻辑。继承 Stub,实现 .hal 中的方法 -
vendor/.../implementation/service.cpp 程序入口main() 函数,注册服务到管理器 -
4. 编译 vendor/.../implementation/Android.bp 服务编译指令。指导生成可执行文件 m vendor.mycompany.hardware.demo@1.0-service
(生成的) /vendor/bin/hw/vendor.mycompany.hardware.demo@1.0-service 最终的可执行程序 (生成)
5. 注册 vendor/.../implementation/*.rc Init启动脚本。告诉Init如何启动我们的服务 (随服务一起安装)
device.mk 产品包配置。决定哪些模块被打包进系统镜像 (整体编译时生效)

最终,当你刷机并启动系统后,init 进程会根据 .rc 文件启动你的服务。服务执行 service.cpp 中的 main() 函数,将自身注册到 hwservicemanager。其他进程(客户端)就可以通过 IDemo::getService() 来获取这个服务的代理(Proxy)并调用其方法了

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐