3. HAL演进与类型

Android HAL(Hardware Abstraction Layer)是连接 Framework 与内核驱动的关键中间层。它的形态并非一成不变,而是随着 Android 版本迭代经历了三次重要演进:Legacy HAL、HIDL HAL 与 AIDL HAL。理解这条演进主线,以及 Passthrough 与 Binderized 两种运行模式,是开发与调试 HAL Service 的基础。

一、核心原理:三代 HAL 的接口与进程模型

Legacy HAL 采用 C 语言头文件加函数指针结构体的方式,Framework 通过 hw_get_module() 加载 .so 动态库,直接以函数调用方式访问硬件。它没有版本化接口,也没有进程隔离,HAL 实现通常与调用方处于同一进程。

HIDL(HAL Interface Definition Language)在 Android 8.0 引入,使用 .hal 文件定义接口,通过 hidl-gen 生成 C++ 或 Java 代理与桩代码。HIDL 支持版本化(如 android.hardware.light@2.0),并允许 HAL 运行在独立进程中,通过 Binder 通信。

AIDL HAL 自 Android 11 起逐步推广,使用 .aidl 文件定义接口,复用 AIDL 工具链与 Binder 机制。相比 HIDL,AIDL HAL 语法更简洁、支持稳定 AIDL、可直接使用 Java 与 C++ 互操作,并成为 Android 14 之后新 HAL 的默认选择。

Android HAL 三代演进与运行模式 Legacy HAL(≤ Android 7) 接口:C 头文件 + hw_module_t 加载:hw_get_module() → .so 进程:与调用方同进程 通信:直接函数调用 版本:无版本化接口 HIDL HAL(Android 8–13) 接口:.hal + hidl-gen 加载:hwservicemanager 注册 进程:可独立进程(Binderized) 通信:HwBinder(hwbinder 域) 版本:@1.0 / @2.0 版本化 AIDL HAL(Android 11+) 接口:.aidl + aidl 工具链 加载:servicemanager 注册 进程:独立进程(Binderized) 通信:Binder(/dev/binder) 版本:稳定 AIDL + VINTF 两种运行模式 Passthrough(直通模式) • HAL 实现以 .so 形式加载到调用进程 • 无独立 HAL 进程,无 Binder 开销 • 常用于 HIDL 兼容旧设备、启动早期 • 调试简单,但隔离性差 Binderized(绑定模式) • HAL 运行在独立进程,通过 Binder 通信 • 进程隔离,崩溃不影响 Framework • HIDL 用 hwbinder,AIDL 用 binder • 现代 HAL 的标准形态

二、方法流程:Passthrough 与 Binderized 的切换

HIDL 时代,同一个 HAL 可以以两种模式运行,由 manifest.xml 与 vintf 配置决定。Passthrough 模式下,Framework 通过 dlopen 加载 HAL 的 .so,接口调用退化为本地函数调用;Binderized 模式下,HAL 作为独立服务注册到 hwservicemanager,Framework 通过 HwBinder 代理访问。

AIDL HAL 则默认采用 Binderized 模式,服务注册到 servicemanager,客户端通过 getService() 获取代理。若需兼容旧行为,可通过 VINTF 声明 passthrough 模式,但新设备应优先使用 Binderized。

# 查看当前设备 HAL 注册情况
adb shell dumpsys -l | grep -i hal
adb shell service list | grep android.hardware

# HIDL 服务查询
adb shell lshal
adb shell lshal --types=b

# AIDL HAL 服务查询
adb shell dumpsys android.hardware.light.ILights/default

三、案例场景:灯光 HAL 的三代实现对比

以灯光 HAL 为例,Legacy 版本直接提供 light_device_t 结构体,Framework 通过 hw_get_module(LIGHT_HARDWARE_MODULE_ID) 获取并调用 set_light()。HIDL 版本定义 android.hardware.light@2.0::ILight,实现 setLightState(),通过 lshal 可看到服务名。AIDL 版本定义 android.hardware.light.ILights,使用稳定 AIDL,服务名形如 android.hardware.light.ILights/default。

调试要点:Legacy HAL 出错时通常表现为 hw_get_module 返回负值或 dlopen 失败;HIDL 出错时 lshal 中服务缺失或 getService 返回空;AIDL 出错时 servicemanager 中查不到实例,或 getService 抛 ServiceSpecificException。

四、各版本适用场景与选型清单

类型 接口定义 通信机制 适用 Android 版本 典型场景
Legacy HAL C 头文件 + hw_module_t 直接函数调用 Android 7 及以下 旧设备维护、简单传感器
HIDL Passthrough .hal + hidl-gen dlopen + 本地调用 Android 8–10 过渡期 启动早期 HAL、兼容旧实现
HIDL Binderized .hal + hidl-gen HwBinder Android 8–13 Camera、Audio、Graphics 等
AIDL Binderized .aidl + aidl 工具链 Binder Android 11+,14 起默认 Light、Vibrator、Power、Thermal

选型建议:新项目优先使用 AIDL HAL;若需兼容 Android 10 以下,可保留 HIDL;Legacy HAL 仅用于维护存量设备。Passthrough 模式适合启动极早期或对性能极敏感的模块,但会牺牲进程隔离带来的稳定性。

常见误区:误以为 HIDL 与 AIDL 可以随意混用同一服务名;误以为 Passthrough 一定比 Binderized 快——在跨进程频繁调用场景下,Binderized 的隔离优势往往比微小开销更重要;忽略 VINTF 清单导致服务注册成功但 Framework 无法发现。

五、复盘:演进背后的设计逻辑

从 Legacy 到 HIDL 再到 AIDL,核心驱动力是接口版本化、进程隔离与工具链统一。Legacy 解决了“能访问硬件”,HIDL 解决了“可版本化与可隔离”,AIDL 则解决了“与 Android 生态统一、降低维护成本”。理解这条主线,就能在调试时快速判断:当前 HAL 属于哪一代、运行在哪种模式、应该用哪套工具链去排查。

实际开发中,建议先通过 lshal 或 service list 确认 HAL 是否注册、运行模式是 Passthrough 还是 Binderized,再结合 logcat 与 dmesg 定位是接口定义、服务注册还是底层驱动的问题。掌握三代 HAL 的差异与适用场景,是后续编写和调试 HAL Service 的前提。