Skip to content

BinderProxy详解

追踪 Java BinderProxy 的 native 对象缓存、transact 前处理、死亡通知持有和 native 生命周期。

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

BinderProxy详解 ​

Java BinderProxy 是 native IBinder 的 Java 代理,不由 Java 业务代码直接 new。native 通过 javaObjectForIBinder() 请求 Java 侧对象,BinderProxy.getInstance() 再以 native IBinder 指针为 key,在进程级 ProxyMap 中复用仍存活的 Java proxy;proxy 的 native 指针由 NativeAllocationRegistry 接管释放。

本文面向已经读过 BpBinder代理对象、IBinder接口 和 JNI层android_util_Binder 前置概念的读者。本文聚焦 Java proxy 的缓存、transact 前置处理、死亡通知持有和回收边界;native BpBinder 的 handle 生命周期不重复展开。

1. 对象创建 ​

1.1 native入口 ​

源码文件:frameworks/base/core/jni/android_util_Binder.cpp

native 代码把一个 IBinder* 交给 Java 对象转换函数;Java 层不拥有传入指针的初始 C++ 引用,proxy 的 native allocation registry 负责对应释放关系。

1.2 Proxy缓存 ​

源码文件:frameworks/base/core/java/android/os/BinderProxy.java

相关函数:getInstance()、ProxyMap

java
private static final class ProxyMap {
    // values are weak references to BinderProxy
    private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
    private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
            new ArrayList[MAIN_INDEX_SIZE];
}

@GuardedBy("sProxyMap")
private static final ProxyMap sProxyMap = new ProxyMap();

private static BinderProxy getInstance(long nativeData, long iBinder) {
    BinderProxy result;
    synchronized (sProxyMap) {
        result = sProxyMap.get(iBinder);
        if (result != null) return result;
        result = new BinderProxy(nativeData);
        NoImagePreloadHolder.sRegistry.registerNativeAllocation(
                result, nativeData);
        sProxyMap.set(iBinder, result);
    }
    return result;
}

key 是 native IBinder 指针,value 是弱引用;Java proxy 被 GC 后,map entry 可以在后续访问时清理。锁保证同一个 native binder 不因并发 getInstance() 创建多个仍存活 proxy。

1.3 Native资源 ​

java
private static final int NATIVE_ALLOCATION_SIZE = 1000;

private BinderProxy(long nativeData) {
    mNativeData = nativeData;
}

构造函数只保存 native pointer;实际释放函数来自 getNativeFinalizer() 注册的 NativeAllocationRegistry。NATIVE_ALLOCATION_SIZE 是 Dalvik/ART native allocation 估算值,不是 Binder transaction buffer 大小。

2. 事务入口 ​

2.1 参数检查 ​

源码文件:frameworks/base/core/java/android/os/BinderProxy.java

相关函数:transact()

java
public boolean transact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    Binder.checkParcel(this, code, data,
            "Unreasonably large binder buffer");
    boolean warnOnBlocking = mWarnOnBlocking;
    ...
    final boolean result = transactNative(code, data, reply, flags);
    ...
    return result;
}

Java proxy 在 JNI/native 发送前先检查 Parcel 大小和 transaction code。这个检查不是 kernel 真实 buffer 上限,而是 Java 层防止不合理大事务的早期门槛。

2.2 Blocking警告 ​

java
if (warnOnBlocking && ((flags & FLAG_ONEWAY) == 0)
        && Binder.sWarnOnBlockingOnCurrentThread.get()) {
    mWarnOnBlocking = false;
    if (Build.IS_USERDEBUG || Build.IS_ENG) {
        Log.wtf(Binder.TAG,
                "Outgoing transactions from this process must be FLAG_ONEWAY");
    } else {
        Log.e(Binder.TAG,
                "Outgoing transactions from this process must be FLAG_ONEWAY");
    }
}

警告只针对同步 outgoing transaction,且按 proxy 缓存一次告警状态;它不把同步调用改成 oneway,也不阻止 transactNative() 执行。

2.3 调用旁路 ​

事务发送前,BinderProxy 读取当前线程的 work source,必要时修改 Parcel header;同步调用还可能开启 noted AppOps 收集。调用结束后恢复 AppOps collection,并通知 ProxyTransactListener。

java
try {
    final boolean result = transactNative(code, data, reply, flags);
    if (reply != null && !warnOnBlocking) {
        reply.addFlags(
                Parcel.FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT);
    }
    return result;
} finally {
    AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
    if (transactListener != null) {
        transactListener.onTransactEnded(session);
    }
}

这些是 Java 层旁路状态,不等于 native BpBinder::transact() 的 driver 状态;native 返回异常时,Java finally 仍负责恢复观测状态。

3. 死亡通知 ​

3.1 Java持有 ​

源码文件:frameworks/base/core/java/android/os/BinderProxy.java

java
private List<DeathRecipient> mDeathRecipients =
        Collections.synchronizedList(new ArrayList<>());

public void linkToDeath(DeathRecipient recipient, int flags)
        throws RemoteException {
    linkToDeathNative(recipient, flags);
    mDeathRecipients.add(recipient);
}

public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
    mDeathRecipients.remove(recipient);
    return unlinkToDeathNative(recipient, flags);
}

JNI 侧使用弱引用接收 Java recipient,Java list 则保持 recipient 至少活到 proxy 被 GC 或显式 unlink。linkToDeathNative() 成功前不会把 recipient 加入 Java list;native 失败会抛 RemoteException。

3.2 回调线程 ​

IBinder.DeathRecipient.binderDied() 由 Binder 线程调用,不保证在注册线程执行;Java callback 需要自行同步。死亡回调不意味着 proxy map 立即删除,map 的弱 value 清理和 native Binder 的死亡状态是两个生命周期。

3.3 Frozen回调 ​

BinderProxy 还用 map 强持有 frozen state callback 的包装对象,JNI 侧只保留弱引用;原始 callback、executor wrapper、native callback 的 owner 不同,不能和 death recipient list 合并描述。

4. 缓存边界 ​

4.1 清理entry ​

ProxyMap 使用弱值,访问时会清理已经被 GC 的 WeakReference。因此 map size 可能大于仍活跃 proxy 数;源码提供 size() 与 unclearedSize() 分别观察总 entry 和未清除 value。

4.2 水位保护 ​

java
private static final int CRASH_AT_SIZE = 25_000;

当 entry 数过大且 GC 后仍有大量未清理 proxy,ProxyMap 会输出 per-UID/interface 诊断并抛 BinderProxyMapSizeException。这是 Java proxy 泄漏保护,不是单次 Binder transaction 失败。

4.3 native与Java复用 ​

native ProcessState 按 handle 缓存 BpBinder;Java ProxyMap 按 native IBinder 指针缓存 BinderProxy。两层 key 不同,但目标是保持对象复用:一个 native proxy 对应一个仍存活的 Java proxy。

5. 测试边界 ​

5.1 接口观察 ​

源码文件:frameworks/base/core/java/android/os/BinderProxy.java

Java proxy 的 map、native allocation 和 transact 逻辑主要通过 framework/instrumentation 与 native Binder 测试间接覆盖;源码范围内没有把所有 GC、ProxyMap 水位和 blocking warning 组合成单一生产单元测试。

5.2 Native对照 ​

源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp

DeathNotificationStrongRef、BinderProxyCount 和 BinderProxyCountCallback 覆盖 native proxy 的死亡及计数;它们不能证明 Java ProxyMap 的弱引用清理或 Java DeathRecipient list 的 GC 时机。

5.3 可执行阅读 ​

bash
rg -n "getInstance|class ProxyMap|NativeAllocationRegistry|mNativeData" \
  frameworks/base/core/java/android/os/BinderProxy.java

rg -n "transact\(|transactNative|checkParcel|mWarnOnBlocking|AppOps" \
  frameworks/base/core/java/android/os/BinderProxy.java

rg -n "linkToDeath|unlinkToDeath|mDeathRecipients|CRASH_AT_SIZE" \
  frameworks/base/core/java/android/os/BinderProxy.java

排查 Java Binder proxy 数量异常时,区分 native BpBinder 数、Java ProxyMap 总 entry、未清理 weak value 和活跃 Java 引用;排查同步调用告警时,检查 FLAG_ONEWAY、线程警告开关和 proxy 的 mWarnOnBlocking,不要把 warning 当作事务被拒绝。