5、Vehicle HAL 开发:VHAL 架构、属性定义与实现、属性订阅与回调、VHAL 模拟器实现
Vehicle HAL,简称 VHAL,是 Android Automotive OS 里最核心的硬件抽象层。说白了,它就是连接上层车载服务和底层车辆硬件的桥梁。你想想看,车机要读取车速、控制空调、检测车门状态,这些信号最终都得通过 VHAL 来传递。
我个人习惯把 VHAL 比作一个「翻译官」。上层服务说 Android 的语言,底层硬件说 CAN 总线或 LIN 总线的语言,VHAL 负责在中间做双向翻译。嗯,这个比喻虽然简单,但很贴切。
5.1 VHAL 架构概览
VHAL 的架构其实不复杂,核心就三层:
- VHAL 接口层:由 Android 框架定义,主要是
IVehicle.hal这个接口文件。它规定了所有 VHAL 实现必须支持的方法,比如get()、set()、subscribe()等。 - VHAL 实现层:由 OEM 或 Tier-1 供应商完成。这一层负责对接实际的硬件,比如通过 CAN 总线读取车速传感器。
- VHAL 客户端层:也就是
CarService和各个车载应用。它们通过VehicleHalClient与 VHAL 通信。
我在项目中遇到过一个问题:某家供应商直接把 CAN 报文解析逻辑写在了 VHAL 实现里,导致每次改一个传感器都得重新编译整个 HAL。这其实是不对的。VHAL 应该只做协议转换,具体的业务逻辑应该放在上层。
5.2 属性定义与实现
VHAL 里的每个数据点都叫一个「属性」(Property)。比如车速是 VEHICLE_SPEED,车门状态是 DOOR_POS。每个属性都有固定的 ID、类型、权限和变化模式。
属性定义通常在 types.hal 里完成。举个例子:
// 定义车速属性
enum VehicleProperty : int32_t {
VEHICLE_SPEED = 0x1110,
DOOR_POS = 0x1120,
HVAC_TEMPERATURE = 0x1130,
};
// 属性配置
struct VehiclePropConfig {
int32_t prop; // 属性 ID
int32_t access; // 权限: 读/写/读写
int32_t changeMode; // 变化模式: 持续/脉冲/静态
int32_t minSampleRate; // 最小采样率
int32_t maxSampleRate; // 最大采样率
};
这里要注意 changeMode 这个字段。它决定了属性值变化时如何通知上层。我个人习惯这样区分:
| 变化模式 | 说明 | 典型场景 |
|---|---|---|
CONTINUOUS |
持续变化,需要定期上报 | 车速、发动机转速 |
ON_CHANGE |
值变化时才上报 | 车门开关、灯光状态 |
STATIC |
只读一次,不会变化 | VIN 码、固件版本 |
5.3 属性订阅与回调
上层服务怎么知道某个属性值变了?靠的就是订阅机制。客户端调用 subscribe() 方法,VHAL 在值变化时通过回调通知客户端。
订阅流程大致是这样的:
- 客户端调用
IVehicle::subscribe(propId, sampleRate) - VHAL 实现层注册对应的硬件监听器
- 硬件值变化时,VHAL 调用
onPropertyEvent()回调 - 回调数据通过
VehiclePropValue结构体传递
代码示例:
// 客户端订阅车速
Status subscribe(VehicleProperty::VEHICLE_SPEED,
/* sampleRate */ 10.0f);
// VHAL 实现中的回调
Return<void> onPropertyEvent(
const hidl_vec<VehiclePropValue>& values) {
for (const auto& val : values) {
if (val.prop == VEHICLE_SPEED) {
float speed = val.value.floatValues[0];
// 通知上层
notifyClients(val);
}
}
return Void();
}
5.4 VHAL 模拟器实现
开发阶段没有真车怎么办?VHAL 模拟器就是你的救星。它模拟了真实车辆的行为,让你可以在桌面上调试整个车载系统。
实现一个 VHAL 模拟器,核心就三步:
- 实现 IVehicle 接口:所有方法都要有,但内部逻辑是模拟的。
- 模拟属性值变化:用一个定时器线程,定期更新车速、转速等动态属性。
- 提供控制接口:通过 ADB 命令或 UI 界面,手动改变模拟值。
下面是一个简化版的模拟器核心代码:
class VehicleSimulator : public IVehicle {
private:
std::thread mSimThread;
std::atomic<float> mSpeed{0.0f};
void simulationLoop() {
while (running) {
// 模拟车速在 0-120 km/h 之间变化
mSpeed = 60.0f + 30.0f * sin(time);
VehiclePropValue value;
value.prop = VEHICLE_SPEED;
value.value.floatValues[0] = mSpeed;
onPropertyEvent({value});
std::this_thread::sleep_for(100ms);
}
}
public:
Return<StatusCode> set(const VehiclePropValue& value) override {
// 模拟设置操作
if (value.prop == HVAC_TEMPERATURE) {
// 模拟空调温度调节
return StatusCode::OK;
}
return StatusCode::INVALID_ARG;
}
};
我记得有一次,客户反馈说他们的空调控制界面偶尔会卡死。我排查了半天,发现是因为模拟器里空调温度值一直返回 25.0,从来没变过。上层代码以为传感器坏了,一直在重试。后来我在模拟器里加了随机波动,问题就再也没出现过。
嗯,VHAL 开发就是这样。看似简单,但细节里全是坑。架构要清晰,属性定义要严谨,回调要高效,模拟器要逼真。把这几点做好了,你的 VHAL 就能稳定运行在千万辆车上。