AIDL语法详解
AIDL 语法不是“Java 接口加几种类型”。Android 17 的 parser grammar 将文档拆成 package、import、interface/parcelable/enum/union 声明,再把方法参数方向、oneway、常量和注解交给 AST/类型检查。语法选择会继续影响 Stub/Proxy、Parcel 读写和不同 backend 的生成约束。
本文面向已经读过 Stub与Proxy代码分析、Java-Parcel序列化 和 BnInterface与BpInterface模板 的读者。本文以 Android 17 parser grammar 和真实 AIDL 样本为事实来源,不展开编译器代码生成模板。
1. 文档结构
1.1 package与import
源码文件:system/tools/aidl/aidl_language_y.yy
document
: optional_package imports decls
;
optional_package
: PACKAGE qualified_name ';'
;
import
: IMPORT qualified_name ';'
;一个文档最多一个 package 前置,再接 import 和声明列表。qualified name 由 identifier 与点组成;import 路径参与类型解析,不是 Java import 的运行时行为。
1.2 声明种类
unannotated_decl
: parcelable_decl
| interface_decl
| enum_decl
| union_decl
;Android 17 parser 明确区分 interface、parcelable、enum 和 union。声明进入 AST 后,类型检查和 backend 再决定哪些目标语言/稳定性组合可生成。
2. Interface语法
2.1 普通与oneway
源码文件:system/tools/aidl/aidl_language_y.yy
interface_decl
: INTERFACE qualified_name '{' interface_members '}'
| ONEWAY INTERFACE qualified_name '{' interface_members '}'
;oneway 是 interface 级语法,生成的方法默认采用异步事务约束;它不是 Java 方法返回类型,也不能只在调用方 flags 中临时添加来改变接口契约。
2.2 真实样本
源码文件:frameworks/native/libs/binder/aidl/android/os/IServiceCallback.aidl
package android.os;
oneway interface IServiceCallback {
void onRegistration(@utf8InCpp String name, IBinder binder);
}样本只有一个 oneway 方法,参数包含 annotated String 与 IBinder。生成 Stub 会接收并解包,Proxy 会以 FLAG_ONEWAY 发出。
2.3 方法成员
parser 将 interface members 解析为 method、constant 或嵌套声明。方法的返回类型、名字、参数列表和注解进入 AidlMethod AST;重复 name、非法类型和 backend 限制在后续检查阶段报告,不由 lexer 单独决定。
3. 参数语法
3.1 方向
源码文件:system/tools/aidl/aidl_language_y.yy
%token IN "in"
%token INOUT "inout"
%token OUT "out"参数方向是语法 token,不是普通类型修饰。in 表示发送方输入,out/inout 需要生成代码在 reply 或回写路径处理,实际可用方向还受 backend 与稳定 AIDL 约束。
3.2 类型与数组
AIDL type grammar 支持基本类型、命名类型、数组和泛型 type arguments;parser 先构造 AidlTypeSpecifier,类型名解析/可用性检查在 AidlTypenames 和 semantic validation 中完成。不能从 parser 接受 identifier 推断所有语言都能生成该类型。
3.3 注解参数
真实样本的 @utf8InCpp String 是参数注解,影响 C++ backend 的字符串表示;Java backend 仍以 Java String 作为接口参数。注解是 AST metadata,不应当被误读成运行时 Parcel 字段。
4. 数据声明
4.1 Parcelable
parcelable_decl
: PARCELABLE qualified_name optional_type_params
optional_unstructured_headers ';'
| PARCELABLE qualified_name optional_type_params
'{' parcelable_members '}'
;unstructured parcelable 只声明类型名,structured parcelable 在大括号内声明字段/常量/嵌套声明。两者的生成与稳定性能力不同,不能只看关键字 parcelable。
4.2 Enum
enum_decl
: ENUM qualified_name enum_decl_body
;
enum_decl_body
: '{' enumerators '}'
| '{' enumerators ',' '}'
;枚举项可显式赋值或使用默认值。常量表达式支持整数/浮点/布尔等 grammar,语义检查负责类型和重复值约束。
4.3 Union
union 与 structured parcelable 共用部分成员声明路径,但生成的数据布局和读取策略不同。它不是 Java sealed class 的直接语法别名,必须查看目标 backend 支持。
5. 常量与注解
5.1 常量表达式
parser grammar 定义逻辑、位运算、比较、移位、算术和一元运算优先级;常量先构造表达式 AST,再由 semantic phase Evaluate/CheckValid。源码中出现合法 token 不等于常量一定符合目标类型范围。
5.2 目标语言注解
AIDL parser token 包含 annotation、cpp_header、ndk_header、rust_type 等扩展入口。它们为 backend 提供额外 metadata;Java、NDK、Rust 的可接受注解集合不同,不能把某个 backend 的注解写成通用 AIDL 语义。
6. 错误边界
parser 对未知字符、缺失分号、括号不闭合和声明结构错误报告语法错误;类型不存在、重复声明、方向不支持和 backend 能力不足属于后续语义/代码生成错误。排查时先区分 parser error、AST validation error 和 backend error。
7. 测试与验证
源码文件:system/tools/aidl/tests/aidl_parser_fuzzer.cpp
fuzzer 将任意输入送入 parser,目标是发现崩溃和解析状态错误,不证明任意语法都应该被接受。
rg -n "optional_package|imports|interface_decl|parcelable_decl|enum_decl|union_decl" system/tools/aidl/aidl_language_y.yy
rg -n "direction|INOUT|OUT|method_decl|parameter" system/tools/aidl/aidl_language_y.yy
rg -n "oneway interface|@utf8InCpp|interface IServiceCallback" frameworks/native/libs/binder/aidl/android/os/IServiceCallback.aidl阅读一个 AIDL 文件时,先标出 package/import,再识别声明种类,然后判断 interface 是否 oneway、每个参数方向和注解,最后再追 semantic validation/backend 约束;不要把 parser 接受等同于生成和运行都成功。
