Stable AIDL
Stable AIDL 同时约束源码、构建快照和运行时 Binder 对象。下面的状态图说明接口从开发态进入冻结版本后,哪些变化必须创建新版本,哪些失败会阻止发布;对应实现分散在 aidl.cpp、aidl_api.go、生成器和 Stability.cpp。
本文面向已经读过 AIDL接口版本管理 和 AIDL后端生成 的读者。这里的 stable 不是“接口看起来稳定”,而是编译器、Soong、生成器和 Binder 运行时共同执行的一组约束。本文只讨论 Android 17 源码中的 VINTF stability、structured AIDL 和冻结流程,不把普通 Java framework AIDL 误称为跨独立更新组件的稳定接口。
1. 稳定标记
1.1 AIDL注解
源码文件:system/tools/aidl/build/tests_vintf/vintf/IFoo.aidl
package vintf;
@VintfStability
interface IFoo {
parcelable Foo { String a; }
union A { String a; int b; }
enum E { A, B, C }
Foo[] abar(in Foo[] f);
}解析器把 @VintfStability 保存到定义类型,而不是保存到某个方法调用。它因此会影响接口本身、嵌套 Parcelable/Union 以及这些类型进入 Parcel 时的稳定性检查。
1.2 构建属性
源码文件:system/tools/aidl/build/Android.bp
aidl_interface {
name: "tests-vintf",
srcs: ["tests_vintf/vintf/IFoo.aidl"],
stability: "vintf",
vendor_available: true,
}注解和模块属性必须成对出现。源码只有注解、编译选项没有 stability: "vintf" 时,AIDL 编译器拒绝生成;反过来模块声明 VINTF、类型没有标注,也会在类型检查阶段失败。
2. 编译约束
2.1 双重检查
源码文件:system/tools/aidl/aidl.cpp
if (defined_type->IsVintfStability()) {
if (options.GetStability() != Options::Stability::VINTF) {
AIDL_ERROR(defined_type)
<< "Must compile @VintfStability type w/ aidl_interface 'stability: \"vintf\"'";
success = false;
}
if (!options.IsStructured()) {
AIDL_ERROR(defined_type)
<< "Must compile @VintfStability type w/ aidl_interface --structured";
success = false;
}
}--structured 禁止依赖编译器不知道字段布局的 unstructured Parcelable。稳定接口必须让每个跨边界类型的字段、默认值和编码规则可被 API dump 与兼容性检查观察。
2.2 容器限制
同一检查阶段还拒绝稳定 Parcelable、Union 和接口中无法确定元素类型的裸 List/Map。这不是风格规则:接收端若没有元素类型,就无法在不同版本中可靠地解释 Parcel。
3. API冻结
3.1 版本门禁
源码文件:system/tools/aidl/build/aidl_api.go
Soong 为 stable 模块建立 current 与冻结版本目录,调用 aidl --checkapi=compatible 检查向后兼容,调用 --checkapi=equal 检查冻结快照是否被修改。frozen: true 时,current 与最近冻结版本出现差异会失败;需要变更时先将模块转为开发状态,再执行 update/freeze 流程。
3.2 哈希完整性
冻结版本还必须存在 .hash 文件。构建系统把快照文件列表交给 verify_hash,文件内容、相对路径或版本号任意变化都会使完整性校验失败。哈希和 API diff 解决的是不同问题:哈希防止快照被改写,diff 判断新声明是否兼容。
3.3 发布版本
源码文件:system/tools/aidl/build/aidl_test.go
TestVintfWithoutVersionInRelease 构造只有 stability: "vintf"、没有版本列表的模块;测试冻结环境要求报错“versions must be set”,而正常测试环境会生成 V1 后端模块。这说明 release 构建不允许把未冻结的 VINTF 接口当作稳定 ABI。
4. 运行标记
4.1 CPP服务端
源码文件:system/tools/aidl/generate_cpp.cpp
if (interface.IsVintfStability()) {
::android::internal::Stability::markVintf(this);
} else {
::android::internal::Stability::markCompilationUnit(this);
}生成的 Bn 构造函数在对象创建时标记 stability。标记发生在 Binder 对象拥有者处,客户端不能通过调用一个方法把普通对象“升级”为 VINTF 对象。
4.2 NDK服务端
源码文件:system/tools/aidl/generate_ndk.cpp
AIBinder* binder = AIBinder_new(...);
AIBinder_markVintfStability(binder);NDK 后端使用 libbinder_ndk 的对应 API;CPP 的 Stability::markVintf 与 NDK 的 AIBinder_markVintfStability 是不同库的实现入口,但表达相同的运行时等级。
4.3 稳定等级
源码文件:frameworks/native/libs/binder/Stability.cpp
void Stability::markVintf(IBinder* binder) {
status_t result = setRepr(binder, Level::VINTF, REPR_LOG);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}setRepr 拒绝未知等级,也拒绝在已设置等级上直接改写;只有显式允许 downgrade 的内部路径可以降级。这样 stability 是 Binder 对象状态,而非调用者可任意修改的标签。
5. 类型传递
5.1 Parcelable等级
源码文件:system/tools/aidl/generate_cpp.cpp
Stable Parcelable 生成 Parcelable::Stability::STABILITY_VINTF,普通类型生成 STABILITY_LOCAL。当稳定接口包含普通 Parcelable 时,aidl.cpp 的类型遍历会拒绝它,避免接口声明稳定而实际字段只能在本编译单元内解释。
5.2 Binder参数
frameworks/native/libs/binder/Stability.cpp 的 requiresVintfDeclaration 根据 Binder 对象当前等级判断是否需要 VINTF 声明。传入稳定接口的 Binder 引用如果等级不足,失败发生在 Parcel/Binder 边界,而不是业务方法内部;排查时应先看对象创建和生成代码的标记路径。
6. 失败路径
6.1 注解不匹配
@VintfStability 加在类型上但模块没有 stability: "vintf",编译器在生成前失败;这类错误不会生成“先运行再报错”的 Stub。
6.2 未冻结发布
源码文件:system/tools/aidl/build/aidl_test.go
TestUnstableVersionUsageInRelease 验证 V2 未冻结开发版本在 release 环境不能被 Java library 依赖,而稳定 V1 可以。开发环境允许 unfrozen 便于迭代,发布环境则关闭这条逃生路径。
6.3 稳定性重复设置
Stability::setRepr 发现对象已经有稳定等级且新等级不是允许的 downgrade 时返回 BAD_TYPE。因此服务端 wrapper、NDK 包装和跨进程代理都必须遵守单一 owner 的标记时机。
7. 阅读检查
遇到“VINTF AIDL 能编译但运行时传 Binder 失败”的问题,按顺序检查:AIDL 类型是否有 @VintfStability、Soong 是否声明 stability: "vintf"、是否启用 structured、生成的 Bn/AIBinder 是否调用稳定性标记、传入对象的 Stability::getRepr 是否足够。能把这五个位置串起来,才能区分 API 冻结问题与 Binder 运行时 stability 问题。
