HAL服务HIDL创建流程
HIDL HAL 服务创建全流程
核心目标是:定义一个接口,实现它,并将其编译成一个可执行文件。这个文件在系统启动时运行,将我们的服务注册到 hwservicemanager
,使得其他进程可以发现并调用它
整个流程涉及两个主要代码位置:
- 接口定义层 (
hardware/interfaces/
): 定义.hal
文件,生成所有项目都会用到的通用代码 - 服务实现层 (
vendor/
或device/
): 编写服务的具体业务逻辑和启动入口
步骤 一: 定义接口(创建 .hal
文件)
目的:创建一份“合同”,明确规定服务提供哪些方法
- 创建目录结构:
包名决定了目录结构。假设我们的包名是vendor.mycompany.hardware.demo@1.0
○# 在AOSP源码根目录下操作 mkdir -p hardware/interfaces/demo/1.0/default/
hardware/interfaces/
: 存放HIDL接口的标准位置
○demo/
: 我们的模块名称
○1.0/
: 主版本号
○default/
: 通常在这里存放服务的实现和启动配置(但我们先专注于接口) - 编写接口文件
IDemo.hal
:
文件内容:vim hardware/interfaces/demo/1.0/IDemo.hal
此文件作用:这是最根本的接口定义。HIDL编译器 (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-gen
) 将读取这个文件来生成C++和Java的代码框架
步骤 二: 生成编译配置并编译接口
目的:告诉构建系统如何编译我们的接口,并实际生成代码
- 创建接口的
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++头文件/源文件 -
编译接口模块:
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通用接口分离
- 创建实现目录:
mkdir -p vendor/mycompany/hardware/demo/implementation/ cd vendor/mycompany/hardware/demo/implementation/
-
编写实现文件
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实现体 -
编写服务入口文件
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
就会编译出可执行文件
步骤 五: 注册和启动(让系统知道这个服务)
目的:确保系统启动时能自动运行我们的服务
- 创建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
类服务启动时被启动 -
将服务加入产品配置:
在你的设备产品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++桩代码。包含 IDemo , Stub , Proxy 类 |
(生成) | |
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)并调用其方法了
更多推荐
所有评论(0)