4. AIDL 接口定义:AIDL for HAL 的语法、稳定性要求、与 HIDL 的异同点

Android 从 Treble 架构演进至 Mainline (Project Mainline) 后,HAL 接口定义语言从 HIDL 逐步迁移至 AIDL。AIDL for HAL 并非简单的 IPC 语言替换,它在语法、稳定性契约、后端代码生成以及版本管理上引入了全新的规则。本节将深入剖析 AIDL for HAL 的核心语法、稳定性要求,并通过对比表格与架构图厘清其与 HIDL 的本质差异。

4.1 AIDL for HAL 语法核心要素

AIDL for HAL 的语法基于标准 Android AIDL,但增加了针对硬件抽象层的特殊注解和类型约束。其核心文件结构包含 .aidl 接口定义文件与对应的 Android.bp 构建配置。

4.1.1 接口定义与注解

与标准 AIDL 不同,HAL 服务接口必须使用 @VintfStability 注解标记,以声明该接口遵循 VINTF (Vendor Interface) 稳定性规则。同时,接口必须显式指定 @Backing 注解以关联后端类型(如 aidl 或 ndk)。

// IMyLight.hal.aidl
package vendor.example.light;

@VintfStability
@Backing(type="ndk")
interface IMyLight {
    /**
     * 设置灯光亮度。
     * @param brightness 亮度值,范围 0-255。
     * @return 操作结果,0 表示成功。
     */
    int setBrightness(byte brightness);

    /**
     * 获取当前灯光状态。
     * @return LightState 包含颜色与亮度信息。
     */
    LightState getState();
}

4.1.2 数据类型与 parcelable 声明

AIDL for HAL 支持标准 AIDL 基本类型,但强烈推荐使用 稳定 parcelable 类型。自定义数据结构必须在独立的 .aidl 文件中声明,并使用 @VintfStability 注解。

// LightState.aidl
package vendor.example.light;

@VintfStability
parcelable LightState {
    int color;
    byte brightness;
    String description;
}

4.1.3 构建配置 (Android.bp)

在 Android.bp 中,需要将 AIDL 接口编译为 NDK 后端库,并指定 stability 属性为 "vintf"。

aidl_interface {
    name: "vendor.example.light-ndk",
    srcs: ["**/*.aidl"],
    backend: {
        ndk: {
            enabled: true,
        },
    },
    stability: "vintf",
}
关键点: 所有暴露给 HAL 的 AIDL 接口必须使用 @VintfStability 注解,并在 Android.bp 中设置 stability: "vintf"。缺少任一环节都会导致 VINTF 对象验证失败,服务无法注册。

4.2 稳定性要求与版本管理

AIDL for HAL 的稳定性要求比传统 AIDL 严格得多,它直接继承了 HIDL 的 二进制稳定性 (ABI Stability) 契约。这意味着接口一旦发布,其方法签名、类型定义和布局必须向后兼容。

4.2.1 稳定性规则清单

规则类别 具体要求 违规后果
方法签名 不允许删除、重命名或修改已有方法的参数类型/顺序。 运行时链接错误,服务无法被客户端绑定。
Parcelable 字段 不允许删除或修改已有字段的类型;只允许在末尾追加新字段。 反序列化崩溃或数据错乱。
接口继承 不允许改变父接口;新增方法必须通过扩展新接口实现。 VINTF 兼容性检查失败。
常量值 不允许修改已定义的 const 值。 条件编译逻辑错误。

4.2.2 版本演进策略

AIDL for HAL 不直接支持 HIDL 的 @1.0、@1.1 版本号语法。版本管理通过 包名路径 和 接口扩展 实现:

  • 包名路径: 使用 vendor.example.light@1.0 风格的包名,但 AIDL 中通过目录结构体现,例如 vendor/example/light/1.0/IMyLight.aidl。
  • 接口扩展: 新版本定义新接口继承旧接口,例如 IMyLightExt 继承 IMyLight,并在新包路径下声明。
  • FQName 唯一性: 每个接口的完全限定名 (FQName) 必须全局唯一,且与 VINTF manifest 中的声明严格匹配。
常见陷阱: 在 AIDL for HAL 中,不允许 像传统 AIDL 那样通过 oneway 关键字随意修改方法调用模式。所有方法默认是同步的,如需异步必须显式使用 @Asynchronous 注解,且该注解一旦发布即成为稳定性契约的一部分,不可更改。

4.3 AIDL 与 HIDL 的异同点

理解 AIDL for HAL 与 HIDL 的差异,是进行驱动移植和问题排查的基础。以下从语言特性、代码生成、性能、工具链四个维度进行对比。

4.3.1 核心差异对比表

对比维度 HIDL AIDL for HAL
接口定义语言 专用 .hal 文件,语法类似 C++ 头文件 标准 .aidl 文件,语法类似 Java 接口
后端代码生成 支持 C++ (passthrough/binderized) 和 Java 支持 NDK (C++17)、Java、Rust (实验性)
稳定性注解 隐式通过版本号 @1.0 声明 显式使用 @VintfStability 注解
数据类型 支持 vec<T>、hidl_string、hidl_handle 等专用类型 使用标准 AIDL 类型 + @VintfStability parcelable
服务注册 通过 hwservicemanager 注册,使用 I*::getService() 通过 servicemanager 注册,使用 BnBinder + registerService()
版本管理 内置 @1.0、@1.1 版本号,支持接口继承 通过包名路径 + 接口扩展实现,无内置版本号语法
性能开销 中等,序列化/反序列化有额外拷贝 较低,NDK 后端直接操作内存,零拷贝路径更短

4.3.2 架构迁移对比图

下图展示了从 HIDL 到 AIDL for HAL 的架构变化,重点关注服务注册与客户端绑定路径的差异。

AIDL for HAL 与 HIDL 架构对比 HIDL 架构 Client (C++/Java) hwservicemanager HAL Service (C++) AIDL for HAL 架构 Client (NDK/Java) servicemanager HAL Service (NDK) 核心差异 服务管理器不同 后端代码生成不同 稳定性声明方式不同 图:HIDL 与 AIDL for HAL 服务注册与绑定架构对比

4.4 移植常见问题与排查思路

在从 HIDL 迁移至 AIDL for HAL 的过程中,以下三类问题最为常见:

  1. VINTF 验证失败: 接口未添加 @VintfStability 注解,或 Android.bp 中未设置 stability: "vintf"。排查时使用 lshal 命令检查服务是否注册成功,并查看 logcat 中关于 VintfObject 的错误日志。
  2. 类型不匹配: HIDL 中的 vec<uint8_t> 对应 AIDL 中的 byte[],但序列化行为不同。排查时对比 Parcel 读写顺序,确保客户端与服务端使用相同的 parcelable 定义。
  3. 服务注册失败: AIDL for HAL 使用 servicemanager 而非 hwservicemanager。如果服务尝试向错误的 manager 注册,会返回 NO_INIT 错误。排查时确认服务端代码中调用的是 ndk::SpAIBinder(service)->registerAsService("name")。
排查工具推荐: 使用 adb shell dumpsys service list 查看所有注册的 AIDL 服务;使用 adb shell lshal 查看 HIDL 服务。如果目标服务在两个列表中均未出现,则说明注册流程存在根本性问题。