3. AIDL与HAL:AIDL在HAL层的新角色、接口定义与迁移策略
聊到Android HAL层,大家第一反应肯定是HIDL。但Google从Android 11开始,悄悄把AIDL推到了HAL层的前台。说实话,我刚开始也觉得这不过是换个接口语言而已,直到我在一个车载项目中真正动手迁移,才发现事情没那么简单。
这一章,我们就来掰扯清楚:AIDL凭什么能取代HIDL?它到底怎么用?以及,从HIDL迁到AIDL,有哪些坑等着你。
3.1 AIDL在HAL层的新角色
先问一个问题:为什么Google要把AIDL塞进HAL层?
答案其实很直接——统一。以前Android的IPC机制是分裂的:App层用AIDL,Framework层用HIDL,内核层用ioctl。每次跨层通信,都得做一次协议转换,维护成本极高。
我个人习惯把AIDL在HAL层的角色总结为三点:
- 统一IPC语言:从App到HAL,全程用AIDL,不再需要HIDL做中间翻译。
- 更轻量的运行时:AIDL后端直接基于binder,没有HIDL的hwservicemanager那一套复杂调度。
- 更好的Java/Kotlin支持:如果你做的是上层HAL封装,AIDL的Java代码生成比HIDL友好得多。
核心变化:AIDL在HAL层不再是"App专用",它变成了Android整个系统的IPC标准。说白了,Google想把所有跨进程通信都收敛到同一套机制上。
3.2 AIDL接口定义与实现
我们直接上手。假设你要为一个温度传感器写HAL服务,用AIDL怎么搞?
3.2.1 定义AIDL接口
在 hardware/interfaces/temperature/aidl/ 下创建 ITemperature.aidl:
// ITemperature.aidl
package hardware.interfaces.temperature;
@VintfStability
interface ITemperature {
/**
* 获取当前温度,单位摄氏度
*/
float getTemperature();
/**
* 设置温度阈值,超过阈值触发回调
*/
void setThreshold(float low, float high);
/**
* 注册温度变化回调
*/
void registerCallback(ITemperatureCallback callback);
}
// ITemperatureCallback.aidl
package hardware.interfaces.temperature;
@VintfStability
interface ITemperatureCallback {
void onTemperatureChanged(float temperature);
void onThresholdExceeded(float current, float low, float high);
}
注意那个 @VintfStability 注解。我在项目中遇到过有人漏掉这个,结果服务死活注册不上。这个注解告诉编译系统:这个AIDL接口是HAL层用的,需要走VINTF兼容性检查。
3.2.2 实现服务端
C++实现,放在 hardware/interfaces/temperature/aidl/default/:
// Temperature.cpp
#include "Temperature.h"
ndk::ScopedAStatus Temperature::getTemperature(float* _aidl_return) {
// 读取硬件寄存器
*_aidl_return = readSensorRegister();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Temperature::setThreshold(float low, float high) {
mLowThreshold = low;
mHighThreshold = high;
// 配置硬件中断
configureInterrupt(low, high);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Temperature::registerCallback(
const std::shared_ptr<ITemperatureCallback>& callback) {
mCallbacks.push_back(callback);
return ndk::ScopedAStatus::ok();
}
3.2.3 注册服务
在 android.hardware.temperature-service.rc 中:
service temperature-hal /vendor/bin/hw/hardware.hardware.temperature-service
class hal
user system
group system
然后在 main.cpp 中注册:
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(4);
auto service = ndk::SharedRefBase::make<Temperature>();
auto status = AServiceManager_addService(
service->asBinder().get(),
"hardware.hardware.temperature-service"
);
ABinderProcess_joinThreadPool();
return 0;
}
小技巧:AIDL HAL服务的注册名建议遵循 hardware.<module>.<instance>-service 格式,这样VINTF能自动识别。我曾经见过有人随便起名,结果CTS测试全挂。
3.3 HIDL到AIDL的迁移策略
这部分是实战中的硬骨头。我去年帮一个手机厂商做Camera HAL迁移,踩了不少坑。下面是我总结的迁移路线图。
3.3.1 迁移前的评估
| 评估项 | HIDL | AIDL | 迁移影响 |
|---|---|---|---|
| 接口定义 | .hal 文件 | .aidl 文件 | 需要重写所有接口 |
| 数据类型 | hidl_vec, hidl_string | std::vector, std::string | 内存管理方式变化 |
| 回调机制 | hidl_callback | AIDL callback接口 | 线程模型需要调整 |
| 服务注册 | hwservicemanager | AServiceManager | 启动脚本需要修改 |
| 死亡通知 | hidl_death_recipient | AIBinder_DeathRecipient | API完全不同 |
3.3.2 分步迁移策略
我建议按这个顺序来,别想着一步到位:
- 先迁移纯数据接口:没有回调、没有复杂生命周期的接口,风险最低。
- 再迁移带回调的接口:注意线程模型的变化。HIDL的回调是同步的,AIDL默认是异步的。
- 最后迁移有状态的服务:比如Camera、Audio这种需要保持连接状态的。
避坑指南:我曾经在迁移一个Sensor HAL时,直接把HIDL的hidl_vec<SensorInfo> 换成了 std::vector<SensorInfo>,结果发现AIDL的序列化方式不同,导致跨进程传输时数据错位。后来我加了一组单元测试,专门验证序列化/反序列化的一致性。
3.3.3 关键代码对比
拿服务注册来说,HIDL和AIDL的差异很明显:
// HIDL 方式
ITemperature::registerAsService("default");
// AIDL 方式
AServiceManager_addService(
service->asBinder().get(),
"hardware.hardware.temperature-service"
);
再看回调注册:
// HIDL 方式 - 同步回调
Return<void> registerCallback(sp<ITemperatureCallback> callback) {
callback->onTemperatureChanged(25.0f);
return Void();
}
// AIDL 方式 - 异步回调
ndk::ScopedAStatus registerCallback(
const std::shared_ptr<ITemperatureCallback>& callback) {
// 需要自己管理线程
std::thread([callback]() {
callback->onTemperatureChanged(25.0f);
}).detach();
return ndk::ScopedAStatus::ok();
}
3.4 知识结构总览
下面这张图,是我做迁移时自己画的,帮你理清AIDL在HAL层的全貌:
3.5 实战中的几个关键点
最后,分享几个我在项目中反复踩过的坑:
- 版本兼容性:AIDL HAL服务需要声明
@VintfStability,否则Android 12以上设备会拒绝加载。我见过有人升级系统后服务起不来,查了两天才发现是少了这个注解。 - 性能对比:AIDL的binder传输比HIDL快约15-20%,因为少了hwservicemanager这一层路由。但如果你用到了大量hidl_memory共享内存,迁移时要注意AIDL的
NativeHandle用法不同。 - 测试策略:迁移后一定要跑VTS测试。Google提供了
VtsHalAidlTargetTest模板,直接套用就行。我曾经偷懒没跑,结果CTS阶段发现接口签名不匹配,返工了一周。
我的建议:如果你现在开始新项目,直接用AIDL。如果是老项目迁移,先评估接口复杂度,从最简单的模块开始试点。别一上来就动Camera或Audio这种大模块,容易翻车。