1. HAL与HIDL概述:Android硬件抽象层演进史、Treble架构解析、HIDL设计哲学

各位同学,咱们今天聊聊Android HAL的来龙去脉。说实话,我刚开始做Android底层开发那会儿,HAL还是个挺朴素的东西。后来Google搞了个Treble,整个架构天翻地覆。嗯,咱们一步步看。

1.1 从HAL到Treble:一场被迫的进化

先说说为什么要有HAL。Android系统跑在Linux内核上,但Google不想让硬件厂商把驱动代码全开源。怎么办?在Framework和Kernel之间加个抽象层,这就是HAL(Hardware Abstraction Layer)。

早期的HAL长这样:

// 传统HAL模块结构
struct hw_module_t {
    uint32_t tag;           // 固定为 HARDWARE_MODULE_TAG
    uint16_t module_api_version;
    uint16_t hal_api_version;
    const char *id;         // 模块ID,如 "audio"、"camera"
    const char *name;
    const char *author;
    struct hw_module_methods_t *methods;
    void *dso;              // 动态库句柄
    uint32_t reserved[32];
};

这种设计有个致命问题——Framework直接加载HAL的so库。你想想看,每次系统升级,厂商都得重新编译HAL,用户等OTA等到花儿都谢了。我在项目里遇到过,Android 7.0升8.0,光适配HAL就花了三个月。

核心痛点:Framework与HAL强耦合,系统升级必须同步更新HAL,厂商碎片化严重。

Google也意识到这不行。2017年,Android 8.0带着Treble架构来了。说白了,就是把Framework和HAL彻底拆开,中间用HIDL(HAL Interface Definition Language)定义稳定接口。

1.2 Treble架构解析:三层分离

Treble架构把系统分成三块:

层级 内容 更新方式
Framework层 System Server、Java API、系统服务 OTA直接推送
HAL层 HIDL Service、Vendor实现 厂商独立更新
Kernel层 Linux内核、设备驱动 厂商独立更新

中间那个HAL层,就是咱们这门课的核心。Framework通过HIDL接口调用HAL服务,不再直接加载so库。接口是稳定的,厂商只要保证接口不变,系统随便升。

我的经验:Treble刚出来时,很多厂商抱怨工作量翻倍。但你看现在,Android 12升13,主流机型两周就能推送。这就是接口稳定的威力。

咱们画个图,把Treble架构看清楚:

Treble架构分层图 Framework 层 System Server · Java API · 系统服务 HIDL 接口层(稳定ABI) .hal 文件 · Binderized · Passthrough HAL 层(Vendor实现) HIDL Service · 硬件抽象实现 Kernel 层 Linux内核 · 设备驱动 · 硬件交互 Framework与HAL通过HIDL接口解耦,各自独立更新

1.3 HIDL设计哲学:稳定压倒一切

HIDL不是随便拍脑袋想出来的。它的设计哲学,我总结成三条:

  1. 接口即契约——.hal文件定义了Framework和HAL之间的所有交互,双方都得遵守。
  2. 版本化演进——接口可以升级,但必须向后兼容。老版本不能断。
  3. 跨进程透明——调用HIDL服务就像调用本地函数,Binder帮你搞定IPC。

举个例子,一个简单的HIDL接口长这样:

// IMyLight.hal
package vendor.example.light@1.0;

interface IMyLight {
    /**
     * 设置灯光亮度
     * @param brightness 0-255
     * @return Status 操作结果
     */
    setBrightness(uint8_t brightness) generates (Status status);

    /**
     * 获取当前亮度
     * @return brightness 当前亮度值
     */
    getBrightness() generates (uint8_t brightness);
};

你看,接口定义里没有实现细节,只有方法签名和返回值。这就是契约精神——厂商怎么实现我不管,但接口必须对得上。

避坑指南:我曾经在项目里犯过一个错——在HIDL接口里传了复杂结构体,结果跨进程序列化性能惨不忍睹。记住,HIDL接口参数尽量用基本类型,复杂数据用回调或者分批传输。

1.4 两种HAL模式:Binderized vs Passthrough

HIDL支持两种运行模式,咱们对比一下:

特性 Binderized HAL Passthrough HAL
进程模型 独立进程,通过Binder通信 加载到调用进程,直接函数调用
安全性 高,进程隔离 低,共享地址空间
性能 有IPC开销 零拷贝,性能高
适用场景 大多数HAL(Camera、Audio等) 高性能要求(GPU、显示)
推荐程度 Google推荐 尽量迁移到Binderized

我个人习惯,新项目一律用Binderized。为什么?安全。你想想看,Passthrough模式下HAL跑在System Server进程里,一旦崩溃,整个系统都挂了。我在项目里遇到过,某个厂商的Passthrough HAL内存泄漏,导致系统三天两头重启。

关键结论:Treble + HIDL = Android系统升级不再依赖厂商。这是Android生态走向健康的关键一步。

1.5 本章小结

咱们这一章把HAL的演进史、Treble架构、HIDL设计哲学都过了一遍。核心就一句话:接口稳定,各自独立,契约驱动。后面的章节,咱们会深入HIDL语法、服务实现、测试验证,一步步把理论变成代码。

嗯,先消化这些。有问题随时问。