Android 系统启动机制(八):系统服务怎样从创建走到可用?SystemServiceManager 和 Boot Phase 各管什么?

SystemServiceManager 这个名字很容易让人想象出一个完整的依赖框架:服务声明依赖,Manager 自动做拓扑排序,依次创建、发布并判定 ready。

Android 16 的真实实现没有这么"自动"。

先说明这个 Manager 真正负责的范围:

system_server 的大顺序主要写死在 SystemServer.java 的显式调用序列中;SystemServiceManager 负责构造并启动由它管理的 SystemService、保存列表、按单调递增的 Boot Phase 依次回调。服务之间更细的依赖,靠源码顺序、Boot Phase、LocalServices/Binder 查询、异步等待和服务专用 systemReady() 等机制共同表达。它不是通用依赖图求解器,也不管理 system_server 中的所有服务。

本文围绕一个核心问题展开: "服务对象已经创建"到"能力真的可用",中间经过哪些里程碑?

源码基线: Android 16 QPR2 android-16.0.0_r4frameworks/base commit 45034f0663f960d9ee5fb0a101a4732b71f6e2f4

一、先把五个容易混成一个点的状态拆开

一个 Framework 服务常见但非强制统一的状态链是:

markdown 复制代码
1. constructed
   Java 对象已经构造
​
2. started
   onStart() / main() / start() 已执行
​
3. published
   Binder 名称进入 ServiceManager,或本地接口进入 LocalServices
​
4. phase-ready
   收到某个 Boot Phase,允许依赖某类系统能力
​
5. service-ready / user-ready
   完成服务专用 systemReady、异步初始化、用户解锁或数据加载

这些状态不能互相替代:对象存在不等于已发布,已发布不等于内部依赖齐全;到达某个 phase 不等于所有异步任务结束,全局 ready 也不等于每个用户都已解锁并收到广播。

二、startService(Class) 实际做了什么?

SystemServiceManager.startService(Class) 的主流程并不复杂:

kotlin 复制代码
确认 class 是 SystemService 子类
  → 查找接收 Context 的 public constructor
  → 反射构造实例
  → startService(instance)
  → 把实例放进 mServices
  → 调用 service.onStart()

用近似伪代码表示:

ini 复制代码
T service = constructor.newInstance(mContext);
mServices.add(service);
service.onStart();
return service;

1. 构造函数用于建立对象,不宜宣称能力已经 ready

SystemService 文档把构造函数描述为接收 System Context、初始化服务对象。需要 Binder 发布、依赖其他服务或启动外部交互的动作,通常放到 onStart() 或更晚阶段。

但这是 Framework 约定,不是运行时魔法。某个具体服务的构造函数究竟做了多少工作,仍要看实现。

2. onStart() 是"启动回调",不是统一的"所有依赖完成回调"

SystemService.onStart() 是 abstract。基类文档建议服务在这里发布 Binder 接口,并可发布 system_server 内部 LocalService。

它可以创建核心对象、发布 Binder/Local 接口、启动线程、注册监听或发起异步加载,并把依赖更晚系统能力的动作留给 onBootPhase()

所以 startService() 正常返回,最多证明构造与 onStart() 没有同步抛异常;不能自动证明服务的所有后台初始化都结束。

三、SystemServiceManager 会自动分析服务依赖吗?

不会。

SystemServiceManager 没有读取一张"Service A dependsOn Service B"的全局图再拓扑排序。启动顺序主要来自 SystemServer.java 中直接书写的顺序:

ini 复制代码
mSystemServiceManager.startService(ServiceA.class);
mSystemServiceManager.startService(ServiceB.class);
LegacyService serviceC = LegacyService.main(...);
...
mSystemServiceManager.startBootPhase(..., PHASE_SYSTEM_SERVICES_READY);

如果 B 的构造或 onStart() 必须同步使用 A,SystemServer 就必须先启动 A,或把依赖动作推迟到双方都应存在的 Boot Phase。

因此,阅读依赖必须同时看 SystemServer 的显式顺序、服务在 onStart() 中查询和发布的对象、onBootPhase() 使用依赖的阶段、初始化线程池与显式等待,以及是否还有专用 systemReady() 调用。

不能只看 SystemServiceManager 一个文件就还原全部依赖图。

四、Boot Phase 解决的是什么问题?

如果所有服务都在 onStart() 里立即访问所有依赖,SystemServer 会陷入复杂的互相等待。Boot Phase 提供一组全局单调里程碑:服务可以先创建/发布最小接口,再把依赖后置。

Android 16 的核心阶段定义在 SystemService.java

Phase 常量 主要语义
100 PHASE_WAIT_FOR_DEFAULT_DISPLAY 默认显示已满足早期服务继续启动的条件
200 PHASE_WAIT_FOR_SENSOR_SERVICE 等待 Native SensorService 可用的阶段
480 PHASE_LOCK_SETTINGS_READY 可以取得 lock settings 数据
500 PHASE_SYSTEM_SERVICES_READY 可以安全调用 PowerManager、PackageManager 等核心能力
520 PHASE_DEVICE_SPECIFIC_SERVICES_READY 设备特定服务已到可使用阶段
550 PHASE_ACTIVITY_MANAGER_READY 可广播 Intent 等 AMS ready 能力
600 PHASE_THIRD_PARTY_APPS_CAN_START 可以启动/绑定第三方 App
1000 PHASE_BOOT_COMPLETED 全局 Framework 启动进入 boot-completed 阶段

数字中故意留有间隔,便于平台插入阶段;调用必须严格递增。

五、startBootPhase() 怎样分发?

SystemServiceManager.startBootPhase() 的核心是:

scss 复制代码
检查 phase > mCurrentPhase
  → 更新 mCurrentPhase
  → 按 mServices 列表顺序
  → 逐个调用 service.onBootPhase(phase)
  → 记录耗时,异常则把启动失败向上抛

这里还有一个诊断上非常重要的顺序:mCurrentPhase 在遍历服务之前就被更新。因此 dumper 显示 Current phase: 500,只能证明 Manager 已经进入 phase 500;它可能仍阻塞在某个服务的 onBootPhase(500) 中。看到更晚 phase、当前 phase 之后的 SystemServer 日志,或 sys.boot_completed=1 这类后续里程碑,才能进一步证明相应同步分发已经返回。

1. 同步分发不等于固定线程,更不是异步任务 barrier

这里要同时保留"文档契约""Manager 实现"和"某次实际调用"三层事实:

观察层 能得出的结论
SystemService 文档契约 生命周期方法应由 system_server 主 Looper 线程调用
SystemServiceManager 实现 startService()startBootPhase() 都在当前调用线程同步执行;方法内部没有统一 post 到主 Looper,也没有线程断言
某一次真实调用 回调实际落在哪条线程,由调用点、进程内调用链以及中途是否显式切线程决定

多数早期 startService() 和 Boot Phase 调用直接发生在 SystemServer 的启动控制流上,此时调用线程就是后来进入主 Looper 的 system_server 主线程。但不能据此推出"所有 phase 在任何路径上都必然运行于主线程"。

Android 16 的 phase 1000 是一个关键反例:WMS 完成显示启用后调用 bootAnimationComplete(),AMS 再直接进入 finishBooting(),最后调用 SystemServiceManager.startBootPhase(1000);这条同步链没有天然重投到主 Looper。实机同一 FrameworkGeneration 的连续日志中,wm_boot_animation_doneStarting phase 1000 落在同一个 system_server 非主 TID(TID != PID),说明那一轮 phase 1000 的实际调用线程不是主线程。第九篇会把这条路径放回"显示完成 → phase 1000 → 属性跃迁"的整体顺序。

因此,写设计契约时可以说"应由主 Looper 调用";分析卡顿、锁或 trace 时,则必须沿实际调用点确认线程。无论同步回调落在哪条线程,只要服务把工作提交给 HandlerThreadSystemServerInitThreadPool 或自己的 executor,就已经进入另一段异步工作;Manager 不会自动等待,除非服务或 SystemServer 显式建立等待关系。

2. 回调顺序沿用服务注册顺序

它没有按运行时依赖重新排序。一个 phase 内先回调哪个服务,由 mServices 的加入顺序决定。

3. 更晚启动的对象不能假设自动补收所有旧阶段

startBootPhase() 对"截至调用时已启动的服务"遍历。若平台在某个阶段后才创建新服务,是否需要旧阶段语义必须由其设计与启动位置保证,不能凭想象认为 Manager 会重放全部历史阶段。

六、SystemServer 中的四个分组不是 Boot Phase

再次对齐两套坐标:

startBootstrap/Core/Other/ApexServices() PHASE_*
SystemServer 源码组织和大体启动顺序 分发给已启动 SystemService 的生命周期里程碑
一个方法中可以穿插多个 phase 同一 phase 会回调多个已启动服务
不是数字状态 有单调递增的 mCurrentPhase
也会直接启动 legacy service 只由 SystemServiceManager 分发给其列表中的服务

尤其不要把 startCoreServices() 与 init rc 的 class_start core 混为一套系统。前者是 Java 方法,后者是 init Service class 命令。

七、Binder 发布与 LocalService 发布有什么区别?

SystemService 提供两类便捷方法。

1. publishBinderService()

publishBinderService() 最终调用:

ini 复制代码
ServiceManager.addService(name, binder, allowIsolated, dumpPriority);

发布后,其他进程可按权限和 SELinux 规则通过 Binder 查找、调用这个接口。

它只证明"名称已可发现",不自动证明所有方法都能成功、底层 daemon/HAL 已连接、当前用户数据已解锁、异步缓存已加载或调用方拥有权限。

服务可以为了打破循环依赖而较早发布接口,再在方法内部拒绝、排队或降级处理尚未 ready 的请求。

2. publishLocalService()

publishLocalService() 把 Java 对象放进 LocalServices,只在 system_server 进程内按 Class 查找,不经过 Binder 驱动,通常用于更高权限、更细粒度的内部接口;具体调用仍可能跨线程、加锁或触发其他 IPC。

"Local"表示进程内发现机制,不等于所有方法都便宜、同步或无锁。

八、统一框架之外:legacy service 与专用 systemReady()

system_server 保留了许多历史形成的启动模式。Android 16 中可直接看到:

scss 复制代码
PackageManagerService.main(...)
WindowManagerService.main(...)
ServiceManager.addService(Context.WINDOW_SERVICE, wm, ...)

这些对象可能有自己的 Lifecycle 包装,也可能由 SystemServer 直接构造、保存并调用 systemReady();并非每个对象都通过 mSystemServiceManager.startService() 加入 mServices。专用 systemReady() 也不是统一接口:复杂旧服务常用它传入依赖、执行回调或取得结果,调用阶段和具体语义必须回到对应类与 SystemServer 调用点判断。

此外,"Android system service"还可能位于别的 Linux 进程,例如 SurfaceFlinger、voldnetd、HAL/vendor service、独立应用或模块进程,以及其他由 init 管理的 daemon。

所以应使用窄结论:

SystemServiceManager 统一管理通过它启动的 SystemService 实例 及其 Boot Phase,而不是 Android 全系统的所有服务;方法都叫 systemReady(),也不代表它们共享同一接口、阶段或完成语义。

九、一个服务怎样设计分阶段启动?

概念模型可以压成三步:构造函数只建立最小对象状态;onStart() 发布最小 Binder/Local 接口;onBootPhase() 再在约定里程碑启用依赖更重的能力。这样可以先打破部分循环依赖,再逐步开放功能。

服务仍必须自己定义"未 ready 调用"如何处理------拒绝、降级、排队还是显式等待。SystemService 基类和 Boot Phase 都不会代替服务判断业务状态。

十、接下来要定义的不是"最后一个 phase",而是"完成"

Android 的服务依赖不是由 SystemServiceManager 自动解图;SystemServer 用显式源码顺序先构造和启动服务,服务通过 Binder/LocalService 发布最小接口,再由单调 Boot Phase 与专用 systemReady() 逐步开放依赖能力。要判断"可用",必须说明是构造、发布、哪个 phase,还是用户级 ready。

现在已经能解释服务怎样从"对象"走到"分阶段可用",仍缺最后一个定义:当 system_server、Binder 名称和 Boot Phase 都已经出现时,哪个时刻才配得上"Android 启动完成"?下一篇专门拆开这个词。

源码定位

相关推荐
hai_android1 小时前
Android 组件化开发实践
android·java·kotlin
mmsx1 小时前
Android 几何构造器的双栈机:链式 API 底层是怎么装配几何的
android
mmsx1 小时前
Android 地图卡成 PPT 之后:双渲染管线与空间网格渐进加载怎么救
android·app
光影少年2 小时前
setImmediate 和 setTimeout(0) 的区别
android·前端·react.js·ios·前端框架
Android-Flutter16 小时前
android compose 知识点
android·compose
其实防守也摸鱼17 小时前
Codex 下载与本地部署实战:从安装到运行全指南
android·大数据·运维·安全·自动化
ttyyttemo18 小时前
协程并发执行与共享状态
android
miaowmiaow18 小时前
我让 AI 给老项目做了一次分层架构升级:对标 Now in Android,从「穿层」到「端口与适配器」
android·ai编程·deepseek
mmsx18 小时前
Android 栅格数据缓冲区的类型包装:用匿名子类破解泛型擦除
android·地图·栅格