1. HAL概述与系统架构
做Android底层开发这么多年,我经常被问到同一个问题:“HAL到底是什么?它为什么这么重要?”
说白了,HAL就是Android系统里那个“翻译官”。它坐在操作系统内核和上层Framework之间,把硬件厂商的私有接口翻译成Google定义的统一接口。没有它,你的手机换个芯片厂商就得重写整个系统——想想都可怕。
1.1 Android系统层次结构
我们先从整体看一遍Android的层次。我习惯把它分成五层,从上往下数:
- 应用层(Application)——你每天用的微信、抖音、Chrome都在这里。
- 应用框架层(Java API Framework)——ActivityManager、WindowManager这些系统服务,给App提供API。
- 系统运行库层(Android Runtime & Native C/C++ Libraries)——ART虚拟机、SurfaceFlinger、MediaServer等核心组件。
- 硬件抽象层(HAL)——我们今天的主角,连接Framework和内核的桥梁。
- Linux内核层(Linux Kernel)——驱动、进程管理、内存管理,真正的硬件打交道的地方。
关键认知:HAL不是驱动,也不是Framework。它是接口规范。硬件厂商实现这个接口,Framework调用这个接口,双方都不需要知道对方内部怎么实现的。
我见过不少刚入行的同学把HAL和内核驱动混为一谈。嗯,这里要注意:驱动在内核态,HAL在用户态。驱动处理寄存器、中断、DMA;HAL处理open/close/ioctl这些上层调用。两者分工完全不同。
1.2 HAL在Android中的位置与作用
我们来看一张结构图,把HAL的位置画清楚:
这张图你看懂了吗?HAL夹在Native层和内核层之间,上下都是标准接口。我当年在做一个Camera HAL项目时,深刻体会到这个位置有多关键——上层Framework改了一版API,下层Sensor驱动换了芯片,但只要HAL接口不变,两边各改各的,互不干扰。
1.3 HAL的核心作用
我总结了一下,HAL在Android系统里干了三件大事:
| 作用 | 说明 | 我遇到的真实案例 |
|---|---|---|
| 解耦 | Framework不依赖具体硬件实现 | 某平板项目换Sensor芯片,只改HAL层,Framework零改动 |
| 标准化 | Google定义接口,厂商实现 | Camera HAL3接口统一后,第三方App兼容性大幅提升 |
| 性能隔离 | 硬件调优局限在HAL层 | Audio HAL里做低延迟调优,不影响上层播放器逻辑 |
我的经验:做HAL开发,最忌讳的就是把业务逻辑塞进HAL。HAL只做“翻译”和“适配”,不做“决策”。我曾经接手过一个项目,前任把图像算法直接写在Camera HAL里,结果换算法得重新烧固件——这就是典型的架构污染。
1.4 HAL设计哲学与演进历史
Android的HAL不是一天建成的。我经历了从HAL1到HAL3的整个演进过程,每次变化背后都有血泪教训。
1.4.1 HAL 1.x时代(Android 1.0 ~ 4.4)
最早的HAL设计其实很粗糙。每个硬件模块用一个.so动态库,Framework通过dlopen直接加载。接口定义也不严格,厂商想加什么函数就加什么函数。
// HAL 1.x 典型风格
struct hw_module_t {
uint32_t tag;
uint16_t module_api_version;
uint16_t hal_api_version;
const char *id;
const char *name;
int (*open)(const struct hw_module_t* module,
const char* id,
struct hw_device_t** device);
};
你想想看,这种设计有什么问题?没有版本控制,没有接口契约。厂商A的Camera HAL和厂商B的接口可能完全不同。Google想升级Framework?对不起,所有厂商都得跟着改——这根本没法规模化。
避坑指南:我曾经在一个老项目里看到HAL 1.x的代码,厂商在open()函数里偷偷传了一个私有结构体。结果Android升级到4.4后,Framework改了hw_device_t的定义,那个私有结构体直接崩了。教训就是:别在HAL接口里夹带私货。
1.4.2 HAL 2.0时代(Android 5.0 ~ 7.0)
Google痛定思痛,在Android 5.0推出了HAL 2.0。核心变化是引入了Stub HAL机制,每个HAL模块必须实现一组固定的接口函数。接口定义放在hardware/libhardware/include/hardware/目录下,厂商不能随意修改。
// HAL 2.0 典型风格
struct camera_module_t {
struct hw_module_t common;
int (*get_number_of_cameras)(void);
int (*get_camera_info)(int camera_id,
struct camera_info *info);
};
这个版本最大的进步是接口标准化。但问题还在:HAL跑在独立进程里,每次调用都要跨进程通信(IPC),性能开销不小。我记得做Audio HAL时,一次音频数据从App到扬声器要经过5次IPC——延迟高得离谱。
1.4.3 HAL 3.0时代(Android 8.0至今)
Android 8.0引入了Treble架构,这是HAL历史上最大的一次重构。核心思想:HAL和Framework彻底分离,通过稳定的HIDL(HAL Interface Definition Language)接口通信。
// HAL 3.0 HIDL 接口定义示例
package android.hardware.camera@3.4;
interface ICameraDevice {
getCameraCharacteristics(int32_t cameraId)
generates (CameraMetadata metadata);
configureStreams(StreamConfiguration config)
generates (Status status);
processCaptureRequest(CaptureRequest request)
generates (Status status);
};
Treble带来的好处很明显:
- Framework和HAL可以独立升级——厂商不用等Google发新版本
- 接口契约化——HIDL文件就是法律,谁都不能越界
- 性能优化——支持直通模式(Passthrough),减少IPC开销
演进对比一览:
| 维度 | HAL 1.x | HAL 2.0 | HAL 3.0 (Treble) |
|---|---|---|---|
| 接口定义 | 松散,厂商自定义 | 标准化头文件 | HIDL严格契约 |
| 通信方式 | dlopen直接调用 | IPC (hwservicemanager) | Binderized / Passthrough |
| 版本兼容 | 差 | 中等 | 强(前后向兼容) |
| 性能 | 高(直接调用) | 低(IPC开销大) | 可配置(直通模式优化) |
| 升级灵活性 | Framework和HAL绑定 | 部分解耦 | 完全独立升级 |
1.5 设计哲学:为什么HAL要这么设计?
聊完历史,我想说说HAL背后的设计哲学。说白了就三个词:抽象、稳定、可替换。
- 抽象——把硬件细节藏起来。Framework不需要知道你的Camera是OV sensor还是Sony sensor,它只调用
processCaptureRequest()。 - 稳定——接口一旦发布,就不能随便改。HIDL的版本号机制保证了这一点。我见过一个厂商在HAL里加了一个“临时”函数,结果三年后还在用——这就是不稳定的代价。
- 可替换——换硬件不换系统。你想想看,同一款手机用不同供应商的屏幕、触控、音频芯片,系统层代码完全不用动,这就是HAL的价值。
我的建议:如果你现在开始做HAL开发,直接学HIDL和Treble架构。HAL 1.x和2.0已经是历史了,但理解它们的演进能帮你更好地理解为什么Treble要这么设计。说白了,所有架构设计都是被现实逼出来的。