3. HIDL 服务端开发:从零搭建你的第一个系统服务
好,咱们进入实战环节。前面两章讲了 HIDL 是什么、为什么用它,现在该动手了。这一章的核心就一件事:创建一个 HIDL 服务端,让它跑在系统里,能被客户端调用。
我刚开始接触 HIDL 时,觉得这东西挺绕——又是 package 又是接口文件,还得注册成系统服务。但说白了,它就是个跨进程调用的壳,你只要把流程走一遍,后面就顺了。
3.1 创建 HIDL Package:命名有讲究
HIDL package 不是随便起的。它决定了你的接口在系统中的位置,也影响后续的代码生成路径。我个人习惯按这个规则来:
| 组成部分 | 示例 | 说明 |
|---|---|---|
| vendor 名 | vendor.nice | 公司或组织标识,小写 |
| hardware 名 | sensor | 硬件模块名称,小写 |
| 版本号 | 1.0 | 主版本.次版本,整数 |
举个例子,我曾在项目中做过一个温控传感器服务,package 就叫 vendor.nice.thermalsensor@1.0。注意版本号是 @1.0 这种写法,不是 v1.0。
vendor.nice.thermalsensor@1.0,对应的目录就是 vendor/nice/thermalsensor/1.0。Android 编译系统会按这个路径找文件。
3.2 定义接口文件:.hal 文件怎么写
接口文件是 HIDL 的核心。它用 .hal 后缀,语法有点像 C++ 和 Java 的混合体。我建议你从最简单的开始:
// IThermalSensor.hal
package vendor.nice.thermalsensor@1.0;
interface IThermalSensor {
/**
* 获取当前温度值
* @return 温度值,单位摄氏度
*/
getTemperature() generates (float temp);
/**
* 设置温度阈值
* @param threshold 阈值
* @return 是否成功
*/
setThreshold(float threshold) generates (bool success);
};
这里有几个关键点:
- package 声明:必须和目录结构一致
- interface 关键字:定义接口名称,建议以 I 开头
- generates:返回值用这个关键字,不是 return
- 方法名:小驼峰,语义清晰
3.3 实现服务端接口:C++ 代码怎么写
接口定义好了,接下来是实现。Android 提供了 hidl-gen 工具,能自动生成 C++ 骨架代码。你只需要填充业务逻辑就行。
我个人习惯手动创建实现类,这样更可控。来看一个完整的实现:
// ThermalSensor.cpp
#include <vendor/nice/thermalsensor/1.0/IThermalSensor.h>
#include <hidl/LegacySupport.h>
#include <thread>
#include <chrono>
using namespace vendor::nice::thermalsensor::V1_0;
using namespace android::hardware;
using namespace std::chrono_literals;
class ThermalSensor : public IThermalSensor {
public:
ThermalSensor() : mCurrentTemp(25.0f), mThreshold(80.0f) {
// 启动一个线程模拟温度变化
mSimThread = std::thread([this]() {
while (true) {
std::this_thread::sleep_for(1s);
// 模拟温度波动
mCurrentTemp += (rand() % 10 - 5) * 0.1f;
if (mCurrentTemp < 0) mCurrentTemp = 0;
if (mCurrentTemp > 100) mCurrentTemp = 100;
}
});
mSimThread.detach();
}
Return<float> getTemperature() override {
return mCurrentTemp;
}
Return<bool> setThreshold(float threshold) override {
if (threshold < 0 || threshold > 100) {
return false;
}
mThreshold = threshold;
return true;
}
private:
float mCurrentTemp;
float mThreshold;
std::thread mSimThread;
};
这段代码做了三件事:
- 继承
IThermalSensor并实现所有方法 - 用线程模拟温度变化(实际项目里会读硬件寄存器)
- 返回值和参数类型必须和 .hal 文件一致
Return<T> 类型,这是 HIDL 的 RPC 机制要求的。如果返回类型是 void,也要写 Return<void>。
3.4 注册为系统服务:让客户端能找到你
实现写好了,但客户端怎么找到它?答案是注册到 hwservicemanager。这是 Android 的 HIDL 服务管理器,类似 DNS 的角色。
注册代码通常写在 main() 函数里:
// main.cpp
#include <hidl/LegacySupport.h>
#include "ThermalSensor.h"
using namespace android::hardware;
int main() {
// 创建服务实例
auto service = new ThermalSensor();
// 注册到 hwservicemanager
// 第一个参数是服务名称,客户端用这个名字查找
status_t status = service->registerAsService("default");
if (status != OK) {
ALOGE("Failed to register ThermalSensor service: %d", status);
return 1;
}
ALOGI("ThermalSensor service registered successfully");
// 进入事件循环,保持服务运行
joinRpcThreadpool();
return 0;
}
这里有个坑,我踩过:registerAsService 的字符串参数是服务实例名。同一个接口可以有多个实例,比如 "default"、"fast"、"lowpower" 等。客户端通过这个名字选择用哪个实例。
device.mk 或 init.rc 里添加启动脚本。否则每次都要手动启动,调试时还好,量产就麻烦了。
3.5 完整流程:一张图看懂
说了这么多,咱们用一张 SVG 图把整个流程串起来。从创建 package 到注册服务,每一步都清晰可见:
3.6 避坑指南:我踩过的那些坑
开发 HIDL 服务时,有几个地方特别容易出问题。我列出来,你遇到时可以少走弯路:
| 问题 | 现象 | 解决方案 |
|---|---|---|
| 服务注册失败 | 客户端找不到服务 | 检查 SELinux 权限,添加 hal_thermal_sensor 域 |
| 接口版本不匹配 | 编译报类型错误 | 确保 .hal 和 C++ 代码版本号一致 |
| 服务进程崩溃 | 客户端调用返回 -32 | 检查 joinRpcThreadpool() 是否调用 |
| 内存泄漏 | 服务运行一段时间后变慢 | 使用 sp<> 智能指针管理对象 |
main() 里调用 joinRpcThreadpool(),结果服务启动后立刻退出。客户端那边一直报 "transport error",我查了半天才发现是主线程跑完就结束了。记住,HIDL 服务需要保持进程存活。
3.7 编译与测试:验证你的服务
代码写完了,怎么编译?在 Android 源码树里,你需要写一个 Android.bp 文件:
// Android.bp
cc_binary {
name: "vendor.nice.thermalsensor@1.0-service",
relative_install_path: "hw",
srcs: [
"ThermalSensor.cpp",
"main.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.nice.thermalsensor@1.0",
],
init_rc: ["vendor.nice.thermalsensor@1.0-service.rc"],
}
编译命令:
source build/envsetup.sh
lunch your_device-userdebug
mma vendor.nice.thermalsensor@1.0-service
测试时,我习惯用 lshal 命令查看服务是否注册成功:
adb shell lshal | grep thermal
# 输出类似:
# vendor.nice.thermalsensor@1.0::IThermalSensor/default
看到这个输出,说明你的服务已经成功注册到系统里了。客户端就可以通过 IThermalSensor::getService() 来调用它。