Binder ioctl入口
打开 Binder 设备并完成映射后,用户态并不会调用许多不同的系统调用来管理 Binder。线程注册、 事务读写、协议版本查询、线程数配置、context manager 注册、冻结状态查询和扩展错误读取,都会 从同一个字符设备 fd 进入 binder_ioctl()。
本文面向已经读过 Binder设备打开、Binder内存映射 和 Binder线程状态模型 的读者,回答 “一个 ioctl 到达驱动后,谁拥有状态,命令如何选择消费者,失败时如何回到用户态”。
文章不把 BINDER_WRITE_READ 的全部 BC/BR 命令展开成事务专题;它只追踪这个命令在总入口中的 位置,并把读写细节留给后续文章。也不把 UAPI 中存在但当前 switch 没有实现的命令写成可用能力。
1. 入口分层
Binder ioctl 有三层边界:
| 层 | 代表对象 | 负责什么 |
|---|---|---|
| 用户态包装 | ProcessState、IPCThreadState | 选择命令、填充参数、处理 errno |
| 总入口 | binder_ioctl() | 绑定 file/proc/thread、选择命令消费者、统一收尾 |
| 子处理器 | binder_ioctl_write_read() 等 | 读取参数、改变具体状态、向用户空间回写结果 |
因此 binder_ioctl() 不是所有 Binder 逻辑的实现位置。它拥有的是“入口控制权”: filp->private_data 指向进程,当前 TID 找到线程,cmd 决定子函数;事务队列、node/ref、冻结 状态和扩展错误仍由各自 owner 管理。
2. 用户调用
2.1 启动配置
源码文件:frameworks/native/libs/binder/ProcessState.cpp
相关函数:open_driver()
static unique_fd open_driver(
const char* driver,
String8* error) {
auto fd = unique_fd(
open(driver, O_RDWR | O_CLOEXEC));
if (!fd.ok())
return {};
int vers = 0;
int result = ioctl(
fd.get(), BINDER_VERSION, &vers);
if (result == -1)
return {};
if (result != 0 ||
vers != BINDER_CURRENT_PROTOCOL_VERSION)
return {};
size_t maxThreads =
DEFAULT_MAX_BINDER_THREADS;
result = ioctl(
fd.get(), BINDER_SET_MAX_THREADS,
&maxThreads);
if (result == -1)
ALOGE("Binder ioctl to set max threads failed");
uint32_t enable =
DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
result = ioctl(
fd.get(),
BINDER_ENABLE_ONEWAY_SPAM_DETECTION,
&enable);
if (result == -1)
ALOGE_IF(
ProcessState::isDriverFeatureEnabled(
ProcessState::DriverFeature::
ONEWAY_SPAM_DETECTION),
"Binder ioctl to enable ... failed");
return fd;
}open_driver() 展示了 ioctl 的两个重要语义:BINDER_VERSION 是失败即放弃的协商,而设置 最大线程和 oneway spam detection 是失败记录日志后继续。不能把“调用过 ioctl”统称为“配置成功”; 每个命令的失败策略由用户态调用方决定。
BINDER_VERSION 和 BINDER_SET_MAX_THREADS 发生在 ProcessState 构造期间,后续线程池启动再 通过同一个 fd 进入 BINDER_WRITE_READ。这解释了为什么总入口必须按当前 fd 找到准确的 proc。
2.2 线程读写
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
相关函数:IPCThreadState::talkWithDriver()
status_t IPCThreadState::talkWithDriver(
bool doReceive) {
binder_write_read bwr;
const size_t outAvail = mOut.dataSize();
bwr.write_size = outAvail;
bwr.write_buffer =
reinterpret_cast<binder_uintptr_t>(
mOut.data());
if (doReceive) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer =
reinterpret_cast<binder_uintptr_t>(
mIn.data());
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
if (ioctl(mProcess->getDriverFD(),
BINDER_WRITE_READ, &bwr) >= 0) {
if (bwr.write_consumed > 0)
mOut.remove(bwr.write_consumed);
if (bwr.read_consumed > 0)
mIn.setDataSize(bwr.read_consumed);
return NO_ERROR;
}
return -errno;
}talkWithDriver() 的 owner 是线程级 IPCThreadState:它提供命令缓冲、接收缓冲和进程级 driver fd;内核 binder_ioctl() 再把同一个调用绑定到 binder_thread。write_consumed 和 read_consumed 是双向契约,用户态据此移动自己的命令游标。
这里的 BINDER_WRITE_READ 不是普通“写命令 ioctl”。同一次调用可以只写、只读,或先写后读; 具体顺序由 binder_ioctl_write_read() 根据两个 size 字段决定。
3. 线程绑定
3.1 入口准备
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_ioctl()
static long binder_ioctl(
struct file *filp,
unsigned int cmd,
unsigned long arg) {
int ret;
struct binder_proc *proc =
filp->private_data;
struct binder_thread *thread;
void __user *ubuf =
(void __user *)arg;
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(
binder_user_error_wait,
binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}入口有两个 owner:proc 来自 file,thread 来自当前 Linux TID。binder_get_thread() 首次看到 这个 TID 时才在 proc 的线程红黑树中创建 binder_thread;因此一次 BINDER_VERSION ioctl 也可能是某线程第一次在驱动建立线程记录的时刻。
binder_user_error_wait 是调试暂停阀。当 binder_stop_on_user_error >= 2 时,ioctl 在命令 分派前等待;这不是业务请求超时,也不是 binder_thread_read() 的工作等待。
3.2 查找创建
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_get_thread()、binder_get_thread_ilocked()
static struct binder_thread *binder_get_thread(
struct binder_proc *proc) {
struct binder_thread *thread;
struct binder_thread *new_thread;
binder_inner_proc_lock(proc);
thread = binder_get_thread_ilocked(
proc, NULL);
binder_inner_proc_unlock(proc);
if (!thread) {
new_thread = kzalloc(
sizeof(*thread), GFP_KERNEL);
if (new_thread == NULL)
return NULL;
binder_inner_proc_lock(proc);
thread = binder_get_thread_ilocked(
proc, new_thread);
binder_inner_proc_unlock(proc);
if (thread != new_thread)
kfree(new_thread);
}
return thread;
}先查找、再无锁分配、重新加锁插入,避免在 proc inner lock 下执行可能睡眠的内存分配。第二次 查找还处理竞态:若其他路径已经插入同 TID 的对象,临时分配的 new_thread 会被释放。
这也是总入口的隐藏结果:命令即使只读取版本或设置线程上限,也可能创建 binder_thread。 不能用“只有发送事务才有线程状态”解释驱动中的线程统计。
3.3 初始字段
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_get_thread_ilocked()
thread = new_thread;
binder_stats_created(BINDER_STAT_THREAD);
thread->proc = proc;
thread->pid = current->pid;
get_task_struct(current);
thread->task = current;
atomic_set(&thread->tmp_ref, 0);
init_waitqueue_head(&thread->wait);
INIT_LIST_HEAD(&thread->todo);
rb_link_node(&thread->rb_node, parent, p);
rb_insert_color(&thread->rb_node,
&proc->threads);
thread->looper_need_return = true;
thread->return_error.work.type =
BINDER_WORK_RETURN_ERROR;
thread->return_error.cmd = BR_OK;
thread->reply_error.work.type =
BINDER_WORK_RETURN_ERROR;
thread->reply_error.cmd = BR_OK;
spin_lock_init(&thread->prio_lock);
thread->prio_state = BINDER_PRIO_SET;
thread->ee.command = BR_OK;
INIT_LIST_HEAD(
&new_thread->waiting_thread_node);looper_need_return = true 是新线程的初始特殊工作状态;命令收尾时总入口会把它清为 false。 return_error、reply_error 和 extended error 先处于 BR_OK,避免第一次读循环看到未初始化 的错误命令。
4. 命令分派
4.1 UAPI集合
源码文件:kernel/common/include/uapi/linux/android/binder.h
相关定义:Binder ioctl 枚举
enum {
BINDER_WRITE_READ =
_IOWR('b', 1,
struct binder_write_read),
BINDER_SET_IDLE_TIMEOUT =
_IOW('b', 3, __s64),
BINDER_SET_MAX_THREADS =
_IOW('b', 5, __u32),
BINDER_SET_IDLE_PRIORITY =
_IOW('b', 6, __s32),
BINDER_SET_CONTEXT_MGR =
_IOW('b', 7, __s32),
BINDER_THREAD_EXIT =
_IOW('b', 8, __s32),
BINDER_VERSION =
_IOWR('b', 9,
struct binder_version),
BINDER_GET_NODE_DEBUG_INFO =
_IOWR('b', 11,
struct binder_node_debug_info),
BINDER_GET_NODE_INFO_FOR_REF =
_IOWR('b', 12,
struct binder_node_info_for_ref),
BINDER_SET_CONTEXT_MGR_EXT =
_IOW('b', 13,
struct flat_binder_object),
BINDER_FREEZE =
_IOW('b', 14,
struct binder_freeze_info),
BINDER_GET_FROZEN_INFO =
_IOWR('b', 15,
struct binder_frozen_status_info),
BINDER_ENABLE_ONEWAY_SPAM_DETECTION =
_IOW('b', 16, __u32),
BINDER_GET_EXTENDED_ERROR =
_IOWR('b', 17,
struct binder_extended_error),
};UAPI 有命令号不等于驱动当前实现了该命令。当前 binder_ioctl() switch 没有处理 BINDER_SET_IDLE_TIMEOUT 和 BINDER_SET_IDLE_PRIORITY,会落入 default 返回 -EINVAL。 这类“定义存在、入口不消费”的差异必须从 switch 和测试一起确认。
4.2 总分派
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_ioctl()
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(
filp, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS: {
u32 max_threads;
if (copy_from_user(&max_threads,
ubuf,
sizeof(max_threads))) {
ret = -EINVAL;
goto err;
}
binder_inner_proc_lock(proc);
proc->max_threads = max_threads;
binder_inner_proc_unlock(proc);
break;
}
case BINDER_THREAD_EXIT:
binder_thread_release(proc, thread);
thread = NULL;
break;
default:
ret = -EINVAL;
goto err;
}switch 内部的状态 owner 不同:BINDER_SET_MAX_THREADS 写 proc,BINDER_THREAD_EXIT 收束 thread,BINDER_WRITE_READ 同时读写 proc/thread 工作队列。thread = NULL 是线程退出分支的 安全标记,防止统一尾部在已释放的 thread 上写字段。
5. 读写子入口
5.1 参数复制
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_ioctl_write_read()
static int binder_ioctl_write_read(
struct file *filp,
unsigned long arg,
struct binder_thread *thread) {
int ret = 0;
struct binder_proc *proc =
filp->private_data;
void __user *ubuf =
(void __user *)arg;
struct binder_write_read bwr;
bool has_special_work = false;
if (copy_from_user(&bwr, ubuf,
sizeof(bwr)))
return -EFAULT;
if (bwr.write_size > 0) {
ret = binder_thread_write(
proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
if (ret < 0) {
bwr.read_consumed = 0;
goto out;
}
}binder_write_read 是用户态和驱动之间的双向信封,不是事务本身。驱动先复制结构体,再按 write_size 选择写路径;写失败时把 read_consumed 清零,避免用户态把未执行的读阶段当作 有效结果。
5.2 读取回写
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_ioctl_write_read()
if (bwr.read_size > 0) {
ret = binder_thread_read(
proc, thread,
bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
binder_inner_proc_lock(proc);
trace_android_vh_binder_has_proc_work_ilocked(
thread, true,
&has_special_work);
if (!binder_worklist_empty_ilocked(
&proc->todo) ||
has_special_work)
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
if (ret < 0)
goto out;
}
out:
if (copy_to_user(ubuf, &bwr,
sizeof(bwr)))
ret = -EFAULT;
return ret;
}读路径把 read_buffer 交给当前 binder_thread;如果读完后 proc 仍有工作或特殊工作,驱动会 唤醒其他合格消费者。O_NONBLOCK 只改变 binder_thread_read() 的等待行为,不改变总入口分派。
即使内核已经消费部分写命令,尾部 copy_to_user() 失败仍会把 ioctl 变成 -EFAULT。因此返回值 与两个 consumed 字段共同构成契约,不能只看其中一个。
5.3 用户游标
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
相关函数:IPCThreadState::talkWithDriver()
if (ioctl(mProcess->getDriverFD(),
BINDER_WRITE_READ, &bwr) >= 0) {
if (bwr.write_consumed > 0)
mOut.remove(bwr.write_consumed);
if (bwr.read_consumed > 0)
mIn.setDataSize(
bwr.read_consumed);
return NO_ERROR;
}
return -errno;驱动只报告 consumed,IPCThreadState 才移动 mOut/mIn 游标。一次 ioctl 可以只消费一部分 BC 命令,下次调用继续从未消费位置发送;把返回 0 理解成“所有输出已经发完”会破坏命令缓冲契约。
6. 配置状态
6.1 线程上限
源码文件:kernel/common/drivers/android/binder.c
相关命令:BINDER_SET_MAX_THREADS
case BINDER_SET_MAX_THREADS: {
u32 max_threads;
if (copy_from_user(&max_threads,
ubuf,
sizeof(max_threads))) {
ret = -EINVAL;
goto err;
}
binder_inner_proc_lock(proc);
proc->max_threads = max_threads;
binder_inner_proc_unlock(proc);
break;
}消费者是后续 looper 读路径:驱动根据 proc->max_threads、已启动线程和等待线程决定是否返回 BR_SPAWN_LOOPER。这个 ioctl 只写一个 proc 字段,不直接创建线程,也不保证线程数量立即变化。
6.2 Oneway开关
源码文件:kernel/common/drivers/android/binder.c
相关命令:BINDER_ENABLE_ONEWAY_SPAM_DETECTION
case BINDER_ENABLE_ONEWAY_SPAM_DETECTION: {
uint32_t enable;
if (copy_from_user(&enable, ubuf,
sizeof(enable))) {
ret = -EFAULT;
goto err;
}
binder_inner_proc_lock(proc);
proc->oneway_spam_detection_enabled =
(bool)enable;
binder_inner_proc_unlock(proc);
break;
}该状态属于 proc,而不是 thread 或 node。后续异步 buffer 分配会结合 allocator 空间读取它; ioctl 返回成功只表示字段更新,不表示已经发现或清除了某个 oneway 事务。
6.3 版本查询
源码文件:kernel/common/drivers/android/binder.c
相关命令:BINDER_VERSION
case BINDER_VERSION: {
struct binder_version __user *ver =
ubuf;
if (put_user(
BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}版本查询只回写协议字段,不读取事务状态。它仍然经过 binder_get_thread(),所以“只读版本”不 意味着“不会建立线程记录”。
7. 管理命令
7.1 Context Manager
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_ioctl_set_ctx_mgr()
static int binder_ioctl_set_ctx_mgr(
struct file *filp,
struct flat_binder_object *fbo) {
int ret = 0;
struct binder_proc *proc =
filp->private_data;
struct binder_context *context =
proc->context;
struct binder_node *new_node;
kuid_t curr_euid = current_euid();
guard(mutex)(
&context->context_mgr_node_lock);
if (context->binder_context_mgr_node)
return -EBUSY;
ret = security_binder_set_context_mgr(
proc->cred);
if (ret < 0)
return ret;
if (uid_valid(
context->binder_context_mgr_uid)) {
if (!uid_eq(
context->binder_context_mgr_uid,
curr_euid))
return -EPERM;
} else {
context->binder_context_mgr_uid =
curr_euid;
}
new_node = binder_new_node(proc, fbo);
if (!new_node)
return -ENOMEM;
context->binder_context_mgr_node =
new_node;
binder_put_node(new_node);
return ret;
}BINDER_SET_CONTEXT_MGR 与扩展版进入同一 helper。所有权路径是 proc → context → binder_context_mgr_node;锁、SELinux hook、UID 约束和“只能设置一次”都在 helper 中发生。总入口只负责把不同参数形态转成 fbo 或 NULL。
7.2 线程退出
源码文件:kernel/common/drivers/android/binder.c
相关命令:BINDER_THREAD_EXIT、binder_thread_release()
case BINDER_THREAD_EXIT:
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d exit\\n",
proc->pid, thread->pid);
binder_thread_release(proc, thread);
thread = NULL;
break;线程退出不只是从红黑树删除一个节点。binder_thread_release() 会标记死亡、处理 transaction stack、释放 thread todo 和 poll wait queue,并用临时引用保证清理期间对象仍存活。局部指针置 NULL 是总入口与释放 helper 之间的生命周期协议。
7.3 冻结目标
源码文件:kernel/common/drivers/android/binder.c
相关命令:BINDER_FREEZE
case BINDER_FREEZE: {
struct binder_freeze_info info;
struct binder_proc **target_procs = NULL;
struct binder_proc *target_proc;
int target_procs_count = 0, i = 0;
if (copy_from_user(&info, ubuf,
sizeof(info))) {
ret = -EFAULT;
goto err;
}
mutex_lock(&binder_procs_lock);
hlist_for_each_entry(
target_proc, &binder_procs,
proc_node) {
if (target_proc->pid == info.pid)
target_procs_count++;
}
/* 分配数组,并为同 PID 的每个 proc 增加 tmp_ref */
mutex_unlock(&binder_procs_lock);
for (i = 0; i < target_procs_count; i++) {
if (ret >= 0)
ret = binder_ioctl_freeze(
&info, target_procs[i]);
binder_proc_dec_tmpref(
target_procs[i]);
}
kfree(target_procs);
}冻结目标不是当前 proc,而是用户传入 PID 对应的所有 binder_proc。驱动在全局锁内收集目标并 增加临时引用,解锁后逐个处理,避免耗时操作占用全局表锁,也防止目标在处理期间被释放。
8. 查询命令
8.1 节点引用
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_ioctl_get_node_info_for_ref()
static int binder_ioctl_get_node_info_for_ref(
struct binder_proc *proc,
struct binder_node_info_for_ref *info) {
struct binder_node *node;
struct binder_context *context =
proc->context;
__u32 handle = info->handle;
if (info->strong_count ||
info->weak_count ||
info->reserved1 ||
info->reserved2 ||
info->reserved3)
return -EINVAL;
mutex_lock(
&context->context_mgr_node_lock);
if (!context->binder_context_mgr_node ||
context->binder_context_mgr_node->proc
!= proc) {
mutex_unlock(
&context->context_mgr_node_lock);
return -EPERM;
}
mutex_unlock(
&context->context_mgr_node_lock);
node = binder_get_node_from_ref(
proc, handle, true, NULL);
if (!node)
return -EINVAL;
info->strong_count =
node->local_strong_refs +
node->internal_strong_refs;
info->weak_count =
node->local_weak_refs;
binder_put_node(node);
return 0;
}该查询只能由 context manager proc 使用。入口复制结构体,helper 验证 reserved 字段、权限和 handle,再回写 node 引用状态。它不是任何客户端都能使用的通用引用计数 API。
8.2 节点遍历
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_ioctl_get_node_debug_info()
static int binder_ioctl_get_node_debug_info(
struct binder_proc *proc,
struct binder_node_debug_info *info) {
struct rb_node *n;
binder_uintptr_t ptr = info->ptr;
memset(info, 0, sizeof(*info));
binder_inner_proc_lock(proc);
for (n = rb_first(&proc->nodes);
n != NULL; n = rb_next(n)) {
struct binder_node *node =
rb_entry(n, struct binder_node,
rb_node);
if (node->ptr > ptr) {
info->ptr = node->ptr;
info->cookie = node->cookie;
info->has_strong_ref =
node->has_strong_ref;
info->has_weak_ref =
node->has_weak_ref;
break;
}
}
binder_inner_proc_unlock(proc);
return 0;
}用户把上次返回的 ptr 作为下一次输入,helper 查找更大的 node 指针。memset 让“没有下一个 节点”表现为 ptr == 0。这是一种逐项遍历协议,不是一次性导出整棵树。
8.3 扩展错误
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_ioctl_get_extended_error()
static int binder_ioctl_get_extended_error(
struct binder_thread *thread,
void __user *ubuf) {
struct binder_extended_error ee;
binder_inner_proc_lock(thread->proc);
ee = thread->ee;
binder_set_extended_error(
&thread->ee, 0, BR_OK, 0);
binder_inner_proc_unlock(thread->proc);
if (copy_to_user(ubuf, &ee,
sizeof(ee)))
return -EFAULT;
return 0;
}扩展错误是 thread owner 的一次性槽位:读取时复制当前值,随后清成 BR_OK。读取成功只表示 错误详情已经取走,不代表原事务已重试或恢复。
9. 统一收尾
9.1 返回尾部
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_ioctl() 结尾
ret = 0;
trace_android_vh_binder_ioctl_end(
current, cmd, arg,
thread, proc, &ret);
err:
if (thread)
thread->looper_need_return = false;
wait_event_interruptible(
binder_user_error_wait,
binder_stop_on_user_error < 2);
if (ret && ret != -EINTR)
pr_info("%d:%d ioctl %x returned %d\\n",
proc->pid, current->pid,
cmd, ret);
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;成功路径也经过名为 err: 的标签,标签名不能代替控制流判断。只要 thread 仍存在,入口就清除 looper_need_return;线程退出分支已经将局部指针置 NULL,因此不会访问释放后的对象。
trace_android_vh_binder_ioctl_end 可以修改 ret,是 vendor hook 边界;trace_binder_ioctl_done 则是所有返回路径的共同观测点。文章不能把 switch 中的初始结果当成最终返回值而忽略尾部。
9.2 错误层次
| 层 | 示例 | 返回方式 |
|---|---|---|
| 等待层 | 调试暂停被信号中断 | -EINTR,不创建 thread |
| 线程层 | binder_get_thread() 分配失败 | -ENOMEM |
| 参数层 | 用户指针或结构体字段无效 | -EFAULT 或 -EINVAL |
| 状态层 | manager 已存在、目标不存在 | -EBUSY、-EINVAL |
| 权限层 | SELinux、UID、manager 身份不符 | hook errno 或 -EPERM |
这些 errno 直接作为 ioctl 系统调用错误返回。事务协议中的 BR_FAILED_REPLY、BR_DEAD_REPLY 属于 BINDER_WRITE_READ 内的 BR 字节流,不能与总入口 errno 互换。
9.3 两次等待
入口前后各有一次 wait_event_interruptible(binder_user_error_wait, ...)。前一次阻止命令进入 switch;后一次在错误调试状态解除后再返回。后一次的返回值没有覆盖原 ret,不是业务层的自动重试。
10. 测试输入
10.1 版本参数
源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp
相关测试:Version、VersionNull、WriteReadNull、SetMaxThreadsNull
TEST_F(BinderDriverInterfaceTest, Version) {
struct binder_version version;
binderTestIoctl(
BINDER_VERSION, &version);
ASSERT_EQ(
BINDER_CURRENT_PROTOCOL_VERSION,
version.protocol_version);
}
TEST_F(BinderDriverInterfaceTest, WriteReadNull) {
binderTestIoctlErr1(
BINDER_WRITE_READ,
nullptr, EFAULT);
}
TEST_F(BinderDriverInterfaceTest,
SetMaxThreadsNull) {
binderTestIoctlErr2(
BINDER_SET_MAX_THREADS,
nullptr, EFAULT, EINVAL);
}测试证明有效版本结构体会被回写,NULL 参数不会被静默接受。SetMaxThreadsNull 接受当前实现的 EFAULT 或 EINVAL 差异,因此不能把某一个 errno 写成跨版本永恒 ABI。
10.2 未实现命令
源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp
相关测试:SetIdleTimeoutNoTest、SetIdlePriorityNoTest
TEST_F(BinderDriverInterfaceTest,
SetIdleTimeoutNoTest) {
int64_t idle_timeout = 100000;
binderTestIoctlUnimplemented(
BINDER_SET_IDLE_TIMEOUT,
&idle_timeout);
}
TEST_F(BinderDriverInterfaceTest,
SetIdlePriorityNoTest) {
int idle_priority = 0;
binderTestIoctlUnimplemented(
BINDER_SET_IDLE_PRIORITY,
&idle_priority);
}这两个测试把“UAPI 定义存在”和“当前 switch 实现”分开。helper 允许 -1/EINVAL,与 default 分支相互验证,但不能证明未来版本或厂商驱动仍未实现。
10.3 线程退出
源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp
相关测试:ThreadExit
TEST_F(BinderDriverInterfaceTest, ThreadExit) {
int32_t dummy = 0;
binderTestIoctl(
BINDER_THREAD_EXIT, &dummy);
static_cast<
BinderDriverInterfaceTestEnv *>
(binder_env)->EnterLooper();
}测试先让当前 thread 离开驱动线程状态,再通过 BC_ENTER_LOOPER 重新进入。它验证 thread exit 后 fd/proc 仍可使用;没有覆盖活动同步事务、嵌套调用或进程 release 期间的线程收束。
11. 源码复现
命令分流
入口、命令分支和用户回写是三层错误边界:命令未知、参数地址错误、子函数权限/状态拒绝会产生不同 errno。
本文主线是:
ProcessState/IPCThreadState ioctl
→ binder_fops.unlocked_ioctl
→ binder_ioctl
→ file对应binder_proc
→ 当前TID对应binder_thread
→ switch(cmd)
→ 命令专属owner
→ consumed、状态或errno
→ looper标志与trace收尾可以在源码 checkout 中执行:
rg -n "talkWithDriver|BINDER_WRITE_READ|ioctl\\(" \\
frameworks/native/libs/binder/IPCThreadState.cpp \\
frameworks/native/libs/binder/ProcessState.cpp
rg -n "binder_ioctl\\(|binder_get_thread\\(|case BINDER_" \\
kernel/common/drivers/android/binder.c
rg -n "BINDER_WRITE_READ|BINDER_SET_IDLE|BINDER_GET_EXTENDED_ERROR" \\
kernel/common/include/uapi/linux/android/binder.h
rg -n "VersionNull|WriteReadNull|SetIdleTimeoutNoTest|ThreadExit" \\
frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp如果能够解释为什么 BINDER_VERSION 也可能创建 binder_thread、未实现 ioctl 为什么返回 errno 而不是 BR 命令,以及 BINDER_THREAD_EXIT 为什么必须把局部 thread 置 NULL,就已经掌握总入口 的控制流。下一篇将沿这里的 binder_ioctl_write_read() 继续拆解写入、读取、部分消费和阻塞边界。
