素材来源:sanyinchen/corpus 仓库,资料整理自哔哩哔哩 Android Binder 源码教程。
一、一次 startActivity 包含两次 Binder 通信
旧版 Android Framework 中,Activity.startActivityForResult() 会经 Instrumentation.execStartActivity() 触发这类调用:
// 取得 IActivityManager 的远端代理。ActivityManagerNative.getDefault()// 向 AMS 发送启动请求。 .startActivity(whoThread, who.getBasePackageName(), intent,// 继续传入解析后的类型和调用方 token。 intent.resolveTypeIfNeeded(who.getContentResolver()), token,// 传入结果接收方、请求码和选项。 target, requestCode, 0, null, options);这一行发生了两次跨进程访问。getDefault() 向 ServiceManager 查询 AMS,客户端由此获得 ActivityManagerProxy,代理内部持有 BpBinder(handle)。随后的 startActivity() 向 system_server 中的 AMS 发送事务。

图一展示客户端、Binder 驱动、ServiceManager 与 system_server 的边界,并标出 AMS 对应的 binder_node。
一次完整往返呈现对称的状态变化。服务端线程在 waitqueue 休眠,客户端写入事务后将其唤醒;客户端随后等待回复,服务端执行方法并写回结果,驱动又唤醒客户端。代理对象不会直接跨进程调用 Java 服务对象,驱动通过 binder_proc、binder_node、binder_ref、handle 与 cookie 建立两端的联系。
二、三个驱动入口与四类对象
一个进程打开 /dev/binder 时,binder_open() 为它建立 binder_proc。每个参与 Binder 的线程由 binder_thread 表示;本地发布的实体对象对应 binder_node;另一个进程持有该实体时,驱动在持有方创建指向 node 的 binder_ref。
// 代码位置 /kernel/msm/drivers/staging/android/binder.c 第 286—317 行// 表示一个打开 Binder 驱动的进程。struct binder_proc { // 按线程 pid 索引 binder_thread。 struct rb_root threads; // 按本地 Binder 指针索引 binder_node。 struct rb_root nodes; // 按 desc(用户态 handle)索引 binder_ref。 struct rb_root refs_by_desc; // 按 binder_node 地址索引 binder_ref。 struct rb_root refs_by_node; // 保存尚未分派到具体线程的工作。 struct list_head todo; // 保存 mmap 后的用户虚拟地址起点。 void __user *buffer; // 保存用户地址与内核地址之间的偏移。 ptrdiff_t user_buffer_offset;// 结束 binder_proc 定义。}; // 代码位置 /kernel/msm/drivers/staging/android/binder.c 第 331—343 行// 表示进程内进入驱动的一个线程。struct binder_thread { // 挂入 binder_proc 的 threads 红黑树。 struct rb_node rb_node; // 保存已分派给当前线程的工作。 struct list_head todo; // 在线程无事可做时承载睡眠与唤醒。 wait_queue_head_t wait;// 结束 binder_thread 定义。}; // 代码位置 /kernel/msm/drivers/staging/android/binder.c 第 217—238 行// 表示服务端进程中的 Binder 实体。struct binder_node { // 挂入所属进程的 nodes 红黑树。 struct rb_node rb_node; // 指向拥有该实体的 binder_proc。 struct binder_proc *proc; // 保存用户态本地 Binder 对象地址。 binder_uintptr_t ptr; // 保存随事务返回服务端的 cookie。 binder_uintptr_t cookie;// 结束 binder_node 定义。}; // 代码位置 /kernel/msm/drivers/staging/android/binder.c 第 246—261 行// 表示某进程对远端 binder_node 的引用。struct binder_ref { // 按 handle 挂入 refs_by_desc。 struct rb_node rb_node_desc; // 按 node 挂入 refs_by_node。 struct rb_node rb_node_node; // 指向实际的 binder_node。 struct binder_node *node; // 作为该进程用户态可见的 handle。 uint32_t desc;// 结束 binder_ref 定义。};这里常被概括成“四棵红黑树”:threads、nodes、按 handle 索引的 refs、按 cookie 索引的 refs。不同旧版源码中第四棵树的字段实际是 refs_by_node,键来自 binder_node *;课程用 cookie 地址解释查找关系。

图二展示 binder_proc 的四棵红黑树,以及它们与 binder_thread、binder_node、binder_ref 的关联。
驱动向用户态提供三个入口:
// 代码位置 /kernel/msm/drivers/staging/android/binder.c 第 3004—3037 行// 打开设备时创建当前进程的 Binder 上下文。static int binder_open(struct inode *nodp, struct file *filp) { // 为 binder_proc 分配内核对象。 struct binder_proc *proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 记录当前进程。 get_task_struct(current->group_leader); // 令文件描述符后续操作都能取得 binder_proc。 filp->private_data = proc; // 返回打开结果。 return 0;// 结束打开流程。} // 代码位置 /kernel/msm/drivers/staging/android/binder.c 第 2905—3002 行// 建立 Binder 接收缓冲区的用户态映射。static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { // 取出 binder_open 创建的进程对象。 struct binder_proc *proc = filp->private_data; // 记录用户虚拟地址起点。 proc->buffer = (void __user *)vma->vm_start; // 安装按页映射所需的 VMA 操作;具体分配在旧版中按需完成。 vma->vm_ops = &binder_vm_ops; // 返回映射结果。 return 0;// 结束映射流程。} // 代码位置 /kernel/msm/drivers/staging/android/binder.c 第 2734—2875 行// 处理用户态发来的 Binder 控制命令。static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { // 取得当前进程的 Binder 状态。 struct binder_proc *proc = filp->private_data; // 取得或创建当前线程的 binder_thread。 struct binder_thread *thread = binder_get_thread(proc); // BINDER_WRITE_READ 同时承载写入和读取。 return binder_ioctl_write_read(filp, cmd, arg, thread);// 结束 ioctl 分派。} // 代码位置 /kernel/common/drivers/android/binder.c 第 4829—4893 行// 根据读写尺寸进入对应方向。static int binder_ioctl_write_read(struct file *filp, unsigned int cmd,// 接收用户参数与当前线程。 unsigned long arg, struct binder_thread *thread) { // 保存用户态提交的读写描述。 struct binder_write_read bwr; // 从用户空间取得 write_size、read_size 和缓冲区地址。 copy_from_user(&bwr, (void __user *)arg, sizeof(bwr)); // 有写数据时解析 BC_TRANSACTION、BC_REPLY 等命令。 if (bwr.write_size > 0) binder_thread_write(thread->proc, thread, &bwr); // 有读缓冲区时读取 BR_TRANSACTION、BR_REPLY 等结果;无工作时可进入等待。 if (bwr.read_size > 0) binder_thread_read(thread->proc, thread, &bwr); // 把已消费和已生成的尺寸写回用户态。 return copy_to_user((void __user *)arg, &bwr, sizeof(bwr));// 结束读写处理。}binder_open 的输出是进程级上下文,binder_mmap 准备接收区,binder_ioctl 才推动命令和状态迁移。BINDER_WRITE_READ 并不限定单一方向:write_size、read_size 可以分别为零,也可以在同一次 ioctl 中同时非零。

图三展示同一物理页在内核地址空间与接收进程用户地址空间中的映射。
“Binder 一次拷贝”描述的是一次事务载荷从发送方用户空间复制到接收方共享物理页的路径。内核可经自己的映射写入该页,接收方则经 mmap 的用户虚拟地址读取它,因此省掉“内核缓冲区再复制到接收方用户缓冲区”。映射区并非一次性给足:课程对照的 6.0 版本驱动的 binder_mmap 只开辟一个物理页,不够用时由 binder_ioctl 按需追加;在 3.0/4.0 之前的早期版本里,binder_mmap 则是一次性直接映射一整块 8K。一个完整请求与回复还包含多条命令和反向载荷,不能据此断言整次调用只发生一次数据复制。
三、等待与唤醒的六个状态

上图展示服务端、驱动与客户端之间的同步事务时序,并标出 todo、waitqueue 和三类返回命令:
system_server的 Binder 线程调用 ioctl 读取待办项。线程 todo 与进程 todo 均为空时,binder_thread_read()把线程挂入 waitqueue 并休眠。- 客户端把
BC_TRANSACTION和扁平化 Parcel 写入BINDER_WRITE_READ。驱动用 handle 找到本进程的binder_ref(对应驱动里的binder_get_ref()),继而到达目标binder_node和它所属的binder_proc;handle 0 对应的全局节点由binder_context_mgr_node持有。 - 驱动把事务挂到目标线程或目标进程的 todo,唤醒
system_server。服务端读到BR_TRANSACTION,用户态开始分派。 - 同步调用尚未收到 reply,客户端再次进入
talkWithDriver()。读取路径没有可返回结果时,线程挂入自己的等待队列休眠。 - AMS 执行完成后,服务端以
BC_REPLY写回。驱动向客户端队列加入回复工作,同时让服务端收到BR_TRANSACTION_COMPLETE,表示该次写事务已被驱动接收完成。 - 客户端被唤醒并读到
BR_REPLY,waitForResponse()解析回复 Parcel。两端线程随后都可继续处理待办项或进入下一轮等待。
BR_TRANSACTION_COMPLETE 用于确认写侧事务提交完成。同步调用的业务返回数据由另一端提交 BC_REPLY,再由驱动生成 BR_REPLY。
四、getDefault 查询 AMS
AMS 启动后通过 addService("activity", binder) 注册到 ServiceManager。实体留在 system_server:驱动为 JavaBBinder 建立 binder_node,ServiceManager 进程得到一个指向它的 binder_ref。客户端查询时,handle 0 让事务发往 Binder context manager,也就是 ServiceManager。下面给出的是 ServiceManager 的 C++ 代理(BpServiceManager / IServiceManager)在 handle=0 上的实现;
// 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 155—165 行// 把服务名和 Binder 实体写入注册事务。virtual status_t addService(const String16& name, const sp<IBinder>& service) { // 创建发往 ServiceManager 的数据包。 Parcel data, reply; // 写接口描述符。 data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); // 写服务名。 data.writeString16(name); // 写入服务 Binder,驱动会翻译其中的对象表示。 data.writeStrongBinder(service); // 经 handle 0 发出 ADD_SERVICE_TRANSACTION。 return remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);// 结束服务注册。} // 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 146—153 行// 按名称查询远端服务。virtual sp<IBinder> getService(const String16& name) const { // 创建查询参数和回复包。 Parcel data, reply; // 写接口描述符。 data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); // 写待查询的服务名。 data.writeString16(name); // 经 handle 0 发出 CHECK_SERVICE_TRANSACTION,读回 AMS 的引用。 remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); // 读出驱动翻译后的 Binder 引用。 return reply.readStrongBinder();// 结束服务查询。}
上图展示客户端经 ServiceManager 查询 AMS 后,驱动为本进程创建引用与 handle,用户态据此构造 BpBinder(handle)。
“把 binder_node 拷进客户端”适合描述关系传播,却不是结构体的物理复制。驱动实际在客户端 binder_proc 中创建 binder_ref,令它指向 system_server 的原 binder_node,并分配递增的非零 desc。返回 Parcel 中的对象类型也会从本地实体表示翻译成 handle 表示。用户态据此取得 BpBinder(handle),再包装为 ActivityManagerProxy。
五、startActivity 从应用侧进入驱动
旧版 ActivityManagerProxy.startActivity() 把参数按接口协议写入 Parcel。这里的 mRemote 就是查询阶段得到的 BpBinder:
// 代码位置 /frameworks/base/core/java/android/app/ActivityManagerNative.java 第 2631—2661 行// 代理侧封装 startActivity 请求。public int startActivity(IApplicationThread caller, String callingPackage,// 接收 Intent、类型及其余 Binder 参数。 Intent intent, String resolvedType, IBinder resultTo) throws RemoteException { // 创建请求与回复 Parcel。 Parcel data = Parcel.obtain(); // 创建同步返回包。 Parcel reply = Parcel.obtain(); // 写入 IActivityManager 接口令牌。 data.writeInterfaceToken(IActivityManager.descriptor); // 写调用进程的 ApplicationThread Binder。 data.writeStrongBinder(caller != null ? caller.asBinder() : null); // 写调用包名。 data.writeString(callingPackage); // 写 Intent。 intent.writeToParcel(data, 0); // 写解析后的 MIME 类型。 data.writeString(resolvedType); // 发出启动事务。 mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0); // 读取远端异常状态。 reply.readException(); // 读取 AMS 返回码。 return reply.readInt();// 结束代理调用。}
上图展示 ActivityManagerProxy.startActivity 经 BpBinder、IPCThreadState 和 ioctl 进入驱动的调用路径。
native 层的调用链较短,各层分别承担对象转发、事务组装和驱动读写:
// 代码位置 /frameworks/native/libs/binder/BpBinder.cpp 第 159—171 行// BpBinder 把对象 handle 交给线程级 IPC 状态机。status_t BpBinder::transact(uint32_t code, const Parcel& data,// 接收回复包、事务标志和调用方参数。 Parcel* reply, uint32_t flags) { // 用 mHandle 标识目标远端对象。 return IPCThreadState::self()->transact(mHandle, code, data, reply, flags);// 结束 BpBinder 转发。} // 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 548—608 行// 组装驱动认识的事务并等待同步结果。status_t IPCThreadState::transact(int32_t handle, uint32_t code,// 接收序列化参数、回复和标志。 const Parcel& data, Parcel* reply, uint32_t flags) { // 把 BC_TRANSACTION 与 binder_transaction_data 写入 mOut。 status_t err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr); // 单向事务只等待驱动确认。 if (flags & TF_ONE_WAY) return waitForResponse(nullptr, nullptr); // 同步事务持续读取,直到 BR_REPLY 或错误。 return waitForResponse(reply, nullptr);// 结束事务提交。} // 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 712—801 行// 循环驱动读写并分派返回命令。status_t IPCThreadState::waitForResponse(Parcel* reply, status_t* acquireResult) { // 持续等待目标结果。 while (true) { // 把 mOut 送入驱动,并把驱动输出装入 mIn。 talkWithDriver(); // 取出一个驱动返回命令。 const int32_t cmd = mIn.readInt32(); // BR_REPLY 到达时解析回复并退出循环。 if (cmd == BR_REPLY) return NO_ERROR; // 其余命令交给线程状态机处理。 executeCommand(cmd); // 结束本轮循环体。 }// 结束回复等待。} // 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 803—902 行// 通过一次 ioctl 同时提交输出并请求输入。status_t IPCThreadState::talkWithDriver(bool doReceive) { // 描述本次写缓冲区和读缓冲区。 binder_write_read bwr; // 设置待写字节数。 bwr.write_size = mOut.dataSize(); // 设置接收区容量。 bwr.read_size = doReceive ? mIn.dataCapacity() : 0; // 进入 Binder 驱动完成读写。 return ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr);// 结束驱动交互。}输入是目标 handle、事务码与 Parcel,输出是 reply 或错误码。调用期间,驱动读取 mOut,mIn 则接收 BR_* 命令。AMS 的 handle 非零,驱动沿普通引用路径查找对象,把事务送到 system_server 的 todo 并唤醒等待线程。
两个进程即使出现数值相同的虚拟地址,也通常映射到不同物理页。客户端不能解引用 system_server 中的 JavaBBinder 地址;该地址以 cookie 形式保存在驱动中,事务回到对象所属进程后才可用于定位实体。
六、system_server 如何定位 Java AMS
system_server 已通过 Binder 线程池阻塞在读路径。事务到达后,joinThreadPool() 从驱动读取命令,getAndExecuteCommand() 把 BR_TRANSACTION 交给 executeCommand():
// 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 477—512 行// 让当前线程加入 Binder 命令循环。void IPCThreadState::joinThreadPool(bool isMain) { // 声明主线程或普通池线程身份。 mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); // 持续读取并执行驱动命令。 do { // 完成一次取命令与分派。 result = getAndExecuteCommand(); // 在线程池未退出时继续。 } while (result != -ECONNREFUSED && result != -EBADF);// 结束线程池循环。} // 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 414—451 行// 从驱动取得一个命令并执行。status_t IPCThreadState::getAndExecuteCommand() { // 阻塞式读写驱动;todo 为空时线程在内核等待。 status_t result = talkWithDriver(); // 读取 BR_TRANSACTION 等命令码。 int32_t cmd = mIn.readInt32(); // 进入用户态命令分派。 return executeCommand(cmd);// 结束命令执行。}驱动交付的 binder_transaction_data 含 target.ptr 与 cookie。服务端命令分派把 cookie 恢复为 BBinder*,再调用 transact()/onTransact()。对 Java 服务而言,该 C++ 对象是 JavaBBinder;它经 JNI 调用 Java Binder.execTransact(),最终落到 AMS 的 onTransact() 和 startActivity()。
命令分派在 BR_TRANSACTION 上按 target.ptr 是否为空决定目标,handle 0(ServiceManager)走的正是 target.ptr 为空的分支:
// 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 1083—1089 行// 服务端命令分派(IPCThreadState::executeCommand)处理 BR_TRANSACTION。case BR_TRANSACTION: { // target.ptr 非空时指向某个本地 Binder 实体。 if (tr.target.ptr) { // cookie 是目标 BBinder 的强指针地址,转型后直接调用。 sp<BBinder> b((BBinder*)tr.cookie); // 把请求分发给该实体。 error = b->transact(tr.code, buffer, &reply, tr.flags); // 实体分支处理结束。 } else { // target.ptr 为空对应 handle 0,转交给进程内置 context object。 error = the_context_object->transact(tr.code, buffer, &reply, tr.flags); // context-manager 分支处理结束。 } // 结束本次事务分派。 break; // 结束 BR_TRANSACTION 分支。}// 代码位置 /frameworks/base/core/jni/android_util_Binder.cpp 第 247—296 行// JavaBBinder 接收 native Binder 事务。status_t JavaBBinder::onTransact(uint32_t code, const Parcel& data,// 接收回复包和事务标志。 Parcel* reply, uint32_t flags) { // 通过弱全局引用取得 Java Binder 对象。 jobject object = mVM->getEnv()->NewLocalRef(mObject); // 调用 Java Binder.execTransact,具体 JNI 签名随版本变化。 return android_os_Binder_execTransact(object, code, data, reply, flags); // 待核实:课程所用 AOSP 分支的封装函数名// 结束 JavaBBinder 分派。}Java 层创建 ActivityManagerService 时,会沿父类 Binder 的构造过程进入 native 初始化。JNI 创建 JavaBBinder,并把 native 持有关系绑定到 Java 对象的 mObject。注册 AMS 时,JavaBBinder 的强指针地址进入驱动 node 的 cookie。事务返回 system_server 后,这个 cookie 才能定位 native 实体,JavaBBinder 随后通过自己持有的 Java 引用找到 AMS。

上图展示 AMS、JavaBBinder、binder_node 与 cookie 在 system_server 内的对应关系,以及 ServiceManager 持有的远端引用。
旧版 Java 分派最后调用 AMS:
// 代码位置 /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 第 3799—3805 行// AMS 对外接收 Activity 启动请求。public final int startActivity(IApplicationThread caller, String callingPackage,// 接收 Intent、类型、结果接收方与请求信息。 Intent intent, String resolvedType, IBinder resultTo, String resultWho,// 接收请求码、启动标志、profiler 与选项。 int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle options) { // 使用 Binder 调用者对应的用户发起启动。 return startActivityAsUser(caller, callingPackage, intent, resolvedType, // 透传结果参数并取得调用用户。 resultTo, resultWho, requestCode, startFlags, profilerInfo, options, // 由驱动保存的调用身份解析用户 ID。 UserHandle.getCallingUserId());// 结束 AMS 入口。}七、AMS 执行与回复
AMS 收到的只是一次启动请求,后续仍需经过 Activity 启动策略与进程状态判断。目标应用进程已存在时,可以在现有进程中调度 Activity;
进程不存在时,系统需创建进程,建立 Application、完成 bind application,再调度 Activity。各 Android 版本在 ActivityStarter、ActivityStack 等内部类上的组织不同,这里不展开版本相关细节。
Java 方法返回后,回复 Parcel 沿 JNI、JavaBBinder 和 IPCThreadState 回到驱动。服务端提交 BC_REPLY;
驱动把 BR_REPLY 放进原客户端线程的队列并唤醒它,同时向服务端报告 BR_TRANSACTION_COMPLETE。客户端的 waitForResponse() 读出返回值,ActivityManagerProxy.startActivity() 得到 AMS 的启动结果码。
八、handle、ref 与 binder_proc 的协作
发布 AMS 时,AMS 的 binder_node 挂入 system_server 的 nodes 树。ServiceManager 接收对象后,在自己的 refs 树中持有指向该 node 的 binder_ref。客户端查询 AMS 时,驱动也会在客户端 binder_proc 中创建 ref,并分配进程局部的递增 handle。handle 只在持有它的进程中有意义,其他进程中的同值 handle 未必指向同一对象;handle 0 则由协议保留给 context manager。还要说明一点:同一个 binder_ref 会同时挂入 refs_by_desc 与 refs_by_node 两棵树,课程里说的「在 ServiceManager 进程挂两个引用」指的是一次插入在两棵树里各留一个节点,并不是创建了两个独立对象。
发起 startActivity 时,驱动从“客户端 binder_proc → handle 索引的 binder_ref → binder_node → node->proc”找到 system_server。服务端接收时,node 的 cookie 把事务交还原 JavaBBinder。由此可见,排查跨进程事务时,binder_proc 是最有效的入口:它同时给出线程、实体、远端引用、接收映射和待办工作,再沿 node 或 ref 才能还原对象关系。
九、总结
getDefault() 经 handle 0 查询 ServiceManager,得到 AMS 的进程局部 handle。startActivity() 把 Parcel 交给 BpBinder 与 IPCThreadState,经 BINDER_WRITE_READ 进入驱动。驱动沿 ref 找到 AMS 的 node 与 system_server,写入 todo 并唤醒线程。服务端读到 BR_TRANSACTION,借 cookie 回到 JavaBBinder 和 AMS。AMS 执行后写 reply,客户端被唤醒并读到 BR_REPLY。
本文由 AgentWorkFlow 生成