Skip to content

事务数据生命周期

追踪 binder_transaction_data 在请求、驱动投递、服务端接收、同步 reply 和失败返回中的字段语义与 owner 变化。

基于android-17.0.0_r1
AndroidBinderbinder_transaction_dataIPC源码阅读

事务数据生命周期 ​

binder_transaction_data 看起来只是一个结构体,但同一字段在发送、驱动投递和 reply 阶段并不 拥有同一种含义。target.handle 是客户端发出的引用描述,服务端收到的则是 target.ptr/cookie; sender_pid/euid 由驱动填写,不能相信发送方初始化的零值;data.ptr 既可以指向发送方 Parcel, 也可以指向目标进程映射的 buffer。

本文面向已经读过 Binder协议状态机 和 Binder对象编码 的读者,专门沿真实源码回答字段由谁填写、何时改写、谁读取、失败时如何解释。本文不展开 allocator、完整对象 translator 和 AIDL 参数编码。

1. 三种形态 ​

核心结构 ​

源码文件:kernel/common/include/uapi/linux/android/binder.h

c
struct binder_transaction_data {
    union {
        __u32 handle;
        binder_uintptr_t ptr;
    } target;
    binder_uintptr_t cookie;
    __u32 code;
    __u32 flags;
    __kernel_pid_t sender_pid;
    __kernel_uid32_t sender_euid;
    binder_size_t data_size;
    binder_size_t offsets_size;
    union {
        struct {
            binder_uintptr_t buffer;
            binder_uintptr_t offsets;
        } ptr;
        __u8 buf[8];
    } data;
};

扩展结构 ​

源码文件:kernel/common/include/uapi/linux/android/binder.h

c
struct binder_transaction_data_secctx {
    struct binder_transaction_data transaction_data;
    binder_uintptr_t secctx;
};

struct binder_transaction_data_sg {
    struct binder_transaction_data transaction_data;
    binder_size_t buffers_size;
};

secctx 只在 BR_TRANSACTION_SEC_CTX 形态附加;buffers_size 只在 SG 命令形态附加。不能把 扩展尾部当作普通结构总是存在。

字段阶段表 ​

字段BC请求BR交付BC replyBR reply
targethandleptr/cookiehandle=-1,仅作占位ptr/cookie 为 0
code调用码原调用码00
flags用户态事务标志驱动保留/补充reply flagsstatus/oneway 等
sender_pid/euid置零驱动填写置零驱动生成,不作为业务调用方身份
data.ptr发送 Parcel 地址目标 buffer 地址reply Parcel 地址reply buffer 地址
offsets发送方对象表目标 buffer 对象表reply 对象表reply 对象表

2. 请求填充 ​

请求字段 ​

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

相关函数:writeTransactionData()

cpp
status_t IPCThreadState::writeTransactionData(int32_t cmd,
        uint32_t binderFlags, int32_t handle,
        uint32_t code, const Parcel& data,
        status_t* statusBuffer) {
    binder_transaction_data tr;
    tr.target.ptr = 0;
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;

    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size =
                data.ipcObjectsCount()
                * sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    }
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
    return NO_ERROR;
}

发送端只提供 handle、code、flags、data/offsets 用户地址和大小。sender 身份字段被清零,target ptr 也被置零;这两个零值都尚未成为接收方语义。

status分支 ​

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

cpp
if (err != NO_ERROR && statusBuffer) {
    tr.flags |= TF_STATUS_CODE;
    *statusBuffer = err;
    tr.data_size = sizeof(status_t);
    tr.data.ptr.buffer =
            reinterpret_cast<uintptr_t>(statusBuffer);
    tr.offsets_size = 0;
    tr.data.ptr.offsets = 0;
} else if (err != NO_ERROR) {
    return mLastError = err;
}

TF_STATUS_CODE 让 data 区从普通 Parcel 变成单个 status_t;接收方必须先检查 flags,再决定 是否建立普通 reply 视图。

3. 驱动填充 ​

事务状态 ​

源码文件:kernel/common/drivers/android/binder.c

c
t->from_pid = proc->pid;
t->from_tid = thread->pid;
t->sender_euid = task_euid(proc->tsk);
t->code = tr->code;
t->flags = tr->flags;
t->is_async = !reply &&
        (tr->flags & TF_ONE_WAY);
t->is_reply = reply;

内核事务保存发送进程、线程、code、flags 和同步/异步状态。用户写入的 sender_pid/euid 不参与 身份来源。

目标选择 ​

源码文件:kernel/common/drivers/android/binder.c

c
if (reply) {
    in_reply_to = thread->transaction_stack;
    target_thread =
            binder_get_txn_from_and_acq_inner(
                    in_reply_to);
} else {
    ref = binder_get_ref_olocked(
            proc, tr->target.handle, true);
    target_node =
            binder_get_node_refs_for_txn(
                    ref->node,
                    &target_proc,
                    &return_error);
}

新请求从 target.handle 查 ref/node;reply 从当前线程 transaction_stack 找回原调用方。 reply 阶段不能再把 target.handle 当普通服务查找入口。

buffer与身份 ​

源码文件:kernel/common/drivers/android/binder.c

c
t->buffer = binder_alloc_new_buf(
        &target_proc->alloc,
        tr->data_size,
        tr->offsets_size,
        extra_buffers_size,
        !reply && (tr->flags & TF_ONE_WAY));

trd->sender_euid =
        from_kuid(current_user_ns(),
                  t->sender_euid);
trd->sender_pid =
        t_from ? task_tgid_nr_ns(
                t_from->proc->tsk,
                task_active_pid_ns(current))
               : 0;

驱动把 data/offsets 尺寸用于目标 buffer 分配,并在 BR_TRANSACTION 中填写接收方可见的 sender 身份。服务端读取的 sender_pid/euid 来自内核事务,不是发送方 Parcel 字段。

4. 服务接收 ​

BR_TRANSACTION ​

源码文件:kernel/common/drivers/android/binder.c

c
if (t->buffer->target_node) {
    trd->target.ptr =
            t->buffer->target_node->ptr;
    trd->cookie =
            t->buffer->target_node->cookie;
    cmd = BR_TRANSACTION;
} else {
    trd->target.ptr = 0;
    trd->cookie = 0;
    cmd = BR_REPLY;
}
trd->code = t->code;
trd->flags = t->flags;
trd->data_size = t->buffer->data_size;
trd->offsets_size =
        t->buffer->offsets_size;
trd->data.ptr.buffer =
        t->buffer->user_data;

服务端收到的 target 是 ptr/cookie,data.ptr.buffer 指向目标进程映射的 buffer;这些字段由驱动 生成。BR_REPLY 的目标由事务栈确定。

secctx与SG ​

若 node 设置 TXN_SECURITY_CTX,驱动返回 BR_TRANSACTION_SEC_CTX 并附加 secctx;SG 命令使用 binder_transaction_data_sg 并增加 buffers_size。读取命令时必须先知道命令形态,再决定结构大小。

5. 服务读取 ​

Parcel视图 ​

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

相关函数:executeCommand()

cpp
Parcel buffer;
buffer.ipcSetDataReference(
        reinterpret_cast<const uint8_t*>(
                tr.data.ptr.buffer),
        tr.data_size,
        reinterpret_cast<const binder_size_t*>(
                tr.data.ptr.offsets),
        tr.offsets_size /
                sizeof(binder_size_t),
        freeBuffer);

服务端 Parcel 不复制 data,而是把 transaction_data 的 buffer/offsets 作为外部视图。freeBuffer 连接 Parcel 生命周期与内核 buffer 释放。

身份恢复 ​

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

cpp
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
mLastTransactionBinderFlags = tr.flags;

服务线程在进入 onTransact 前保存调用方 pid/uid 和 flags;嵌套调用结束后恢复旧身份。

6. Reply形态 ​

BC_REPLY ​

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

相关函数:sendReply()

cpp
status_t IPCThreadState::sendReply(
        const Parcel& reply, uint32_t flags) {
    status_t statusBuffer;
    status_t err = writeTransactionData(
            BC_REPLY, flags, -1, 0,
            reply, &statusBuffer);
    if (err < NO_ERROR)
        return err;
    return waitForResponse(
            nullptr, nullptr);
}

BC_REPLY 明确写入 handle=-1、code=0;它们不表示原业务目标。驱动通过服务线程 transaction_stack 找到等待客户端。

BR_REPLY ​

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

cpp
case BR_REPLY: {
    binder_transaction_data tr;
    err = mIn.read(&tr, sizeof(tr));
    if ((tr.flags & TF_STATUS_CODE) == 0) {
        reply->ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(
                        tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const binder_size_t*>(
                        tr.data.ptr.offsets),
                tr.offsets_size /
                        sizeof(binder_size_t),
                freeBuffer);
    } else {
        err = *reinterpret_cast<
                const status_t*>(tr.data.ptr.buffer);
        freeBuffer(
                reinterpret_cast<const uint8_t*>(
                        tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const binder_size_t*>(
                        tr.data.ptr.offsets),
                tr.offsets_size /
                        sizeof(binder_size_t));
    }
    goto finish;
}

BR_REPLY 可能承载普通 Parcel,也可能承载 TF_STATUS_CODE 的错误值。先检查 flags,再决定解析 对象还是读取 status_t。

7. flags边界 ​

flags结构生效者影响
TF_ONE_WAYtransaction_data驱动/服务线程无业务 reply
TF_STATUS_CODEtransaction_dataIPCThreadStatedata 解释为 status
TF_ACCEPT_FDStransaction_data驱动fd 转换许可
TF_CLEAR_BUFtransaction_data驱动完成后清理 buffer
TF_UPDATE_TXNtransaction_data驱动合并异步事务
FLAT_BINDER_FLAG_*flat objectnode节点属性

8. 测试输入 ​

驱动序列 ​

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

相关测试:Transaction()

测试构造 BC_TRANSACTION + PING_TRANSACTION,允许非阻塞第一次读取 EAGAIN,再检查 BR_NOOP、BR_TRANSACTION_COMPLETE、BR_REPLY 和 reply 字段。它证明结构阶段和命令顺序,不证明 业务 AIDL 返回值。

大小边界 ​

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

相关测试:VectorSent()、GargantuanVectorSent()、LimitExceededVectorSent()

测试用小 vector 验证 round trip,用接近 buffer 上限的 vector 验证成功,再用更大的 vector 断言 FAILED_TRANSACTION。测试数字是输入,不是所有设备的固定上限。

身份字段 ​

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

相关测试:GetCallingUidWithSetuid()、GotSid()、WorkSourcePropagatedForAllFollowingBinderCalls()

这些测试检查 calling uid、安全上下文和 work source 恢复/传播。它们证明字段由驱动和 IPCThreadState 上下文交付,不证明所有服务都开启 security context 或 work source 传播。

9. 失败边界 ​

非法目标 ​

无效 handle 或死亡 node 会在 target 解析处失败,服务端不会执行 onTransact。

数据复制 ​

data/offsets 用户指针不可访问、尺寸溢出或对象 offset 非法,会产生 BR_FAILED_REPLY;失败事务 的 buffer 和已转换对象需要回滚。

身份误读 ​

BC 请求中的 sender_pid/euid 为零不是匿名调用,而是等待内核覆盖的未生效字段;只有服务端收到 BR_TRANSACTION 后,它们才是可用于调用方判断的驱动身份。

10. 阅读边界 ​

字段生命周期 ​

同一个 binder_transaction_data 在发送、驱动内部和接收阶段字段含义不同。下面的时序突出“谁写入、谁消费、 何时失效”,正文各源码段可按 debug id 对照。

发送端的用户指针在驱动复制完成后不再是接收端数据地址;接收端 trd.data.ptr.buffer 指向目标映射。reply 和释放分别结束事务状态与 buffer 状态,不能用一个“完成”时间替代两者。

本文证明了 binder_transaction_data 的字段生命周期、target 双语义、身份来源、data/offsets 指针、secctx/SG 扩展、reply/status 分支和测试边界。没有展开完整对象 translator、allocator、 AIDL 序列化、死亡通知、线程池、RPC Binder 或 SELinux。

继续阅读 Binder数据转换链 复习 data/offsets owner,再读 Binder协议状态机 对照 BC/BR 命令状态。