文章内容
- 一、从“添加 Media 服务”讲起
- 二、进程间通信的地基:ProcessState::self()
- 2.1 单例与进程锁
- 2.2 构造阶段打开驱动、建立映射
- 三、拿到“总调度台”:defaultServiceManager()
- 3.1 从 0 号句柄到接口代理
- 3.2 接口类型与传输对象的分工
- 四、一次 addService 调用流程
- 4.1 请求字段按接口约定排列
- 4.2 服务查询
- 五、transact:把数据交给内核
- 5.1 程资源与线程收发状态
- 5.2 驱动通知
- 六、数据跨进程
- 6.1 三层框架
- 6.2 字段布局的证据边界
- 七、Parcel 怎么把“对象”拍平
- 八、ServiceManager 进程侧逻辑:收到即处理
- 8.1 两份必须对齐的标识定义
- 8.2 收到事务后按标识分发
- 8.3 添加服务分支落到哪里
- 九、完整调用链总览
一、从“添加 Media 服务”讲起
mediaserver 由 init 解析 rc 脚本启动。它的 main() 先准备 Binder 进程状态、取得 ServiceManager 接口,再调用一串 instantiate() 注册媒体服务。这里的关键问题是:进程内的一个服务对象,怎样变成另一个进程能够查询的服务?
注册时,mediaserver 是 Binder 调用的 Client,ServiceManager 是接收注册请求的 Server;被注册的 Media 服务则是后续媒体调用的服务端。

先看入口:
// 代码位置 /frameworks/av/media/mediaserver/main_mediaserver.cpp 第 122—142 行// mediaserver 的 main() 入口:作为 Android 系统服务进程被 init 启动// 进入 mediaserver 的进程入口。int main(int argc, char** argv) { // 取得进程级 Binder 状态并持有强引用。 sp<ProcessState> proc(ProcessState::self()); // 取得 ServiceManager 接口代理。 sp<IServiceManager> sm = defaultServiceManager(); // 记录当前 ServiceManager 代理地址。 ALOGI("ServiceManager: %p", sm.get()); // 一个个把媒体相关服务注册到 ServiceManager // 注册 AudioFlinger 服务。 AudioFlinger::instantiate(); // 注册 MediaPlayerService 服务。 MediaPlayerService::instantiate(); // 注册 CameraService 服务。 CameraService::instantiate(); // 注册 AudioPolicyService 服务。 AudioPolicyService::instantiate(); // 注册 SoundTriggerHwService 服务。 SoundTriggerHwService::instantiate(); // 注册 RadioService 服务。 RadioService::instantiate(); // 注册 ResourceManagerService 服务。 ResourceManagerService::instantiate(); // 执行扩展注册入口。 registerExtensions(); // 启动持久的 Binder 线程池并让当前线程加入 // 启动 Binder 线程池。 ProcessState::self()->startThreadPool(); // 让当前线程加入 Binder 线程池。 IPCThreadState::self()->joinThreadPool(); // 正常返回。 return 0;// 结束当前作用域。}入口不使用片段中的命令行参数;执行后先持有 ProcessState 和 ServiceManager 代理,再依次进入服务注册入口,最后启动并加入 Binder 线程池。素材没有展开各个 instantiate() 的实现,不能据此补出每个服务的注册名称或构造参数;后文使用素材明确给出的 media.player 作为请求示例。
理解这条链需要 C/C++ 对象与指针、智能指针、Linux 文件描述符及系统调用知识。继续读驱动还涉及内核和数据结构,包括红黑树。本文以素材展示的接口与代码为边界;具体 Android 版本待核实,不把这些片段视为所有版本通用的完整源码。
二、进程间通信的地基:ProcessState::self()
2.1 单例与进程锁
先看 self(),因为驱动描述符和接收映射属于进程级状态,多个线程需要取得同一个实例。
// 代码位置 /frameworks/native/libs/binder/ProcessState.cpp 第 70—78 行// 提供进程级单例入口。sp<ProcessState> ProcessState::self() { // 持有进程锁,离开作用域时自动释放。 Mutex::Autolock _l(gProcessMutex); // 判断进程单例是否已存在。 if (gProcess != NULL) { // 返回进程级单例。 return gProcess; // 结束当前作用域。 } // 首次调用时创建进程级状态。 gProcess = new ProcessState; // 返回进程级单例。 return gProcess;// 结束当前作用域。}该方法没有输入参数,输出 sp<ProcessState>。首次调用在锁内创建 gProcess,后续调用返回已有实例;局部锁对象在离开作用域时释放。它说明单例的检查与创建受同一把进程锁保护,并不意味着所有 Binder 调用都被这把锁串行化。
2.2 构造阶段打开驱动、建立映射
看构造函数,是为了确定“拿到单例”实际准备了哪些资源,以及哪些失败会让进程无法继续。
// 代码位置 /frameworks/native/libs/binder/ProcessState.cpp 第 339—371 行// 初始化进程级 Binder 状态。ProcessState::ProcessState() // 打开 Binder 驱动并保存文件描述符。 : mDriverFD(open_driver()) // 将映射地址初始化为失败标记。 , mVMStart(MAP_FAILED) // 初始化线程计数锁。 , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER) // 初始化线程计数条件变量。 , mThreadCountDecrement(PTHREAD_COND_INITIALIZER) // 将执行线程数初始化为零。 , mExecutingThreadsCount(0) // 保存最大 Binder 线程数配置。 , mMaxThreads(DEFAULT_MAX_BINDER_THREADS) // 初始化上下文管理状态。 , mManagingContexts(false) // 初始化上下文检查函数指针。 , mBinderContextCheckFunc(NULL) // 初始化上下文用户数据指针。 , mBinderContextUserData(NULL) // 标记线程池尚未启动。 , mThreadPoolStarted(false) // 初始化线程池序号。 , mThreadPoolSeq(1)// 开始函数体。{ // 仅在驱动成功打开时建立映射。 if (mDriverFD >= 0) { // 为接收事务映射一段虚拟地址空间 // 申请用于接收事务的只读映射。 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, // 指定私有、不预留交换空间的映射标志及驱动描述符。 MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); // 检查映射是否失败。 if (mVMStart == MAP_FAILED) { // 记录本分支的失败信息。 ALOGE("Using /dev/binder failed: unable to mmap transaction memory\n"); // 映射失败后关闭驱动描述符。 close(mDriverFD); // 将驱动描述符标记为不可用。 mDriverFD = -1; // 结束当前作用域。 } // 进入前一条件不成立时的分支。 } else { // 将驱动描述符标记为不可用。 mDriverFD = -1; // 结束当前作用域。 } // 驱动不可用时终止进程。 LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating.");// 结束当前作用域。}初始化列表调用 open_driver(),保存文件描述符,并设置线程池相关初始状态;函数体随后建立接收事务的地址映射。映射失败会关闭描述符并设为 -1,最后的致命检查终止进程。这段代码还表明,保存线程上限与启动线程池是不同步骤,后者出现在 main() 尾部。
进一步看 open_driver(),用于区分打开设备与配置驱动这两个动作。
// 代码位置 /frameworks/native/libs/binder/ProcessState.cpp 第 311—337 行// 封装驱动打开和线程上限配置。static int open_driver() { // 以读写方式打开 Binder 设备。 int fd = open("/dev/binder", O_RDWR); // 检查设备是否打开成功。 if (fd >= 0) { // 通知内核:本进程需要接收多大的线程池(默认 15) // 向驱动提交最大线程数设置;第三参数形式待核实。 int result = ioctl(fd, BINDER_SET_MAX_THREADS,DEFAULT_MAX_BINDER_THREADS); // 检查线程上限设置是否失败。 if (result < 0) { // 记录本分支的失败信息。 ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); // 结束当前作用域。 } // 进入前一条件不成立时的分支。 } else { // 记录本分支的失败信息。 ALOGE("opening /dev/binder failed: %s\n", strerror(errno)); // 结束当前作用域。 } // 返回设备文件描述符或失败值。 return fd;// 结束当前作用域。}返回值是 /dev/binder 的描述符或打开失败值。设备打开成功后,代码尝试用 BINDER_SET_MAX_THREADS 设置默认值 15;设置失败只记录日志。
再看两个宏,是为了给“接收区域”和“线程上限”提供确定数值。
// 代码位置 /frameworks/native/libs/binder/ProcessState.cpp 第 44—45 行// 定义接收映射大小为 1 MiB 减去 8 KiB。#define BINDER_VM_SIZE ((1 * 1024 * 1024) - (4096 * 2))// 定义默认最大 Binder 线程数为 15。#define DEFAULT_MAX_BINDER_THREADS 15宏分别提供映射长度和线程配置值,不执行资源分配。按源码计算,映射长度为 1,040,384 字节,即 1 MiB 减去 8 KiB;素材通常写作“1MB-8K”。PROT_READ 表示用户侧只读,MAP_PRIVATE | MAP_NORESERVE 是实际传入的映射标志,这片地址用于接收事务,不是后面 mOut 的发送缓冲。

三、拿到“总调度台”:defaultServiceManager()
3.1 从 0 号句柄到接口代理
ProcessState 解决进程如何接入驱动,defaultServiceManager() 解决注册请求发给谁。教程给出的转换关系是:getContextObject(NULL) 取得对应 0 号句柄的 BpBinder(0),interface_cast<IServiceManager> 再得到 BpServiceManager。
看单例获取函数,是为了确认代理的缓存方式,以及初始化未就绪时的等待行为。
// 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 33—48 行// 提供默认 ServiceManager 代理获取入口。sp<IServiceManager> defaultServiceManager() { // 已有代理时直接返回。 if (gDefaultServiceManager != NULL) return gDefaultServiceManager; // 持有默认 ServiceManager 单例锁。 AutoMutex _l(gDefaultServiceManagerLock); // 加锁后再次检查代理是否为空。 if (gDefaultServiceManager == NULL) { // 通过本地 0 号句柄拿到进程内唯一的 ServiceManager 代理 // 将 Binder 上下文对象转换为 ServiceManager 接口。 gDefaultServiceManager = interface_cast<IServiceManager>( // 取得 ServiceManager 的上下文对象。 ProcessState::self()->getContextObject(NULL)); // 结束当前作用域。 } // service manager 由 init 第一个创建,但可能尚未初始化完成,轮询等待 // 代理为空时进入等待循环。 while (gDefaultServiceManager == NULL) { // 暂停一秒;该片段没有重新获取代理。 sleep(1); // 结束当前作用域。 } // 返回缓存的 ServiceManager 代理。 return gDefaultServiceManager;// 结束当前作用域。}ServiceManager 和 mediaserver 是不同进程,它们由 init 启动,且 ServiceManager 先创建。
3.2 接口类型与传输对象的分工
需要将 interface_cast 的转换关联到 IMPLEMENT_META_INTERFACE 生成的 asInterface,接口标识为 android.os.IServiceManager。BpServiceManager 提供 addService() 等类型化方法,底层 BpBinder 持有目标句柄并承接事务发送。0 是这次事务的目的地,不是待注册 Media 服务自己的句柄。

四、一次 addService 调用流程
4.1 请求字段按接口约定排列
看 BpServiceManager::addService(),是为了明确跨进程发送的不是 C++ 调用栈,而是按顺序写入 Parcel 的请求字段。
// 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 155—165 行// 声明服务注册方法,输入服务名和 Binder 对象。virtual status_t addService(const String16& name, const sp<IBinder>& service, // 接收是否允许隔离进程访问的参数。 bool allowIsolated) { // 分别创建请求和应答 Parcel。 Parcel data, reply; // 写入接口标识 token // 写入 ServiceManager 接口标识。 data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); // 写入服务名称。 data.writeString16(name); // 将服务 Binder 对象写入请求。 data.writeStrongBinder(service); // 把隔离访问标志编码为整数。 data.writeInt32(allowIsolated ? 1 : 0); // 使用相应事务码经远端 Binder 发送请求。 status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); // 传输成功时返回应答异常码,否则返回传输错误。 return err == NO_ERROR ? reply.readExceptionCode() : err;// 结束当前作用域。}输入是名称、服务 Binder 强引用和隔离访问标志;输出是事务错误,或者传输成功后从应答读取的异常码。
4.2 服务查询
服务对照查询和枚举方法,可以确认接口 token、事务码与业务参数各自负责什么,而无需为每个操作假设不同的底层传输机制。
// 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 146—153 行// 按名称获取服务 Binder。virtual sp<IBinder> getService(const String16& name) { // 分别创建请求和应答 Parcel。 Parcel data, reply; // 写入 ServiceManager 接口标识。 data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); // 写入服务名称。 data.writeString16(name); // 使用相应事务码经远端 Binder 发送请求。 status_t err = remote()->transact(GET_SERVICE_TRANSACTION, data, &reply); // 成功时读取应答中的 Binder,否则返回空值。 return err == NO_ERROR ? reply.readStrongBinder() : NULL;// 结束当前作用域。} // 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 167—181 行// 枚举 ServiceManager 中的服务名称。virtual Vector<String16> listServices() { // 创建结果列表。 Vector<String16> res; // 将枚举索引初始化为零。 int n = 0; // 持续枚举,直到事务返回错误。 for (;;) { // 分别创建请求和应答 Parcel。 Parcel data, reply; // 写入 ServiceManager 接口标识。 data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); // 写入当前索引并递增。 data.writeInt32(n++); // 使用相应事务码经远端 Binder 发送请求。 status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply); // 事务失败时结束枚举。 if (err != NO_ERROR) break; // 把应答中的服务名称加入结果列表。 res.add(reply.readString16()); // 结束当前作用域。 } // 返回已获取的服务名称。 return res;// 结束当前作用域。}getService() 输入名称,发送查询事务,成功时返回应答中的 Binder,失败时返回空值。listServices() 从索引 0 开始,每次请求写入递增索引,成功后收集一个名称,遇到事务错误时退出并返回已收集结果。
五、transact:把数据交给内核
5.1 程资源与线程收发状态
注册请求经 BpServiceManager 的 remote()->transact() 进入 BpBinder::transact(),再进入 IPCThreadState::transact()。素材给出的后续顺序是 writeTransactionData(BC_TRANSACTION)、talkWithDriver()、ioctl(BINDER_WRITE_READ, &bwr),最终进入驱动的 binder_ioctl。
// 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 686—696 行// 初始化线程级 Binder 状态。IPCThreadState::IPCThreadState() // 关联当前进程的 ProcessState。 : mProcess(ProcessState::self()) // 保存当前线程 ID。 , mMyThreadId(gettid()) // 初始化严格模式策略。 , mStrictModePolicy(0) // 初始化上次事务标志。 , mLastTransactionBinderFlags(0)// 开始函数体。{ // 把该状态对象存入线程局部存储。 pthread_setspecific(gTLS, this); // 为输出缓冲区设置 256 字节初始容量。 mOut.setDataCapacity(256);// 结束当前作用域。} // 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 702—709 行// 发送应答 Parcel,接收事务标志。status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) { // 用 BC_REPLY 组装应答命令。 status_t err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, 0); // 组装失败时立即返回错误。 if (err < NO_ERROR) return err; // 等待驱动反馈。 return waitForResponse(NULL, NULL);// 结束当前作用域。}构造时取得共享 ProcessState、保存线程 ID、把自身放入 TLS,并将 mOut 初始容量设为 256。这个容量不是事务最大长度,也不是前面的接收映射长度。sendReply() 输入应答 Parcel 和 flags,使用 BC_REPLY 组装数据,失败即返回,否则等待反馈;它展示的是应答路径,注册请求使用的是 BC_TRANSACTION。
命令及参数组装:
// 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 678—684 行// IPCThreadState::clearDeathNotification 片段(素材仅给出这三行,非完整函数体,前后上下文待核实)mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION);mOut.writeInt32(handle);mOut.writePointer((uintptr_t)proxy);5.2 驱动通知
// 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 712—801 行// 等待应答或引用获取结果。status_t IPCThreadState::waitForResponse(Parcel* reply, status_t* acquireResult) { // 保存从输入缓冲读取的驱动命令。 uint32_t cmd; // 保存驱动交互的返回状态。 int32_t err; // 循环处理驱动返回的命令。 while (1) { // 与驱动交换数据,失败时退出循环。 if ((err = talkWithDriver()) < NO_ERROR) break; // 从输入缓冲读取下一条命令。 cmd = (uint32_t)mIn.readInt32(); // 按驱动返回命令分派处理。 switch (cmd) { // 处理事务提交完成通知。 case BR_TRANSACTION_COMPLETE: // 没有应答及引用结果需求时结束等待。 if (!reply && !acquireResult) goto finish; // 结束当前分支。 break; // 处理目标对象死亡通知。 case BR_DEAD_REPLY: // 把错误设置为对象已死亡。 err = DEAD_OBJECT; // 跳到统一返回位置。 goto finish; // 处理事务失败通知。 case BR_FAILED_REPLY: // 把错误设置为事务失败。 err = FAILED_TRANSACTION; // 跳到统一返回位置。 goto finish; // 处理引用获取结果,具体逻辑在素材中省略。 case BR_ACQUIRE_RESULT: // ... // 结束当前分支。 break; // 结束当前作用域。 } // 结束当前作用域。 }// 统一返回标签。finish: // 返回处理状态。 return err;// 结束当前作用域。}
输入决定调用方是否需要应答 Parcel 或引用获取结果,返回值报告交互状态。循环先调用 talkWithDriver(),再从 mIn 读取命令。BR_DEAD_REPLY 和 BR_FAILED_REPLY 分别转换为对象死亡和事务失败错误;BR_TRANSACTION_COMPLETE 仅在两种结果都不需要时结束等待。
六、数据跨进程
6.1 三层框架
// 代码位置 /bionic/libc/kernel/uapi/linux/binder.h 第 59—67 行// 一次 ioctl(BINDER_WRITE_READ) 的读写缓冲结构// 定义一次驱动读写交换的外层结构。struct binder_write_read { // 记录待写入驱动的字节数。 binder_size_t write_size; // 记录驱动已消费的写入字节数。 binder_size_t write_consumed; // 保存输出缓冲地址。 binder_uintptr_t write_buffer; // 记录输入缓冲可接收的字节数。 binder_size_t read_size; // 记录已读取的字节数。 binder_size_t read_consumed; // 保存输入缓冲地址。 binder_uintptr_t read_buffer;// 结束结构或联合体声明。}; // 代码位置 /bionic/libc/kernel/uapi/linux/binder.h 第 95—117 行// 一次跨进程事务的描述// 展示素材中的事务描述片段,字段布局待核实。struct binder_transaction_data { // 开始联合体,各成员共享存储空间。 union { // 保存 Binder 句柄;具体含义取决于所在结构。 __u32 handle; // 保存目标指针表示。 binder_uintptr_t ptr; // 结束目标联合体并命名为 target。 } target; // 保存与 Binder 对象相关的 cookie。 binder_uintptr_t cookie; // 保存事务码。 __u32 code; // 保存事务或对象标志。 __u32 flags; // 开始联合体,各成员共享存储空间。 union { // 开始嵌套数据结构。 struct { // 记录素材片段中的数据大小字段。 binder_size_t size; // 保留素材中的 offset 字段,其准确布局待核实。 binder_size_t offset; // 保存素材片段中的数据地址字段。 binder_uintptr_t buffer; // 结束嵌套结构并命名为 data。 } data; // 结束结构或联合体声明。 }; // ...// 结束结构或联合体声明。}; // 代码位置 /bionic/libc/kernel/uapi/linux/binder.h 第 47—56 行// Parcel 里被拍平的 binder 对象// 定义 Parcel 中的 Binder 对象描述。struct flat_binder_object { // 标记本地 Binder、远端句柄或弱引用类型。 __u32 type; // 保存事务或对象标志。 __u32 flags; // 开始联合体,各成员共享存储空间。 union { // 在本地对象分支中保存弱引用管理对象地址。 binder_uintptr_t binder; // 保存 Binder 句柄;具体含义取决于所在结构。 __u32 handle; // 结束结构或联合体声明。 }; // 保存与 Binder 对象相关的 cookie。 binder_uintptr_t cookie;// 结束结构或联合体声明。};binder_write_read 描述一次驱动交互:写方向由 write_size 和 write_buffer 指定,读方向由 read_size 和 read_buffer 指定,对应的 consumed 字段记录已消费量。写缓冲中承载命令及参数;本次主线的命令是 BC_TRANSACTION,后接事务描述。
binder_transaction_data 的 target.handle 在注册请求中为 0,code 是 ADD_SERVICE_TRANSACTION。flat_binder_object 则位于请求 Parcel 内,描述被注册的服务对象。目标句柄回答“请求送到谁”,对象描述回答“此次注册哪个 Binder 对象”。
6.2 字段布局的证据边界
概念上可以确认:事务描述包含目标、事务码,以及指向 Parcel 数据的地址与大小。这里的地址不是允许 ServiceManager 直接解引用 mediaserver 用户指针的承诺。

七、Parcel 怎么把“对象”拍平
// 代码位置 /frameworks/native/libs/binder/Parcel.cpp 第 177—207 行// 把强引用 Binder 转换为扁平对象并写入 Parcel。status_t flatten_binder(const sp<ProcessState>&, const sp<IBinder>& binder, Parcel* out) { // 创建待写入的对象描述。 flat_binder_object obj; // 设置对象标志并声明接受文件描述符。 obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; // 区分有效 Binder 与空 Binder。 if (binder != NULL) { // 尝试取得本地 Binder 对象。 IBinder* local = binder->localBinder(); // 进入本地对象分支。 if (local != NULL) { // 将对象类型设为本地 Binder。 obj.type = BINDER_TYPE_BINDER; // 记录本地对象的弱引用管理对象地址。 obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs()); // 记录本地 Binder 对象地址。 obj.cookie = reinterpret_cast<uintptr_t>(local); // 进入前一条件不成立时的分支。 } else { // 将对象类型设为远端句柄。 obj.type = BINDER_TYPE_HANDLE; // 记录代理在当前进程中的句柄。 obj.handle = binder->remoteBinder()->handle(); // 结束当前作用域。 } // 进入前一条件不成立时的分支。 } else { // 将对象类型设为本地 Binder。 obj.type = BINDER_TYPE_BINDER; // 为空 Binder 清零对象地址。 obj.binder = 0; // 为空 Binder 清零 cookie。 obj.cookie = 0; // 结束当前作用域。 } // 交给统一写入入口完成对象序列化。 return finish_flatten_binder(binder, obj, out);// 结束当前作用域。}输入是 Binder 强引用与输出 Parcel,未命名的 ProcessState 参数在片段中没有使用;输出由 finish_flatten_binder() 返回。本地对象分支写入 BINDER_TYPE_BINDER,binder 保存 local->getWeakRefs() 地址,cookie 保存 local 地址。这里记录的是对象身份相关信息,代码没有序列化 Media 服务的业务成员。
远端代理分支写入 BINDER_TYPE_HANDLE 和代理的 handle();空对象分支使用 BINDER_TYPE_BINDER 并清零 binder 与 cookie。所有分支先设置 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS。远端分支没有展示 cookie 赋值,其完整初始化及驱动解释待核实,不在整理时自行添加语句。
八、ServiceManager 进程侧逻辑:收到即处理
到达 ServiceManager 之后,请求需要从通用 Binder 事务转成具体服务管理操作。
| 分发标识 | 对应操作 |
|---|---|
SVC_MGR_GET_SERVICE |
获取服务 |
SVC_MGR_CHECK_SERVICE |
检查服务 |
SVC_MGR_ADD_SERVICE |
添加服务 |
SVC_MGR_LIST_SERVICES |
枚举服务 |
本次注册落在添加服务分支。客户端已经准备了接口标识、名称、对象与访问标志;服务端按照操作类型消费相应请求,并通过 Binder 应答链返回处理结果。
BnServiceManager::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags):
// 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 189—190 行// 素材仅给出该签名,函数体未提供,待核实virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);8.1 两份必须对齐的标识定义
// 代码位置 /frameworks/native/libs/binder/IServiceManager.h 第 59—62 行// 客户端使用的四个事务码,从 FIRST_CALL_TRANSACTION 起依次递增enum { GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, CHECK_SERVICE_TRANSACTION, ADD_SERVICE_TRANSACTION, LIST_SERVICES_TRANSACTION,};// 代码位置 /frameworks/native/cmds/servicemanager/binder.h 第 31—42 行// ServiceManager 进程使用的分发标识,注释要求与另外两个头文件保持一致#define BINDER_SERVICE_MANAGER 0U#define SVC_MGR_NAME "android.os.IServiceManager" enum { /* Must match definitions in IBinder.h and IServiceManager.h */ PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), SVC_MGR_GET_SERVICE = 1, SVC_MGR_CHECK_SERVICE, SVC_MGR_ADD_SERVICE, SVC_MGR_LIST_SERVICES,};IBinder.h 第 48 行把 FIRST_CALL_TRANSACTION 定为 0x00000001,四个事务码因此是 1、2、3、4;服务端那份定义从 1 开始枚举,取值和顺序都对得上。表格里的四个名字来自这个枚举,不是宏。两处定义分处 C++ 头文件和 C 头文件,一致性只由 binder.h 里那行注释维持:任何一边插入新项,编译期没有任何提示,事务会落到 default 分支。PING_TRANSACTION 两边都写成 B_PACK_CHARS('_','P','N','G'),由同一个宏展开,这一条不会错位。
8.2 收到事务后按标识分发
ServiceManager 进程在 6.0 里跑的是 C 实现:main() 打开驱动、用 binder_become_context_manager() 把自己登记成上下文管理器,最后 binder_loop(bs, svcmgr_handler) 进入循环,每收到一个事务回调一次 svcmgr_handler()(service_manager.c 第 357—384 行)。
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 244—338 行// ServiceManager 进程的分发入口:先校验目标和接口,再按 txn->code 选择操作int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data *txn, struct binder_io *msg, struct binder_io *reply){ struct svcinfo *si; uint16_t *s; size_t len; uint32_t handle; uint32_t strict_policy; int allow_isolated; // 目标必须是 0 号句柄,也就是 ServiceManager 自己 if (txn->target.ptr != BINDER_SERVICE_MANAGER) return -1; if (txn->code == PING_TRANSACTION) return 0; // 等价于 Parcel::enforceInterface():读严格模式掩码和接口名 strict_policy = bio_get_uint32(msg); s = bio_get_string16(msg, &len); if (s == NULL) { return -1; } // 接口名必须等于 android.os.IServiceManager if ((len != (sizeof(svcmgr_id) / 2)) || memcmp(svcmgr_id, s, sizeof(svcmgr_id))) { fprintf(stderr,"invalid id %s\n", str8(s, len)); return -1; } // 此处省略 sehandle 的 SELinux 标签刷新,与参数读取无关 switch(txn->code) { // 获取与检查在服务端共用一段逻辑 case SVC_MGR_GET_SERVICE: case SVC_MGR_CHECK_SERVICE: s = bio_get_string16(msg, &len); if (s == NULL) { return -1; } handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid); if (!handle) break; bio_put_ref(reply, handle); return 0; // 本次注册走这个分支:按顺序取出名称、对象引用、隔离访问标志 case SVC_MGR_ADD_SERVICE: s = bio_get_string16(msg, &len); if (s == NULL) { return -1; } handle = bio_get_ref(msg); allow_isolated = bio_get_uint32(msg) ? 1 : 0; if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, txn->sender_pid)) return -1; break; // 枚举按索引沿服务链表取一个名字,越界即失败 case SVC_MGR_LIST_SERVICES: { uint32_t n = bio_get_uint32(msg); if (!svc_can_list(txn->sender_pid)) { ALOGE("list_service() uid=%d - PERMISSION DENIED\n", txn->sender_euid); return -1; } si = svclist; while ((n-- > 0) && si) si = si->next; if (si) { bio_put_string16(reply, si->name); return 0; } return -1; } default: ALOGE("unknown code %d\n", txn->code); return -1; } // 添加服务分支走 break 落到这里,回一个 0 表示成功 bio_put_uint32(reply, 0); return 0;}函数进入分发前有一段固定的前置处理:先确认事务目标是 0 号句柄,遇到 PING_TRANSACTION 直接返回,再读出严格模式掩码和接口名。接口名要和 svcmgr_id(android.os.IServiceManager)逐字节相等,这正是 4.1 节里 writeInterfaceToken() 写入的内容,也是它不能省的原因:名字不匹配时 ServiceManager 直接返回 -1,事务失败。
switch 的四个分支对应表格里的四种操作。获取和检查在服务端是同一段代码,两者的区别留在客户端:getService() 循环五次调用 checkService(),每次失败后 sleep(1),而 checkService() 只发一次事务(IServiceManager.cpp 第 134—153 行)。添加服务分支按顺序取出名称、对象引用和隔离访问标志,与客户端 addService() 的写入次序一一对应:writeString16() 对 bio_get_string16(),writeStrongBinder() 对 bio_get_ref(),writeInt32() 对 bio_get_uint32()。binder_io 是按位置消费的字节流,没有字段名,两边顺序一旦不一致,读到的就是错位的数据,而且不会报错。添加服务分支走 break 落到函数尾部,bio_put_uint32(reply, 0) 就是 4.1 节里 reply.readExceptionCode() 取到的那个 0。
8.3 添加服务分支落到哪里
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 194—242 行// 把一次注册写入服务表,或者覆盖同名服务的旧记录int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle, uid_t uid, int allow_isolated, pid_t spid){ struct svcinfo *si; // 句柄无效或名字长度越界时拒绝注册 if (!handle || (len == 0) || (len > 127)) return -1; // 按服务名查 SELinux 标签,判断调用方是否有 add 权限 if (!svc_can_register(s, len, spid)) { ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n", str8(s, len), handle, uid); return -1; } si = find_svc(s, len); if (si) { // 同名服务已存在:先释放旧引用,再用新句柄覆盖 if (si->handle) { ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n", str8(s, len), handle, uid); svcinfo_death(bs, si); } si->handle = handle; } else { // 新名字:分配表项并插到服务表头部 si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); if (!si) { ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n", str8(s, len), handle, uid); return -1; } si->handle = handle; si->len = len; memcpy(si->name, s, (len + 1) * sizeof(uint16_t)); si->name[len] = '\0'; si->death.func = (void*) svcinfo_death; si->death.ptr = si; si->allow_isolated = allow_isolated; si->next = svclist; svclist = si; } // 持有服务对象引用,并挂上死亡通知 binder_acquire(bs, handle); binder_link_to_death(bs, handle, &si->death); return 0;}do_add_service() 才是把注册写进服务表的地方。它先拒绝两类输入:handle 为 0,或者名字长度为 0 或超过 127。随后 svc_can_register() 做 SELinux 检查,按服务名查 service_contexts 得到目标类型,再用调用方 pid 判断有没有 add 权限,uid 只用于日志(service_manager.c 第 100—114 行)。名字已存在时不报错,而是打印 ALREADY REGISTERED, OVERRIDE,用 svcinfo_death() 释放旧引用,再覆盖 handle;名字不存在就分配一个 svcinfo 插到 svclist 表头。同名注册是覆盖,不是拒绝,服务表里不会出现两份记录。
最后两行是这次注册在内核留下的状态:binder_acquire() 增加对服务对象的引用,binder_link_to_death() 给这个引用挂上死亡通知。没有这两步,mediaserver 退出后 ServiceManager 仍会返回一个已经失效的句柄;有了它们,进程退出时回调 svcinfo_death(),binder_release() 释放引用并把 si->handle 清零,后续查询就查不到这个服务。
allowIsolated=0 的作用点在查询侧:它存进 si->allow_isolated,do_find_service() 遇到 !si->allow_isolated 且调用方 uid 落在 AID_ISOLATED_START 到 AID_ISOLATED_END 之间时返回 0(service_manager.c 第 178—185 行),也就是拒绝。
应答方向用的是同一套扁平对象格式:bio_put_ref() 写回的 flat_binder_object 同样带 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS,类型为 BINDER_TYPE_HANDLE。
九、完整调用链总览
把各层串联起来,注册前先有进程状态和目的地代理,随后才是一次具体事务的编码、驱动传送和服务端处理。

ProcessState::self()在进程锁内建立单例,打开设备、配置线程上限并映射接收区。defaultServiceManager()将 0 号句柄对应的 Binder 转换成BpServiceManager接口代理。addService()把 token、名称、服务对象描述和隔离访问标志写入 Parcel,以添加服务事务码发起调用。BpBinder将事务交给线程级状态;IPCThreadState组织BC_TRANSACTION与事务描述,通过binder_write_read向驱动交换数据。- 驱动依据目标句柄把请求送往 ServiceManager,后者按添加服务操作处理;调用方通过返回命令及应答获取结果。
本文由 AgentWorkFlow 生成