Android-Framework(十八)-bindService 的通信过程

文章内容

一次 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:

java
// 打开 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 只声明客户端与服务端共同认可的方法集合:

java
// 定义 `UserAidl` AIDL 接口,约定客户端可调用的方法interface UserAidl {    // 声明跨进程获取用户名的方法    String getUserName();    // 声明跨进程获取密码的方法    String getUserPassword();// 结束 UserAidl 接口}

独立进程来自 Manifest 中的 Service 声明:

xml
<!-- 开始声明 MessageService 组件 --><service    <!-- 把 Service 实现类指定为 MessageService -->    android:name=".MessageService"    <!-- 让 MessageService 运行在应用的 message 私有进程并结束组件声明 -->    android:process=":message" />

bindService 一次绑定的全局角色与四次跨进程握手(客户端进程、AMS、Service 进程与 Binder 驱动)

1.2 为什么不能直接把 ServiceConnection 递给 AMS

ServiceConnection 是应用侧的普通 Java 回调对象。它的引用值只在客户端虚拟地址空间内有效,system_server 无法解引用这段地址。框架因此增加了一层可跨进程表达的接口:IServiceConnection。客户端交给 AMS 的对象是 LoadedApk.ServiceDispatcher.InnerConnection,它继承 IServiceConnection.Stub;ServiceDispatcher 仍持有原始 ServiceConnection。AMS 调用 connected 后,请求先到 InnerConnection,再转给应用实现。

InnerConnection 用弱引用找到 ServiceDispatcher,并把连接结果继续分发:

java
        // 打开 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 行:

java
    // 定义 `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 行:

java
    // 定义 `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 行只展示了入口:

java
    // 定义 `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 行:

cpp
// 定义 `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 对象的实际类型决定。

Java 层 Binder 对象与 native 层 JavaBBinderHolder 的互相持有关系

2.3 writeStrongBinder:从 Java 对象取出 BBinder,序列化为 flat_binder_object

ibinderForJavaObject 先判断传入对象属于 Binder 还是 BinderProxy。本地 Binder 通过 holder 取得 BBinder,代理对象则从 mObject 读取 native 指针。代码位置为 android_util_Binder.cpp 第 603—620 行:

cpp
// 定义 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:

cpp
// 定义 `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 行保留了到无读写数据时返回的位置:

cpp
// 定义 `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 定义把这些进程级索引、工作队列与等待队列集中在同一个结构体中:

c
// 定义 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 行:

c
// 定义 `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 写入这两个字段时就明确区分了来源:

cpp
        // 本地 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 开始递增:

c
// 定义 `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 行:

c
		// 处理 `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。

客户端提交 BINDER_TYPE_BINDER:驱动在本进程建 binder_node、在目标进程建 binder_ref 并改写成 handle

3.5 唤醒目标进程

完成对象转换后,驱动把事务工作加入目标 todo,把完成通知加入发送线程的 todo。目标存在等待队列时,wake_up_interruptible(target_wait) 唤醒接收线程。代码位置为 binder.c 第 1675—1681 行:

c
	// 把目标工作项标记为 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 表项中:

cpp
            // 用当前 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:

cpp
    // 调用 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 后不等待客户端返回:

java
// 声明 `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 侧 handle 变回 Java 对象:binder_ref 到 BpBinder、BinderProxy,再到 IServiceConnection.Stub.Proxy

五、AMS 请求绑定:第二次跨进程与 node 的复制

5.1 AMS.bindService → bindServiceLocked → requestServiceBindingLocked

客户端代理调用到达 system_server 后,入口是 ActivityManagerService.bindService。它检查调用者与参数,在 AMS 锁内转入 mServices.bindServiceLocked:

java
    // 定义 `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 行:

java
        // 仅当 `(!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 行给出了这条目标解析路径:

c
	// 事务没有直接指定本地 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 数值一致。

三个进程各自的 binder_proc 与三棵红黑树:同一个 node 在客户端与 Service 进程里对应各自的 handle

六、服务端回传:publishService 与第三次跨进程

6.1 MessageService.onBind 返回 mBinder

Service 进程收到 scheduleBindService 后创建 Service 并调用 onBind。示例返回的 mBinder 是 UserAidl.Stub 实例,它同样继承 Binder,所以返回 AMS 时仍从 BINDER_TYPE_BINDER 开始走 node/ref 转换:

java
// 打开 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:

java
    // 定义 `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):

java
                            // 开始执行可能抛出异常的 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 回填到即将交给用户态的事务数据中:

c
		// 当前事务缓冲区存在本地目标 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 并发起本地调用:

cpp
            // 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:

c
		// 处理 `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() 等远端方法。

publishServiceLocked 调 connected:cookie 还原对象,回调落到 onServiceConnected

八、进程执行流程

8.1 app_main.cpp 的 onZygoteInit → startThreadPool

应用进程创建时,app_main.cpp 的 onZygoteInit 取得 ProcessState 并启动 Binder 线程池。代码位置为第 91—96 行:

cpp
    // 定义 `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 在第一次调用时设置状态,并创建主池线程:

cpp
// 定义 `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:

cpp
// 定义 `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 判断线程能否等待进程级工作:

c
// 设置 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 行展示了两个分支:

c
	// 把 `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 线程池、驱动等待队列,以及事务入队后的定向唤醒。

进程侧的等待与唤醒循环:joinThreadPool 到 binder_thread_read 的睡眠,以及 binder_transaction 的唤醒

九、一次 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 引用它;地址只在对象所属进程恢复。

十、小结

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 生成

觉得不错?给作者点个赞吧

分类:Android系统

评论 (0)

无需注册,留言经站长审核后公开。

加载评论中…

Android-Framework(十八)-bindService 的通信过程 · Yinchen 的博客