本文整理自 Android 6.0(Marshmallow)时期的 servicemanager 源码与配套素材。讨论对象是独立的 servicemanager 进程,以及它自带的
service_manager.c、binder.c实现。
一、ServiceManager
ServiceManager 开机后完成三件事:binder_open 打开 /dev/binder 并映射 128KB 只读内存(Android 6.0);binder_become_context_manager 向驱动登记上下文管理者身份;binder_loop 进入循环,等待驱动送来请求。第三步收到 BR_TRANSACTION 后,binder_parse 解出事务,svcmgr_handler 再按 txn->code 分发。注册和查询最终都落在一张 svcinfo 链表上。
Android 6.0 中,init 解析 init.rc 后会创建独立的 servicemanager 进程,android.os.IServiceManager 就由这个进程承载。它的特殊之处是 handle 固定为 0。其他进程注册服务时,调用链从 BpServiceManager.addService 进入 BpBinder、IPCThreadState,经 ioctl(BINDER_WRITE_READ) 把事务交给驱动。事务目标为 handle=0,驱动便把它转给 ServiceManager。

主链可以压缩为:BpServiceManager.addService → BpBinder.transact → IPCThreadState.transact → talkWithDriver → ioctl(BINDER_WRITE_READ) → Binder 驱动 → binder_parse → svcmgr_handler → do_add_service。
二、第一步:binder_open 打开驱动

main() 的结构不长,三次关键调用分别建立驱动通道、登记身份和进入服务循环。
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 347—387 行// 程序入口int main(int argc, char **argv){ // 保存 binder 驱动状态 struct binder_state *bs; // 指定 binder 设备节点 char *driver = "/dev/binder"; // 第一步:打开 binder 驱动,映射 128KB 只读内存 bs = binder_open(driver, 128 * 1024); // 检查驱动是否打开成功 if (!bs) { // 打开失败直接退出 ALOGE("failed to open binder driver: %s\n", driver); return -1; } // 第二步:向驱动声明“我是上下文管理者”,之后 handle=0 就能找到本进程 if (binder_become_context_manager(bs)) { // 登记失败时记录原因 ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } // SELinux 相关初始化:注册 audit/log 回调、获取 sehandle 与服务上下文 selinux_enabled = is_selinux_enabled(); // 获取服务上下文查询句柄 sehandle = selinux_android_service_context_handle(); // 打开 SELinux 状态监听 selinux_status_open(true); // 只在 SELinux 启用时检查上下文 if (selinux_enabled > 0) { // 检查服务上下文句柄 if (sehandle == NULL) { // 缺少句柄时记录错误 ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n"); // 中止进程 abort(); } // 获取 ServiceManager 自身安全上下文 if (getcon(&service_manager_context) != 0) { // 获取失败时记录错误 ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n"); // 中止进程 abort(); } } // 声明 SELinux 回调联合体 union selinux_callback cb; // 绑定审计回调 cb.func_audit = audit_callback; // 注册审计回调 selinux_set_callback(SELINUX_CB_AUDIT, cb); // 第三步:进入死循环,等待并处理客户端请求 binder_loop(bs, svcmgr_handler); // 正常路径理论上不会走到这里 return 0;}输入是 binder 设备路径和 128KB 映射大小。前两步成功后,进程已经持有 binder_state,并被驱动识别为上下文管理者;binder_loop 随后接管线程,正常运行期间不会返回。
binder_state 只保存三项:设备文件描述符、映射首地址和映射长度。binder_open 要完成 open、版本校验、mmap,任何一环失败都会回收已经取得的资源。
// 代码位置 /frameworks/native/cmds/servicemanager/binder.c 第 89—94 行// 描述用户空间持有的 binder 驱动状态struct binder_state { // /dev/binder 的文件描述符 int fd; // mmap 映射的虚拟内存首地址 void *mapped; // 映射大小(128KB) size_t mapsize;}; // 代码位置 /frameworks/native/cmds/servicemanager/binder.c 第 96—137 行// 打开驱动并建立接收区映射struct binder_state *binder_open(size_t mapsize){ // 保存状态对象 struct binder_state *bs; // 保存驱动协议版本 struct binder_version vers; // 分配并初始化 binder_state 对象 bs = malloc(sizeof(*bs)); // 检查内存分配结果 if (!bs) { // 记录内存不足 errno = ENOMEM; // 返回空指针 return NULL; } // 打开 binder 驱动设备节点 bs->fd = open("/dev/binder", O_RDWR); // 检查设备打开结果 if (bs->fd < 0) { // 输出设备打开错误 fprintf(stderr, "binder: cannot open device (%s)\n", strerror(errno)); // 跳到统一释放路径 goto fail_open; } // 获取内核驱动版本,必须与用户空间期望的版本一致 if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || // 比较驱动协议版本 (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) { // 输出版本不一致信息 fprintf(stderr, // 给出内核与用户空间版本 "binder: kernel driver version (%d) differs from user space version (%d)\n", // 传入两侧版本号 vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION); // 走打开失败清理路径 goto fail_open; } // 保存映射长度 bs->mapsize = mapsize; // 映射一块只读、私有的虚拟内存,binder 数据通过这块内存做单次拷贝 bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); // 检查映射结果 if (bs->mapped == MAP_FAILED) { // 输出映射错误 fprintf(stderr, "binder: cannot map device (%s)\n", strerror(errno)); // 跳转到映射失败清理路径 goto fail_map; } // 返回完整状态对象 return bs; // 映射失败的清理入口fail_map: // 关闭设备文件 close(bs->fd);// 打开或版本检查失败的清理入口fail_open: // 释放状态对象 free(bs); // 返回空指针表示失败 return NULL;}open("/dev/binder", O_RDWR) 建立用户空间与 Binder 驱动的通道。BINDER_VERSION 随后校验双方协议版本,避免用不匹配的数据结构继续通信。mmap 建立 PROT_READ | MAP_PRIVATE 的 128KB 映射,用户空间从中读取驱动投递的数据。Binder 单次拷贝的具体实现属于驱动侧,本文不把这句标志位推演成驱动源码结论。
三、第二步:成为上下文管理者
打开驱动只说明进程能使用 Binder,还不足以让所有客户端通过 handle=0 找到它。binder_become_context_manager(bs) 向文件描述符发出 BINDER_SET_CONTEXT_MGR ioctl,驱动据此登记当前进程。
这次登记建立了关键约定:上下文管理者对应 handle=0。普通 Binder 引用的 handle 从 1 开始分配,而 0 不表示“没有目标”,它是进入 ServiceManager 的固定入口。因此,客户端创建 BpServiceManager 时使用 handle=0,驱动能够把请求送往已经登记的进程。驱动内部如何保存这项身份、如何按 handle 定位目标,留到驱动源码部分处理。
四、第三步:binder_loop 循环等待
binder_write_read 同时描述写区和读区,包含 write_size、write_consumed、write_buffer、read_size、read_consumed、read_buffer 六个字段。ServiceManager 进入循环前先发送一个 4 字节的 BC_ENTER_LOOPER,通知驱动当前线程将承担循环处理工作。
看 binder_loop,要抓住两个状态:
- 线程先登记为 looper,然后反复阻塞在
BINDER_WRITE_READ。没有请求时,线程进入睡眠; - 请求到达后,驱动唤醒线程并填充读缓冲区。
// 代码位置 /frameworks/native/cmds/servicemanager/binder.c 第 372—407 行// 进入 Binder 请求处理循环void binder_loop(struct binder_state *bs, binder_handler func){ // 保存系统调用结果 int res; // 描述本次读写缓冲区 struct binder_write_read bwr; // 准备 32 个命令字的读缓冲区 uint32_t readbuf[32]; // 先清空写区 bwr.write_size = 0; // 清空已消费写入字节数 bwr.write_consumed = 0; // 清空写缓冲区地址 bwr.write_buffer = 0; // 写入进入循环线程的命令 readbuf[0] = BC_ENTER_LOOPER; // 发送 4 字节命令给驱动 binder_write(bs, readbuf, sizeof(uint32_t)); // 永久处理驱动返回的数据 for (;;) { // 每次循环设置读区大小 bwr.read_size = sizeof(readbuf); // 重置驱动已填充的字节数 bwr.read_consumed = 0; // 把读区指向本地缓冲区 bwr.read_buffer = (uintptr_t) readbuf; // 阻塞等待驱动返回命令 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); // 检查 ioctl 结果 if (res < 0) { // 记录循环失败原因 ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno)); // 退出循环 break; } // 解析读到的数据并逐条执行 svcmgr_handler res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); // 零表示出现意外回复 if (res == 0) { // 记录意外结果 ALOGE("binder_loop: unexpected reply?\n"); // 退出循环 break; } // 负值表示解析错误 if (res < 0) { // 记录解析错误 ALOGE("binder_loop: io error %d %s\n", res, strerror(errno)); // 退出循环 break; } }}函数输入是驱动状态和处理回调 svcmgr_handler。每轮输出体现在两处:驱动更新 read_consumed 并把命令放入 readbuf,解析器处理这些命令并生成回复。客户端写给驱动的是 BC_TRANSACTION,ServiceManager 读到的是驱动下发的 BR_TRANSACTION;二者分属协议的发送方向和返回方向,不能混为同一条命令。
五、binder_parse 把驱动数据解包
读缓冲区不是单个 C 结构体,而是一串“4 字节命令字 + 命令附带数据”。ptr 从 read_buffer 起步,end 等于 read_buffer + read_consumed。循环每读出一条命令就移动指针,因此同一个缓冲区可以连续容纳多条命令。
binder_parse 的关键分支是 BR_TRANSACTION。它把命令后的内存解释成 binder_transaction_data,初始化 reply,包装客户端数据为 msg,调用业务回调,随后用 binder_send_reply 把结果送回驱动。
// 代码位置 /frameworks/native/cmds/servicemanager/binder.c 第 204—287 行// 解析驱动写入的命令序列int binder_parse(struct binder_state *bs, struct binder_io *bio, // 接收缓冲区地址、长度和业务回调 uintptr_t ptr, size_t size, binder_handler func){ // 默认返回解析错误 int r = -1; // 计算待解析区间结束地址 uintptr_t end = ptr + (uintptr_t) size; // 持续读取缓冲区中的命令 while (ptr < end) { // 读取当前 4 字节命令字 uint32_t cmd = *(uint32_t *) ptr; // 跳过命令字 ptr += sizeof(uint32_t); // 根据驱动命令选择处理方式 switch (cmd) { // 空操作无需处理 case BR_NOOP: // 事务写入完成也无需额外处理 case BR_TRANSACTION_COMPLETE: break; // 增加弱引用 case BR_INCREFS: // 增加强引用 case BR_ACQUIRE: // 释放强引用 case BR_RELEASE: // 释放弱引用 case BR_DECREFS: // 引用计数类命令跳过 ptr/cookie 结构 ptr += sizeof(struct binder_ptr_cookie); break; // 收到一笔事务 case BR_TRANSACTION: { // 命令后面是 binder_transaction_data struct binder_transaction_data *txn = // 把当前位置解释为事务结构 (struct binder_transaction_data *) ptr; // 检查缓冲区是否容得下完整事务头 if ((end - ptr) < sizeof(*txn)) { // 记录事务过小错误 ALOGE("parse: txn too small!\n"); // 返回解析失败 return -1; } // 打印事务信息 binder_dump_txn(txn); // 有业务回调时处理事务 if (func) { // 准备 256 字节回复数据区 unsigned rdata[256 / 4]; // 包装客户端发来的数据 struct binder_io msg; // 包装要写给客户端的回复 struct binder_io reply; // 保存业务处理结果 int res; // 初始化回复缓冲区 bio_init(&reply, rdata, sizeof(rdata), 4); // 用事务数据初始化请求视图 bio_init_from_txn(&msg, txn); // 调用 ServiceManager 业务回调 res = func(bs, txn, &msg, &reply); // 将业务回复写回驱动 binder_send_reply(bs, &reply, txn->data.ptr.buffer, res); } // 跳过事务头 ptr += sizeof(*txn); // 离开事务分支 break; } // 处理未知命令 default: // 记录未知命令 ALOGE("parse: unknown command %d\n", cmd); // 返回解析失败 return -1; } } // 返回解析结果 return r;}binder_io 用 data/data0、offs/offs0、used/capacity 等字段描述数据区和对象偏移区。收到事务时无需复制出一份 msg;bio_init_from_txn 直接让它引用 txn 指向的共享数据。
// 代码位置 /frameworks/native/cmds/servicemanager/binder.c 第 409—416 行// 从事务结构初始化 binder_io 读取视图void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn){ // 直接使用 txn 的 buffer,并记录数据区起点 bio->data = bio->data0 = (char *) (intptr_t) txn->data.ptr.buffer; // 保存可读取的数据字节数 bio->data_avail = txn->data_size; // 按 size_t 换算偏移数组元素数 bio->offs_avail = txn->offsets_size / sizeof(size_t); // 标记这段内存由事务共享 bio->flags = BIO_F_SHARED;}它的输入是 binder_transaction_data,输出是可按 Parcel 布局读取的 binder_io 视图;底层 buffer 的所有权没有转移。
六、svcmgr_handler 分发服务请求
驱动协议解析完毕,业务入口只剩 svcmgr_handler。它先核对事务目标,再处理 Ping,随后读取 strict policy 头和接口名。接口名必须是 android.os.IServiceManager,否则请求会被拒绝。这相当于 native 侧的 Parcel::enforceInterface()。

事务码从 IBinder::FIRST_CALL_TRANSACTION=1 开始递增:GET_SERVICE 为 1,CHECK_SERVICE 为 2,ADD_SERVICE 为 3。LIST_SERVICES 走独立的 list 权限检查。
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 244—338 行// 统一处理发给 ServiceManager 的事务int svcmgr_handler(struct binder_state *bs, // 接收事务头 struct binder_transaction_data *txn, // 接收请求数据 struct binder_io *msg, // 接收回复缓冲区 struct binder_io *reply){ // 保存链表节点 struct svcinfo *si; // 保存 UTF-16 字符串 uint16_t *s; // 保存字符串长度 size_t len; // 保存服务 handle uint32_t handle; // 保存 strict mode 头 uint32_t strict_policy; // 保存 isolated 访问标志 int allow_isolated; // 目标必须是 handle 0 对应的上下文管理者 if (txn->target.ptr != BINDER_SERVICE_MANAGER) // 目标不符时拒绝 return -1; // Ping 测试不需要业务数据 if (txn->code == PING_TRANSACTION) // 直接返回成功 return 0; // 读取 strict mode 头 strict_policy = bio_get_uint32(msg); // 读取接口名 s = bio_get_string16(msg, &len); // 检查接口名是否存在 if (s == NULL) { // 缺少接口名时拒绝 return -1; } // 检查接口名长度与内容 if ((len != (sizeof(svcmgr_id) / 2)) || // 比较 android.os.IServiceManager memcmp(svcmgr_id, s, sizeof(svcmgr_id))) { // 输出错误接口名 fprintf(stderr, "invalid id %s\n", str8(s, len)); // 拒绝发错目标的请求 return -1; } // 按事务码分发业务 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); // 没有有效 handle 时进入默认回复 if (!handle) break; // 将 handle 写为 Binder 引用 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); // 读取 isolated 访问标志 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); // 检查 list 权限 if (!svc_can_list(txn->sender_pid)) { // 记录权限拒绝 ALOGE("list_service() uid=%d - PERMISSION DENIED\n", // 传入调用方 uid txn->sender_euid); // 返回权限错误 return -1; } // 从链表头开始 si = svclist; // 移动到第 n 个节点 while ((n-- > 0) && (si = si->next)); // 命中节点时返回名字 if (si) { // 把 UTF-16 服务名写入回复 bio_put_string16(reply, si->name); // 返回成功 return 0; } } // 没有对应节点时返回失败 return -1; // 处理未知事务码 default: // 记录未知事务码 ALOGE("unknown code %d\n", txn->code); // 返回失败 return -1; } // 没有提前返回的分支默认回复 0 bio_put_uint32(reply, 0); // 返回成功 return 0;}handler 的输入是事务头和 msg,输出写入 reply。它自身不保存服务;ADD 分支把状态变化交给 do_add_service,GET/CHECK 分支则从链表取得 handle,并由 bio_put_ref 写回引用。
七、注册服务:do_add_service
ServiceManager 管理服务所需的数据并不多。每个 svcinfo 节点记录名字、handle、死亡回调和 isolated 访问策略,svclist 指向链表头。
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 128—135 行// 定义每个已注册服务的管理节点struct svcinfo { // 指向下一个服务 struct svcinfo *next; // 保存驱动分配并替换后的 handle uint32_t handle; // 保存服务进程死亡回调 struct binder_death death; // 标记是否允许 isolated 进程访问 int allow_isolated; // 保存 UTF-16 服务名长度 size_t len; // 在结构体尾部保存服务名 uint16_t name[0];}; // 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 138 行// 初始化全局服务链表为空struct svcinfo *svclist = NULL;结构体的输入来自一次成功的 ADD_SERVICE,输出是链表中的长期状态。ServiceManager 没有保留客户端 Parcel 的全部内容,只持有后续查询和死亡处理需要的信息。

注册前要在 svclist 中按“长度相同且 UTF-16 内容相同”匹配服务名。这段线性查找同时服务于注册覆盖和服务查询。
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 140—150 行// 按 UTF-16 名字查找服务节点struct svcinfo *find_svc(const uint16_t *s16, size_t len){ // 保存遍历指针 struct svcinfo *si; // 从链表头逐个遍历 for (si = svclist; si; si = si->next) { // 先比较长度,再比较名字内容 if ((len == si->len) && // 比较 UTF-16 字节序列 memcmp(s16, si->name, len * sizeof(uint16_t)) == 0) { // 返回命中的节点 return si; } } // 没有命中时返回空指针 return NULL;}输入是服务名和长度,输出是节点指针或 NULL;该函数不修改链表。
do_add_service 先拒绝无效 handle、空名字和超过 127 个 UTF-16 单元的名字,再通过 svc_can_register 检查 SELinux add 权限。普通进程没有相应策略授权时,注册会在这里失败。
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 194—243 行// 注册服务到全局链表int do_add_service(struct binder_state *bs, // 接收服务名与长度 const uint16_t *s, size_t len, // 接收 handle、uid、访问策略和 pid uint32_t handle, uid_t uid, int allow_isolated, pid_t spid){ // 保存目标服务节点 struct svcinfo *si; // 校验 handle 和服务名长度 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", // 传入服务名、handle 和 uid str8(s, len), handle, uid); // 返回注册失败 return -1; } // 在链表中查找同名服务 si = find_svc(s, len); // 已存在时覆盖原节点 if (si) { // 旧节点仍持有有效 handle if (si->handle) { // 记录覆盖注册 ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n", // 传入服务名、handle 和 uid str8(s, len), handle, uid); // 释放旧引用并清空旧 handle svcinfo_death(bs, si); } // 保存新的 handle si->handle = handle; // 不存在时新建节点 } else { // 分配结构体和带结尾零的 UTF-16 名字空间 si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); // 检查内存分配结果 if (!si) { // 记录内存不足 ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n", // 传入服务名、handle 和 uid str8(s, len), handle, uid); // 返回注册失败 return -1; } // 保存 handle 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; // 保存 isolated 访问策略 si->allow_isolated = allow_isolated; // 让新节点指向旧链表头 si->next = svclist; // 以头插法更新全局链表 svclist = si; } // 增加对服务 handle 的引用 binder_acquire(bs, handle); // 请求驱动发送服务死亡通知 binder_link_to_death(bs, handle, &si->death); // 返回注册成功 return 0;}已存在的名字会复用节点:先调用 svcinfo_death 释放旧 handle,再写入新 handle。新名字则分配“结构体 + 名字”的连续内存,填充字段后头插进 svclist。成功路径最后调用 binder_acquire 持有引用,并用 binder_link_to_death 登记死亡通知。
八、服务进程死亡后的状态
注册成功不代表服务会永久存在。服务进程退出后,Binder 驱动把死亡通知送给 ServiceManager,先前登记的 svcinfo_death 因而执行。
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 153—161 行// 处理服务所在进程的死亡通知void svcinfo_death(struct binder_state *bs, void *ptr){ // 把回调参数还原为服务节点 struct svcinfo *si = (struct svcinfo *) ptr; // 记录死亡服务名 ALOGI("service '%s' died\n", str8(si->name, si->len)); // 只处理仍然有效的 handle if (si->handle) { // 释放驱动侧引用 binder_release(bs, si->handle); // 清零 handle,使后续查询失败 si->handle = 0; }}输入是对应的 svcinfo 节点,状态变化是释放驱动引用并把 handle 置为 0。节点仍在链表里,但 do_find_service 会把它当成不可用服务。反方向的故障更严重:ServiceManager 自身死亡会触发系统服务进程重启;反复失败达到 init 的处理条件时,系统可能重启。
九、查询服务:do_find_service
查询路径先按名字取节点,然后依次检查 handle、isolated 访问策略和 SELinux find 权限。通过这些条件后,返回值仍然只是一个 handle。
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 170—192 行// 按名字查询服务 handleuint32_t do_find_service(struct binder_state *bs, const uint16_t *s, size_t len, // 接收调用方 uid 和 pid uid_t uid, pid_t spid){ // 在服务链表中按名字查找 struct svcinfo *si = find_svc(s, len); // 检查节点和 handle 是否有效 if (!si || !si->handle) { // 查不到时返回 0 return 0; } // 服务未允许 isolated 进程访问时继续检查 if (!si->allow_isolated) { // 从 uid 中取出 appid uid_t appid = uid % AID_USER; // 判断 appid 是否位于 isolated 区间 if (appid >= AID_ISOLATED_START && appid < AID_ISOLATED_END) { // isolated 调用方不能取得该服务 return 0; } } // 检查调用方的 SELinux find 权限 if (!svc_can_find(s, len, spid)) { // 权限不足时返回 0 return 0; } // 返回服务 handle return si->handle;}输入包含服务名、uid 和 pid,输出为 handle 或 0。allow_isolated 为假时,appid 落在 AID_ISOLATED_START 到 AID_ISOLATED_END 区间的调用方会被拒绝。权限通过以后,handler 使用 bio_put_ref 创建 BINDER_TYPE_HANDLE 类型的 flat_binder_object 回复客户端。
三类操作使用不同的 SELinux 权限:注册检查 add,查询检查 find,枚举检查 list。按服务名检查的路径会结合 service_contexts 查询安全上下文。
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 110—114 行// 检查注册服务所需的 add 权限static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid){ // 指定权限名称为 add const char *perm = "add"; // 按服务上下文查询并返回布尔结果 return check_mac_perms_from_lookup(spid, perm, str8(name, name_len)) ? 1 : 0;} // 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 116—120 行// 检查枚举服务所需的 list 权限static int svc_can_list(pid_t spid){ // 指定权限名称为 list const char *perm = "list"; // 按调用方上下文检查并返回布尔结果 return check_mac_perms_from_getcon(spid, perm) ? 1 : 0;} // 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 122—126 行// 检查查询服务所需的 find 权限static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid){ // 指定权限名称为 find const char *perm = "find"; // 按服务上下文查询并返回布尔结果 return check_mac_perms_from_lookup(spid, perm, str8(name, name_len)) ? 1 : 0;}这些函数不改动 svclist。它们以发送方 pid、服务名为输入,返回权限判断结果,把“谁能注册、查询或枚举”从链表操作中分离出来。
十、客户端发出的对象与服务端收到的对象
以注册 media.player 为例,客户端交给驱动的数据包含目标接口名 android.os.IServiceManager、服务名 media.player、服务对象和 allowIsolated=0。服务对象此时是 flat_binder_object,类型为 BINDER_TYPE_BINDER:binder 字段保存的是服务对象的本地弱引用,cookie 保存的是强指针,handle 还是空:它指向客户端进程里的本地 Binder 对象,还没有可供 ServiceManager 使用的远端 handle。
驱动转发事务时会处理 Binder 对象:送到 ServiceManager 一侧的类型变为 BINDER_TYPE_HANDLE,handle 已由驱动分配,cookie 为 0。Android 6.0 这条路径中,普通 handle 从 1 递增。具体怎样维护引用表、选择 handle 数值和改写对象属于 Binder 驱动实现,不能仅凭用户空间代码补全。

于是,ServiceManager 收到 ADD_SERVICE 后无需保存客户端发来的完整数据。它从 msg 中读出名字、handle 和 allow_isolated,写进 svcinfo;查询命中时,再把 handle 作为 Binder 引用返回。svclist 这张单链表就是该版本 ServiceManager 的服务目录,主要状态为 handle/name/len/death/allow_isolated。
十一、总结
ServiceManager 成为 Binder 服务总管,依赖的是一条连续的状态链:
- 打开驱动并建立映射,登记为 handle=0 的上下文管理者,进入 ioctl 等待循环;
- 收到事务后,协议解析器把请求交给 handler,handler 再通过
svcinfo链表完成注册、查询、枚举和死亡处理。
本文由 AgentWorkFlow 生成