4. Treble 架构与 HIDL:Android O 引入的 Treble 项目
Android O(API 级别 26)引入的 Treble 项目是 Android 系统架构自诞生以来最重大的一次重构。其核心目标是将 Vendor 实现(硬件厂商的 HAL 和内核)与 Android 系统框架(Framework)解耦,使得厂商无需修改上层代码即可升级 Android 系统版本。这一架构变革的核心技术载体便是 HIDL(HAL Interface Definition Language,HAL 接口定义语言)。
4.1 Treble 架构概览
在 Treble 架构下,Android 系统被清晰地划分为两个主要分区:System 分区(包含 Android 框架)和 Vendor 分区(包含厂商的 HAL 实现、内核驱动等)。两者之间通过 HIDL 定义的稳定接口进行通信。
4.2 HIDL 接口定义语言基础
HIDL 是 Treble 架构的核心接口定义语言,用于描述 HAL 与框架之间的契约。它类似于 AIDL,但专为硬件抽象层设计,支持更严格的数据类型和接口版本控制。
4.2.1 HIDL 文件结构
一个典型的 HIDL 接口文件以 .hal 为扩展名,遵循 package@version 的命名规范。以下是一个简单的 HIDL 接口示例:
// IHello.hal
package vendor.example.hello@1.0;
interface IHello {
/**
* 打招呼接口
* @param name 用户名
* @return 问候语字符串
*/
hello(string name) generates (string result);
/**
* 获取硬件版本号
* @return 版本号整数
*/
getVersion() generates (int32_t version);
};
- package:定义接口的命名空间,格式为
vendor.厂商名.模块名@版本号。 - interface:声明一个 HAL 接口,内部包含方法定义。
- generates:HIDL 使用
generates关键字指定返回值,而非 Java 的return。 - 数据类型:支持
int8_t、uint32_t、string、vec<T>、struct等。
4.2.2 HIDL 数据类型对比
| HIDL 类型 | 对应 C++ 类型 | 说明 |
|---|---|---|
int8_t |
int8_t |
有符号 8 位整数 |
uint32_t |
uint32_t |
无符号 32 位整数 |
int64_t |
int64_t |
有符号 64 位整数 |
float |
float |
单精度浮点数 |
double |
double |
双精度浮点数 |
string |
std::string |
UTF-8 字符串 |
vec<T> |
std::vector<T> |
动态数组 |
struct |
struct |
自定义结构体 |
enum |
enum class |
枚举类型 |
handle |
native_handle_t* |
原生句柄,用于传递文件描述符等 |
4.3 Binderized HAL 与 Passthrough HAL
Treble 架构定义了两种 HAL 实现模式:Binderized HAL 和 Passthrough HAL。两者在进程模型、通信方式和适用场景上存在显著差异。
4.3.1 Binderized HAL(绑定式 HAL)
Binderized HAL 是 Treble 架构推荐的首选模式。HAL 实现作为一个独立的服务进程运行在 Vendor 分区中,通过 HwBinder(硬件 Binder)与框架进行跨进程通信(IPC)。
- 进程模型:HAL Server 运行在独立进程中,与 Framework 进程隔离。
- 通信方式:使用 HwBinder 驱动(
/dev/hwbinder)进行 IPC。 - 生命周期:由
hwservicemanager管理服务的注册与发现。 - 优势:进程隔离,安全性高;支持热插拔;便于调试和升级。
- 劣势:IPC 开销略高,不适合高频、低延迟的调用。
4.3.2 Passthrough HAL(直通式 HAL)
Passthrough HAL 是传统 HAL 的演进形态,用于兼容旧有的 hw_module_t 架构。HAL 实现以共享库(.so)的形式存在,被框架进程直接 dlopen 加载,运行在同一进程空间内。
- 进程模型:HAL 库被加载到调用方进程(如
system_server)中。 - 通信方式:直接函数调用,无 IPC 开销。
- 生命周期:随调用进程的启动而加载,随进程退出而卸载。
- 优势:零 IPC 开销,性能最优;适合对延迟敏感的传感器、音频等场景。
- 劣势:无进程隔离,HAL 崩溃会导致框架进程崩溃;安全性较低。
4.3.3 两种模式对比
| 对比维度 | Binderized HAL | Passthrough HAL |
|---|---|---|
| 进程模型 | 独立进程(Vendor 分区) | 加载到调用方进程 |
| 通信方式 | HwBinder IPC | 直接函数调用 |
| 性能开销 | 中等(IPC 序列化/反序列化) | 极低(无 IPC) |
| 安全性 | 高(进程隔离) | 低(共享进程空间) |
| 稳定性 | 高(HAL 崩溃不影响框架) | 低(HAL 崩溃导致框架崩溃) |
| 适用场景 | 复杂 HAL(Camera、GPS、Radio) | 简单、高频 HAL(Sensor、Input) |
| 推荐程度 | 首选(Google 推荐) | 兼容旧 HAL 时使用 |
4.3.4 两种模式的架构对比图
4.4 HIDL 服务注册与发现机制
在 Binderized HAL 模式下,服务的注册与发现由 hwservicemanager 统一管理。其工作流程如下:
- 服务注册:HAL Server 启动后,调用
registerAsService()将自身注册到 hwservicemanager。 - 服务发现:Framework 端的 Client 通过
getService()向 hwservicemanager 请求指定名称的服务。 - 代理获取:hwservicemanager 返回服务的 Binder 代理对象,Client 通过代理调用远程方法。
- 服务保活:hwservicemanager 监控服务进程状态,服务异常退出时可触发重启策略。
@1.0、@1.1)。当接口升级时,必须保持向后兼容:可以添加新方法,但不能删除或修改已有方法的签名。这是 Treble 保证 Vendor 分区与 System 分区独立升级的核心约束。
4.5 从传统 HAL 到 Treble HAL 的迁移路径
对于已有的传统 HAL(基于 hw_module_t),迁移到 Treble 架构通常遵循以下步骤:
| 步骤 | 操作内容 | 关键产出 |
|---|---|---|
| 1 | 分析现有 HAL 接口,提取所有对外暴露的函数和数据结构 | 接口清单文档 |
| 2 | 使用 HIDL 重新定义接口,编写 .hal 文件 |
HIDL 接口定义 |
| 3 | 运行 hidl-gen 工具生成 C++ 客户端和服务端代码 |
自动生成的骨架代码 |
| 4 | 实现 HAL Server,将原有逻辑封装到生成的接口实现中 | HAL Server 可执行文件 |
| 5 | 编写 android.hardware.xxx@1.0-service.rc 启动脚本 |
init.rc 服务配置 |
| 6 | 在 Framework 端实现 Client,替换原有的 hw_get_module() 调用 |
HAL Client 代码 |
| 7 | 集成测试,验证接口兼容性和性能 | 测试报告 |
- 迁移过程中,可以先用 Passthrough HAL 模式快速验证 HIDL 接口的正确性,再切换到 Binderized HAL 模式。
- 使用
lshal命令可以查看当前系统中所有已注册的 HIDL 服务及其状态。 - HIDL 接口的版本号一旦发布,不可修改;升级时需创建新的
.hal文件并递增版本号。