文章内容
- 一、一次跨进程绑定,客户端到底交出了什么
- 1.1 demo 的三个角色与两次往返
- 1.2 为什么不能直接把 ServiceConnection 递给 AMS
- 1.3 bindServiceCommon 里先做的那一步:getServiceDispatcher
- 二、Java 对象在 native 层长什么样
- 2.1 Binder() 构造函数里的 init()
- 2.2 androidosBinderinit:JavaBBinderHolder 与 mObject
- 2.3 writeStrongBinder:从 Java 对象取出 BBinder,序列化为 flatbinderobject
- 2.4 跨进程传的是地址,地址在另一个进程里无效
- 三、客户端进入驱动:BINDERTYPEBINDER 变成 handle
- 3.1 transact 到 ioctl(BINDERWRITEREAD) 的路径
- 3.2 bindergetnode / bindernewnode:在本进程 nodes 树里按 ptr 查或建 bindernode
- 3.3 bindergetreffornode:在目标进程建 binderref,desc 就是 handle
- 3.4 flatbinderobject
- 3.5 唤醒目标进程
- 四、AMS 侧:handle 又变回 Java 对象
- 4.1 binderthreadread 读到 BINDERTYPEHANDLE
- 4.2 BpBinder 与 BinderProxy:javaObjectForIBinder 创建代理
- 4.3 再包一层 IServiceConnection.Stub.Proxy
- 五、AMS 请求绑定:第二次跨进程与 node 的复制
- 5.1 AMS.bindService → bindServiceLocked → requestServiceBindingLocked
- 5.2 目标进程由事务的 target handle 决定
- 5.3 三个进程各自的三棵红黑树
- 六、服务端回传:publishService 与第三次跨进程
- 6.1 MessageService.onBind 返回 mBinder
- 6.2 publishService → publishServiceLocked → conn.connected
- 七、回到客户端:cookie 是怎么把对象找回来的
- 7.1 handle 只在各自进程的命名空间里有意义
- 7.2 binderthreadread 读出 handle,cookie 还原成本进程地址
- 7.3 回调落到 onServiceConnected
- 八、进程执行流程
- 8.1 appmain.cpp 的 onZygoteInit → startThreadPool
- 8.2 PoolThread::threadLoop → joinThreadPool → talkWithDriver
- 8.3 binderthreadread:todo 为空时阻塞在 wait 队列上
- 8.4 唤醒发生在 bindertransaction 末尾
- 九、一次 bindService 的完整时序与验证入口
- 9.1 三次跨进程的汇总
- 十、小结
- 10.1 ptr 与 cookie 各是什么
- 10.2 handle 为什么在不同进程里数字不同
- 10.3 IServiceConnection 为什么使用 oneway
- 10.4 asInterface 只是包装
一次 bindService 会跨过客户端、system_server 和 Service 进程。Java 层传出的对象先写成传输结构,Binder 驱动再为目标进程建立引用。客户端提交的是 BINDER_TYPE_BINDER,其中保存对象地址与 cookie;驱动为目标进程建立 binder_ref、分配 handle,接收方此后凭 handle 发起调用。回调返回原进程时,cookie 保存的本进程地址让调用重新落到原对象上。
一、一次跨进程绑定,客户端到底交出了什么
1.1 demo 的三个角色与两次往返
示例里有三个进程。客户端是 com.example.myapplication,下文沿用“QQ 音乐进程”这个代号;AMS 运行在 system_server;MessageService 通过 android:process=":message" 运行在 com.example.myapplication:message。一次绑定包含两次请求方向的跨进程移动:客户端把绑定请求交给 AMS,AMS 再通知 Service 进程执行绑定。服务端发布 Binder 后,AMS 还会沿保存的连接回调客户端,因此观察驱动事务时会看到第三次跨进程传递。
客户端实现 ServiceConnection,在 onCreate 中发起绑定,并在 onServiceConnected 中把收到的 IBinder 包装成 UserAidl:
// 打开 MainActivity 类定义,并声明它实现 ServiceConnectionpublic class MainActivity extends AppCompatActivity implements ServiceConnection { // 说明客户端需要保存 AIDL 接口,绑定成功后才能调用服务方法 // 客户端一定要获取 aidl 的实例 // 绑定成功后保存 AIDL 接口,供后续业务调用 private UserAidl userAidl; // 创建 ServiceConnection 回调对象,接收绑定与断开事件 private ServiceConnection mServiceConn = new ServiceConnection() { // 标记 `onServiceConnected` 覆写父类或接口中的同名方法 @Override // 定义 `onServiceConnected`,接收服务端返回的 IBinder,并建立客户端 AIDL 代理 public void onServiceConnected(ComponentName name, IBinder service) { // 说明 service 参数就是绑定完成后收到的服务端 IBinder // 链接好了 service 就是服务端给我们的 IBinder // 把服务端 IBinder 包装为 UserAidl,并保存到 userAidl userAidl = UserAidl.Stub.asInterface(service); // 结束 onServiceConnected 方法 } // 标记 `onServiceDisconnected` 覆写父类或接口中的同名方法 @Override // 定义 `onServiceDisconnected`,处理服务连接意外断开的通知 public void onServiceDisconnected(ComponentName name) { // 说明此回调处理 Service 连接断开的情况 // 断开链接 // 结束 onServiceDisconnected 方法 } // 结束 ServiceConnection 匿名实现,同时结束匿名对象定义 }; // 标记 `onCreate` 覆写父类或接口中的同名方法 @Override // 定义 `onCreate`,初始化 Activity 并发起 Service 绑定 protected void onCreate(Bundle savedInstanceState) { // 调用父类 onCreate,完成 Activity 基础初始化 super.onCreate(savedInstanceState); // 加载 activity_main 作为当前 Activity 的界面 setContentView(R.layout.activity_main); // 构造指向 MessageService 的显式 Intent Intent intent = new Intent(this, MessageService.class); // 发起绑定,BIND_AUTO_CREATE 表示目标进程不存在时由系统拉起 bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE); // 结束 onCreate 方法 }// 结束 MainActivity 类}AIDL 只声明客户端与服务端共同认可的方法集合:
// 定义 `UserAidl` AIDL 接口,约定客户端可调用的方法interface UserAidl { // 声明跨进程获取用户名的方法 String getUserName(); // 声明跨进程获取密码的方法 String getUserPassword();// 结束 UserAidl 接口}独立进程来自 Manifest 中的 Service 声明:
<!-- 开始声明 MessageService 组件 --><service <!-- 把 Service 实现类指定为 MessageService --> android:name=".MessageService" <!-- 让 MessageService 运行在应用的 message 私有进程并结束组件声明 --> android:process=":message" />
1.2 为什么不能直接把 ServiceConnection 递给 AMS
ServiceConnection 是应用侧的普通 Java 回调对象。它的引用值只在客户端虚拟地址空间内有效,system_server 无法解引用这段地址。框架因此增加了一层可跨进程表达的接口:IServiceConnection。客户端交给 AMS 的对象是 LoadedApk.ServiceDispatcher.InnerConnection,它继承 IServiceConnection.Stub;ServiceDispatcher 仍持有原始 ServiceConnection。AMS 调用 connected 后,请求先到 InnerConnection,再转给应用实现。
InnerConnection 用弱引用找到 ServiceDispatcher,并把连接结果继续分发:
// 打开 InnerConnection 类定义,使它成为可跨进程回调的 IServiceConnection Stub private static class InnerConnection extends IServiceConnection.Stub { // 用弱引用保存 ServiceDispatcher,避免 InnerConnection 延长其生命周期 final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher; // 定义 `InnerConnection`,实现 `InnerConnection` 对应的处理逻辑 InnerConnection(LoadedApk.ServiceDispatcher sd) { // 把传入的 ServiceDispatcher 包装成弱引用保存 mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd); // 结束 InnerConnection 构造函数 } // 定义 `connected`,把 AMS 的连接结果转发给 ServiceDispatcher public void connected(ComponentName name, IBinder service) throws RemoteException { // 从弱引用中取出仍然存活的 ServiceDispatcher LoadedApk.ServiceDispatcher sd = mDispatcher.get(); // 仅当 `sd != null` 成立时执行该分支 if (sd != null) { // 把组件名和服务 Binder 转交给 ServiceDispatcher sd.connected(name, service); // 结束 if (sd != null) 分支 } // 结束 connected 方法 } // 结束 InnerConnection 类 }这层转换建立了可供 AMS 调用的回调入口,但 Java 引用仍不能直接跨过进程边界。Stub 继承自 Binder,其 native 对象会被写进 Parcel,再由驱动转换成目标进程可用的 handle。
1.3 bindServiceCommon 里先做的那一步:getServiceDispatcher
ContextImpl.bindService 只负责转入公共实现。以下代码对应 ContextImpl.java 第 1283—1287 行:
// 定义 `bindService`,接收绑定参数并转入 bindServiceCommon public boolean bindService(Intent service, ServiceConnection conn, // 补充绑定标志参数并打开 ContextImpl.bindService 方法体 int flags) { // 检查调用是否来自 system_server,并在不合适时给出警告 warnIfCallingFromSystemProcess(); // 带上当前用户句柄调用 bindServiceCommon,并返回绑定结果 return bindServiceCommon(service, conn, flags, Process.myUserHandle()); // 结束 bindService 方法 }bindServiceCommon 先检查 conn,再调用 mPackageInfo.getServiceDispatcher 取得 IServiceConnection sd。跨进程调用 ActivityManagerNative.getDefault().bindService 时,参数里携带的是 sd,原始 conn 没有直接进入 AMS。代码位置为 ContextImpl.java 第 1296—1329 行:
// 定义 `bindServiceCommon`,把 ServiceConnection 包装为 Binder 接口并请求 AMS 绑定 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, // 补充目标用户参数并打开 bindServiceCommon 方法体 UserHandle user) { // 声明跨进程传给 AMS 的 IServiceConnection IServiceConnection sd; // 仅当 `conn == null` 成立时执行该分支 if (conn == null) { // conn 为空时抛出参数异常,阻止无回调对象的绑定 throw new IllegalArgumentException("connection is null"); // 结束 if (conn == null) 分支 } // 仅当 `mPackageInfo != null` 成立时执行该分支 if (mPackageInfo != null) { // 用原始 ServiceConnection 和 Context 创建 ServiceDispatcher sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), // 传入主线程 Handler 与绑定标志,完成 IServiceConnection 包装 mMainThread.getHandler(), flags); // 结束 if (mPackageInfo != null) 分支,条件未命中时进入 else 分支 } else { // 缺少 LoadedApk 时拒绝在 system context 中执行该绑定路径 throw new RuntimeException("Not supported in system context"); // 结束 else 分支 } // 校验 Service Intent 是否满足跨进程调用要求 validateServiceIntent(service); // 开始执行可能抛出异常的 bindServiceCommon 参数包装与 AMS 调用 调用 try { // 取得当前 Activity token,供 AMS 识别绑定来源 IBinder token = getActivityToken(); // 仅当 `token == null && (flags&BIND_AUTO_CREATE` 成立时执行该分支 if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null // 继续读取应用 targetSdkVersion,补全旧版本兼容条件 && mPackageInfo.getApplicationInfo().targetSdkVersion // 确认目标版本低于 ICE_CREAM_SANDWICH 后进入兼容分支 < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { // 为旧目标版本且无 Activity token 的调用补上 BIND_WAIVE_PRIORITY flags |= BIND_WAIVE_PRIORITY; // 结束 旧版本无 Activity token 的兼容分支 } // 在 Intent 离开客户端进程前完成跨进程准备 service.prepareToLeaveProcess(); // 调用 AMS 的 bindService,并准备把返回码保存到 res int res = ActivityManagerNative.getDefault().bindService( // 向 AMS 传入应用线程、Activity token 与 Service Intent mMainThread.getApplicationThread(), getActivityToken(), service, // 解析并传入 Intent 的 MIME 类型 service.resolveTypeIfNeeded(getContentResolver()), // 传入 IServiceConnection、绑定标志、包名和 userId,完成 AMS 调用 sd, flags, getOpPackageName(), user.getIdentifier()); // 仅当 `res < 0` 成立时执行该分支 if (res < 0) { // AMS 返回负数时准备抛出安全异常 throw new SecurityException( // 把被拒绝的 Service 写入安全异常消息 "Not allowed to bind to service " + service); // 结束 if (res < 0) 分支 } // 把 AMS 返回码转换为 bindService 的布尔结果 return res != 0; // 结束 try 调用段,捕获 `RemoteException e` 后进入错误处理 } catch (RemoteException e) { // 把跨进程 RemoteException 包装为运行时异常抛出 throw new RuntimeException("Failure from system", e); // 结束 RemoteException e 处理分支 } // 结束 bindServiceCommon 方法 }追踪 Binder 对象时应从 sd 开始。应用传入的回调由 ServiceDispatcher 保存,跨进程层处理的是 InnerConnection 对应的 Stub。
二、Java 对象在 native 层长什么样
2.1 Binder() 构造函数里的 init()
IServiceConnection.Stub 最终继承 Binder。构造这个 Java 对象时,Binder() 会调用 native init(),于是 Java 实例刚创建便有了 native 侧的关联对象。Binder.java 第 199—200 行只展示了入口:
// 定义 `Binder`,构造 Java Binder 对象并初始化 native 关联 public Binder() { // 调用 native init,为 Java Binder 建立 native 侧关联 init();这里需要区分四层表示。Java 本地端是 Binder,远端代理是 BinderProxy;native 本地端是 BBinder,远端代理是 BpBinder;写入 Parcel 后使用 flat_binder_object;进入驱动后则由 binder_node 和 binder_ref 管理所有权与引用。它们描述同一条通信关系,却不共享同一种对象布局。
2.2 android_os_Binder_init:JavaBBinderHolder 与 mObject
JNI 初始化函数创建 JavaBBinderHolder,增加强引用后,把指针写入 Java Binder 的 mObject 字段。代码位置为 android_util_Binder.cpp 第 805—815 行:
// 定义 `android_os_Binder_init`,创建 JavaBBinderHolder 并回写 Java Binderstatic void android_os_Binder_init(JNIEnv* env, jobject obj)// 打开 android_os_Binder_init 函数体{ // 分配 JavaBBinderHolder,准备保存 Java Binder 对应的 BBinder JavaBBinderHolder* jbh = new JavaBBinderHolder(); // 仅当 `jbh == NULL` 成立时执行该分支 if (jbh == NULL) { // holder 分配失败时向 Java 层抛出 OutOfMemoryError jniThrowException(env, "java/lang/OutOfMemoryError", NULL); // 内存分配失败后立即结束 Binder 初始化 return; // 结束 if (jbh == NULL) 分支 } // 输出 Java Binder 与 holder 地址,便于跟踪首次强引用 ALOGV("Java Binder %p: acquiring first ref on holder %p", obj, jbh); // 为 holder 增加强引用,避免初始化后被提前释放 jbh->incStrong((void*)android_os_Binder_init); // 把 holder 指针写入 Java Binder 的 mObject 字段 env->SetLongField(obj, gBinderOffsets.mObject, (jlong)jbh);// 结束 android_os_Binder_init 函数}mObject 连接 Java 对象与 native 对象。对于 Java Binder,该字段指向 JavaBBinderHolder;对于 BinderProxy,它指向对应的 native Binder 对象。二者都使用 mObject,取值后的解释方式由 Java 对象的实际类型决定。

2.3 writeStrongBinder:从 Java 对象取出 BBinder,序列化为 flat_binder_object
ibinderForJavaObject 先判断传入对象属于 Binder 还是 BinderProxy。本地 Binder 通过 holder 取得 BBinder,代理对象则从 mObject 读取 native 指针。代码位置为 android_util_Binder.cpp 第 603—620 行:
// 定义 ibinderForJavaObject,按 Java 对象类型取得 native IBindersp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)// 打开 ibinderForJavaObject 函数体{ // Java 对象为空时直接返回空 native Binder if (obj == NULL) return NULL; // 仅当 `env->IsInstanceOf(obj, gBinderOffsets.mClass)` 成立时执行该分支 if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) { // 声明 holder 指针,准备读取 Java Binder 的 mObject JavaBBinderHolder* jbh = (JavaBBinderHolder*) // 从 Java Binder 的 mObject 字段取出 JavaBBinderHolder env->GetLongField(obj, gBinderOffsets.mObject); // holder 存在时取得 BBinder,否则返回空指针 return jbh != NULL ? jbh->get(env, obj) : NULL; // 结束 if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) 分支 } // 仅当 `env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)` 成立时执行该分支 if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) { // 准备把 BinderProxy 的 native 指针转换为 IBinder return (IBinder*) // 从 BinderProxy 的 mObject 字段取出 native IBinder env->GetLongField(obj, gBinderProxyOffsets.mObject); // 结束 if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) 分支 } // 记录无法识别为 Binder 或 BinderProxy 的 Java 对象地址 ALOGW("ibinderForJavaObject: %p is not a Binder object", obj); // 对象既非 Binder 也非 BinderProxy 时返回空指针 return NULL;// 结束 ibinderForJavaObject 函数}Parcel.writeStrongBinder 的 JNI 实现把这个 IBinder 交给 native Parcel:
// 定义 `android_os_Parcel_writeStrongBinder`,把 Java Binder 转为 native IBinder 后写入 Parcelstatic void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)// 打开 android_os_Parcel_writeStrongBinder 函数体{ // 把 Java 保存的 nativePtr 还原为 Parcel 指针 Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); // 仅当 `parcel != NULL` 成立时执行该分支 if (parcel != NULL) { // 取得 object 对应的 IBinder,并以强引用形式写入 Parcel const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object)); // 仅当 `err != NO_ERROR` 成立时执行该分支 if (err != NO_ERROR) { // 写入失败时把 native 错误转换为 Java 异常 signalExceptionForError(env, clazz, err); // 结束 if (err != NO_ERROR) 分支 } // 结束 if (parcel != NULL) 分支 }// 结束 android_os_Parcel_writeStrongBinder 函数}客户端写入本地 Binder 时,传输结构的类型是 BINDER_TYPE_BINDER,并携带对象地址与 cookie。强引用和弱引用分别对应 Binder 与 Weak Binder 类型;驱动会在处理事务时识别这些类型,并把接收方看到的表示改成 handle。
2.4 跨进程传的是地址,地址在另一个进程里无效
flat_binder_object 中的地址可以帮助驱动确认发送进程暴露的是哪个本地 Binder,却不能交给 AMS 直接使用。进程拥有独立的虚拟地址空间,同一个数值在另一个进程中没有原对象的含义。驱动必须为这个地址建立 binder_node,再在目标进程创建指向该 node 的 binder_ref。目标进程得到的是 binder_ref.desc,也就是 handle。
这里的转换不会序列化出另一个 Java 对象。驱动保存对象归属关系,并为每个目标进程建立独立引用;Java 层随后基于该引用创建代理。

三、客户端进入驱动:BINDER_TYPE_BINDER 变成 handle
3.1 transact 到 ioctl(BINDER_WRITE_READ) 的路径
native 调用沿 IPCThreadState::transact 进入 talkWithDriver。后者准备 binder_write_read:write_buffer 指向待发送命令,接收缓冲区可用时再填写 read_buffer。随后通过 ioctl(fd, BINDER_WRITE_READ, &bwr) 与驱动交换数据。IPCThreadState.cpp 第 803—846 行保留了到无读写数据时返回的位置:
// 定义 `talkWithDriver`,组织 binder_write_read 并与 Binder 驱动交换数据status_t IPCThreadState::talkWithDriver(bool doReceive)// 打开 talkWithDriver 函数体{ // 仅当 `mProcess->mDriverFD <= 0` 成立时执行该分支 if (mProcess->mDriverFD <= 0) { // 把 `-EBADF` 作为当前函数的返回结果 return -EBADF; // 结束 if (mProcess->mDriverFD <= 0) 分支 } // 声明 `bwr`,保存 与 Binder 驱动交换读写缓冲区 的中间状态 binder_write_read bwr; // 说明 needRead 用于判断输入缓冲区是否已经读空 // Is the read buffer empty? // 把 `mIn.dataPosition() >= mIn.dataSize()` 的结果写入 `needRead`,记录输入缓冲区是否已经读空 const bool needRead = mIn.dataPosition() >= mIn.dataSize(); // 说明输入缓冲区仍有数据时暂不向驱动写入新命令 // We don't want to write anything if we are still reading // 补充前一条说明:调用方仍在消费输入缓冲区的剩余数据 // from data left in the input buffer and the caller // 补充前一条说明:调用方同时请求读取下一批驱动数据 // has requested to read the next data. // 把 `(!doReceive || needRead) ? mOut.dataSize() : 0` 的结果写入 `outAvail`,决定本次允许写给驱动的字节数 const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; // 把 `outAvail` 的结果写入 `bwr.write_size`,设置 ioctl 本次提交的写缓冲区长度 bwr.write_size = outAvail; // 把 `(uintptr_t)mOut.data()` 的结果写入 `bwr.write_buffer`,设置 ioctl 本次提交的写缓冲区地址 bwr.write_buffer = (uintptr_t)mOut.data(); // 说明下方为本次 ioctl 配置接收缓冲区 // This is what we'll read. // 仅当 `doReceive && needRead` 成立时执行该分支 if (doReceive && needRead) { // 把 `mIn.dataCapacity()` 的结果写入 `bwr.read_size`,设置 ioctl 本次可接收的数据长度 bwr.read_size = mIn.dataCapacity(); // 把 `(uintptr_t)mIn.data()` 的结果写入 `bwr.read_buffer`,设置 ioctl 接收驱动数据的缓冲区地址 bwr.read_buffer = (uintptr_t)mIn.data(); // 结束 if (doReceive && needRead) 分支,条件未命中时进入 else 分支 } else { // 把 `0` 的结果写入 `bwr.read_size`,设置 ioctl 本次可接收的数据长度 bwr.read_size = 0; // 把 `0` 的结果写入 `bwr.read_buffer`,设置 ioctl 接收驱动数据的缓冲区地址 bwr.read_buffer = 0; // 结束 else 分支 } // 定义 `IF_LOG_COMMANDS`,实现 `IF_LOG_COMMANDS` 对应的处理逻辑 IF_LOG_COMMANDS() { // 创建日志输出作用域,把本段命令详情写入 alog TextOutput::Bundle _b(alog); // 仅当 `outAvail != 0` 成立时执行该分支 if (outAvail != 0) { // 输出即将发送给 Binder 驱动的命令标题并增加缩进 alog << "Sending commands to driver: " << indent; // 把 `(const void*)bwr.write_buffer` 的结果写入 `cmds`,让命令游标指向写缓冲区起点 const void* cmds = (const void*)bwr.write_buffer; // 把 `((const uint8_t*)cmds)+bwr.write_size` 的结果写入 `end`,计算写缓冲区末尾,作为命令解析边界 const void* end = ((const uint8_t*)cmds)+bwr.write_size; // 按十六进制输出本次写缓冲区的完整内容 alog << HexDump(cmds, bwr.write_size) << endl; // 逐条解析写缓冲区命令,并把游标推进到下一条命令 while (cmds < end) cmds = printCommand(alog, cmds); // 结束命令明细输出并恢复日志缩进 alog << dedent; // 结束 if (outAvail != 0) 分支 } // 开始输出接收缓冲区大小及本次读写状态 alog << "Size of receive buffer: " << bwr.read_size // 补充 needRead 与 doReceive 的值并结束状态日志 << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; // 结束 Binder 命令日志输出段 } // 说明读写长度都为零时可以直接结束本次驱动交互 // Return immediately if there is nothing to do. // 仅当 `(bwr.write_size == 0) && (bwr.read_size == 0)` 成立时执行该分支 if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;驱动写侧继续进入 binder_thread_write,事务命令由 binder_transaction 处理。此时,Parcel 中的 flat_binder_object 进入 node/ref 转换阶段。
3.2 binder_get_node / binder_new_node:在本进程 nodes 树里按 ptr 查或建 binder_node
每个打开 /dev/binder 的进程都有一个 binder_proc。其中 nodes 是按本进程 Binder 地址组织的红黑树;refs_by_desc 按 handle 索引外部引用,refs_by_node 按目标 node 索引同一批引用。threads、todo 与 wait 分别保存线程状态、待处理工作和等待队列。
Linux 3.18 的 binder_proc 定义把这些进程级索引、工作队列与等待队列集中在同一个结构体中:
// 定义 Binder 驱动为单个用户进程维护的状态结构struct binder_proc { // 把当前进程挂入驱动维护的 binder_proc 哈希链表 struct hlist_node proc_node; // 按线程标识组织当前进程的 Binder 线程红黑树 struct rb_root threads; // 按本地 Binder 地址组织当前进程导出的 binder_node 红黑树 struct rb_root nodes; // 按 handle 组织当前进程持有的 binder_ref 红黑树 struct rb_root refs_by_desc; // 按目标 binder_node 组织同一批 binder_ref 的红黑树 struct rb_root refs_by_node; // 保存当前 Binder 进程的进程号 int pid; // 保存 Binder 映射对应的用户虚拟内存区域 struct vm_area_struct *vma; // 保存建立 Binder 映射时对应的内存描述符 struct mm_struct *vma_vm_mm; // 保存代表该进程的任务结构 struct task_struct *tsk; // 保存该进程打开的文件表 struct files_struct *files; // 把延迟工作挂入驱动的 deferred_work 链表 struct hlist_node deferred_work_node; // 记录当前进程待执行的延迟工作位标志 int deferred_work; // 保存驱动为该进程分配的 Binder 缓冲区起始地址 void *buffer; // 保存内核缓冲区地址到用户地址的换算偏移 ptrdiff_t user_buffer_offset; // 串联当前进程管理的全部 Binder 缓冲区 struct list_head buffers; // 按地址组织当前进程的空闲 Binder 缓冲区 struct rb_root free_buffers; // 按地址组织当前进程已经分配的 Binder 缓冲区 struct rb_root allocated_buffers; // 记录异步事务仍可使用的缓冲区空间 size_t free_async_space; // 保存 Binder 映射所使用的物理页数组 struct page **pages; // 记录该进程 Binder 映射缓冲区的总大小 size_t buffer_size; // 标记 Binder 缓冲区是否已经释放 uint32_t buffer_free; // 保存尚未分配给具体线程的进程级 Binder 工作 struct list_head todo; // 供 Binder 线程在进程级工作为空时阻塞和被唤醒 wait_queue_head_t wait; // 汇总当前 Binder 进程的驱动统计数据 struct binder_stats stats; // 串联已经投递给用户态的死亡通知 struct list_head delivered_death; // 保存用户态允许驱动创建的最大 Binder 线程数 int max_threads; // 记录驱动已经请求用户态创建的线程数量 int requested_threads; // 记录已响应创建请求并启动的线程数量 int requested_threads_started; // 记录当前可接收进程级工作的就绪线程数 int ready_threads; // 保存该进程 Binder 线程使用的默认调度优先级 long default_priority; // 保存 binder_proc 对应的 debugfs 目录项 struct dentry *debugfs_entry;// 结束 binder_proc 结构体定义};驱动在发送进程的 nodes 树中按 fp->binder 查找。没有匹配项时,binder_new_node 分配节点,将 ptr 和 cookie 写入,并插入红黑树。代码位置为 binder.c 第 862—919 行:
// 定义 `binder_get_node`,按对象地址从当前进程的 nodes 树查找 binder_nodestatic struct binder_node *binder_get_node(struct binder_proc *proc, // 补充 ptr 参数,用于接收待查找的本地 Binder 地址 binder_uintptr_t ptr)// 打开 binder_get_node 函数体{ // 让 n 指向当前进程 nodes 红黑树的根节点 struct rb_node *n = proc->nodes.rb_node; // 声明 node,用于保存查找或创建得到的 binder_node struct binder_node *node; // 沿红黑树继续查找 `n` 对应位置 while (n) { // 把 `rb_entry(n, struct binder_node, rb_node)` 的结果写入 `node`,保存查询或创建得到的 binder_node node = rb_entry(n, struct binder_node, rb_node); // 仅当 `ptr < node->ptr` 成立时执行该分支 if (ptr < node->ptr) // 把 `n->rb_left` 的结果写入 `n`,移动红黑树遍历游标 n = n->rb_left; // 前一条件不成立时继续检查 `else if (ptr > node->ptr)` else if (ptr > node->ptr) // 把 `n->rb_right` 的结果写入 `n`,移动红黑树遍历游标 n = n->rb_right; // 前一条件不成立时执行紧随其后的单行分支 else // 把 `node` 作为当前函数的返回结果 return node; // 结束 while (n) 循环 } // 把 `NULL` 作为当前函数的返回结果 return NULL;// 结束 binder_get_node 函数} // 定义 `binder_new_node`,为本地 Binder 地址创建并登记 binder_nodestatic struct binder_node *binder_new_node(struct binder_proc *proc, // 补充 ptr 参数,记录新 binder_node 对应的本地 Binder 地址 binder_uintptr_t ptr, // 补充 cookie 参数,保存回到本进程时恢复对象所需的地址 binder_uintptr_t cookie)// 打开 binder_new_node 函数体{ // 让 p 指向 nodes 红黑树根指针,供插入新节点时更新链接 struct rb_node **p = &proc->nodes.rb_node; // 初始化 parent,遍历时记录新节点的父节点 struct rb_node *parent = NULL; // 声明 node,用于保存查找或创建得到的 binder_node;此处承接 `struct rb_node *parent = NULL;` 后的 `struct binder_node *node;` struct binder_node *node; // 沿红黑树继续查找 `p` 对应位置 while (*p) { // 把 `*p` 的结果写入 `parent`,记录当前红黑树节点,供新节点挂接 parent = *p; // 把 `rb_entry(parent, struct binder_node, rb_node)` 的结果写入 `node`,保存查询或创建得到的 binder_node node = rb_entry(parent, struct binder_node, rb_node); // 仅当 `ptr < node->ptr` 成立时执行该分支;此处承接 `node = rb_entry(parent, struct binder_node, rb_node);` 后的 `if (ptr < node->ptr)` if (ptr < node->ptr) // 把 `&(*p)->rb_left` 的结果写入 `p`,更新红黑树下一步查找或插入的位置 p = &(*p)->rb_left; // 前一条件不成立时继续检查 `else if (ptr > node->ptr)`;此处承接 `p = &(*p)->rb_left;` 后的 `else if (ptr > node->ptr)` else if (ptr > node->ptr) // 把 `&(*p)->rb_right` 的结果写入 `p`,更新红黑树下一步查找或插入的位置 p = &(*p)->rb_right; // 前一条件不成立时执行紧随其后的单行分支;此处承接 `p = &(*p)->rb_right;` 后的 `else` else // 把 `NULL` 作为当前函数的返回结果;此处承接 `else` 后的 `return NULL;` return NULL; // 结束 while (*p) 循环 } // 把 `kzalloc(sizeof(*node), GFP_KERNEL)` 的结果写入 `node`,保存查询或创建得到的 binder_node node = kzalloc(sizeof(*node), GFP_KERNEL); // 仅当 `node == NULL` 成立时执行该分支 if (node == NULL) // 把 `NULL` 作为当前函数的返回结果;此处承接 `if (node == NULL)` 后的 `return NULL;` return NULL; // 增加 Binder node 的创建统计计数 binder_stats_created(BINDER_STAT_NODE); // 把新 node 挂到 parent 下由 p 指定的位置 rb_link_node(&node->rb_node, parent, p); // 对插入 node 后的 nodes 红黑树重新着色并保持平衡 rb_insert_color(&node->rb_node, &proc->nodes); // 把 `++binder_last_id` 的结果写入 `node->debug_id`,为新 binder_node 分配递增的调试编号 node->debug_id = ++binder_last_id; // 把 `proc` 的结果写入 `node->proc`,记录 binder_node 归属的 binder_proc node->proc = proc; // 把 `ptr` 的结果写入 `node->ptr`,保存本地 Binder 对象地址 node->ptr = ptr; // 把 `cookie` 的结果写入 `node->cookie`,保存回到所属进程时恢复对象的 cookie node->cookie = cookie; // 把 `BINDER_WORK_NODE` 的结果写入 `node->work.type`,把 node 工作项标记为 BINDER_WORK_NODE node->work.type = BINDER_WORK_NODE; // 初始化 node 工作项的链表节点 INIT_LIST_HEAD(&node->work.entry); // 初始化 node 的异步事务待办队列 INIT_LIST_HEAD(&node->async_todo); // 开始输出新建 binder_node 的内部引用调试日志 binder_debug(BINDER_DEBUG_INTERNAL_REFS, // 设置日志格式,依次记录进程、node id、ptr 与 cookie "%d:%d node %d u%016llx c%016llx created\n", // 向调试日志传入进程 pid、线程 pid 与 node 调试编号 proc->pid, current->pid, node->debug_id, // 向调试日志传入 node 的 ptr 与 cookie 地址值 (u64)node->ptr, (u64)node->cookie); // 把 `node` 作为当前函数的返回结果;此处承接 `(u64)node->ptr, (u64)node->cookie);` 后的 `return node;` return node;// 结束 binder_new_node 函数}ptr 来自本地对象的弱引用控制块地址,cookie 来自本地对象自身的地址,二者本来就是两个不同对象的地址,不会重合。ptr 只用于驱动内部按地址查找或建立 binder_node;事务回到对象所属进程时,真正用来恢复本地对象的是 cookie。flatten_binder 写入这两个字段时就明确区分了来源:
// 本地 Binder 没有已登记 handle 时写入本地对象分支 } else { // 把传输对象标记为本地强 Binder obj.type = BINDER_TYPE_BINDER; // 把本地对象的弱引用控制块地址写入 binder 字段 obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs()); // 把本地 Binder 对象自身地址写入 cookie 字段 obj.cookie = reinterpret_cast<uintptr_t>(local); // 结束本地 Binder 对象序列化分支 }3.3 binder_get_ref_for_node:在目标进程建 binder_ref,desc 就是 handle
node 属于 Binder 对象所在进程。AMS 要引用这个 node,驱动就在 AMS 的 binder_proc 中创建 binder_ref。binder_get_ref_for_node 先按 node 查 refs_by_node;不存在时分配新 ref,并为它计算 desc。service manager 使用 0,普通引用从 1 开始递增:
// 定义 `binder_get_ref_for_node`,在目标进程创建指向 node 的 binder_refstatic struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, // 补充 node 参数,指定目标进程需要引用的 Binder 节点 struct binder_node *node)// 打开 binder_get_ref_for_node 函数体{ // 声明 n,用于遍历 refs_by_desc 红黑树 struct rb_node *n; // 让 p 指向目标进程 refs_by_node 红黑树的根指针 struct rb_node **p = &proc->refs_by_node.rb_node; // 初始化 parent,记录新 binder_ref 的插入父节点 struct rb_node *parent = NULL; // 声明 ref 与 new_ref,分别保存遍历结果和待创建引用 struct binder_ref *ref, *new_ref; // 沿红黑树继续查找 `p` 对应位置 while (*p) { // 把 `*p` 的结果写入 `parent`,记录当前红黑树节点,供新节点挂接 parent = *p; // 把 `rb_entry(parent, struct binder_ref, rb_node_node)` 的结果写入 `ref`,保存按 node 或 handle 查到的 binder_ref ref = rb_entry(parent, struct binder_ref, rb_node_node); // 仅当 `node < ref->node` 成立时执行该分支 if (node < ref->node) // 把 `&(*p)->rb_left` 的结果写入 `p`,更新红黑树下一步查找或插入的位置 p = &(*p)->rb_left; // 前一条件不成立时继续检查 `else if (node > ref->node)` else if (node > ref->node) // 把 `&(*p)->rb_right` 的结果写入 `p`,更新红黑树下一步查找或插入的位置 p = &(*p)->rb_right; // 前一条件不成立时执行紧随其后的单行分支 else // 把 `ref` 作为当前函数的返回结果 return ref; // 结束 while (*p) 循环 } // 把 `kzalloc(sizeof(*ref), GFP_KERNEL)` 的结果写入 `new_ref`,保存目标进程中新建或复用的 binder_ref new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); // 仅当 `new_ref == NULL` 成立时执行该分支 if (new_ref == NULL) // 把 `NULL` 作为当前函数的返回结果 return NULL; // 增加 Binder ref 的创建统计计数 binder_stats_created(BINDER_STAT_REF); // 把 `++binder_last_id` 的结果写入 `new_ref->debug_id`,为新 binder_ref 分配递增的调试编号 new_ref->debug_id = ++binder_last_id; // 把 `proc` 的结果写入 `new_ref->proc`,记录 binder_ref 所属的目标进程 new_ref->proc = proc; // 把 `node` 的结果写入 `new_ref->node`,让 binder_ref 指向被引用的 binder_node new_ref->node = node; // 把 new_ref 按 node 索引挂入目标位置 rb_link_node(&new_ref->rb_node_node, parent, p); // 重新平衡目标进程的 refs_by_node 红黑树 rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); // 把 `(node == binder_context_mgr_node) ? 0 : 1` 的结果写入 `new_ref->desc`,设置目标进程内使用的 handle 值 new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1; // 按 desc 顺序遍历 refs_by_desc,为新引用寻找可用 handle for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { // 把 `rb_entry(n, struct binder_ref, rb_node_desc)` 的结果写入 `ref`,保存按 node 或 handle 查到的 binder_ref ref = rb_entry(n, struct binder_ref, rb_node_desc); // 仅当 `ref->desc > new_ref->desc` 成立时执行该分支 if (ref->desc > new_ref->desc) // 发现 handle 空档后停止遍历 refs_by_desc break; // 把 `ref->desc + 1` 的结果写入 `new_ref->desc`,设置目标进程内使用的 handle 值 new_ref->desc = ref->desc + 1; // 结束 refs_by_desc 的 handle 分配循环 }同一个 node 进入不同进程时,各进程分别分配 desc。因此 handle 是进程内的引用编号,不是 Binder 对象的全局编号。
3.4 flat_binder_object
binder_transaction 的 Binder 分支把发送进程的地址表示换成目标进程的引用表示。它先查找或创建 node,核对 cookie,再调用 binder_get_ref_for_node(target_proc, node)。强 Binder 的 type 变成 BINDER_TYPE_HANDLE,handle 填入 ref->desc;弱 Binder 则对应弱 handle。代码位置为 binder.c 第 1528—1568 行:
// 处理 `BINDER_TYPE_BINDER` 类型的 flat_binder_object case BINDER_TYPE_BINDER: // 处理 `BINDER_TYPE_WEAK_BINDER` 类型的 flat_binder_object case BINDER_TYPE_WEAK_BINDER: { // 声明 ref,用于保存目标进程中指向当前 node 的引用 struct binder_ref *ref; // 按 fp->binder 地址查询发送进程已有的 binder_node struct binder_node *node = binder_get_node(proc, fp->binder); // nodes 树未找到对象时进入 binder_node 创建流程 if (node == NULL) { // 把 `binder_new_node(proc, fp->binder, fp->cookie)` 的结果写入 `node`,保存查询或创建得到的 binder_node node = binder_new_node(proc, fp->binder, fp->cookie); // binder_node 分配失败时准备返回错误 if (node == NULL) { // 把 `BR_FAILED_REPLY` 的结果写入 `return_error`,记录事务失败码,供错误路径返回给发送方 return_error = BR_FAILED_REPLY; // 跳转到 `err_binder_new_node_failed`,统一执行当前错误清理路径 goto err_binder_new_node_failed; // 结束 binder_node 创建失败分支 } // 把 `fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK` 的结果写入 `node->min_priority`,保存发送对象允许的最低调度优先级 node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; // 把 `!!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS)` 的结果写入 `node->accept_fds`,记录该 Binder 节点是否接受文件描述符 node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); // 结束 查无 binder_node 后的创建分支 } // 仅当 `fp->cookie != node->cookie` 成立时执行该分支 if (fp->cookie != node->cookie) { // 开始记录 ptr 对应 node 与本次 cookie 不一致的用户错误 binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n", // 向错误日志传入发送进程 pid 与当前 Binder 线程 pid proc->pid, thread->pid, // 向错误日志传入 Binder 地址与 node 调试编号 (u64)fp->binder, node->debug_id, // 向错误日志传入本次 cookie 与 node 已保存的 cookie (u64)fp->cookie, (u64)node->cookie); // 把 `BR_FAILED_REPLY` 的结果写入 `return_error`,记录事务失败码,供错误路径返回给发送方;此处承接 `(u64)fp->cookie, (u64)node->cookie);` 后的 `return_error = BR_FAILED_REPLY;` return_error = BR_FAILED_REPLY; // 跳转到 `err_binder_get_ref_for_node_failed`,统一执行当前错误清理路径 goto err_binder_get_ref_for_node_failed; // 结束 if (fp->cookie != node->cookie) 分支 } // 把 `binder_get_ref_for_node(target_proc, node)` 的结果写入 `ref`,保存按 node 或 handle 查到的 binder_ref ref = binder_get_ref_for_node(target_proc, node); // 仅当 `ref == NULL` 成立时执行该分支 if (ref == NULL) { // 把 `BR_FAILED_REPLY` 的结果写入 `return_error`,记录事务失败码,供错误路径返回给发送方;此处承接 `if (ref == NULL) {` 后的 `return_error = BR_FAILED_REPLY;` return_error = BR_FAILED_REPLY; // 跳转到 `err_binder_get_ref_for_node_failed`,统一执行当前错误清理路径;此处承接 `return_error = BR_FAILED_REPLY;` 后的 `goto err_binder_get_ref_for_node_failed;` goto err_binder_get_ref_for_node_failed; // 结束 if (ref == NULL) 分支 } // 仅当 `fp->type == BINDER_TYPE_BINDER` 成立时执行该分支 if (fp->type == BINDER_TYPE_BINDER) // 把 `BINDER_TYPE_HANDLE` 的结果写入 `fp->type`,改写 flat_binder_object 的对象类型 fp->type = BINDER_TYPE_HANDLE; // 前一条件不成立时执行紧随其后的单行分支 else // 把 `BINDER_TYPE_WEAK_HANDLE` 的结果写入 `fp->type`,改写 flat_binder_object 的对象类型 fp->type = BINDER_TYPE_WEAK_HANDLE; // 把 `ref->desc` 的结果写入 `fp->handle`,把目标进程的 handle 写入传输对象 fp->handle = ref->desc; // 按强 handle 或弱 handle 类型增加目标 binder_ref 的引用 binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, // 把可能产生的引用工作项关联到当前线程 todo 队列 &thread->todo); // 记录本次事务从 binder_node 转为 binder_ref 的跟踪事件 trace_binder_transaction_node_to_ref(t, node, ref); // 开始输出 node 转 ref 的事务调试日志 binder_debug(BINDER_DEBUG_TRANSACTION, // 设置日志格式,展示 node 地址到 ref handle 的映射 " node %d u%016llx -> ref %d desc %d\n", // 向事务日志传入 node 调试编号与本地对象地址 node->debug_id, (u64)node->ptr, // 向事务日志传入 ref 调试编号与分配出的 handle ref->debug_id, ref->desc); // 结束 BINDER_TYPE_BINDER 与 BINDER_TYPE_WEAK_BINDER 的转换分支,并退出 switch } break;改写后的对象随事务进入 AMS。AMS 不需要,也不能访问客户端的原始地址;handle 已经能在 AMS 自己的 refs_by_desc 中定位 binder_ref,后者继续指向客户端拥有的 binder_node。

3.5 唤醒目标进程
完成对象转换后,驱动把事务工作加入目标 todo,把完成通知加入发送线程的 todo。目标存在等待队列时,wake_up_interruptible(target_wait) 唤醒接收线程。代码位置为 binder.c 第 1675—1681 行:
// 把目标工作项标记为 Binder 事务 t->work.type = BINDER_WORK_TRANSACTION; // 把事务工作追加到目标线程或进程的 todo 队列 list_add_tail(&t->work.entry, target_list); // 把发送方完成项标记为事务完成通知 tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; // 把完成通知加入发送线程的 todo 队列 list_add_tail(&tcomplete->entry, &thread->todo); // 仅当 `target_wait` 成立时执行该分支 if (target_wait) // 唤醒等待在目标队列上的 Binder 线程 wake_up_interruptible(target_wait); // 事务与完成通知入队后结束 binder_transaction return;此时“发给 AMS”已有精确含义:数据被放入 AMS 对应的待处理队列,阻塞中的 Binder 线程在唤醒条件满足后继续读取。
四、AMS 侧:handle 又变回 Java 对象
4.1 binder_thread_read 读到 BINDER_TYPE_HANDLE
AMS 的 Binder 线程从驱动读取事务。flat_binder_object 此时携带 BINDER_TYPE_HANDLE,驱动可以用 handle 在 AMS 的 refs_by_desc 树中取得 binder_ref,并沿 ref->node 找到客户端创建的 node。用户空间拿到的是对远端 node 的引用表达,不会获得可直接解引用的客户端地址。
这次翻译(fp->type 改写成 BINDER_TYPE_HANDLE、fp->handle = ref->desc)已经在 3.4 节的 binder_transaction 写路径里完成,binder_thread_read 只是把改写后的数据交给 AMS 用户态,本身不会再查一次 refs_by_desc。
4.2 BpBinder 与 BinderProxy:javaObjectForIBinder 创建代理
native 层为 handle 创建 BpBinder,对象内部保存这个 handle。JNI 再由 javaObjectForIBinder 把 native IBinder 表示转换为 Java 对象;远端 Binder 在 Java 层表现为 BinderProxy,其 mObject 指回 native 对象。AMS 最终拿到 Java 可持有的代理,实际调用仍需经过 BpBinder 和驱动。
ProcessState::getStrongProxyForHandle 为 handle 创建 BpBinder,并把它缓存到 handle 表项中:
// 用当前 handle 创建 native 层的 BpBinder 代理 b = new BpBinder(handle); // 把新建的 BpBinder 缓存到对应 handle 表项 e->binder = b; // 代理创建成功时保存它的弱引用控制块 if (b) e->refs = b->getWeakRefs(); // 把 BpBinder 作为本次查找的强引用结果 result = b;javaObjectForIBinder 随后创建 Java 层 BinderProxy,并把 native IBinder 地址写入它的 mObject:
// 调用 BinderProxy 构造函数创建 Java 代理对象 object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor); // 仅在 BinderProxy 创建成功时继续绑定 native 对象 if (object != NULL) { // 记录为 native Binder 新建 Java 代理对象的调试信息 LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object); // 说明 BinderProxy 会持有 native 对象引用 // The proxy holds a reference to the native object. // 把 native IBinder 地址写入 BinderProxy 的 mObject 字段 env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get()); // 增加 native 对象的强引用,确保 BinderProxy 存活期间对象有效 val->incStrong((void*)javaObjectForIBinder);代理不会复制客户端 InnerConnection 的字段。它只保留指向远端 node 的引用,方法参数和事务码由 AIDL 生成代码封装。
4.3 再包一层 IServiceConnection.Stub.Proxy
AMS 方法签名要求 IServiceConnection,所以 AIDL 的 asInterface 会基于 BinderProxy 建立 IServiceConnection.Stub.Proxy。这个 Proxy 实现接口方法,内部持有远端 IBinder;调用 connected 时,它负责写 Parcel 并触发下一次 Binder 事务。
IServiceConnection 被声明为 oneway。AMS 发出 connected 后不等待客户端返回:
// 声明 `IServiceConnection` 为 oneway 接口,使 connected 调用无需等待返回oneway interface IServiceConnection { // 声明无需等待返回的连接完成回调,并携带组件名和服务 Binder void connected(in ComponentName name, IBinder service);// 结束 IServiceConnection 接口}课程中的 testClick 还显式调用 IServiceConnection.Stub.asInterface(mBinder) 取得代理,再执行 connected(null, mBinder) 观察回调路径;调试器里第二个参数显示为 BpBinder(20)。

五、AMS 请求绑定:第二次跨进程与 node 的复制
5.1 AMS.bindService → bindServiceLocked → requestServiceBindingLocked
客户端代理调用到达 system_server 后,入口是 ActivityManagerService.bindService。它检查调用者与参数,在 AMS 锁内转入 mServices.bindServiceLocked:
// 定义 `bindService`,接收绑定参数并转入 bindServiceCommon public int bindService(IApplicationThread caller, IBinder token, Intent service, // 补充 MIME 类型、连接代理、绑定标志与调用包名参数 String resolvedType, IServiceConnection connection, int flags, String callingPackage, // 补充 userId,并声明该入口可能抛出 TransactionTooLargeException int userId) throws TransactionTooLargeException { // 拒绝隔离进程调用 AMS.bindService enforceNotIsolatedCaller("bindService"); // 说明下方检查用于拒绝 Intent 携带并泄漏文件描述符 // Refuse possible leaked file descriptors // 仅当 `service != null && service.hasFileDescriptors() == true` 成立时执行该分支 if (service != null && service.hasFileDescriptors() == true) { // Intent 携带文件描述符时抛出参数异常 throw new IllegalArgumentException("File descriptors passed in Intent"); // 结束 if (service != null && service.hasFileDescriptors() == true) 分支 } // 仅当 `callingPackage == null` 成立时执行该分支 if (callingPackage == null) { // 调用包名为空时抛出参数异常 throw new IllegalArgumentException("callingPackage cannot be null"); // 结束 if (callingPackage == null) 分支 } // 定义 `synchronized`,实现 `synchronized` 对应的处理逻辑 synchronized(this) { // 在 AMS 锁内把绑定请求转给 ActiveServices return mServices.bindServiceLocked(caller, token, service, // 补齐类型、连接、标志、包名与 userId,并返回绑定结果 resolvedType, connection, flags, callingPackage, userId); // 结束 AMS 同步锁保护段 } // 结束 bindService 方法 }服务记录准备好后,requestServiceBindingLocked 通过应用线程调度 scheduleBindService,让 Service 进程执行绑定。代码位置为 ActiveServices.java 第 1171—1181 行:
// 仅当 `(!i.requested || rebind) && i.apps.size() > 0` 成立时执行该分支 if ((!i.requested || rebind) && i.apps.size() > 0) { // 开始执行可能抛出异常的 AMS 调度 Service 执行 onBind 调用 try { // 把 Service 标记为正在执行 bind,纳入执行超时管理 bumpServiceExecutingLocked(r, execInFg, "bind"); // 把目标进程状态至少提升到 SERVICE 级别 r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE); // 通过应用线程安排 Service 进程执行绑定 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, // 把目标进程上报状态作为 scheduleBindService 的最后参数 r.app.repProcState); // 仅当 `!rebind` 成立时执行该分支 if (!rebind) { // 首次绑定请求发出后记录 requested 状态 i.requested = true; // 结束 if (!rebind) 分支 } // 记录当前 IntentBindRecord 已完成绑定调度 i.hasBound = true; // 清除重新绑定标记,等待本次绑定结果 i.doRebind = false;android:process=":message" 使目标成为独立的 Service 进程。这次事务的方向是 system_server 到 com.example.myapplication:message。
5.2 目标进程由事务的 target handle 决定
这次事务的目标由 handle 指定。binder_transaction 用 tr 里的 target handle 在发送进程的 refs 表中查找 binder_ref,再由 ref 找到 binder_node;target_node->proc 就是目标进程。binder.c 第 1374—1394 行给出了这条目标解析路径:
// 事务没有直接指定本地 binder_node 时按 handle 解析目标 } else { // 非零 target handle 表示目标是发送进程持有的远端引用 if (tr->target.handle) { // 声明 ref,用于保存 target handle 对应的 binder_ref struct binder_ref *ref; // 在发送进程的引用表中查找 target handle ref = binder_get_ref(proc, tr->target.handle); // target handle 无法解析时进入失败分支 if (ref == NULL) { // 记录事务使用无效目标 handle 的错误 binder_user_error("%d:%d got transaction to invalid handle\n", // 向错误日志传入发送进程和 Binder 线程的 pid proc->pid, thread->pid); // 把无效目标 handle 标记为事务失败 return_error = BR_FAILED_REPLY; // 跳转到无效目标 handle 的统一清理路径 goto err_invalid_target_handle; // 结束 target handle 无法解析的失败分支 } // 通过 binder_ref 取得事务真正指向的 binder_node target_node = ref->node; // target handle 为零时改用 Binder 上下文管理节点 } else { // 取得驱动登记的 Binder 上下文管理节点 target_node = binder_context_mgr_node; // 上下文管理节点尚未建立时进入死亡回复分支 if (target_node == NULL) { // 把缺少上下文管理节点标记为目标已死亡 return_error = BR_DEAD_REPLY; // 跳转到无上下文管理节点的统一清理路径 goto err_no_context_mgr_node; // 结束上下文管理节点为空的失败分支 } // 结束 target handle 是否为零的目标选择分支 } // 把目标 binder_node 的调试编号写入事务日志条目 e->to_node = target_node->debug_id; // 由目标 binder_node 的归属确定接收事务的 binder_proc target_proc = target_node->proc;这次 AMS 传给 Service 进程的本地对象是 ServiceRecord token,在 5.1 引用的 requestServiceBindingLocked 里就是 r。它是 AMS 自己新建、尚未导出过的对象,所以走第三节的 BINDER_TYPE_BINDER 路径:驱动在 AMS 的 nodes 树建立 node,在 Service 进程建立 ref,并分配该进程自己的 handle。
目标进程确定、ServiceRecord token 完成 node/ref 转换后,Service 进程才能接收 scheduleBindService 并进入后续绑定流程。
5.3 三个进程各自的三棵红黑树
QQ 音乐进程、AMS 与 QQ 音乐 message 进程各有一个 binder_proc,也各有自己的 nodes、refs_by_desc、refs_by_node。nodes 记录本进程向外暴露的本地 Binder;两棵 refs 树从 handle 和 node 两个索引方向维护远端引用。
因此,课程中某个进程显示 handle 1、另一个进程显示 handle 2,并不矛盾。数字只需在所属进程的 refs_by_desc 命名空间内唯一。跨进程关系由 binder_ref.node 指向共同 node,而不是依赖 handle 数值一致。

六、服务端回传:publishService 与第三次跨进程
6.1 MessageService.onBind 返回 mBinder
Service 进程收到 scheduleBindService 后创建 Service 并调用 onBind。示例返回的 mBinder 是 UserAidl.Stub 实例,它同样继承 Binder,所以返回 AMS 时仍从 BINDER_TYPE_BINDER 开始走 node/ref 转换:
// 打开 MessageService 类定义,使它作为独立进程中的 Service 实现public class MessageService extends Service { // 标记 `onBind` 覆写父类或接口中的同名方法 @Override // 定义 `onBind`,把服务端的 UserAidl Stub 返回给绑定方 public IBinder onBind(Intent intent) { // 把 UserAidl.Stub 实例作为 onBind 的结果返回 return mBinder; // 结束 onBind 方法 } // 创建固定的 UserAidl Stub,承接客户端远程调用 private final UserAidl.Stub mBinder = new UserAidl.Stub() { // 标记 `getUserName` 覆写父类或接口中的同名方法 @Override // 定义 `getUserName`,向客户端返回示例用户名 public String getUserName() throws RemoteException { // 返回示例中约定的用户名 Darren@163.com return "Darren@163.com"; // 结束 getUserName 方法 } // 标记 `getUserPassword` 覆写父类或接口中的同名方法 @Override // 定义 `getUserPassword`,声明密码查询接口,方法体仍待核实 public String getUserPassword() throws RemoteException { // 标明 getUserPassword 的实现信息不完整,不能据此补写业务逻辑 // 课程里没有给出该方法的完整实现,返回值待核实 // 保留 null 返回值,不推断缺失的方法实现 return null; // 结束 getUserPassword 方法 } // 结束 UserAidl.Stub 匿名实现,同时结束匿名对象定义 };// 结束 MessageService 类}getUserName() 返回 "Darren@163.com"。课程没有给出 getUserPassword() 的完整实现,示例里只保留了 return null 和“待核实”注释,不能据此推断服务逻辑。
6.2 publishService → publishServiceLocked → conn.connected
Service 进程通过 publishService 把 mBinder 发回 AMS。AMS 入口检查文件描述符和 token,再交给 publishServiceLocked:
// 定义 `publishService`,校验服务 Binder 后交给 ActiveServices 发布 public void publishService(IBinder token, Intent intent, IBinder service) { // 说明下方检查用于拒绝 Intent 携带并泄漏文件描述符 // Refuse possible leaked file descriptors // 仅当 `intent != null && intent.hasFileDescriptors() == true` 成立时执行该分支 if (intent != null && intent.hasFileDescriptors() == true) { // 抛出 `IllegalArgumentException`,终止当前调用路径 throw new IllegalArgumentException("File descriptors passed in Intent"); // 结束 if (intent != null && intent.hasFileDescriptors() == true) 分支 } // 定义 `synchronized`,实现 `synchronized` 对应的处理逻辑 synchronized(this) { // 仅当 `!(token instanceof ServiceRecord)` 成立时执行该分支 if (!(token instanceof ServiceRecord)) { // token 不是 ServiceRecord 时拒绝发布服务 throw new IllegalArgumentException("Invalid service token"); // 结束 if (!(token instanceof ServiceRecord)) 分支 } // 把合法 token、Intent 和服务 Binder 交给 ActiveServices 发布 mServices.publishServiceLocked((ServiceRecord)token, intent, service); // 结束 AMS 同步锁保护段 } // 结束 publishService 方法 }驱动已经把服务端本地 Binder 转成 AMS 中的 handle,上层对应 BpBinder 和 BinderProxy。publishServiceLocked 遍历相关连接记录,对保存的 c.conn 调用 connected(r.name, service):
// 开始执行可能抛出异常的 AMS 向客户端发送连接回调 调用 try { // 通过保存的 IServiceConnection 把服务 Binder 回调给客户端 c.conn.connected(r.name, service); // 结束 try 调用段,捕获 `Exception e` 后进入错误处理 } catch (Exception e) { // 开始记录向客户端发送 Service 连接回调失败的警告 Slog.w(TAG, "Failure sending service " + r.name + // 在警告日志中补充目标 IServiceConnection 的 Binder 标识 " to connection " + c.conn.asBinder() + // 在警告日志中补充客户端进程名与捕获到的异常 " (in " + c.binding.client.processName + ")", e); // 结束 Exception e 处理分支 }c.conn 是客户端此前交给 AMS 的 IServiceConnection 代理,service 是刚从 Service 进程返回的远端 Binder。该调用启动第三次跨进程传递,目标回到客户端。
七、回到客户端:cookie 是怎么把对象找回来的
7.1 handle 只在各自进程的命名空间里有意义
AMS 持有两类关键 handle:一类指向客户端的 InnerConnection,另一类指向 Service 进程的 UserAidl.Stub。AMS 调用 conn.connected 时,前者决定事务发往哪个客户端 node,后者作为方法参数被传给客户端。客户端为服务 Binder 得到的 handle 由客户端自己的 ref 树分配,不要求等于 AMS 中的数字。
handle 不能脱离 binder_proc 解析。同一个整数出现在两个进程里,可以指向不同 node;同一个 node 被多个进程引用时,也可以对应不同整数。
7.2 binder_thread_read 读出 handle,cookie 还原成本进程地址
回调事务到达客户端后,Binder 线程读取 connected 请求。服务 Binder 参数仍以 handle 表示,并转换为 BpBinder、BinderProxy,最后作为 IBinder service 交给回调。调用目标 InnerConnection 原本属于客户端;引用回到 node 所属进程时,驱动通过 node->ptr 与 node->cookie 恢复本地 Binder 表示。
cookie 的作用范围由对象归属限定。它不是任意进程都能使用的跨进程指针,而是驱动在事务回到原进程时交还的本地地址信息。驱动先维护 node/ref 关系,再在正确进程恢复 cookie,这两层条件共同避免了把外部地址当成本地对象。
binder_thread_read 把目标 node 保存的 ptr 与 cookie 回填到即将交给用户态的事务数据中:
// 当前事务缓冲区存在本地目标 node 时回填对象标识 if (t->buffer->target_node) { // 取出事务目标所属进程中的 binder_node struct binder_node *target_node = t->buffer->target_node; // 把弱引用控制块地址写入用户态事务的 target.ptr tr.target.ptr = target_node->ptr; // 把本地 Binder 对象地址写入用户态事务的 cookie tr.cookie = target_node->cookie;IPCThreadState::executeCommand 收到事务后,使用 cookie 直接恢复 BBinder 并发起本地调用:
// target.ptr 非零表示事务目标是当前进程的本地 Binder if (tr.target.ptr) { // 把事务携带的 cookie 强转为当前进程中的 BBinder sp<BBinder> b((BBinder*)tr.cookie); // 调用恢复出的 BBinder 处理本次事务 error = b->transact(tr.code, buffer, &reply, tr.flags); // target.ptr 为空时改由上下文对象处理事务 } else { // 调用 Binder 上下文对象处理没有显式本地目标的事务 error = the_context_object->transact(tr.code, buffer, &reply, tr.flags); // 结束本地 Binder 与上下文对象的选择分支 }这一步才是 cookie 真正变回对象的地方——tr.cookie 就是对象所属进程里的 Binder 地址,native 层直接把它当成 BBinder 使用。
Service 进程通过 publishService 把 mBinder 发回 AMS 后,AMS 手里持有的是指向 Service 进程 binder_node 的 handle。publishServiceLocked 调用 conn.connected(r.name, service) 把这个 service 转发给客户端进程时,目标进程既不是发送方 AMS,也不是这个 node 的归属进程(Service 进程),于是驱动进入 BINDER_TYPE_HANDLE 分支的 else 子句,用 binder_get_ref_for_node(target_proc, ref->node) 在客户端进程建立新的 ref,并分配客户端自己的 handle:
// 处理 `BINDER_TYPE_HANDLE` 类型的 flat_binder_object case BINDER_TYPE_HANDLE: // 处理 `BINDER_TYPE_WEAK_HANDLE` 类型的 flat_binder_object case BINDER_TYPE_WEAK_HANDLE: { // 按发送进程中的 handle 查找对应 binder_ref struct binder_ref *ref = binder_get_ref(proc, fp->handle); // 仅当 `ref == NULL` 成立时执行该分支 if (ref == NULL) { // 开始记录事务携带无效 handle 的用户错误 binder_user_error("%d:%d got transaction with invalid handle, %d\n", // 向无效 handle 日志传入发送进程 pid proc->pid, // 向无效 handle 日志传入线程 pid 与错误 handle thread->pid, fp->handle); // 把 `BR_FAILED_REPLY` 的结果写入 `return_error`,记录事务失败码,供错误路径返回给发送方 return_error = BR_FAILED_REPLY; // 跳转到 `err_binder_get_ref_failed`,统一执行当前错误清理路径 goto err_binder_get_ref_failed; // 结束 if (ref == NULL) 分支 } // 仅当 `ref->node->proc == target_proc` 成立时执行该分支 if (ref->node->proc == target_proc) { // 仅当 `fp->type == BINDER_TYPE_HANDLE` 成立时执行该分支 if (fp->type == BINDER_TYPE_HANDLE) // 把 `BINDER_TYPE_BINDER` 的结果写入 `fp->type`,改写 flat_binder_object 的对象类型 fp->type = BINDER_TYPE_BINDER; // 前一条件不成立时执行紧随其后的单行分支 else // 把 `BINDER_TYPE_WEAK_BINDER` 的结果写入 `fp->type`,改写 flat_binder_object 的对象类型 fp->type = BINDER_TYPE_WEAK_BINDER; // 把 `ref->node->ptr` 的结果写入 `fp->binder`,恢复 binder_node 在所属进程中的本地地址 fp->binder = ref->node->ptr; // 把 `ref->node->cookie` 的结果写入 `fp->cookie`,恢复 binder_node 保存的本地 cookie fp->cookie = ref->node->cookie; // 按恢复后的强弱类型增加原 binder_node 的引用 binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL); // 记录事务从 binder_ref 还原为本地 node 的跟踪事件 trace_binder_transaction_ref_to_node(t, ref); // 开始输出 ref 还原为 node 的事务调试日志 binder_debug(BINDER_DEBUG_TRANSACTION, // 设置日志格式,展示 handle 到本地 node 地址的映射 " ref %d desc %d -> node %d u%016llx\n", // 向事务日志传入 ref 编号、handle 与 node 编号 ref->debug_id, ref->desc, ref->node->debug_id, // 向事务日志传入 node 对应的本地 Binder 地址 (u64)ref->node->ptr); // 结束 if (ref->node->proc == target_proc) 分支,条件未命中时进入 else 分支 } else { // 声明 new_ref,保存目标进程新建或复用的 binder_ref struct binder_ref *new_ref; // 把 `binder_get_ref_for_node(target_proc, ref->node)` 的结果写入 `new_ref`,保存目标进程中新建或复用的 binder_ref new_ref = binder_get_ref_for_node(target_proc, ref->node); // 仅当 `new_ref == NULL` 成立时执行该分支 if (new_ref == NULL) { // 把 `BR_FAILED_REPLY` 的结果写入 `return_error`,记录事务失败码,供错误路径返回给发送方;此处承接 `if (new_ref == NULL) {` 后的 `return_error = BR_FAILED_REPLY;` return_error = BR_FAILED_REPLY; // 跳转到 `err_binder_get_ref_for_node_failed`,统一执行当前错误清理路径 goto err_binder_get_ref_for_node_failed; // 结束 if (new_ref == NULL) 分支 }这里建立的是同一 Service binder_node 到客户端进程的引用关系,并没有复制 Java 对象;客户端拿到的是由自己 refs_by_desc 命名空间分配的新 handle。
7.3 回调落到 onServiceConnected
InnerConnection.connected 先取得 ServiceDispatcher,后者把结果派发给原始 ServiceConnection。最终,MainActivity.onServiceConnected 收到服务 Binder,并执行 UserAidl.Stub.asInterface(service)。如果 Binder 属于远端进程,asInterface 返回基于 BinderProxy 的 AIDL Proxy。这个 Proxy 只完成接口包装,通信发生在后续方法调用触发的 transact 中。
至此,客户端最初提交的回调 Stub 和服务端返回的业务 Stub 都有了各自的代理链。前者让 AMS 能通知连接完成,后者让客户端能调用 getUserName() 等远端方法。

八、进程执行流程
8.1 app_main.cpp 的 onZygoteInit → startThreadPool
应用进程创建时,app_main.cpp 的 onZygoteInit 取得 ProcessState 并启动 Binder 线程池。代码位置为第 91—96 行:
// 定义 `onZygoteInit`,在应用进程初始化时启动 Binder 线程池 virtual void onZygoteInit() // 打开 onZygoteInit 函数体 { // 取得当前应用进程唯一的 ProcessState sp<ProcessState> proc = ProcessState::self(); // 记录应用进程开始启动 Binder 线程池 ALOGV("App process: starting thread pool.\n"); // 在 Zygote 初始化回调中启动应用 Binder 线程池 proc->startThreadPool(); // 结束 onZygoteInit 函数 }startThreadPool 在第一次调用时设置状态,并创建主池线程:
// 定义 `startThreadPool`,确保 Binder 线程池只启动一次void ProcessState::startThreadPool()// 打开 startThreadPool 函数体{ // 持有 ProcessState 锁,串行保护线程池启动状态 AutoMutex _l(mLock); // 仅当 `!mThreadPoolStarted` 成立时执行该分支 if (!mThreadPoolStarted) { // 记录 Binder 线程池已经启动,避免重复创建 mThreadPoolStarted = true; // 创建线程池中的首个主 Binder 线程 spawnPooledThread(true); // 结束 if (!mThreadPoolStarted) 分支 }// 结束 startThreadPool 函数}普通应用因此不必在业务代码中手动启动 Binder 接收循环。Binder 线程与主线程的 Looper 分工不同,主线程仍负责界面与消息循环。
8.2 PoolThread::threadLoop → joinThreadPool → talkWithDriver
PoolThread 的线程体进入 IPCThreadState::joinThreadPool:
// 定义 `PoolThread`,封装 Binder 池线程的执行入口class PoolThread : public Thread// 打开 PoolThread 类定义体{// 将后续 PoolThread 成员设为 public 访问级别public: // 定义 PoolThread 构造函数,接收该线程是否为主池线程的标志 PoolThread(bool isMain) // 把构造参数 isMain 保存到 PoolThread 成员 : mIsMain(isMain) // 打开 PoolThread 构造函数体 { // 结束 PoolThread 构造函数 } // 将后续 PoolThread 成员设为 protected 访问级别protected: // 定义 `threadLoop`,让池线程进入 Binder 请求循环 virtual bool threadLoop() // 打开 threadLoop 函数体 { // 让当前 PoolThread 进入 Binder 请求处理循环 IPCThreadState::self()->joinThreadPool(mIsMain); // joinThreadPool 退出后返回 false,不再重复执行 threadLoop return false; // 结束 threadLoop 函数 } // 保存该 PoolThread 是否为主 Binder 池线程 const bool mIsMain;// 结束 PoolThread 类};joinThreadPool 循环执行 getAndExecuteCommand(),其中通过前文的 talkWithDriver() 和 ioctl(BINDER_WRITE_READ) 从驱动取命令。对于没有界面且主线程可以长期等待的进程,还可以手动调用 ProcessState::self()->startThreadPool(),再让主线程执行 IPCThreadState::self()->joinThreadPool();main_mediaserver.cpp 展示的是这种形态。普通应用不能让主线程停在该循环中,因为主线程还要运行 Looper。
8.3 binder_thread_read:todo 为空时阻塞在 wait 队列上
驱动先根据事务栈和线程 todo 判断线程能否等待进程级工作:
// 设置 binder_thread_read 的重试入口retry: // 先确认当前线程没有正在处理的事务栈 wait_for_proc_work = thread->transaction_stack == NULL && // 再确认线程 todo 为空,据此决定是否等待进程级工作 list_empty(&thread->todo);没有可处理事务时,线程标记为等待状态。进程级工作为空则阻塞在 proc->wait,只等待当前线程工作时则阻塞在 thread->wait。binder.c 第 2156—2185 行展示了两个分支:
// 把 `BINDER_LOOPER_STATE_WAITING` 标志合并到 `thread->looper` thread->looper |= BINDER_LOOPER_STATE_WAITING; // 仅当 `wait_for_proc_work` 成立时执行该分支 if (wait_for_proc_work) // 增加进程就绪线程计数,表示当前线程可接收进程级工作 proc->ready_threads++; // 在线程进入等待前释放 Binder 全局锁 binder_unlock(__func__); // 记录 Binder 线程即将等待的跟踪事件 trace_binder_wait_for_work(wait_for_proc_work, // 把线程是否存在事务栈写入等待跟踪参数 !!thread->transaction_stack, // 把线程 todo 是否非空写入等待跟踪参数 !list_empty(&thread->todo)); // 仅当 `wait_for_proc_work` 成立时执行该分支;此处承接 `!list_empty(&thread->todo));` 后的 `if (wait_for_proc_work) {` if (wait_for_proc_work) { // 仅当 `!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED ` 成立时执行该分支 if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | // 未注册或未进入 looper 时进入错误检查分支 BINDER_LOOPER_STATE_ENTERED))) { // 开始记录线程未注册 looper 就等待进程工作的错误 binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n", // 向错误日志传入进程 pid、线程 pid 与 looper 状态 proc->pid, thread->pid, thread->looper); // 把违规线程挂到 binder_user_error_wait,等待错误策略允许继续 wait_event_interruptible(binder_user_error_wait, // 当用户错误停止级别低于 2 时结束错误等待 binder_stop_on_user_error < 2); // 结束 未注册或未进入 looper 的错误检查分支 } // 把 Binder 线程优先级恢复为进程默认值 binder_set_nice(proc->default_priority); // 非阻塞读取进程级工作时进入即时检查 if (non_block) { // 仅当 `!binder_has_proc_work(proc, thread)` 成立时执行该分支 if (!binder_has_proc_work(proc, thread)) // 把 `-EAGAIN` 的结果写入 `ret`,保存非阻塞检查或等待操作的返回状态 ret = -EAGAIN; // 结束进程级 non_block 检查,阻塞模式改为等待 proc->wait } else // 把 `wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread))` 的结果写入 `ret`,保存非阻塞检查或等待操作的返回状态 ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); // 结束 if (wait_for_proc_work) 分支,条件未命中时进入 else 分支 } else { // 非阻塞读取线程级工作时进入即时检查 if (non_block) { // 仅当 `!binder_has_thread_work(thread)` 成立时执行该分支 if (!binder_has_thread_work(thread)) // 把 `-EAGAIN` 的结果写入 `ret`,保存非阻塞检查或等待操作的返回状态;此处承接 `if (!binder_has_thread_work(thread))` 后的 `ret = -EAGAIN;` ret = -EAGAIN; // 结束线程级 non_block 检查,阻塞模式改为等待 thread->wait } else // 把 `wait_event_freezable(thread->wait, binder_has_thread_work(thread))` 的结果写入 `ret`,保存非阻塞检查或等待操作的返回状态 ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); // 结束 else 分支 }等待不是轮询。线程阻塞在 wait queue 上,条件满足后继续执行,从 todo 取出事务并交给用户空间。
8.4 唤醒发生在 binder_transaction 末尾
第三节中的 binder_transaction 尾部把工作加入 target_list,随后对 target_wait 调用 wake_up_interruptible。目标线程被唤醒后,binder_thread_read 再检查工作条件。Service 进程和客户端能及时接收请求,依赖进程启动时建立的 Binder 线程池、驱动等待队列,以及事务入队后的定向唤醒。

九、一次 bindService 的完整时序与验证入口
9.1 三次跨进程的汇总
第一次事务从客户端到 AMS。getServiceDispatcher 创建的 InnerConnection 作为本地 Binder 写入 Parcel,驱动在客户端建立 node,在 AMS 建立 ref,并把 BINDER_TYPE_BINDER 改写为 BINDER_TYPE_HANDLE。AMS 用户空间得到 BpBinder、BinderProxy 和最外层的 IServiceConnection.Stub.Proxy。
第二次事务从 AMS 到 Service 进程。AMS 请求绑定时携带已有的 handle,驱动进入 HANDLE 分支。目标不是客户端这个 node 的所属进程时,驱动在 Service 进程建立新的 ref,并分配该进程自己的 handle。Service 进程随后执行 onBind。
第三次事务包含两个阶段。Service 进程先通过 publishService 把 UserAidl.Stub 交给 AMS,AMS 再调用保存的 conn.connected 回调客户端。客户端恢复自己的 InnerConnection,同时为服务 Binder 获得代理,最后进入 onServiceConnected。这三次传递共享同一套规则:本地对象以 node 表示,远端进程以 ref 和 handle 引用它;地址只在对象所属进程恢复。
十、小结
10.1 ptr 与 cookie 各是什么
ptr 用于在 Binder 对象所属进程的 nodes 红黑树中查找 node,cookie 保存回到原进程时恢复对象所需的地址信息。ptr 取本地对象的弱引用控制块地址,cookie 取本地对象自身地址,驱动分别校验和保存它们。理解字段要看来源与使用位置,不能合并两者的职责。
10.2 handle 为什么在不同进程里数字不同
binder_get_ref_for_node 在目标 binder_proc 的 refs_by_desc 中选择可用 desc。每个进程独立分配,0 预留给 service manager,普通引用从 1 递增。示例中一个进程显示 1、另一个进程显示 2,表示它们各自引用表中的位置,不表示对象发生了两次复制。
10.3 IServiceConnection 为什么使用 oneway
oneway 让 AMS 发出 connected 后无需等待客户端返回。它改变的是这次 AIDL 方法调用的等待语义,不会取消 Binder 驱动中的对象转换,也不会绕过 todo、wait queue 和接收线程。
10.4 asInterface 只是包装
UserAidl.Stub.asInterface(service) 与 IServiceConnection.Stub.asInterface(binder) 根据 Binder 是否本地决定返回本地接口或 Proxy。对于远端 Binder,它们创建接口层包装,让调用者能按 Java 接口发起调用。数据传输仍由 Proxy 写 Parcel、native Binder 发起 transact、驱动路由事务来完成。把 asInterface 当成通信动作,会漏掉 handle、node/ref 和线程唤醒这条主线。
本文由 AgentWorkFlow 生成