AIDL 接口中间插入方法导致第三方调用异常

Source

AIDL 接口中间插入方法导致第三方调用异常

问题现象

IServiceAidlInterface 新增 unInit 接口后,未更新 AIDL 的第三方 App 出现:

  • start() 调用正常
  • registerListener() 调用异常

根本原因

AIDL Transaction Code 机制

AIDL 基于 Android Binder IPC,客户端调用服务端方法时,传递的不是方法名,而是一个整数编号 transaction code。Android 编译器按 .aidl 文件中方法的声明顺序从 1 开始依次分配该编号。

两端(客户端 Proxy / 服务端 Stub)各自根据自己持有的 .aidl 文件生成代码,调用链如下:

客户端 Proxy                          服务端 Stub
registerListener()  →  发送 code=N  →  onTransact(code=N)  →  路由到第 N 个方法

只要两端 code 映射一致,通信正常。一旦在接口中间插入新方法,后续所有方法的 code 全部错位,两端映射不再匹配。

本次变更的错位分析

unInit 被插入在 stopregisterListener 之间:

Transaction Code 服务端(新 AIDL) 第三方客户端(旧 AIDL) 结果
1 start() start() ✅ 正常
2 stop() stop() ✅ 正常
3 unInit() registerListener() ❌ 异常
4 registerListener() unregisterListener() ❌ 异常
5 unregisterListener() (不存在) ❌ 异常
  • 第三方调用 registerListener() 时发送 code = 3
  • 服务端将 code 3 路由到 unInit()
  • 参数类型不匹配,调用异常
  • start() 仍是 code 1,两端一致,所以不受影响

修复方案

新增 AIDL 方法必须追加到接口末尾,严禁插入到已有方法之间。

interface IServiceAidlInterface {
    // ── 原有方法,顺序和位置不变 ──────────────────────────────
    int start();
    int stop();

    void registerListener(in IServiceListener listener);
    void unregisterListener(in IServiceListener listener);

    // ── 新增方法统一追加到末尾 ────────────────────────────────
    int unInit();
}

这样旧客户端持有的所有 transaction code 映射保持不变,向后兼容。

规范约束

操作 是否允许
在接口末尾追加新方法 ✅ 允许
在已有方法之间插入新方法 ❌ 禁止
删除或重排已有方法 ❌ 禁止
修改已有方法的参数类型或顺序 ❌ 禁止

AIDL 接口一旦对外发布,已有方法的顺序、签名、位置均不可更改,只能在末尾追加扩展。这与 Protobuf 字段编号、COM 接口 vtable 布局遵循同样的原则。