4. Treble 架构与 HIDL:Android O 引入的 Treble 项目

Android O(API 级别 26)引入的 Treble 项目是 Android 系统架构自诞生以来最重大的一次重构。其核心目标是将 Vendor 实现(硬件厂商的 HAL 和内核)与 Android 系统框架(Framework)解耦,使得厂商无需修改上层代码即可升级 Android 系统版本。这一架构变革的核心技术载体便是 HIDL(HAL Interface Definition Language,HAL 接口定义语言)。

核心价值:Treble 通过标准化 HAL 接口,将 Android 框架与厂商实现之间的耦合从“源代码级别”降低为“二进制接口(ABI)级别”,大幅缩短了系统升级的适配周期。

4.1 Treble 架构概览

在 Treble 架构下,Android 系统被清晰地划分为两个主要分区:System 分区(包含 Android 框架)和 Vendor 分区(包含厂商的 HAL 实现、内核驱动等)。两者之间通过 HIDL 定义的稳定接口进行通信。

Treble 架构分层与通信路径 System 分区(Android 框架) Java API Framework System Services HAL Client(Binderized) HwBinder 通信层 Vendor 分区(厂商实现) HAL Server(Binderized) HAL 实现库(.so) 内核驱动接口 硬件设备 HIDL 接口 (HwBinder) 关键解耦点 System 分区可独立升级(OTA),无需重新编译 Vendor 分区中的 HAL 实现。 Vendor 分区只需保证 HIDL 接口的 ABI 兼容性,即可适配不同版本的 Android 框架。 传统架构(Pre-Treble) HAL 以 .so 直接链接到 system_server 进程 Treble 架构 HAL 运行在独立进程(Vendor) 通过 HwBinder 跨进程通信

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 两种模式的架构对比图

Binderized HAL vs Passthrough HAL 架构对比 Binderized HAL Framework 进程 (System Server) HAL Server 进程 (Vendor 分区) HwBinder IPC 服务注册到 hwservicemanager 框架通过服务名动态获取代理 Passthrough HAL Framework 进程 (System Server) HAL 共享库 (.so 文件) dlopen + 直接调用 HAL 库被加载到框架进程地址空间 通过函数指针表(hw_module_t)调用 选择建议 新开发 HAL 优先使用 Binderized 模式; 仅在对延迟极度敏感且 HAL 实现非常稳定的场景下考虑 Passthrough 模式。 HIDL 接口定义 → 代码生成(hidl-gen)→ 实现 Server/Client → 注册/发现服务 无论选择哪种模式,HIDL 接口定义是第一步,也是 Treble 兼容性的基石。

4.4 HIDL 服务注册与发现机制

在 Binderized HAL 模式下,服务的注册与发现由 hwservicemanager 统一管理。其工作流程如下:

  1. 服务注册:HAL Server 启动后,调用 registerAsService() 将自身注册到 hwservicemanager。
  2. 服务发现:Framework 端的 Client 通过 getService() 向 hwservicemanager 请求指定名称的服务。
  3. 代理获取:hwservicemanager 返回服务的 Binder 代理对象,Client 通过代理调用远程方法。
  4. 服务保活:hwservicemanager 监控服务进程状态,服务异常退出时可触发重启策略。
版本兼容性注意:HIDL 接口支持版本化(如 @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 文件并递增版本号。

4.6 本章小结