Skip to content

Stub与Proxy代码分析

以真实 IServiceCallback AIDL 使用为入口,解释 Java Stub/Proxy 的本地选择、oneway 回调和 Binder 转换边界。

基于android-17.0.0_r1
AndroidAIDLStubProxyJava框架源码阅读

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

java
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

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() 负责:

  1. 对 INTERFACE_TRANSACTION 写回 descriptor;
  2. 对业务 code 调用 data.enforceInterface(DESCRIPTOR);
  3. 按 AIDL 类型顺序读取 String 和 IBinder;
  4. 调用抽象业务方法 onRegistration();
  5. 对非 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() 生成代码会:

  1. obtain data Parcel;
  2. 写入接口 descriptor;
  3. 写入 UTF-8/Java String;
  4. 写入 strong Binder;
  5. 调用 mRemote.transact(TRANSACTION_onRegistration, data, null, FLAG_ONEWAY);
  6. 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

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 可执行阅读 ​

bash
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/状态更新。