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引用的映射。
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句柄,避免阻止进程退出。
3.5 系统服务启动流程
Android系统启动时,init进程按init.rc脚本启动关键守护进程,其中SM是第一批启动的服务之一。典型启动顺序如下:
- init 启动SM:执行
servicemanager二进制,SM初始化Binder线程并进入事件循环; - init 启动其他守护进程:如
surfaceflinger、mediaserver、logd等; - 服务向SM注册:每个守护进程初始化完成后,调用
addService()将自己注册到SM; - Zygote 启动后注册Java服务:Zygote进程孵化system_server,system_server中的AMS、WMS等Java服务通过
ServiceManager.addService()注册; - 客户端按需查找:应用进程在需要服务时调用
getService()获取代理。
3.6 信任链路与安全模型
SM本身不进行加密或签名校验,信任基于以下链路:
- UID验证:SM通过Binder驱动获取调用方UID,仅允许UID为AID_SYSTEM、AID_ROOT或通过SELinux策略允许的进程注册服务;
- SELinux:SM进程的SELinux上下文为
u:r:servicemanager:s0,允许其接收指定域的服务管理命令; - 查找无限制:任何进程均可查询,避免过度约束影响灵活性。
3.7 选型权衡与风险点
| 选型维 | 集中式SM | 替代方案(如服务广播) |
|---|---|---|
| 复杂度 | 低,单进程维护映射 | 高,需分布式一致协议 |
| 性能 | 单点瓶颈,但Binder驱动分担部分负载 | 无单点,但协调开销大 |
| 单点故障 | 存在,依赖init重启 | 较难避免,需冗余机制 |
| Android适用性 | 默认且高效 | 不兼容现有框架 |
主要风险点:
- SM本身内存有限,若服务注册过多(理论<500),可能耗尽Binder缓冲区;
- 恶意进程模拟系统服务注册,需依赖SELinux完全拦截;
- 查找服务时的阻塞等待可能导致客户端ANR。
3.8 落地步骤与验收标准
若要在AOSP中添加自定义系统服务并完成注册,执行以下步骤:
- 在init.rc中添加服务定义,确保servicemanager已启动;
- 在Native服务进程中,调用
defaultServiceManager()->addService(String16("my_service"), binder);; - 更新SELinux策略(
service_contexts文件),允许服务域访问servicemanager; - 在客户端通过
getService(String16("my_service"))获取代理并验证。
验收标准:
- 服务注册后,通过
dumpsys -l能列出服务名; - 客户端调用代理方法能正确回到服务进程;
- 未授权的应用进程无法注册同名服务(返回
PERMISSION_DENIED); - SM进程重启后,服务需重新注册(客户端应处理Binder死亡通知)。
3.9 核心数据流与控制流(设计总图)
3.10 关键设计决策总结
- 为什么SM采用单进程模型? 简化权限控制,避免跨进程状态同步,Binder驱动已提供高效IPC;
- 为何不缓存服务引用到客户端本地? 避免服务进程崩溃后客户端持有过期句柄,每次查询强制通过SM获得最新引用;
- 服务启动顺序如何保证? 通过init.rc的
class和trigger机制确保SM先启动,依赖SM的服务后启动。
frameworks/native/cmds/servicemanager/ 目录下的 service_manager.c 源码,理解上下文对象管理的链表操作(旧版)或哈希表操作(新版)。这是理解Binder架构的起点。