Binder对象编码
flat_binder_object 不是任意 Binder 元数据的总称。UAPI 还定义 binder_fd_object、 binder_buffer_object 和 binder_fd_array_object:它们共享 binder_object_header,但字段布局、 对象大小和驱动 translator 都不同。
本文面向已经读过 Binder数据转换链 的读者,专门回答 type 如何 决定 union 字段、Parcel 如何记录对象 offset、驱动如何改写 Binder/handle/fd,以及接收端如何 恢复本地对象或代理。本文不展开 transaction_data、allocator 页算法和 AIDL 编码。
1. 对象类型
公共header
源码文件:kernel/common/include/uapi/linux/android/binder.h
struct binder_object_header {
__u32 type;
};
#define B_PACK_CHARS(c1, c2, c3, c4) \
((((c1) << 24)) | (((c2) << 16)) | \
(((c3) << 8)) | (c4))
#define B_TYPE_LARGE 0x85
enum {
BINDER_TYPE_BINDER =
B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_BINDER =
B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
BINDER_TYPE_HANDLE =
B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE =
B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_FD =
B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
BINDER_TYPE_FDA =
B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
BINDER_TYPE_PTR =
B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};type 是协议标签,驱动用它选择实际结构大小和转换函数。strong/weak、binder/handle 等语义编码 在常量中,但不能只靠名字决定对象 owner,还要结合当前进程和 transaction 方向。
结构分流
| type | 结构 | 主要字段 | 驱动处理 |
|---|---|---|---|
| BINDER/WEAK_BINDER | flat_binder_object | binder、cookie | translate_binder |
| HANDLE/WEAK_HANDLE | flat_binder_object | handle | translate_handle |
| FD | binder_fd_object | fd、cookie | translate_fd |
| PTR | binder_buffer_object | buffer、length、parent | buffer fixup |
| FDA | binder_fd_array_object | num_fds、parent | translate_fd_array |
2. flat布局
union解释
源码文件:kernel/common/include/uapi/linux/android/binder.h
struct flat_binder_object {
struct binder_object_header hdr;
__u32 flags;
union {
binder_uintptr_t binder;
__u32 handle;
};
binder_uintptr_t cookie;
};| type | union 有效字段 | cookie |
|---|---|---|
| BINDER/WEAK_BINDER | 本地 weak-ref 指针 | 本地 BBinder 指针 |
| HANDLE/WEAK_HANDLE | 当前进程 handle | 0 |
cookie 不能作为跨进程地址使用。只有 node 所属进程会收到驱动恢复后的 ptr/cookie;其他进程 看到的是各自 binder_ref 的 handle。
扩展结构
源码文件:kernel/common/include/uapi/linux/android/binder.h
struct binder_fd_object {
struct binder_object_header hdr;
__u32 pad_flags;
union {
binder_uintptr_t pad_binder;
__u32 fd;
};
binder_uintptr_t cookie;
};
struct binder_buffer_object {
struct binder_object_header hdr;
__u32 flags;
binder_uintptr_t buffer;
binder_size_t length;
binder_size_t parent;
binder_size_t parent_offset;
};
struct binder_fd_array_object {
struct binder_object_header hdr;
__u32 pad;
binder_size_t num_fds;
binder_size_t parent;
binder_size_t parent_offset;
};FD 布局为兼容旧用户空间保留 padding;PTR/FDA 描述 scatter-gather 关系。它们不是 flat object union 的额外分支。
3. flags字段
节点属性
源码文件:kernel/common/include/uapi/linux/android/binder.h
enum flat_binder_object_flags {
FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
FLAT_BINDER_FLAG_SCHED_POLICY_MASK =
3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT,
FLAT_BINDER_FLAG_INHERIT_RT = 0x800,
FLAT_BINDER_FLAG_TXN_SECURITY_CTX = 0x1000,
};flat flags 描述 node 的 fd、调度和安全上下文属性,不是 transaction flags。TF_ONE_WAY、 TF_STATUS_CODE 等属于 binder_transaction_data.flags。
Node生效
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_init_node_ilocked()
priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->sched_policy =
(flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK)
>> FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
node->min_priority =
to_kernel_prio(node->sched_policy, priority);
node->accept_fds =
!!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
node->inherit_rt =
!!(flags & FLAT_BINDER_FLAG_INHERIT_RT);
node->txn_security_ctx =
!!(flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX);这些位在 node 创建时成为内核状态,影响后续事务;不是服务方法每次动态读取的业务参数。
4. Parcel编码
本地与远程
源码文件:frameworks/native/libs/binder/Parcel.cpp
相关函数:flattenBinder()
if (binder != nullptr) {
if (BBinder* local = binder->localBinder()) {
obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (local->isRequestingSid())
obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
if (local->isInheritRt())
obj.flags |= FLAT_BINDER_FLAG_INHERIT_RT;
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(
local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
} else {
BpBinder* proxy = binder->remoteBinder();
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0;
obj.handle =
proxy->getPrivateAccessor().binderHandle();
obj.cookie = 0;
}
}本地实体编码 ptr/cookie,远程代理编码 handle。Parcel 不复制 C++ 对象,只写驱动可转换的引用。
fd编码
源码文件:frameworks/native/libs/binder/Parcel.cpp
flat_binder_object object;
object.hdr.type = BINDER_TYPE_FD;
object.flags = 0;
object.binder = 0;
object.handle = fd;
object.cookie = takeOwnership ? 1 : 0;
return writeObject(object, true);Parcel 为兼容布局使用 flat_binder_object 临时变量写 fd object;驱动仍按 binder_fd_object 解释。 cookie 在用户态表示 fd ownership,不是服务端对象指针。
offsets索引
源码文件:frameworks/native/libs/binder/Parcel.cpp
相关函数:writeObject()
*reinterpret_cast<flat_binder_object*>(
mData + mDataPos) = value;
if (value.hdr.type == BINDER_TYPE_FD) {
if (!mAllowFds)
return FDS_NOT_ALLOWED;
fields->mHasFds = fields->mFdsKnown = true;
}
if (nullMetaData || value.binder != 0) {
fields->mObjects[fields->mObjectsSize] =
mDataPos;
acquire_object(ProcessState::self(),
value, this, true);
fields->mObjectsSize++;
}对象本体进入 data,起始位置进入 objects offsets。没有 offset 的同型字节不会被驱动识别为对象。
5. Parcel持有
Acquire
源码文件:frameworks/native/libs/binder/Parcel.cpp
相关函数:acquire_object()
case BINDER_TYPE_BINDER:
if (obj.binder)
reinterpret_cast<IBinder*>(
obj.cookie)->incStrong(who);
return;
case BINDER_TYPE_HANDLE: {
const sp<IBinder> binder =
proc->getStrongProxyForHandle(obj.handle);
if (binder != nullptr)
binder->incStrong(who);
return;
}
case BINDER_TYPE_FD:
if (tagFds && obj.cookie != 0)
FdTag(obj.handle, nullptr, who);
return;Parcel 对本地对象、远程代理和 owned fd 建立不同的持有关系。release_object 反向递减引用或 关闭 owned fd。对象元数据不仅影响传输,也影响发送方 Parcel 自身生命周期。
6. 驱动识别
大小校验
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_get_object()
read_size = min_t(size_t,
sizeof(*object),
buffer->data_size - offset);
if (offset > buffer->data_size ||
read_size < sizeof(*hdr) ||
!IS_ALIGNED(offset, sizeof(u32)))
return 0;
switch (object->hdr.type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER:
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE:
object_size = sizeof(struct flat_binder_object);
break;
case BINDER_TYPE_FD:
object_size = sizeof(struct binder_fd_object);
break;
case BINDER_TYPE_PTR:
object_size = sizeof(struct binder_buffer_object);
break;
case BINDER_TYPE_FDA:
object_size = sizeof(struct binder_fd_array_object);
break;
default:
return 0;
}驱动先验证 offset、对齐、header 和完整结构大小,再执行 translator。未知 type 或截断对象在 业务服务执行前失败。
Binder转handle
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_translate_binder()
node = binder_get_node(proc, fp->binder);
if (!node)
node = binder_new_node(proc, fp);
if (fp->cookie != node->cookie)
return -EINVAL;
ret = binder_inc_ref_for_node(
target_proc, node,
fp->hdr.type == BINDER_TYPE_BINDER,
&thread->todo, &rdata);
fp->hdr.type = fp->hdr.type == BINDER_TYPE_BINDER
? BINDER_TYPE_HANDLE
: BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
fp->handle = rdata.desc;
fp->cookie = 0;驱动用 ptr 查 node,并校验 cookie;随后在目标 proc 创建或增加 ref,把对象改写成目标进程 handle。
Handle转对象
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_translate_handle()
node = binder_get_node_from_ref(
proc, fp->handle,
fp->hdr.type == BINDER_TYPE_HANDLE,
&src_rdata);
if (!node)
return -EINVAL;
if (node->proc == target_proc) {
fp->hdr.type =
fp->hdr.type == BINDER_TYPE_HANDLE
? BINDER_TYPE_BINDER
: BINDER_TYPE_WEAK_BINDER;
fp->binder = node->ptr;
fp->cookie = node->cookie;
} else {
binder_inc_ref_for_node(
target_proc, node,
fp->hdr.type == BINDER_TYPE_HANDLE,
NULL, &dest_rdata);
fp->binder = 0;
fp->handle = dest_rdata.desc;
fp->cookie = 0;
}目标正是 node owner 时恢复 ptr/cookie;否则生成目标 proc 自己的 handle。相同 node 在不同进程 中的 handle 不要求相同。
7. 其他对象
fd转换
源码文件:kernel/common/drivers/android/binder.c
case BINDER_TYPE_FD: {
struct binder_fd_object* fdObject =
to_binder_fd_object(hdr);
binder_size_t fdOffset =
object_offset +
(uintptr_t)&fdObject->fd -
(uintptr_t)fdObject;
ret = binder_translate_fd(
fdObject->fd, fdOffset,
transaction, thread,
in_reply_to);
fdObject->pad_binder = 0;
break;
}fd 整数不能原样跨进程。驱动保存 struct file,并在目标 task 上下文安装新 fd;目标 fd 数字可能 不同。
PTR与FDA
PTR 表示额外 buffer,驱动复制其内容并把 buffer 地址修正为目标用户地址;HAS_PARENT 要求修正 父 buffer 内部的指针。FDA 指向 parent buffer 中的 fd 数组,驱动验证 parent、offset 和长度后 逐个安装 fd。两者的失败回滚比 flat object 更复杂。
8. 接收恢复
unflatten
源码文件:frameworks/native/libs/binder/Parcel.cpp
相关函数:unflattenBinder()
const flat_binder_object* flat =
readObject(false);
if (!flat)
return BAD_TYPE;
switch (flat->hdr.type) {
case BINDER_TYPE_BINDER:
return finishUnflattenBinder(
sp<IBinder>::fromExisting(
reinterpret_cast<IBinder*>(
flat->cookie)), out);
case BINDER_TYPE_HANDLE:
return finishUnflattenBinder(
ProcessState::self()
->getStrongProxyForHandle(
flat->handle), out);
default:
return BAD_TYPE;
}接收端读取的是驱动转换后的 type。本地 ptr/cookie 恢复原对象;handle 进入 ProcessState 代理缓存。
外部视图
ipcSetDataReference 还会验证 offsets 单调递增,并只接受用户态支持的 BINDER、HANDLE 和 FD 类型。 PTR/FDA 由驱动处理其嵌套数据,但普通 libbinder Parcel 不把它们暴露成可直接 readObject 的对象。
9. 失败回滚
Cookie与权限
cookie 不匹配返回 EINVAL;security_binder_transfer_binder 拒绝时返回 EPERM;无效 handle 也会 失败。三者都发生在服务实现执行前。
已处理范围
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction_buffer_release()
for (buffer_offset = off_start_offset;
buffer_offset < off_end_offset;
buffer_offset += sizeof(binder_size_t)) {
binder_alloc_copy_from_buffer(
&proc->alloc, &object_offset,
buffer, buffer_offset,
sizeof(object_offset));
object_size = binder_get_object(
proc, NULL, buffer,
object_offset, &object);
switch (object.hdr.type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER:
binder_dec_node(node,
object.hdr.type ==
BINDER_TYPE_BINDER, 0);
break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE:
binder_dec_ref_for_handle(
proc, object.fbo.handle,
object.hdr.type ==
BINDER_TYPE_HANDLE,
&rdata);
break;
}
}驱动只回滚已成功处理的 offsets 范围,避免释放尚未取得所有权的对象。
10. 测试输入
offsets边界
源码文件:frameworks/native/libs/binder/tests/binderParcelUnitTest.cpp
相关测试:AppendWithBinderPartial()、AppendFromPartialObjectList()、 WriteObjectUnsortedThenValidate()、AppendOverObject()
测试覆盖四种输入:只复制 Binder 对象的一部分、只复制对象列表中的一个完整对象、故意写出失序 offsets,以及尝试覆盖 fd object 区域。断言分别检查 object count、读回对象和 PERMISSION_DENIED。 它们证明 data 与 offsets 必须一起维护。
非法PTR
源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp
相关测试:BinderLibTest.BufRejected()
binder_buffer_object object {
.hdr = { .type = BINDER_TYPE_PTR },
.flags = 0,
.buffer = reinterpret_cast<
binder_uintptr_t>(&buffer),
.length = 4,
};
data.writeFileDescriptor(0);
memcpy(const_cast<uint8_t*>(
data.data()), &object,
sizeof(object));
data.setDataSize(sizeof(object));
EXPECT_THAT(server->transact(
BINDER_LIB_TEST_REJECT_OBJECTS,
data, &reply),
Not(StatusEq(NO_ERROR)));测试借 fd object 建立合法 offset,再把 data 覆盖成未经合法 parent/SG 构造的 PTR object,断言 事务不能成功。type 正确并不足以证明对象合法。
11. 阅读边界
类型分流图
flat_binder_object.hdr.type 决定驱动后续进入哪条转换函数;flags 只补充 node 行为,不能把一种对象伪装成另一种对象。
读者应先从 offsets 找对象,再按 type 选择转换函数,最后检查目标资源和失败回滚;不能只看 Parcel 中的原始 handle 或 cookie 数值。
本文证明了 header、type、flat union、flags、Parcel offsets、node/ref/handle/fd/PTR/FDA 转换、 接收恢复和失败回滚。没有展开 transaction_data 全字段、allocator、AIDL 序列化、死亡通知、 线程池、RPC Binder 和 SELinux。
下一篇 BD008 将深入 binder_transaction_data。复查对象问题时,沿 offset、type、object size、translator、target rewrite、cleanup 的顺序走源码。
