Skip to content

Binder协议状态机

从 BINDER_WRITE_READ 信封、BC/BR 命令流、增量消费和引用/线程/死亡握手解释 Binder 用户态驱动协议。

基于android-17.0.0_r1
AndroidBinderBC协议BR协议源码阅读

Binder协议状态机 ​

Binder 用户态与驱动之间不是“一次 ioctl 对应一次 RPC”的固定关系。一个 BINDER_WRITE_READ 可以同时提交多条 BC 命令并接收多条 BR 命令;一次同步事务还可能经历 BR_TRANSACTION_COMPLETE、BR_TRANSACTION、BC_REPLY 和 BR_REPLY 等多个阶段。

本文面向已经读过 Binder调用源码地图 的读者,专门解释 协议层:外层信封如何描述两个缓冲区,命令码如何携带参数尺寸,驱动怎样增量消费,用户态怎样 解释返回命令,以及事务、引用、线程池、buffer、死亡和冻结为何都需要自己的握手。

本文不逐项复制全部枚举,也不展开 binder_transaction 内部路由和引用计数算法。读完后,读者 应能从一段 BC/BR 命令流恢复当前状态,并区分“驱动接受命令”“服务收到事务”和“客户端得到 业务 reply”。

1. 两层协议 ​

ioctl信封 ​

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

c
struct binder_write_read {
    binder_size_t write_size;
    binder_size_t write_consumed;
    binder_uintptr_t write_buffer;
    binder_size_t read_size;
    binder_size_t read_consumed;
    binder_uintptr_t read_buffer;
};

enum {
    BINDER_WRITE_READ =
            _IOWR('b', 1, struct binder_write_read),
    BINDER_SET_MAX_THREADS = _IOW('b', 5, __u32),
    BINDER_VERSION = _IOWR('b', 9, struct binder_version),
};

BINDER_WRITE_READ 是外层 ioctl;write_buffer 和 read_buffer 才承载 BC/BR 命令。两个 consumed 字段允许驱动报告实际消费量。协议调用方不能假定传入的每个字节都已被处理。

命令序列 ​

write_buffer 与 read_buffer 都采用“uint32 命令码 + 命令参数”的连续布局。命令值使用 _IO、_IOR、_IOW 编码,因此命令号同时携带方向和参数大小信息;驱动 switch 仍按完整 cmd 值分派。

2. 用户态信封 ​

mOut与mIn ​

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

相关函数:talkWithDriver()

cpp
binder_write_read bwr;
const bool needRead =
        mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail =
        (!doReceive || needRead) ? mOut.dataSize() : 0;

bwr.write_size = outAvail;
bwr.write_buffer =
        reinterpret_cast<uintptr_t>(mOut.data());

if (doReceive && needRead) {
    bwr.read_size = mIn.dataCapacity();
    bwr.read_buffer =
            reinterpret_cast<uintptr_t>(mIn.data());
} else {
    bwr.read_size = 0;
    bwr.read_buffer = 0;
}

mOut 可以积累多条命令;mIn 也可能还有未消费的返回命令。若 mIn 尚有数据,talkWithDriver 不会覆盖它。由此可见,业务调用次数与 ioctl 次数并非一一对应。

ioctl重试 ​

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

cpp
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
    if (ioctl(mProcess->mDriverFD,
            BINDER_WRITE_READ, &bwr) >= 0) {
        err = NO_ERROR;
    } else {
        err = -errno;
    }
} while (err == -EINTR);

EINTR 在用户态重试;UAPI 注释还要求 ECONNREFUSED 后把驱动连接视为不可继续使用。错误处理 属于 ioctl 信封层,不等于 BR_FAILED_REPLY 这种事务命令。

消费收束 ​

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

cpp
if (err >= NO_ERROR) {
    if (bwr.write_consumed > 0) {
        if (bwr.write_consumed < mOut.dataSize()) {
            LOG_ALWAYS_FATAL(
                    "Driver did not consume write buffer");
        } else {
            mOut.setDataSize(0);
            processPostWriteDerefs();
        }
    }
    if (bwr.read_consumed > 0) {
        mIn.setDataSize(bwr.read_consumed);
        mIn.setDataPosition(0);
    }
    return NO_ERROR;
}

libbinder 当前把“驱动成功但只消费部分 mOut”视为致命不变量破坏;read_consumed 则决定 mIn 的有效长度。协议分析时必须同时查看 ioctl 返回值和两个 consumed 字段。

3. 驱动写侧 ​

增量解析 ​

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

相关函数:binder_thread_write()

c
void __user *buffer =
        (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;

while (ptr < end &&
        thread->return_error.cmd == BR_OK) {
    uint32_t cmd;
    if (get_user(cmd, (uint32_t __user *)ptr))
        return -EFAULT;
    ptr += sizeof(uint32_t);
    trace_binder_command(cmd);

    switch (cmd) {
        /* 每个 case 继续读取自己的参数 */
    }
}
*consumed = ptr - buffer;

解析起点是上次 consumed,而不是 buffer 起始地址。每个 case 必须按命令格式移动 ptr;参数 地址无效时返回 EFAULT。thread->return_error 不为 BR_OK 时,驱动停止继续消费新的 BC 命令。

事务命令 ​

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

c
case BC_TRANSACTION_SG:
case BC_REPLY_SG: {
    struct binder_transaction_data_sg tr;
    if (copy_from_user(&tr, ptr, sizeof(tr)))
        return -EFAULT;
    ptr += sizeof(tr);
    binder_transaction(proc, thread,
            &tr.transaction_data,
            cmd == BC_REPLY_SG,
            tr.buffers_size);
    break;
}
case BC_TRANSACTION:
case BC_REPLY: {
    struct binder_transaction_data tr;
    if (copy_from_user(&tr, ptr, sizeof(tr)))
        return -EFAULT;
    ptr += sizeof(tr);
    binder_transaction(proc, thread, &tr,
            cmd == BC_REPLY, 0);
    break;
}

普通事务与 scatter-gather 事务参数大小不同。BC_REPLY 没有目标 handle,而是依赖当前服务 线程的 transaction_stack;协议值相似不代表状态前置条件相同。

其他命令 ​

驱动写侧还处理四组控制状态:

组典型 BC 命令驱动 owner
引用INCREFS/ACQUIRE/RELEASE/DECREFSbinder_ref/node
bufferFREE_BUFFERbinder_alloc
looperREGISTER/ENTER/EXIT_LOOPERbinder_thread/proc
通知REQUEST/CLEAR_DEATH、FREEZEref death/freeze work

这些命令不是事务 payload,却与代理和服务线程能否继续工作直接相关。

4. 驱动读侧 ​

空读等待 ​

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

相关函数:binder_thread_read()

c
if (*consumed == 0) {
    if (put_user(BR_NOOP,
            (uint32_t __user *)ptr))
        return -EFAULT;
    ptr += sizeof(uint32_t);
}

if (non_block) {
    if (!binder_has_work(thread,
            wait_for_proc_work))
        ret = -EAGAIN;
} else {
    ret = binder_wait_for_work(
            thread, wait_for_proc_work);
}

read buffer 首次会预留 BR_NOOP;无 work 时,阻塞 fd 等待,非阻塞 fd 返回 EAGAIN。BR_NOOP 可在需要时被 BR_SPAWN_LOOPER 替换,所以它不是业务事件。

work转命令 ​

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

c
switch (w->type) {
case BINDER_WORK_RETURN_ERROR:
    cmd = error->cmd;
    put_user(cmd, (uint32_t __user *)ptr);
    ptr += sizeof(uint32_t);
    break;
case BINDER_WORK_TRANSACTION_COMPLETE:
    cmd = BR_TRANSACTION_COMPLETE;
    put_user(cmd, (uint32_t __user *)ptr);
    ptr += sizeof(uint32_t);
    break;
case BINDER_WORK_TRANSACTION:
    t = container_of(w,
            struct binder_transaction, work);
    break;
}

驱动内部 work 类型和公开 BR 命令不是同一枚举;binder_thread_read 负责把内部状态编码成 UAPI 命令。

事务返回 ​

源码文件: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;

有 target_node 表示服务端收到新事务;没有 target_node 表示客户端收到 reply。随后驱动写入 cmd 和 transaction_data,并以 read_consumed 告诉用户态有效字节数。

5. 事务状态 ​

同步序列 ​

同步事务的核心序列是:

BR_TRANSACTION_COMPLETE 只确认上一条 transaction/reply 命令被驱动处理。客户端有 reply 对象时仍必须等待 BR_REPLY,不能把 complete 当成服务完成。

waitForResponse ​

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

相关函数:waitForResponse()

cpp
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
    if (!reply && !acquireResult)
        goto finish;
    break;
case BR_DEAD_REPLY:
    err = DEAD_OBJECT;
    goto finish;
case BR_FAILED_REPLY:
    err = FAILED_TRANSACTION;
    goto finish;
case BR_FROZEN_REPLY:
    err = enableFrozenObjectErrorCode()
            ? FROZEN_OBJECT : FAILED_TRANSACTION;
    goto finish;
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);
    }
    goto finish;
default:
    err = executeCommand(cmd);
    break;
}

等待 reply 的线程也会遇到引用、死亡或嵌套 transaction 等其他 BR 命令;default 交给 executeCommand。因此同步等待并非只读取一个 reply 包。

oneway序列 ​

oneway 仍收到 BR_TRANSACTION_COMPLETE,但没有业务 BR_REPLY。服务端 onTransact 的错误和 reply 数据通常不会返回原调用者。冻结目标还可能产生 BR_TRANSACTION_PENDING_FROZEN, oneway 洪泛检测可能产生 BR_ONEWAY_SPAM_SUSPECT。

6. 引用握手 ​

驱动通知 ​

BR_ACQUIRE/BR_INCREFS 携带 ptr/cookie,通知本地进程增加对象强/弱引用。用户态不能只修改 RefBase 后结束,还要回写确认命令。

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

cpp
case BR_ACQUIRE:
    refs = (RefBase::weakref_type*)
            mIn.readPointer();
    obj = (BBinder*)mIn.readPointer();
    obj->incStrong(mProcess.get());
    mOut.writeInt32(BC_ACQUIRE_DONE);
    mOut.writePointer((uintptr_t)refs);
    mOut.writePointer((uintptr_t)obj);
    break;

case BR_INCREFS:
    refs = (RefBase::weakref_type*)
            mIn.readPointer();
    obj = (BBinder*)mIn.readPointer();
    refs->incWeak(mProcess.get());
    mOut.writeInt32(BC_INCREFS_DONE);
    mOut.writePointer((uintptr_t)refs);
    mOut.writePointer((uintptr_t)obj);
    break;

引用通知与 DONE 命令构成握手。驱动会检查是否存在 pending acquire/increfs;重复或 cookie 不匹配会记录协议错误。

延迟释放 ​

BR_RELEASE/BR_DECREFS 不一定立即在当前 switch 中删除对象;libbinder 把 strong/weak deref 放入 pending 容器,等写命令安全提交后处理。这避免在解析返回缓冲区期间提前销毁仍被使用的 对象。

7. 线程握手 ​

进入线程池 ​

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

相关函数:joinThreadPool()

cpp
mOut.writeInt32(
        isMain ? BC_ENTER_LOOPER
               : BC_REGISTER_LOOPER);

do {
    processPendingDerefs();
    result = getAndExecuteCommand();
    if (result < NO_ERROR &&
            result != TIMED_OUT &&
            result != -ECONNREFUSED &&
            result != -EBADF) {
        abort();
    }
} while (result != -ECONNREFUSED &&
        result != -EBADF);

主线程主动 ENTER,驱动请求创建的线程 REGISTER。两个命令更新 binder_thread looper 状态, 并影响驱动是否可把 proc todo 分配给该线程。

生成线程 ​

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

cpp
case BR_SPAWN_LOOPER:
    mProcess->spawnPooledThread(false);
    break;

驱动只有在等待线程不足且未超过 max_threads 等条件下才产生 BR_SPAWN_LOOPER。用户态收到后 创建线程,线程再发送 BC_REGISTER_LOOPER;这不是单向通知,而是请求—注册闭环。

8. Buffer握手 ​

读取buffer ​

BR_TRANSACTION/BR_REPLY 的 data.ptr.buffer 指向目标进程映射中的 Binder buffer。libbinder 用 ipcSetDataReference 建立 Parcel 视图,并注册 freeBuffer 回调。

释放命令 ​

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

c
BC_FREE_BUFFER =
        _IOW('c', 3, binder_uintptr_t);

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

c
case BC_FREE_BUFFER: {
    binder_uintptr_t data_ptr;
    get_user(data_ptr,
            (binder_uintptr_t __user *)ptr);
    ptr += sizeof(binder_uintptr_t);

    buffer = binder_alloc_prepare_to_free(
            &proc->alloc, data_ptr);
    if (IS_ERR_OR_NULL(buffer))
        break;
    binder_free_buf(proc, thread,
            buffer, false);
    break;
}

收到 BR_TRANSACTION 不意味着 buffer 已释放;Parcel 消费结束后 freeBuffer 才生成 BC_FREE_BUFFER。地址无匹配、buffer 尚未允许用户释放或正在释放都会被驱动拒绝。

9. 通知握手 ​

死亡通知 ​

典型闭环为:

  1. BC_REQUEST_DEATH_NOTIFICATION 提交 handle/cookie;
  2. node 死亡后驱动返回 BR_DEAD_BINDER(cookie);
  3. 用户态执行 DeathRecipient;
  4. 回写 BC_DEAD_BINDER_DONE(cookie)。

清除通知则由 BC_CLEAR_DEATH_NOTIFICATION 和 BR_CLEAR_DEATH_NOTIFICATION_DONE 配对。

冻结通知 ​

Android 17 UAPI 还包含 REQUEST/CLEAR_FREEZE_NOTIFICATION、BR_FROZEN_BINDER 和 BR_CLEAR_FREEZE_NOTIFICATION_DONE。它们与死亡通知使用不同状态,不能把 frozen 等同于 dead。

10. 错误边界 ​

ioctl错误 ​

EFAULT 表示外层结构或用户指针不可访问;EAGAIN 常见于非阻塞读且无 work;EINTR 由 libbinder 重试;ECONNREFUSED 表示该进程的驱动连接不再接受操作。

BR错误 ​

命令含义用户态结果
BR_DEAD_REPLY目标死亡DEAD_OBJECT
BR_FAILED_REPLY事务失败FAILED_TRANSACTION
BR_FROZEN_REPLY同步目标冻结FROZEN_OBJECT 或失败
BR_TRANSACTION_PENDING_FROZENoneway 发往冻结目标告警后结束等待
BR_ERROR驱动携带 errno读取 int32 error

这些错误发生在不同层,不能全部归类为 RemoteException 根因。

未知命令 ​

驱动写侧对未支持的 BC 命令通常返回 EINVAL;libbinder 返回侧的 default 会调用 executeCommand,不认识的 BR 命令也会进入错误路径。协议版本必须在 BINDER_VERSION 上先协商, 不能靠忽略未知命令维持兼容。

11. 测试输入 ​

版本与空读 ​

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

相关测试:Version、WriteReadNull、WriteReadEmpty、Read

Version 调用 BINDER_VERSION 并断言 BINDER_CURRENT_PROTOCOL_VERSION;WriteReadNull 传空指针 断言 EFAULT;空信封断言成功;非阻塞空读断言 EAGAIN 和 read_consumed 为 0。这些测试分别覆盖 版本、外层指针、空命令和无 work,不证明事务业务结果。

事务序列 ​

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

相关测试:Transaction

cpp
bwr.write_buffer = (uintptr_t)&bc1;
bwr.write_size = sizeof(bc1);
bwr.read_buffer = (uintptr_t)&br;
bwr.read_size = sizeof(br);

binderTestIoctlSuccessOrError(
        BINDER_WRITE_READ, &bwr, EAGAIN);
EXPECT_EQ(sizeof(bc1), bwr.write_consumed);

if (bwr.read_consumed > offsetof(typeof(br), cmd0))
    EXPECT_EQ(BR_NOOP, br.cmd0);
if (bwr.read_consumed > offsetof(typeof(br), cmd1))
    EXPECT_EQ(BR_TRANSACTION_COMPLETE, br.cmd1);
if (bwr.read_consumed > offsetof(typeof(br), cmd2))
    EXPECT_EQ(BR_REPLY, br.cmd2);

输入是发往 handle 0 的 PING_TRANSACTION;测试允许第一次 ioctl 因非阻塞读返回 EAGAIN,再 poll 并继续读取,最终检查 NOOP、TRANSACTION_COMPLETE 和 REPLY 顺序。随后它发送 BC_FREE_BUFFER 并断言 write_consumed,覆盖 buffer 释放闭环。

引用与通知 ​

同一测试文件的 IncRefsAcquireReleaseDecRefs 把四条引用命令连续放进 write_buffer,并断言全部 消费;RequestDeathNotification 组合引用、请求、清除和 decrefs,检查对应 BR 命令。它们证明 命令可批量编码和握手,不证明完整引用计数算法。

12. 阅读边界 ​

状态转移图 ​

前文代码分别展示 write/read 的实现;下面把命令消费者和线程状态放在同一图中,便于沿 BINDER_WRITE_READ 复核一条事务。

BR_TRANSACTION_COMPLETE 位于 DriverConsume→UserRead 之间,只表示写命令被驱动接收;真正的同步终点是 BR_REPLY 或失败命令。图中的 BufferFree 是 Parcel 消费后的释放动作,不是服务方法返回的同义词。

本文证明了 ioctl 信封、命令流、增量 consumed、事务阶段、引用/线程/buffer/通知握手和错误层次。 没有逐项解释所有命令的数据结构,也没有展开 binder_transaction、node/ref、allocator、线程池 策略和死亡通知内部算法。

下一步可进入 BD006 数据传输模型,继续分析 transaction_data、offsets 和对象转换;也可进入 BD021/BD022 分别深挖 BC_TRANSACTION/BC_REPLY 与 BR_TRANSACTION/BR_REPLY。复查协议时不要只 列命令名,而应写出“谁发送、前置状态、参数长度、谁消费、需要何种确认、失败后状态”。