Skip to content

APEX服务

追踪 APEX 服务清单、SDK 条件、jar classloader、SystemServiceManager 封口与失败路径。

基于android-17.0.0_r1
AndroidSystemServerAPEX源码阅读

APEX服务 ​

startApexServices() 位于 SystemServer 服务启动链的最后。它不扫描任意 /apex 目录,而是消费 ApexManager 已经根据 active APEX 包整理出的 ApexSystemServiceInfo 列表;每个条目包含服务类名、可选 jar 路径和 init order。SystemServer 再按“类名 + 当前 classpath”或“类名 + standalone jar”两条分支启动,最后调用 sealStartedServices(),把“不能再启动新系统服务”变成 SystemServiceManager 的状态不变量。

1. 入口与调试开关 ​

源码文件:frameworks/base/services/java/com/android/server/SystemServer.java

java
private void startApexServices(
        @NonNull TimingsTraceAndSlog t) {
    // 仅用于 debug 构建中的 RescueParty 故障注入。
    if (Build.IS_DEBUGGABLE
            && SystemProperties.getBoolean(
                    "debug.crash_system", false)) {
        throw new RuntimeException();
    }

    t.traceBegin("startApexServices");
    List<ApexSystemServiceInfo> services =
            ApexManager.getInstance().getApexSystemServices();

debug.crash_system 只在 debuggable 构建生效,用于验证 SystemServer 故障恢复;它不是 APEX 服务启动失败。正常路径从 ApexManager 获取已排序列表,并为每个服务建立独立 trace section。

2. APEX清单 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/ApexManager.java

java
for (ParsedApexSystemService service
        : scanResult.pkg.getApexSystemServices()) {
    String minSdkVersion = service.getMinSdkVersion();
    if (minSdkVersion != null
            && !UnboundedSdkLevel.isAtLeast(minSdkVersion)) {
        continue;
    }
    String maxSdkVersion = service.getMaxSdkVersion();
    if (maxSdkVersion != null
            && !UnboundedSdkLevel.isAtMost(maxSdkVersion)) {
        continue;
    }

    if (scanResult.apexInfo.isActive) {
        mApexSystemServices.add(new ApexSystemServiceInfo(
                service.getName(), service.getJarPath(),
                service.getInitOrder()));
    }
}
Collections.sort(mApexSystemServices);

清单只接受 active APEX 的服务,并先过滤 min_sdk_version/max_sdk_version。相同服务名会抛 duplicate 错误;同一 module 出现两个 active package 也会失败。initOrder 决定排序,SystemServer 不重新解释排序规则。

3. Classloader分支 ​

源码文件:frameworks/base/services/java/com/android/server/SystemServer.java、frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

java
for (ApexSystemServiceInfo info : services) {
    String name = info.getName();
    String jarPath = info.getJarPath();
    t.traceBegin("starting " + name);
    if (TextUtils.isEmpty(jarPath)) {
        mSystemServiceManager.startService(name);
    } else {
        mSystemServiceManager.startServiceFromJar(
                name, jarPath);
    }
    t.traceEnd();
}

无 jar path 的服务从 SystemServer 当前 classloader 加载;有 jar path 的服务使用 SystemServerClassLoaderFactory 创建/复用 PathClassLoader,再反射加载类。startServiceFromJar() 适用于 PRODUCT_STANDALONE_SYSTEM_SERVER_JARS 或 APEX standalone jar,不应替代编译期可见的显式 startService(Class)。

4. 启动语义 ​

java
public SystemService startServiceFromJar(
        String className, String path) {
    PathClassLoader loader =
            SystemServerClassLoaderFactory.getOrCreateClassLoader(
                    path, this.getClass().getClassLoader(),
                    isJarInTestApex(path));
    Class<SystemService> serviceClass =
            loadClassFromLoader(className, loader);
    return startService(serviceClass);
}

private void startService(@NonNull SystemService service) {
    String className = service.getClass().getName();
    if (mServiceClassnames.contains(className)) {
        Slog.i(TAG, "Not starting an already started service "
                + className);
        return;
    }
    mServiceClassnames.add(className);
    mServices.add(service);
    service.onStart();
}

SystemServiceManager 先按 class name 去重,再把实例加入 mServices,最后调用 onStart()。类找不到会抛出带 classloader/path 的 RuntimeException;onStart() 抛异常会被包装为“Failed to start service”。因此 APEX 服务的构造、注册和生命周期失败都发生在 seal 之前,可阻止整个 StartServices 阶段。

5. 服务封口 ​

源码文件:frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

java
public void sealStartedServices() {
    mServiceClassnames = Collections.emptySet();
    mServices = Collections.unmodifiableList(mServices);
}

封口后,已启动服务列表变为不可修改列表,class name 集合变成空集合。这个设计不是删除服务,而是禁止后续 startService() 再把新服务加入启动集合;boot phase、用户回调和 dump 仍可遍历已有 mServices。源码注释把封口放在 APEX 之后,避免 APEX 更新引入的新依赖破坏平台服务稳定性。

6. 失败与生效时机 ​

  • debug crash property:在 debug 构建中于列表读取前主动抛异常;
  • SDK 条件不满足:ApexManager 跳过条目,不进入 SystemServer 启动;
  • duplicate service/module:清单形成阶段抛异常;
  • classloader/class not found:startService 阶段失败;
  • onStart() 异常:SystemServiceManager 包装后抛出,StartServices 中止;
  • seal 后尝试新服务:违反启动编排不变量,应视为调用方错误。

7. 导航与后续 ​

bash
# 输入 SystemServer APEX 启动方法,输出清单消费、jar 分支和封口。
rg -n "startApexServices|getApexSystemServices|startServiceFromJar|sealStartedServices|debug.crash_system" \
  frameworks/base/services/java/com/android/server/SystemServer.java

# 输入 ApexManager,输出 active、SDK 过滤、重复检查和排序。
rg -n "getApexSystemServices|minSdkVersion|maxSdkVersion|Duplicate apex-system-service|Collections.sort" \
  frameworks/base/services/core/java/com/android/server/pm/ApexManager.java

# 输入生命周期管理器,输出 classloader、去重、onStart 和封口实现。
rg -n "startServiceFromJar|loadClassFromLoader|mServiceClassnames|onStart|sealStartedServices" \
  frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

下一篇将进入 SystemServer 的 boot phase 顺序,解释 startBootPhase() 如何遍历已封口服务并触发 onBootPhase();不会重复 APEX 清单和 classloader 分支。