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
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资源
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()
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警告
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。
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
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 水位保护
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 可执行阅读
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 当作事务被拒绝。
