Skip to content

writeStrongBinder与readStrongBinder

追踪强 Binder 写入、BINDER_TYPE_BINDER/HANDLE 编码、对象表登记、代理重建与读取失败路径。

基于android-17.0.0_r1
AndroidBinderParcelStrongBinder源码阅读

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

cpp
status_t Parcel::writeStrongBinder(const sp<IBinder>& val) {
    return flattenBinder(val);
}

真正的差异在 flattenBinder():它先判断 Parcel 是 kernel Binder 还是 RPC,再决定对象格式和所有权。

1.2 本地对象 ​

cpp
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 远程代理 ​

cpp
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

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 本地类型 ​

cpp
case BINDER_TYPE_BINDER: {
    sp<IBinder> binder = sp<IBinder>::fromExisting(
            reinterpret_cast<IBinder*>(flat->cookie));
    return finishUnflattenBinder(binder, out);
}

本地类型只在拥有该对象的进程上下文中有意义;读取 cookie 仍通过 fromExisting 建立引用,不把它转换为陌生进程地址。

3.3 Handle类型 ​

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

bash
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()。