第四章:车机硬件抽象层(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系统跑在车机硬件上。到时候,我会拿一个真实项目来拆解,敬请期待。