第四章:车机硬件抽象层(HAL)
各位同学,欢迎来到第四章。这一章我们聊聊HAL——硬件抽象层。说实话,HAL是Android系统里最“接地气”的一层,也是我们做车机移植时绕不开的硬骨头。
我刚开始接触车机项目时,总觉得HAL不就是个接口封装吗?后来踩了坑才明白,HAL的设计好坏,直接决定了你的系统能不能稳定跑起来。尤其是车机这种对实时性、可靠性要求极高的场景,HAL更是重中之重。
4.1 HAL架构原理:到底在抽象什么?
先问大家一个问题:为什么Android要搞个HAL出来?
说白了,就是为了解耦。Linux内核驱动千奇百怪,每家芯片厂商的寄存器配置都不一样。如果上层框架直接调用驱动,那Google得累死——每换一颗芯片就得改一遍Framework代码。
HAL的作用,就是在Framework和内核驱动之间,加一层“翻译官”。Framework只认HAL接口,HAL负责把上层请求转成具体的硬件操作。
我个人的理解,HAL其实就干三件事:
- 封装硬件差异:不管你是高通、联发科还是瑞芯微,HAL接口统一。
- 提供稳定ABI:上层不用关心底层怎么实现,接口不变就行。
- 支持多厂商实现:每家芯片厂自己写HAL库,互不干扰。
嗯,这里要注意一点:HAL不是驱动,它运行在用户空间。驱动在内核空间,HAL通过设备节点(比如/dev/xxx)或者系统调用跟驱动打交道。
核心要点:HAL的本质是“接口约定”。只要接口不变,底层随便换硬件,上层代码一行都不用改。
4.2 HIDL与AIDL的区别:两个“IDL”的恩怨
做Android移植,你一定会遇到两个缩写:HIDL和AIDL。很多新手搞不清它们的关系,我当年也迷糊过一阵子。
简单说:HIDL是Treble时代的产物,AIDL是更早的IPC机制。但到了Android 11之后,Google开始用AIDL统一HAL接口,这事儿就变得有点微妙了。
我列个表格,大家一看就明白:
| 对比项 | HIDL | AIDL |
|---|---|---|
| 全称 | HAL Interface Definition Language | Android Interface Definition Language |
| 诞生时间 | Android 8.0(Treble) | Android 1.0就有 |
| 主要用途 | HAL与Framework通信 | Service与Service通信 |
| 传输方式 | Binder(但有自己的HwBinder) | Binder |
| 稳定性 | 强(接口版本化严格) | 中等(需要自己管理版本) |
| 当前趋势 | 逐渐被AIDL替代 | Google推荐统一使用 |
为什么会这样?其实Google自己也觉得维护两套IDL太累了。从Android 11开始,他们推动“AIDL化HAL”,也就是用AIDL来定义HAL接口。你想想看,以后所有IPC都用AIDL,多清爽。
不过,我在项目中遇到过一个问题:老平台(比如Android 9/10)的HAL还是HIDL写的,新平台(Android 12+)开始推AIDL。如果你做的是车机移植,很可能要同时维护两套代码。嗯,这时候就得靠兼容层了。
我的建议:新项目直接用AIDL写HAL。如果必须兼容老平台,可以用HIDL封装一层,上层再用AIDL暴露出去。别问我为什么,问就是少踩坑。
4.3 车机专用HAL:三大金刚
车机跟手机最大的区别是什么?手机主要管通话、上网、拍照。车机呢?管车控、管音频、管摄像头。这三个功能对应的HAL,就是Vehicle HAL、Audio HAL和Camera HAL。
4.3.1 Vehicle HAL:车机的“神经中枢”
Vehicle HAL是车机特有的,手机上没有。它负责跟车辆CAN总线通信,获取车速、车门状态、空调温度、方向盘按键等信号。
我记得第一次调Vehicle HAL时,最头疼的是信号映射。CAN总线上发过来的原始数据是十六进制,你得解析成Android能理解的属性值。比如车速信号,CAN报文里可能是0x1234,你得知道它代表多少km/h。
Vehicle HAL的核心接口是IVehicle,主要方法包括:
get():读取车辆属性set():设置车辆属性(比如关窗、调空调)subscribe():订阅属性变化(比如车速变化时通知上层)
代码示例(简化版):
// Vehicle HAL 接口定义(AIDL版本)
interface IVehicle {
// 读取车辆速度
int getVehicleSpeed();
// 设置空调温度
void setAcTemperature(int temp);
// 订阅车门状态变化
void subscribeDoorStatus(IVehicleCallback callback);
}
避坑指南:我曾经在某个项目里,Vehicle HAL的subscribe回调没处理好,导致上层UI卡死。原因是回调线程跟UI线程冲突了。记住:HAL回调一定要切到主线程再更新UI。
4.3.2 Audio HAL:声音的“调度员”
车机的音频比手机复杂得多。手机就一个扬声器,车机呢?左前门、右前门、左后门、右后门、中置、低音炮……还有导航音、电话音、媒体音、提示音,这些声音要混在一起,还要考虑优先级。
Audio HAL的核心是IAudioFlinger,但车机场景下,我们更关心的是音频策略。比如:
- 导航播报时,媒体音量要自动降低(俗称“闪避”)
- 电话接通时,所有其他声音暂停
- 倒车时,雷达提示音优先级最高
这些逻辑,一部分在AudioPolicyService里,一部分需要HAL层配合。我个人的经验是:Audio HAL的难点不在接口实现,而在延迟控制。车机对音频延迟要求很高,尤其是倒车雷达提示音,延迟超过50ms就会感觉“卡顿”。
代码示例(音频焦点管理):
// Audio HAL 中处理音频焦点
void AudioHAL::requestAudioFocus(audio_attributes_t attr) {
if (attr.usage == AUDIO_USAGE_NAVIGATION) {
// 导航音请求焦点,降低媒体音量
setStreamVolume(AUDIO_STREAM_MUSIC, 0.3f);
} else if (attr.usage == AUDIO_USAGE_VOICE_COMMUNICATION) {
// 电话请求焦点,暂停所有其他音频
pauseAllStreams();
}
}
4.3.3 Camera HAL:车机的“眼睛”
车机的Camera HAL跟手机最大的不同是:多路同时输入。手机一般就前后两个摄像头,车机呢?前视、后视、环视、车内监控……可能同时有6路甚至更多摄像头在跑。
Camera HAL的核心是ICameraDevice,但车机场景下,我们更关注:
- 多路同步:环视影像需要4路摄像头同时采集,时间戳要对齐
- 低延迟:倒车影像延迟必须小于100ms,否则司机看着画面倒车会撞
- 分辨率适配:车机屏幕分辨率五花八门,1920x720、1280x480都有
我在做环视项目时,遇到一个坑:4路摄像头的时间戳不同步,导致拼接画面撕裂。后来发现是驱动层没做硬件同步,只能靠HAL层做软件对齐。嗯,虽然能解决,但性能开销不小。
关键点:车机Camera HAL的难点不在“能不能拍到”,而在“能不能同时拍到且不卡顿”。多路流管理是核心。
4.4 小结:HAL是车机移植的“地基”
这一章我们聊了HAL的架构原理,理清了HIDL和AIDL的区别,也深入了Vehicle、Audio、Camera三个车机专用HAL。说实话,HAL层的工作很琐碎,但它是整个系统稳定性的基石。
我个人习惯是:做移植前,先把HAL接口文档吃透,再动手写代码。别急着调上层UI,先把HAL层跑通,后面就顺了。
下一章,我们会进入更具体的移植实战——如何把Android系统跑在车机硬件上。到时候,我会拿一个真实项目来拆解,敬请期待。