writeStrongBinder与readStrongBinder
writeStrongBinder() 写入的不是一个 C++ 指针,而是根据 Binder 所在进程把对象编码成 flat_binder_object:本地 BBinder 使用 BINDER_TYPE_BINDER,远程 BpBinder 使用 BINDER_TYPE_HANDLE。readStrongBinder() 再根据类型恢复本地对象或通过 handle 表取得代理。对象位置另外登记到 Parcel offsets,驱动才能在跨进程传输时翻译它。
本文面向已经读过 Parcel序列化详解、BpBinder代理对象 和 Binder远程引用 的读者。本文只讲强 Binder 对象的 flatten/unflatten 主线、kernel/RPC 分叉和引用释放,不重复介绍普通整数和字符串。
1. 写入分叉
1.1 入口
源码文件:frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeStrongBinder(const sp<IBinder>& val) {
return flattenBinder(val);
}真正的差异在 flattenBinder():它先判断 Parcel 是 kernel Binder 还是 RPC,再决定对象格式和所有权。
1.2 本地对象
if (local != nullptr) {
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(
local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
}本地对象把弱引用地址放入 binder,把 BBinder* 放入 cookie;驱动会据此创建或查找 node。对象不是把强 sp 指针直接写入 Parcel,因此接收方不能把 cookie 当作自己的可解引用地址。
1.3 远程代理
if (proxy != nullptr) {
if (proxy->isRpcBinder()) {
return INVALID_OPERATION;
}
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0;
obj.handle = proxy->getPrivateAccessor().binderHandle();
obj.cookie = 0;
}远程 kernel Binder 只写 handle;socket RPC Binder 不能被塞进 kernel Parcel,直接返回 INVALID_OPERATION。因此同为 IBinder 的对象,传输上下文仍决定是否可写。
2. 对象表
2.1 writeObject
flattenBinder() 在写入 flat_binder_object 后调用 writeObject(),把对象在数据区的偏移追加到 kernel fields 的对象表。ipcObjectsCount() 因而会增加;只复制数据区而不复制 offsets 会使接收端无法识别 Binder。
2.2 引用边沿
Parcel 在构造、复制、清理时会对 BINDER_TYPE_BINDER 的 cookie 对应对象执行 strong inc/dec;BINDER_TYPE_HANDLE 则通过 ProcessState::getStrongProxyForHandle() 获取对应代理,再调整其强引用。对象表释放是引用生命周期的一部分。
3. 读取分叉
3.1 入口
源码文件:frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::readNullableStrongBinder(
sp<IBinder>* val) const {
return unflattenBinder(val);
}
sp<IBinder> Parcel::readStrongBinder() const {
sp<IBinder> val;
readNullableStrongBinder(&val);
return val;
}无 out status 的便捷 API 会忽略错误并返回空;需要区分“合法 null Binder”和“对象格式错误”时,应使用带 status 的重载。
3.2 本地类型
case BINDER_TYPE_BINDER: {
sp<IBinder> binder = sp<IBinder>::fromExisting(
reinterpret_cast<IBinder*>(flat->cookie));
return finishUnflattenBinder(binder, out);
}本地类型只在拥有该对象的进程上下文中有意义;读取 cookie 仍通过 fromExisting 建立引用,不把它转换为陌生进程地址。
3.3 Handle类型
case BINDER_TYPE_HANDLE: {
sp<IBinder> binder =
ProcessState::self()->getStrongProxyForHandle(
flat->handle);
return finishUnflattenBinder(binder, out);
}接收远程 handle 时,ProcessState 复用或创建 BpBinder;这一步把 Parcel 对象读取接回 handle 缓存和驱动引用生命周期。
4. 失败边界
4.1 Null与错误
readNullableStrongBinder() 允许空对象;readStrongBinder(sp<IBinder>*) 在成功但得到 null 时返回 UNEXPECTED_NULL;无 status 的 readStrongBinder() 为历史兼容可能静默返回 nullptr。调用者必须选择符合协议的重载。
4.2 对象类型
对象 offset 指向未知类型、越界或错误 Parcel 上下文时,unflattenBinder() 返回 BAD_TYPE/其他错误。对象表和数据区不一致时,读取不能靠“多读几个字节”修复。
4.3 RPC隔离
RPC Parcel 的对象位置和 session 由 RPC fields 管理;kernel flat_binder_object 不可直接解释为 RPC object。跨 kernel/RPC appendFrom() 返回 BAD_TYPE。
5. 测试边界
5.1 对象扫描
源码文件:frameworks/native/libs/binder/tests/binderParcelUnitTest.cpp
DebugReadAllBinders 写入两个 Binder 和一个 null Binder,断言扫描结果只包含两个非空对象;HasBinders 与 HasBindersInRange 验证对象表存在性和范围判断。
5.2 追加对象
AppendWithBinder 验证追加后两个 Parcel 对象都能读取且 objectsCount()==2;AppendWithBinderPartial 和 AppendFromPartialObjectList 验证部分范围只复制对应对象,错误截断不会凭空补全对象列表。
5.3 可执行阅读
rg -n "writeStrongBinder|flattenBinder|BINDER_TYPE_BINDER|BINDER_TYPE_HANDLE" \
frameworks/native/libs/binder/Parcel.cpp
rg -n "readStrongBinder|readNullableStrongBinder|unflattenBinder|finishUnflattenBinder" \
frameworks/native/libs/binder/Parcel.cpp
rg -n "DebugReadAllBinders|HasBinders|AppendWithBinder|PartialObjectList" \
frameworks/native/libs/binder/tests/binderParcelUnitTest.cpp排查 Binder 参数为空时,先确认写端对象是本地、远程还是 RPC,再检查 offsets 是否登记,读取端使用的是 nullable 还是 non-null 重载,最后沿 handle 到 getStrongProxyForHandle();不要只检查 Parcel 的 dataSize()。
