第3章 ServiceManager设计:注册与查找机制、上下文对象管理、系统服务启动流程

3.1 模块边界与系统分层

ServiceManager(简称SM)是Android系统服务的“电话总机”,运行在独立的servicemanager进程中,属于Native层核心守护进程。它不依赖任何Java虚拟机,直接通过Binder驱动与所有进程通信。在系统分层中,它处于应用框架层与Binder驱动层之间的关键位置:

  • 应用框架层:Java ServiceManager API(android.os.ServiceManager)封装了Native接口;
  • Native层:libbinder中的IServiceManager接口,SM进程本身解析Binder协议;
  • Binder驱动层:/dev/binder提供字节流传输,SM不直接操作驱动,而是通过标准Binder事务。

SM的模块边界清晰:它只做三件事——注册、查找、上下文管理。不参与任何业务逻辑,不存储服务数据,只维护服务名到Binder引用的映射。

架构特征:SM是Android系统唯一的“服务注册中心”,类似于微服务架构中的Consul或Eureka,但更轻量且无集群能力。

3.2 注册机制

当系统服务(例如SurfaceFlinger、ActivityManagerService)启动后,会调用 addService() 将自身注册到SM。注册过程本质是向SM进程发送一个“服务名+Binder引用”的BC_TRANSACTION请求。SM收到后,在内部上下文表中建立映射,并返回确认。

关键接口定义(位于IServiceManager.h):

// 注册服务
status_t addService(const String16& name, const sp<IBinder>& service,
                    bool allowIsolated = false, int dumpsysFlags = 0);

// 上下文对象(服务引用的句柄)存储结构
struct svcinfo {
    String16 name;          // 服务名称(如 "SurfaceFlinger")
    sp<IBinder> binder;     // 服务进程的Binder引用
    int32_t uid;            // 注册进程的UID(用于权限检查)
};

注册流程需经过SM权限校验:只有满足SELinux策略(service_manager_type)且UID属于系统或特定进程才能注册。普通应用即使拥有Binder句柄也无法注册系统服务。

3.3 查找机制

客户端进程通过 getService() 按服务名查询,SM返回对应服务的Binder代理(弱引用)。若服务未注册,SM会阻塞等待直到服务注册(或超时)。查找数据流与注册相反:客户端发送BC_TRANSACTION,SM从上下文表中取出Binder引用,通过驱动返回给客户端。

对比维度 注册(addService) 查找(getService)
发起方 服务提供进程 服务使用进程(客户端)
传输方向 服务进程 → SM 客户端 → SM → 服务进程(间接)
权限要求 严格:需系统UID + SELinux允许 宽松:任何进程都可查询
返回值 状态码(OK/PERMISSION_DENIED) Binder代理对象
超时行为 立即完成 阻塞等待,默认5秒超时

3.4 上下文对象管理

SM内部维护一个单链表(早期实现)或哈希表(Android 10+)来管理svcinfo节点。每个节点包含服务名、Binder引用、注册者UID、dumpsys标志等。上下文对象管理的核心职责:

  • 生命周期:服务进程退出时,Binder驱动会向SM发送BC_DEAD_BINDER通知,SM自动从表中移除对应条目;
  • 重复注册:若同一服务名注册两次,后注册的覆盖前者,需谨慎(addService("activity", …) 不允许重复);
  • 引用计数:SM本身不持有服务进程的强引用,只保存Binder句柄,避免阻止进程退出。
风险点:SM是单进程单线程模型(后改为多线程处理),一旦SM崩溃,所有服务注册丢失,系统将无法启动或运行。Android通过init重启servicemanager机制缓解,但重启期间新服务无法注册。

3.5 系统服务启动流程

Android系统启动时,init进程按init.rc脚本启动关键守护进程,其中SM是第一批启动的服务之一。典型启动顺序如下:

  1. init 启动SM:执行 servicemanager 二进制,SM初始化Binder线程并进入事件循环;
  2. init 启动其他守护进程:如 surfaceflinger、mediaserver、logd 等;
  3. 服务向SM注册:每个守护进程初始化完成后,调用 addService() 将自己注册到SM;
  4. Zygote 启动后注册Java服务:Zygote进程孵化system_server,system_server中的AMS、WMS等Java服务通过 ServiceManager.addService() 注册;
  5. 客户端按需查找:应用进程在需要服务时调用 getService() 获取代理。

3.6 信任链路与安全模型

SM本身不进行加密或签名校验,信任基于以下链路:

  • UID验证:SM通过Binder驱动获取调用方UID,仅允许UID为AID_SYSTEM、AID_ROOT或通过SELinux策略允许的进程注册服务;
  • SELinux:SM进程的SELinux上下文为 u:r:servicemanager:s0,允许其接收指定域的服务管理命令;
  • 查找无限制:任何进程均可查询,避免过度约束影响灵活性。
安全边界设计:SM作为“白名单注册中心”,只信任系统层进程,应用层无法冒充系统服务。若需开放服务给三方应用,需通过系统服务内部代理或权限检查。

3.7 选型权衡与风险点

选型维 集中式SM 替代方案(如服务广播)
复杂度 低,单进程维护映射 高,需分布式一致协议
性能 单点瓶颈,但Binder驱动分担部分负载 无单点,但协调开销大
单点故障 存在,依赖init重启 较难避免,需冗余机制
Android适用性 默认且高效 不兼容现有框架

主要风险点:

  • SM本身内存有限,若服务注册过多(理论<500),可能耗尽Binder缓冲区;
  • 恶意进程模拟系统服务注册,需依赖SELinux完全拦截;
  • 查找服务时的阻塞等待可能导致客户端ANR。

3.8 落地步骤与验收标准

若要在AOSP中添加自定义系统服务并完成注册,执行以下步骤:

  1. 在init.rc中添加服务定义,确保servicemanager已启动;
  2. 在Native服务进程中,调用 defaultServiceManager()->addService(String16("my_service"), binder);;
  3. 更新SELinux策略(service_contexts文件),允许服务域访问servicemanager;
  4. 在客户端通过 getService(String16("my_service")) 获取代理并验证。

验收标准:

  • 服务注册后,通过dumpsys -l能列出服务名;
  • 客户端调用代理方法能正确回到服务进程;
  • 未授权的应用进程无法注册同名服务(返回PERMISSION_DENIED);
  • SM进程重启后,服务需重新注册(客户端应处理Binder死亡通知)。

3.9 核心数据流与控制流(设计总图)

ServiceManager 注册与查找数据流 ServiceManager (上下文对象管理) Binder 驱动 (内核层) 服务进程 (如 SurfaceFlinger) 客户端进程 (如 SystemUI) 注册(addService) 查找请求(getService) 返回 Binder 代理 说明: 注册时,服务进程通过Binder驱动向SM发送BC_TRANSACTION;查找时,客户端发起请求,SM返回Binder引用。 虚线表示查找数据流,实线表示注册数据流。所有数据流必经Binder驱动。

3.10 关键设计决策总结

  • 为什么SM采用单进程模型? 简化权限控制,避免跨进程状态同步,Binder驱动已提供高效IPC;
  • 为何不缓存服务引用到客户端本地? 避免服务进程崩溃后客户端持有过期句柄,每次查询强制通过SM获得最新引用;
  • 服务启动顺序如何保证? 通过init.rc的class和trigger机制确保SM先启动,依赖SM的服务后启动。
学习路径建议:阅读AOSP中 frameworks/native/cmds/servicemanager/ 目录下的 service_manager.c 源码,理解上下文对象管理的链表操作(旧版)或哈希表操作(新版)。这是理解Binder架构的起点。