Skip to content

Binder事务发送

追踪 binder_transaction 的目标解析、事务对象、目标 buffer、对象翻译、入队唤醒和失败清理。

基于android-17.0.0_r1
AndroidBindertransaction驱动源码阅读

Binder事务发送 ​

binder_transaction() 是 Binder 驱动把一条 BC_TRANSACTION 或 BC_REPLY 变成目标进程 work 的主函数。它不是简单的“复制数据”:函数必须先确定目标 node/proc/thread,再为目标 allocator 建立 buffer,校验并翻译 offsets、Binder object、fd 和 pointer object,最后按同步或 oneway 语义排队。

本文面向已经读过 BINDER_WRITE_READ命令、Binder数据转换链、事务数据生命周期 的读者,回答一次事务从 BC descriptor 到目标 work 的源码路径。

本文不重复讲完整的 BC/BR 字节循环,不把 binder_node、binder_ref 或 allocator 拆成独立参考手册;这些对象只在事务主线真正改变 owner 或状态的位置出现。事务成功不等于服务已执行,成功发送只表示目标 work 已排队或进入 pending 状态。

1. 两种方向 ​

输入reply目标来源发送方状态
BC_TRANSACTION0handle 或 context manager node同步请求入 transaction stack;oneway 不入
BC_REPLY1当前线程 transaction stack 的原发送线程弹出当前 reply 栈项

2. 事务对象 ​

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

相关函数:binder_transaction()

c
static void binder_transaction(
        struct binder_proc *proc,
        struct binder_thread *thread,
        struct binder_transaction_data *tr,
        int reply,
        binder_size_t extra_buffers_size) {
    struct binder_transaction *t;
    struct binder_work *w;
    struct binder_work *tcomplete;
    struct binder_proc *target_proc = NULL;
    struct binder_thread *target_thread = NULL;
    struct binder_node *target_node = NULL;
    struct binder_transaction *in_reply_to = NULL;
    uint32_t return_error = 0;
    struct list_head sgc_head;
    struct list_head pf_head;
    const void __user *user_buffer =
        (const void __user *)(uintptr_t)
            tr->data.ptr.buffer;

    INIT_LIST_HEAD(&sgc_head);
    INIT_LIST_HEAD(&pf_head);
    /* 记录 transaction log,随后初始化 t */
}

t 是内核事务 owner,t->buffer 将属于目标 proc 的 allocator;tcomplete 是发送方收到的完成 work。sgc_head 保存 scatter-gather 延迟复制,pf_head 保存父对象 fixup。它们必须在错误标签中一并释放,不能只释放 t。

源码文件:kernel/common/drivers/android/binder_internal.h

相关结构:struct binder_transaction

c
struct binder_transaction {
    int debug_id;
    struct binder_work work;
    struct binder_thread *from;
    struct binder_proc *to_proc;
    struct binder_thread *to_thread;
    struct binder_transaction *from_parent;
    struct binder_transaction *to_parent;
    unsigned is_async:1;
    unsigned is_reply:1;
    struct binder_buffer *buffer;
    unsigned int code;
    unsigned int flags;
    struct binder_priority priority;
    kuid_t sender_euid;
    struct list_head fd_fixups;
    spinlock_t lock;
};

同步请求使用 from 和 from_parent 维护回复链;目标接收后通过 to_thread、to_parent 维护接收栈。oneway 没有发送方等待 reply 的 from 链,reply 事务则通过 in_reply_to 找回原请求。

3. 目标解析 ​

3.1 Reply路径 ​

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

相关函数:binder_transaction()、binder_get_txn_from_and_acq_inner()

c
if (reply) {
    binder_inner_proc_lock(proc);
    in_reply_to = thread->transaction_stack;
    if (!in_reply_to) {
        binder_inner_proc_unlock(proc);
        return_error = BR_FAILED_REPLY;
        return_error_param = -EPROTO;
        goto err_empty_call_stack;
    }
    if (in_reply_to->to_thread != thread) {
        binder_inner_proc_unlock(proc);
        return_error = BR_FAILED_REPLY;
        return_error_param = -EPROTO;
        goto err_bad_call_stack;
    }
    thread->transaction_stack =
            in_reply_to->to_parent;
    binder_inner_proc_unlock(proc);

    target_thread =
        binder_get_txn_from_and_acq_inner(
            in_reply_to);
    if (!target_thread) {
        return_error = BR_DEAD_REPLY;
        goto err_dead_binder;
    }
    target_proc = target_thread->proc;
    target_proc->tmp_ref++;
    binder_inner_proc_unlock(target_proc);
}

reply 不是根据 tr->target.handle 查找目标。当前线程必须有 transaction stack,且栈顶事务的 to_thread 必须正是当前 thread;检查通过后弹出当前栈项,再从原请求的 from 找回发送线程。发送线程死亡返回 BR_DEAD_REPLY,栈关系错误返回 BR_FAILED_REPLY/-EPROTO。

3.2 Handle路径 ​

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

相关函数:binder_transaction()、binder_get_node_refs_for_txn()

c
if (!reply && tr->target.handle) {
    struct binder_ref *ref;

    binder_proc_lock(proc);
    ref = binder_get_ref_olocked(
            proc, tr->target.handle, true);
    if (ref) {
        target_node =
            binder_get_node_refs_for_txn(
                ref->node, &target_proc,
                &return_error);
    } else {
        binder_user_error(
            "%d:%d invalid handle\n",
            proc->pid, thread->pid);
        return_error = BR_FAILED_REPLY;
    }
    binder_proc_unlock(proc);
}

handle 只属于发送方 proc。驱动先在发送方的 ref 红黑树找到 binder_ref,再从 ref 指向 node;binder_get_node_refs_for_txn() 同时增加 node 临时引用和目标 proc 临时引用,保证构造事务期间目标不会被 release。

3.3 Context路径 ​

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

相关函数:binder_transaction()

c
if (!reply && !tr->target.handle) {
    mutex_lock(
        &context->context_mgr_node_lock);
    target_node =
        context->binder_context_mgr_node;
    if (target_node) {
        target_node =
            binder_get_node_refs_for_txn(
                target_node, &target_proc,
                &return_error);
    } else {
        return_error = BR_DEAD_REPLY;
    }
    mutex_unlock(
        &context->context_mgr_node_lock);
}

handle 0 是 context manager 的特殊入口。若目标不存在,返回 BR_DEAD_REPLY;若当前 proc 就是 context manager 所属 proc,驱动拒绝 self transaction。随后还要执行 security_binder_transaction(proc->cred, target_proc->cred),权限失败返回 BR_FAILED_REPLY/-EPERM。

3.4 同步嵌套 ​

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

相关函数:binder_transaction()

c
if (!reply && !(tr->flags & TF_ONE_WAY) &&
        thread->transaction_stack) {
    struct binder_transaction *tmp =
            thread->transaction_stack;

    while (tmp) {
        struct binder_thread *from;
        spin_lock(&tmp->lock);
        from = tmp->from;
        if (from && from->proc == target_proc) {
            atomic_inc(&from->tmp_ref);
            target_thread = from;
            is_nested = true;
            spin_unlock(&tmp->lock);
            break;
        }
        spin_unlock(&tmp->lock);
        tmp = tmp->from_parent;
    }
}

同步嵌套调用可能直接定位到调用栈上关联的发送线程,而不是总是从目标 proc 的 waiting_threads 选择线程。is_nested 会进入后续 priority 和栈处理,不能把所有同步事务都当成普通 proc work。

4. 初始化与优先级 ​

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

相关函数:binder_transaction()

c
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (!t) {
    return_error = BR_FAILED_REPLY;
    return_error_param = -ENOMEM;
    goto err_alloc_t_failed;
}
INIT_LIST_HEAD(&t->fd_fixups);
spin_lock_init(&t->lock);
binder_stats_created(
        BINDER_STAT_TRANSACTION);
t->debug_id = t_debug_id;
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->work.type = BINDER_WORK_TRANSACTION;
t->is_async = !reply &&
        (tr->flags & TF_ONE_WAY);
t->is_reply = reply;
if (!reply && !(tr->flags & TF_ONE_WAY))
    t->from = thread;

同步请求设置 t->from,oneway 和 reply 不设置。发送方 UID 来自打开 proc 记录的 group leader task,而不是用户在 descriptor 中可任意填写的字段。

c
tcomplete = kzalloc(
        sizeof(*tcomplete), GFP_KERNEL);
if (!tcomplete) {
    return_error = BR_FAILED_REPLY;
    return_error_param = -ENOMEM;
    goto err_alloc_tcomplete_failed;
}
binder_stats_created(
        BINDER_STAT_TRANSACTION_COMPLETE);

if (!reply && !(t->flags & TF_ONE_WAY) &&
        binder_supported_policy(current->policy)) {
    t->priority.sched_policy =
            current->policy;
    t->priority.prio = current->prio;
} else {
    t->priority = target_proc->default_priority;
}

同步事务继承支持的发送线程调度策略;oneway 或不支持的策略使用目标 proc 默认优先级。tcomplete 的创建和 priority 选择都发生在 buffer 分配前,失败时要沿对应标签释放。

5. 目标Buffer ​

5.1 安全上下文 ​

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

相关函数:binder_transaction()

c
if (target_node &&
        target_node->txn_security_ctx) {
    ret = security_secid_to_secctx(
            secid, &lsmctx);
    if (ret < 0) {
        return_error = BR_FAILED_REPLY;
        return_error_param = ret;
        goto err_get_secctx_failed;
    }
    added_size = ALIGN(
            lsmctx.len, sizeof(u64));
    extra_buffers_size += added_size;
    if (extra_buffers_size < added_size) {
        return_error = BR_FAILED_REPLY;
        return_error_param = -EINVAL;
        goto err_bad_extra_size;
    }
}

目标 node 请求 security context 时,驱动把发送方 cred 转成字符串并把对齐后的长度加入 extra buffer。整数溢出和 secctx 获取失败在 allocator 之前终止;security context 的释放也属于失败清理的一部分。

5.2 Allocator申请 ​

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

相关函数:binder_alloc_new_buf()

c
t->buffer = binder_alloc_new_buf(
        &target_proc->alloc,
        tr->data_size,
        tr->offsets_size,
        extra_buffers_size,
        !reply && (t->flags & TF_ONE_WAY));
if (IS_ERR(t->buffer)) {
    ret = PTR_ERR(t->buffer);
    return_error_param = ret;
    return_error = ret == -ESRCH
            ? BR_DEAD_REPLY
            : BR_FAILED_REPLY;
    t->buffer = NULL;
    goto err_binder_alloc_buf_failed;
}

binder_alloc_new_buf() 先确认目标 allocator mapped,再对齐 data/offsets/extra 大小,从 free tree 取区间并按需安装页。-ESRCH 表示目标 VMA 已清除或目标正在死亡,-ENOSPC 表示地址空间/async 空间不足,-ENOMEM 表示分配失败;它们最终转换成发送方可见的 BR 错误语义。

5.3 Buffer owner ​

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

相关函数:binder_transaction()

c
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
t->buffer->target_node = target_node;
t->buffer->clear_on_free =
        !!(t->flags & TF_CLEAR_BUF);

if (lsmctx.context) {
    size_t offset =
        ALIGN(tr->data_size, sizeof(void *)) +
        ALIGN(tr->offsets_size, sizeof(void *)) +
        ALIGN(extra_buffers_size, sizeof(void *)) -
        ALIGN(lsmctx.len, sizeof(u64));
    t->security_ctx =
        t->buffer->user_data + offset;
    binder_alloc_copy_to_buffer(
        &target_proc->alloc, t->buffer,
        offset, lsmctx.context, lsmctx.len);
}

buffer 属于 target_proc 的用户可见地址空间,但 transaction 对象仍由驱动持有。buffer->transaction 防止 allocator 在事务完成前释放它;buffer->target_node 决定读端产生 BR_TRANSACTION 还是 BR_REPLY。

6. 数据与对象 ​

6.1 Offsets复制 ​

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

相关函数:binder_transaction()、binder_alloc_copy_user_to_buffer()

c
if (binder_alloc_copy_user_to_buffer(
        &target_proc->alloc, t->buffer,
        ALIGN(tr->data_size, sizeof(void *)),
        (const void __user *)(uintptr_t)
            tr->data.ptr.offsets,
        tr->offsets_size)) {
    return_error = BR_FAILED_REPLY;
    return_error_param = -EFAULT;
    goto err_copy_data_failed;
}
if (!IS_ALIGNED(tr->offsets_size,
                sizeof(binder_size_t))) {
    return_error = BR_FAILED_REPLY;
    return_error_param = -EINVAL;
    goto err_bad_offset;
}

offsets 区先进入目标 buffer,随后驱动逐个读取 offset,检查单调递增和对象大小,再复制对象前后的普通 payload。offsets 不是直接暴露给接收方的独立内核数组。

6.2 对象循环 ​

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

相关函数:binder_transaction()

c
for (buffer_offset = off_start_offset;
        buffer_offset < off_end_offset;
        buffer_offset += sizeof(binder_size_t)) {
    struct binder_object object;
    binder_size_t object_offset;
    size_t object_size;

    binder_alloc_copy_from_buffer(
        &target_proc->alloc, &object_offset,
        t->buffer, buffer_offset,
        sizeof(object_offset));
    if (object_offset < off_min ||
            object_offset > tr->data_size)
        goto err_bad_offset;

    object_size = binder_get_object(
        target_proc, user_buffer, t->buffer,
        object_offset, &object);
    if (!object_size || object_offset < off_min)
        goto err_bad_offset;
    user_offset = object_offset + object_size;

    switch (object.hdr.type) {
    case BINDER_TYPE_BINDER:
    case BINDER_TYPE_WEAK_BINDER:
        ret = binder_translate_binder(
            &object.fbo, t, thread);
        break;
    case BINDER_TYPE_HANDLE:
    case BINDER_TYPE_WEAK_HANDLE:
        ret = binder_translate_handle(
            &object.fbo, t, thread);
        break;
    case BINDER_TYPE_FD:
        ret = binder_translate_fd(
            object.fdo.fd, object_offset,
            t, thread, in_reply_to);
        break;
    default:
        ret = -EINVAL;
        break;
    }
    if (ret < 0)
        goto err_translate_failed;
}

当前循环不是只检查 offset。它把本地 binder、远端 handle、fd 等对象转换到 target proc 的语义,并把修正后的对象写回目标 buffer;任一种 object 校验或翻译失败都会进入统一回滚。

6.3 Pointer与FDA ​

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

相关函数:binder_transaction()、binder_defer_copy()、binder_fixup_parent()

c
case BINDER_TYPE_PTR: {
    struct binder_buffer_object *bp =
        to_binder_buffer_object(&object.hdr);

    if (bp->length >
            sg_buf_end_offset - sg_buf_offset)
        goto err_bad_offset;
    ret = binder_defer_copy(
        &sgc_head, sg_buf_offset,
        (const void __user *)(uintptr_t)
            bp->buffer, bp->length);
    if (ret)
        goto err_translate_failed;

    bp->buffer =
        t->buffer->user_data + sg_buf_offset;
    sg_buf_offset += ALIGN(
        bp->length, sizeof(u64));
    ret = binder_fixup_parent(
        &pf_head, t, thread, bp,
        off_start_offset, num_valid,
        last_fixup_obj_off,
        last_fixup_min_off);
    if (ret < 0)
        goto err_translate_failed;
}

BINDER_TYPE_PTR 的外部数据先登记为 deferred copy,接收 buffer object 的 pointer 再改成目标地址;FDA 还要验证 parent object、parent offset 的顺序,fd array 的 fixup 不能越过前一个父对象。

6.4 尾部复制 ​

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

相关函数:binder_transaction()

c
if (binder_alloc_copy_user_to_buffer(
        &target_proc->alloc, t->buffer,
        user_offset,
        user_buffer + user_offset,
        tr->data_size - user_offset)) {
    return_error = BR_FAILED_REPLY;
    return_error_param = -EFAULT;
    goto err_copy_data_failed;
}

ret = binder_do_deferred_txn_copies(
        &target_proc->alloc, t->buffer,
        &sgc_head, &pf_head);
if (ret) {
    return_error = BR_FAILED_REPLY;
    return_error_param = ret;
    goto err_copy_data_failed;
}

对象循环只在对象边界之间复制普通数据,完成对象修复后才复制尾部并执行 deferred copies。这样普通 payload、objects、外部 pointer data 和 fd fixup 的 owner 不会混成一个字节拷贝。

7. 入队唤醒 ​

7.1 Reply投递 ​

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

相关函数:binder_transaction()

c
if (reply) {
    binder_enqueue_thread_work(
            thread, tcomplete);

    binder_inner_proc_lock(target_proc);
    if (target_thread->is_dead) {
        binder_inner_proc_unlock(target_proc);
        return_error = BR_DEAD_REPLY;
        goto err_dead_proc_or_thread;
    }
    binder_pop_transaction_ilocked(
            target_thread, in_reply_to);
    binder_enqueue_thread_work_ilocked(
            target_thread, &t->work);
    target_proc->outstanding_txns++;
    binder_inner_proc_unlock(target_proc);

    wake_up_interruptible_sync(
            &target_thread->wait);
    binder_restore_priority(
            thread, &in_reply_to->saved_priority);
    binder_free_transaction(in_reply_to);
}

reply 的目标是原同步调用线程。驱动先给当前回复线程排入 transaction complete,再从原发送线程 的 stack 弹出 in_reply_to,把新的 reply transaction work 放入该线程 todo 并同步唤醒。原请求 事务随后释放;如果发送线程已经死亡,reply 转入 dead-reply 清理。

7.2 同步请求 ​

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

相关函数:binder_transaction()、binder_proc_transaction()

c
else if (!(t->flags & TF_ONE_WAY)) {
    binder_enqueue_deferred_thread_work_ilocked(
        thread, tcomplete);
    t->from_parent =
        thread->transaction_stack;
    thread->transaction_stack = t;

    return_error = binder_proc_transaction(
        t, target_proc, target_thread);
    if (return_error) {
        binder_pop_transaction_ilocked(
            thread, t);
        goto err_dead_proc_or_thread;
    }
}

同步请求先把发送方 transaction 放入自己的 stack,再交给目标 proc。binder_proc_transaction() 如果指定了 target_thread 就入该线程 todo,否则从 waiting_threads 选择;没有可用线程就进 proc todo。失败时必须把发送方 stack 弹回。这里的 else if 表明 reply 已由上一分支处理,不能再次 当成普通同步请求排队。

7.3 Oneway事务 ​

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

相关函数:binder_transaction()、binder_proc_transaction()

c
if (t->flags & TF_ONE_WAY) {
    BUG_ON(target_node == NULL);
    return_error = binder_proc_transaction(
        t, target_proc, NULL);
    if (return_error ==
            BR_TRANSACTION_PENDING_FROZEN) {
        tcomplete->type =
            BINDER_WORK_TRANSACTION_PENDING;
    }
    binder_enqueue_thread_work(
        thread, tcomplete);
    if (return_error &&
            return_error !=
                BR_TRANSACTION_PENDING_FROZEN)
        goto err_dead_proc_or_thread;
}

oneway 不预先指定目标线程。某 node 没有活动异步事务时,第一笔 oneway 仍可选择 waiting thread 或进入 proc todo;node->has_async_transaction 已为 true 时,后续事务才进入 node->async_todo 串行等待。目标冻结时 oneway 可以进入 pending 队列并返回 BR_TRANSACTION_PENDING_FROZEN;同步事务则返回 BR_FROZEN_REPLY。

7.4 目标选择 ​

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

相关函数:binder_proc_transaction()

c
if (proc->is_frozen) {
    frozen = true;
    proc->sync_recv |= !oneway;
    proc->async_recv |= oneway;
}
if ((frozen && !oneway) ||
        proc->is_dead ||
        (thread && thread->is_dead))
    return frozen ?
        BR_FROZEN_REPLY : BR_DEAD_REPLY;

if (!thread && !pending_async)
    thread = binder_select_thread_ilocked(proc);

if (thread)
    binder_enqueue_thread_work_ilocked(
        thread, &t->work);
else if (!pending_async)
    binder_enqueue_work_ilocked(
        &t->work, &proc->todo);
else
    binder_enqueue_work_ilocked(
        &t->work, &node->async_todo);

if (!pending_async)
    binder_wakeup_thread_ilocked(
            proc, thread, !oneway);

目标 proc 的 frozen/dead 状态先于入队检查。同步 work 优先投递到指定或等待线程,普通 proc work 没有线程时留在 proc todo;oneway 已有活动 async work 时进入 node async_todo。pending async 不会立即唤醒线程,只有当前异步事务释放 buffer 时才把下一项移到 proc todo 并唤醒;所有成功 入队的事务都会增加 proc outstanding_txns。

8. 完成与释放 ​

8.1 成功收束 ​

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

相关函数:binder_transaction()

c
if (target_thread)
    binder_thread_dec_tmpref(
        target_thread);
binder_proc_dec_tmpref(target_proc);
if (target_node)
    binder_dec_node_tmpref(target_node);
smp_wmb();
WRITE_ONCE(e->debug_id_done,
        t_debug_id);
return;

成功后释放目标 thread/proc/node 的临时引用;transaction 本身继续由目标 work、transaction stack 或 buffer 生命周期持有。发送方的 tcomplete 会在目标回复或事务完成路径中变成 BR_TRANSACTION_COMPLETE。

8.2 失败收束 ​

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

相关函数:binder_transaction() 错误标签

c
err_translate_failed:
err_bad_offset:
err_copy_data_failed:
    binder_cleanup_deferred_txn_lists(
        &sgc_head, &pf_head);
    binder_free_txn_fixups(t);
    binder_transaction_buffer_release(
        target_proc, NULL, t->buffer,
        buffer_offset, true);
    if (target_node)
        binder_dec_node_tmpref(
            target_node);
    t->buffer->transaction = NULL;
    binder_alloc_free_buf(
        &target_proc->alloc, t->buffer);
    kfree(tcomplete);
    binder_stats_deleted(
        BINDER_STAT_TRANSACTION_COMPLETE);

err_dead_binder:
err_invalid_target_handle:
    if (target_node) {
        binder_dec_node(target_node, 1, 0);
        binder_dec_node_tmpref(target_node);
    }
    binder_netlink_report(
        proc, t, tr->data_size, return_error);
    kfree(t);

失败路径按已建立资源分层清理:deferred copies、fd fixups、buffer 内容、allocator buffer、tcomplete、node/proc/thread 临时引用各有对应释放。return_error 进入发送线程的 error/reply work,用户态最终看到 BR 错误或扩展错误,而不是一个“静默丢弃”。

8.3 事务状态 ​

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

相关函数:binder_transaction()、binder_set_txn_from_error()

c
if (from->ee.command == BR_OK)
    binder_set_extended_error(
        &from->ee, id, command, param);

驱动只在发送线程的 extended error 为空时写入错误,避免后续失败覆盖第一根因。事务失败日志还记录 debug id、方向、目标 PID/TID、命令码、数据大小和错误行,适合与 BINDER_GET_EXTENDED_ERROR 对照。

9. 测试输入 ​

9.1 空事务 ​

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

相关测试:Transaction

cpp
struct {
    uint32_t cmd;
    binder_transaction_data tr;
} __attribute__((packed)) bc = {
    .cmd = BC_TRANSACTION,
    .tr = {
        .target = {0},
        .code = android::IBinder::PING_TRANSACTION,
        .flags = 0,
        .data_size = 0,
        .offsets_size = 0,
    },
};
bwr.write_buffer = (uintptr_t)&bc;
bwr.write_size = sizeof(bc);
bwr.read_buffer = (uintptr_t)&br;
bwr.read_size = sizeof(br);
binderTestIoctlSuccessOrError(
    BINDER_WRITE_READ, &bwr, EAGAIN);

测试输入覆盖 BC descriptor、空 data/offsets 和 read buffer。后续断言检查 BR_NOOP、BR_TRANSACTION_COMPLETE、BR_REPLY 及 reply descriptor 的零字段,证明入口和基本 reply 形状,不证明非空对象翻译或真实服务业务。

9.2 Oneway ​

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

相关测试:NopTransactionOneway、OnewayQueueing

cpp
TEST_F(BinderLibTest, NopTransactionOneway) {
    Parcel data, reply;
    EXPECT_THAT(
        m_server->transact(
            BINDER_LIB_TEST_NOP_TRANSACTION,
            data, &reply, TF_ONE_WAY),
        StatusEq(NO_ERROR));
}

测试断言用户态 oneway 调用返回成功,但服务处理和回调时序由后续 queueing 测试验证。OnewayQueueing 使用一个延迟 callback 和一个立即 callback,检查同一 node 的 async queue 顺序;它不证明所有多线程服务都串行。

9.3 冻结 ​

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

相关测试:FreezeTxn

cpp
EXPECT_EQ(NO_ERROR,
    IPCThreadState::self()->freeze(
        pid, true, 1000));
EXPECT_EQ(frozenError(),
    server->transact(
        BINDER_LIB_TEST_NOP_TRANSACTION,
        data, &reply));
EXPECT_EQ(NO_ERROR,
    IPCThreadState::self()->freeze(
        pid, false, 0));
EXPECT_EQ(NO_ERROR,
    server->transact(
        BINDER_LIB_TEST_NOP_TRANSACTION,
        data, &reply));

冻结测试输入是目标 PID、冻结开关和超时;断言冻结期间同步事务返回 frozen error,解冻后同一调用恢复成功。它证明 binder_proc_transaction() 的 frozen 分支可见于用户态,不覆盖设备权限和所有 pending oneway 变体。

10. 失败边界 ​

阶段失败结果
reply 栈无栈或栈目标错误BR_FAILED_REPLY/-EPROTO
目标 ref/nodehandle 无效或 node 死亡BR_FAILED_REPLY 或 BR_DEAD_REPLY
安全检查cred 不允许BR_FAILED_REPLY/-EPERM
allocatorVMA 消失、空间不足、内存不足dead/failed reply
offsets/object越界、未对齐、类型或 parent 错误buffer 回滚、failed reply
目标状态frozen/dead thread/procfrozen/dead reply
入队后target work 消费前进程退出cleanup transaction、dead reply

事务函数中的 BR 错误是发送方协议可见结果;外层 BINDER_WRITE_READ 还可能因为用户 buffer 回写失败返回 errno。两层错误必须分开定位。

11. 源码复现 ​

事务阶段 ​

图中 Allocate、Copy、Fixup 是不同失败点;它们最终可能都映射成客户端失败,但清理对象和下一步 诊断证据不同。事务到达 Queue 后才有机会被服务线程消费。

本文主线:

text
BC_TRANSACTION/BC_REPLY
→ binder_thread_write
→ binder_transaction
→ reply栈或 handle/context node
→ target proc/thread + refs
→ transaction/tcomplete
→ allocator buffer与按需页
→ payload/object/fd/pointer翻译
→ binder_proc_transaction
→ thread/proc/node async work
→ 唤醒与 BR 结果
→ 引用、buffer、事务清理

源码搜索:

bash
rg -n "binder_transaction\(|binder_proc_transaction\(" \
  kernel/common/drivers/android/binder.c

rg -n "binder_get_node_refs_for_txn|binder_get_txn_from_and_acq_inner" \
  kernel/common/drivers/android/binder.c

rg -n "binder_alloc_new_buf|binder_translate_|binder_fixup_parent|binder_do_deferred_txn_copies" \
  kernel/common/drivers/android/binder.c \
  kernel/common/drivers/android/binder_alloc.c

rg -n "NopTransactionOneway|OnewayQueueing|FreezeTxn|TEST_F.*Transaction" \
  frameworks/native/libs/binder/tests/binderLibTest.cpp \
  frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp

如果能够解释 reply 为什么从 transaction stack 找目标、为什么 handle 需要 node/proc 临时引用、为什么 object 翻译失败必须释放目标 buffer,以及为什么 oneway work 进入 node async_todo 而不是直接指定线程,就已经建立了 binder_transaction() 的可继续阅读模型。