Stub与Proxy代码分析
Java AIDL 的 Stub/Proxy 不是两个可以随意替换的类:Stub 是本地 Binder 实现,负责 descriptor 和 onTransact() 解包;Proxy 是远程 IBinder 包装,负责写 Parcel、调用 transact()、读取结果。Android 17 的 ServiceManager 回调接口 IServiceCallback 是真实样本:AIDL 声明为 oneway,framework 的 ServiceHolder 用 IServiceCallback.Stub 接收服务注册通知。
本文面向已经读过 BnInterface与BpInterface模板、Java-IBinder接口 和 Binder基类详解 的读者。本文根据仓库中的 AIDL 声明和真实调用方解释生成代码必须承担的契约;生成 Java 文件不在当前源码 checkout 中时,不伪造其具体行号。
1. AIDL输入
1.1 真实声明
源码文件:frameworks/native/libs/binder/aidl/android/os/IServiceCallback.aidl
package android.os;
oneway interface IServiceCallback {
void onRegistration(@utf8InCpp String name, IBinder binder);
}这里有三个生成约束:接口全名是 android.os.IServiceCallback;方法只有 name 和 binder 两个参数;oneway 让跨进程通知没有同步 reply。生成器会把这些约束展开为 Java Stub、Proxy 和 Default 等类型。
1.2 真实调用方
源码文件:frameworks/base/services/core/java/com/android/server/audio/ServiceHolder.java
private final IServiceCallback mServiceListener =
new IServiceCallback.Stub() {
@Override
public void onRegistration(String name, IBinder binder) {
onServiceInited(binder);
}
};调用方直接继承生成 Stub,说明它是接收端本地对象;它不需要手写 Binder.transact() 或读取 Parcel。
2. Stub职责
2.1 asInterface
生成 Stub 通常提供 asInterface(IBinder obj):先调用 queryLocalInterface(DESCRIPTOR)),匹配则返回本地 Stub;否则创建 Proxy。这个选择复用 Java Binder的 local interface 契约,与 nativeBnInterface/BpInterface` 对应。
2.2 onTransact
Stub 的 onTransact() 负责:
- 对
INTERFACE_TRANSACTION写回 descriptor; - 对业务 code 调用
data.enforceInterface(DESCRIPTOR); - 按 AIDL 类型顺序读取
String和IBinder; - 调用抽象业务方法
onRegistration(); - 对非 oneway 方法写 reply 或 exception。
oneway 生成分支不会等待业务 reply;如果服务实现抛异常,发送方没有同步异常 Parcel 可读。
2.3 参数顺序
@utf8InCpp 影响 native/backend 表示和生成序列化策略,但 Java Stub 对调用者仍表现为 String。Binder 参数通过 Parcel.readStrongBinder() 恢复为 Java Binder 或 BinderProxy,随后可交给 castFunction/AIDL Stub 转换。
3. Proxy职责
3.1 代理构造
生成 Proxy 保存一个 IBinder mRemote。调用 IServiceCallback.Stub.asInterface(remote) 时,如果 remote 不是本地 Stub,就构造 Proxy;调用者只看到 IServiceCallback 接口。
3.2 oneway调用
Proxy 的 onRegistration() 生成代码会:
- obtain data Parcel;
- 写入接口 descriptor;
- 写入 UTF-8/Java String;
- 写入 strong Binder;
- 调用
mRemote.transact(TRANSACTION_onRegistration, data, null, FLAG_ONEWAY); - finally recycle data。
具体生成文件可能位于 out/soong intermediates,不作为仓库公开事实;上述步骤由 AIDL 声明、Java IBinder 契约和真实调用方共同确定。
3.3 失败边界
Proxy 仍可能抛 RemoteException,例如远端进程已死或驱动提交失败。oneway 的“返回快”不等于服务实现已执行,也不等于参数已经被业务消费;它只表示发送方不等待 reply。
4. 真实消费
4.1 ServiceHolder
源码文件:frameworks/base/services/core/java/com/android/server/audio/ServiceHolder.java
public void registerForNotifications() {
mServiceProvider.registerForNotifications(
mServiceName, mServiceListener);
}
private void onServiceInited(IBinder binder) {
I service = mCastFunction.apply(binder);
...
}ServiceHolder 把生成 Stub 当作注册监听者;收到 onRegistration 后再把 Binder 转为具体接口并更新 AtomicReference<I>。AIDL callback 只负责通知,服务对象的强引用、死亡处理和 onStart/onDeath task 由 ServiceHolder owner 管理。
4.2 注册前后
ServiceManager 注册回调可能在服务已存在时立即派发,也可能在未来 addService 时派发。Stub/Proxy 代码只负责一条通知事务;“是否马上收到”由 ServiceManager 的注册时序决定。
4.3 DeathRecipient
ServiceHolder 还实现 IBinder.DeathRecipient,收到服务 Binder 死亡后执行 onDeath 任务。callback Binder 的生命周期与被监听服务 Binder 不同:前者是通知通道,后者是业务服务,不能共用一个死亡状态。
5. 生成边界
5.1 生成文件
AIDL 编译器生成的 Java Stub/Proxy 不等于仓库中的手写 source;它们依赖 build variant、backend 和输出目录。文章引用稳定 AIDL 输入与真实消费方,避免把某次 out 目录产物当作固定源码。
5.2 Java与NDK
同一 IServiceCallback.aidl 可以生成 Java、NDK/C++ 或 Rust backend,接口语义相同但 Parcel API、生命周期和类型包装不同。本文只承诺 Java Stub/Proxy 的契约,不把 native Bn/Bp 生成代码混写成 Java 实现。
6. 测试边界
6.1 ServiceHolder
源码文件:frameworks/base/services/tests/audio/src/com/android/server/audio/ServiceHolderTest.java
测试通过 callback 捕获 IServiceCallback.Stub,模拟注册前后 Binder 和死亡回调,断言 ServiceHolder 更新服务引用、触发 onStart/onDeath task。它验证真实消费方状态,不直接执行生成 Proxy 的跨进程 Parcel。
6.2 AIDL测试
源码文件:frameworks/base/core/tests/coretests/src/android/os/AidlTest.java
AidlTest 使用本地 Binder/mock queryLocalInterface(),覆盖 Stub 本地选择和接口转换边界;它不代替驱动上的 oneway 调度测试。
6.3 可执行阅读
rg -n "oneway interface IServiceCallback|onRegistration" \
frameworks/native/libs/binder/aidl/android/os/IServiceCallback.aidl \
frameworks/base/services/core/java/com/android/server/audio/ServiceHolder.java
rg -n "IServiceCallback\.Stub|registerForNotifications|onServiceInited|DeathRecipient" \
frameworks/base/services/core/java/com/android/server/audio/ServiceHolder.java
rg -n "queryLocalInterface|IServiceCallback|Stub" \
frameworks/base/core/tests/coretests/src/android/os/AidlTest.java \
frameworks/base/services/tests/audio/src/com/android/server/audio/ServiceHolderTest.java排查 AIDL callback 不生效时,先确认方法是否 oneway、Proxy 是否写入同一 descriptor/参数顺序,再确认 Stub 是否收到 onTransact,最后检查 ServiceManager 注册时序和 ServiceHolder 的 Binder death/状态更新。
