Skip to content

PMS启动时序

沿 SystemServer、PMS.main、构造扫描、Binder 发布、systemReady 与应用数据屏障追踪 Android 17 PMS 的真实启动时序。

基于android-17.0.0_r1
AndroidPackageManagerServicePMSSystemServer系统启动源码阅读

PMS启动时序 ​

本文面向已经知道 system_server 是系统服务宿主、了解 Binder 服务注册含义的读者。建议先阅读 Bootstrap服务、Binder调用源码地图 和 PMS架构全景。本文解决的问题不是“PMS 有哪些类”,而是 PMS 从 SystemServer 的一个同步调用,如何逐步变成可查询、可安装、能与其他系统服务协作的运行期服务。

启动时最容易混淆三个时间点:PackageManagerService.main() 返回、PackageManagerService.systemReady() 返回、第三方应用获准启动。它们不是同一个“ready”。读完本文,应能从 SystemServer.startBootstrapServices() 追到包状态恢复、系统包与 /data/app 扫描、Binder/Local 接口发布,再解释为什么应用数据准备的异步任务必须在 PHASE_THIRD_PARTY_APPS_CAN_START 前被等待。

本文只展开足以解释启动时序的扫描骨架。分区扫描规则、冲突处理、并行解析和 Settings 文件格式分别属于后续专题,避免在这里把所有启动代码压缩成一篇无法验证的“大全”。

1. 三道屏障 ​

PMS 启动不是一次构造函数调用,而是三道职责不同的屏障。

屏障入口已完成的状态尚未保证
核心构造PackageManagerService.main() 返回Settings 与扫描结果进入内存,查询 snapshot 建立,package/package_native 已发布运行期 observer、默认权限、安装会话恢复未全部完成
系统就绪PackageManagerService.systemReady() 返回Settings/User/Permission/Installer 等子系统接入运行期,staged session 开始恢复启动时异步 app-data 修复可能仍在执行
应用放行waitForAppDataPrepared() 返回system user 的关键应用数据修复与准备任务结束不代表所有开机后台优化都已完成

下面的状态图只表达真实的生命周期屏障,不把内部数百个初始化动作伪装成单一状态。

第一个屏障建立“PMS 自己的世界”,第二个屏障把它接入“系统已经可协作的世界”,第三个屏障保护即将运行的应用不碰到尚未准备好的数据目录。把三者都称为“PMS 启动完成”,会掩盖 Binder 可达性、运行期副作用和应用数据一致性的不同边界。

2. 前置依赖 ​

2.1 SystemServer入口 ​

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

java
// SystemServer 主线程按固定阶段同步启动服务。
t.traceBegin("StartServices");
try {
    startBootstrapServices(t);
    startCoreServices(t);
    startOtherServices(t);
    startApexServices(t);
} catch (Throwable ex) {
    Slog.e("System", "******************************************");
    Slog.e("System", "************ Failure starting system services", ex);
    throw ex;
} finally {
    t.traceEnd();
}

调用方是 SystemServer.run(),owner 是 system_server 主线程。四个阶段是同步顺序:Bootstrap 未返回,Core 不会开始;未被局部捕获的异常会到达这里,被记录后重新抛出。PMS 的构造发生在 Bootstrap,但 systemReady() 位于 Other 阶段,因此两个阶段之间仍会启动大量依赖 PMS 查询能力的服务。

2.2 PMS之前 ​

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

java
// 以下代码按 Android 17 源码中的先后顺序摘录;中间无关服务已省略。
ArtModuleServiceInitializer.setArtModuleServiceManager(
        new ArtModuleServiceManager());

final Watchdog watchdog = Watchdog.getInstance();
watchdog.start();

PlatformCompat platformCompat = new PlatformCompat(mSystemContext);
ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);

mSystemServiceManager.startService(FileIntegrityService.class);
Installer installer = mSystemServiceManager.startService(Installer.class);

ActivityTaskManagerService atm = mSystemServiceManager.startService(
        ActivityTaskManagerService.Lifecycle.class).getService();
mActivityManagerService = ActivityManagerService.Lifecycle.startService(
        mSystemServiceManager, atm);

mDataLoaderManagerService = mSystemServiceManager.startService(
        DataLoaderManagerService.class);
mIncrementalServiceHandle = startIncrementalService();

mPowerManagerService = mSystemServiceManager.startService(
        PowerManagerService.class);
mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class);

这段顺序说明“在 PMS 之前已经启动”不等于“PMS 构造函数会直接调用每一个服务”。真正由 main() 显式消费的是 Context、Installer、DomainVerificationService;Injector 还会从 ServiceManager、LocalServices 或 Context.getSystemService() 获取 PlatformCompat、AppOps、IncrementalManager 等对象。PowerManager、AMS/ATMS 和 RecoverySystem 已经存在,但不能据此虚构 PMS 对它们的直接构造依赖。

ArtModuleServiceInitializer 被刻意放在 PMS 前。源码注释指出 PMS 初始化会分配大量对象并触发 GC,而 ART module 类来自另一个 dex;提前触发 class linker 可以避免它稍后与 GC 的互斥冲突。这里的消费者不是 PMS 构造函数本身,而是 PMS 返回后初始化的 DexUseManagerLocal 与 ART 本地管理接口。

2.3 Display屏障 ​

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

java
// PMS 会读取默认显示的 metrics,因此 DisplayManager 先启动并等待默认显示。
mDisplayManagerService = mSystemServiceManager.startService(
        DisplayManagerService.class);
mSystemServiceManager.startBootPhase(
        t, SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);

DomainVerificationService domainVerificationService =
        new DomainVerificationService(
                mSystemContext, SystemConfig.getInstance(), platformCompat);
mSystemServiceManager.startService(domainVerificationService);

构造函数稍后会执行 getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics)。因此这里不是“显示服务大概应该先启动”,而是有明确消费者的前置屏障:默认显示不存在时,PMS 无法得到解析资源所需的 metrics。DomainVerificationService 随后创建,并作为真实参数传给 PackageManagerService.main()。

3. 主线程调用 ​

3.1 同步入口 ​

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

java
if (!mRuntimeRestart) {
    FrameworkStatsLog.write(
            FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
            FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__
                    PACKAGE_MANAGER_INIT_START,
            SystemClock.elapsedRealtime());
}

t.traceBegin("StartPackageManagerService");
try {
    Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
    mPackageManagerService = PackageManagerService.main(
            mSystemContext, installer, domainVerificationService,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF);
} finally {
    Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
}

mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
t.traceEnd();

main() 没有被投递到后台线程,SystemServer 主线程会同步等待构造、Settings 恢复和启动扫描结束。Watchdog 暂停的也只是“当前线程”的监控,并不是停止整个 Watchdog。finally 保证构造成功或抛异常时都恢复监控,否则一次失败可能让主线程永久脱离死锁检测。

mFirstBoot 只能在 main() 返回后读取,因为它由 Settings.readLPw() 是否找到持久化文件决定。mSystemContext.getPackageManager() 此时获得 framework 客户端对象;真正的 Binder 服务已在 main() 返回前注册。

mRuntimeRestart 只控制两次 statsd elapsed-time 事件是否上报,不跳过 PMS 构造。工厂测试模式则被转换为布尔参数,进入 PMS 状态,供后续条件路径使用。

3.2 总体时序 ​

时序图中最重要的间隔是 Binder 发布到 systemReady() 之间。接口已经能被发现,但某些方法的语义仍受 mSystemReady、子系统 ready 状态和系统启动顺序约束。发布不是“所有后处理已经完成”,而是“构造出的核心查询状态可以进入服务发现体系”。

4. main工厂 ​

4.1 锁与线程 ​

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

java
public static PackageManagerService main(Context context,
        Installer installer,
        @NonNull DomainVerificationService domainVerificationService,
        boolean factoryTest) {
    final TimingsTraceAndSlog t = new TimingsTraceAndSlog(
            TAG + "Timing", Trace.TRACE_TAG_PACKAGE_MANAGER);
    t.traceBegin("create package manager");

    final PackageManagerTracedLock lock =
            new PackageManagerTracedLock("mLock");
    final PackageManagerTracedLock installLock =
            new PackageManagerTracedLock("mInstallLock");

    HandlerThread backgroundThread = new ServiceThread(
            "PackageManagerBg", Process.THREAD_PRIORITY_BACKGROUND,
            true /* allowIo */);
    backgroundThread.start();
    Handler backgroundHandler = new Handler(
            backgroundThread.getLooper(), BACKGROUND_HANDLER_CALLBACK);

    // Injector 的 producer 参数在下一段按职责展开。

两把锁与后台线程由 main() 创建,再通过 Injector 交给 PMS 和子组件,因此它们的生命周期与 system_server 进程一致。mLock 保护包内存状态,mInstallLock 序列化 installd 与慢磁盘操作;PackageManagerBg 负责可异步执行的写入或清理。构造扫描本身仍由 SystemServer 主线程发起,并不会因为创建了 background thread 就自动并行化。

4.2 Injector装配 ​

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

java
PackageManagerServiceInjector injector = new PackageManagerServiceInjector(
        context, lock, installer, installLock, new PackageAbiHelperImpl(),
        backgroundHandler, SYSTEM_PARTITIONS,
        (i, pm) -> new ComponentResolver(
                i.getUserManagerService(), pm.mUserNeedsBadging),
        (i, pm) -> PermissionManagerService.create(context,
                i.getSystemConfig().getAvailableFeatures()),
        (i, pm) -> LocalServices.getService(AppOpsManagerInternal.class),
        (i, pm) -> new UserManagerService(context, pm,
                new UserDataPreparer(installer, installLock, context), lock),
        (i, pm) -> new Settings(Environment.getDataDirectory(),
                RuntimePermissionsPersistence.createInstance(),
                i.getPermissionManagerServiceInternal(),
                domainVerificationService, backgroundHandler, lock),
        (i, pm) -> AppsFilterImpl.create(i,
                i.getLocalService(PackageManagerInternal.class)),
        (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
        (i, pm) -> SystemConfig.getInstance(),
        (i, pm) -> new DynamicCodeLogger(i.getInstaller()),
        (i, pm) -> new ArtManagerService(i.getContext()),
        (i, pm) -> ApexManager.getInstance(),
        (i, pm) -> (IncrementalManager)
                i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
        (i, pm) -> new DefaultAppProvider(
                () -> context.getSystemService(RoleManager.class),
                () -> LocalServices.getService(UserManagerInternal.class)),
        (i, pm) -> new DisplayMetrics(),
        (i, pm) -> new PackageParser2(pm.mSeparateProcesses,
                i.getDisplayMetrics(),
                new PackageCacher(pm.mCacheDir, pm.mPackageParserCallback),
                pm.mPackageParserCallback),
        (i, pm) -> new PackageParser2(pm.mSeparateProcesses,
                i.getDisplayMetrics(), null, pm.mPackageParserCallback),
        (i, pm) -> new PackageParser2(pm.mSeparateProcesses,
                i.getDisplayMetrics(), null, pm.mPackageParserCallback),
        (i, pm, developerVerifierPackage) -> new PackageInstallerService(
                i.getContext(), pm, i::getScanningPackageParser,
                developerVerifierPackage),
        (i, pm, cn) -> new InstantAppResolverConnection(
                i.getContext(), cn,
                Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
        (i, pm) -> new ModuleInfoProvider(i.getContext()),
        (i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
        (i, pm) -> domainVerificationService,
        (i, pm) -> {
            HandlerThread thread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
            thread.start();
            return new PackageHandler(thread.getLooper(), pm);
        },
        new DefaultSystemWrapper(),
        LocalServices::getService,
        context::getSystemService,
        (i, pm) -> IBackupManager.Stub.asInterface(
                ServiceManager.getService(Context.BACKUP_SERVICE)),
        (i, pm) -> new SharedLibrariesImpl(pm, i),
        (i, pm) -> new CrossProfileIntentFilterHelper(
                i.getSettings(), i.getUserManagerService(), i.getLock(),
                i.getUserManagerInternal(), context),
        (i, pm) -> new UpdateOwnershipHelper(),
        (i, pm) -> new PackageMonitorCallbackHelper());

Injector 保存依赖来源与 producer,构造函数通过 bootstrap(this) 把当前 PMS 设为 producer 参数,再按需取得对象。它解决的是创建顺序和可测试替换问题,不意味着所有对象都“懒到第一次 Binder 调用才创建”。例如 UserManager、ComponentResolver、PermissionManager、Settings 在构造早期立即被取出;PackageInstallerService 则要等扫描完成并确定 developer verifier 后才创建。

4.3 构造与发布 ​

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

java
PackageManagerService m = new PackageManagerService(
        injector, factoryTest, PackagePartitions.FINGERPRINT,
        Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT,
        Build.VERSION.INCREMENTAL, Build.VERSION.SDK_INT_FULL);

// 构造完成后才注册兼容性监听器和用户类型 allowlist。
injector.getCompatibility().registerListener(
        SELinuxMMAC.SELINUX_LATEST_CHANGES, selinuxChangeListener);
injector.getCompatibility().registerListener(
        SELinuxMMAC.SELINUX_R_CHANGES, selinuxChangeListener);
m.installAllowlistedSystemPackages();

IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();
ServiceManager.addService("package", iPackageManager);
final PackageManagerNative pmn = new PackageManagerNative(m);
ServiceManager.addService("package_native", pmn);
return m;

这里给出了发布不变量:构造函数正常返回之前,package 和 package_native 都不会加入 ServiceManager。远程调用者因此不会看到一个只装了一半 Settings 或尚未建立 snapshot 的 PMS。IPackageManagerImpl 是 Java Binder owner,PMS 本身不是 IPackageManager.Stub;PackageManagerNative 则为 native 客户端提供受限查询接口。

installAllowlistedSystemPackages() 可能修改各用户的系统包安装状态;若发生变化,它调度 package restrictions 和 Settings 写入。这个动作位于 Binder 发布前,避免首个远程查询观察到尚未应用用户类型 allowlist 的状态。

5. 构造阶段 ​

5.1 状态owner ​

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

java
mInjector.bootstrap(this);
mLock = injector.getLock();
mPackageStateWriteLock = mLock;
mInstallLock = injector.getInstallLock();
LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
EventLog.writeEvent(
        EventLogTags.BOOT_PROGRESS_PMS_START,
        SystemClock.uptimeMillis());

// 先发布进程内接口,再让依赖它们的 producer 创建子系统。
LocalServices.addService(
        PackageManagerInternal.class, new PackageManagerInternalImpl());
LocalManagerRegistry.addManager(
        PackageManagerLocal.class, new PackageManagerLocalImpl(this));
LocalServices.addService(TestUtilityService.class, this);

mUserManager = injector.getUserManagerService();
mComponentResolver = injector.getComponentResolver();
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();

Local 接口在构造函数早期注册,因为 AppsFilter 等同进程组件的 producer 会查询 PackageManagerInternal。这是一种受控的构造期可见性:LocalServices 已有对象引用,但外部 Binder 尚未发布;调用者范围被限制在 system_server 内部,且构造顺序由 Injector 控制。

随后 PMS 创建 BroadcastHelper、AppDataHelper、RemovePackageHelper、DeletePackageHelper、InstallPackageHelper、ResolveIntentHelper、DexOptHelper、StorageEventHelper 等编排组件。它们共享 PMS 的状态 owner,却各自承担不同写路径,后续安装、删除和解析文章会进入这些 helper。

5.2 Snapshot起点 ​

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

java
synchronized (mLock) {
    // 状态对象安装后立刻建立 live Computer,供构造期查询使用。
    mSnapshotStatistics = new SnapshotStatistics();
    sSnapshotPendingVersion.incrementAndGet();
    mLiveComputer = createLiveComputer();
    registerObservers(true);
}

Computer computer = mLiveComputer;
try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
    synchronized (mLock) {
        mHandler = injector.getHandler();
        Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
        // Settings 恢复与扫描在两把锁保护下继续。

第一个 mLiveComputer 是构造期查询视图。Settings、resolver、包集合等 Watchable owner 已安装后,PMS 注册 observer;后续状态变化会使 snapshot 失效。构造尾部还会再建一次 live Computer,因为扫描和解析会补齐大量字段。

扫描期间同时持有 mInstallLock 与 mLock 是启动期特例:此时没有正常业务并发,代码优先保证 Settings、磁盘与内存索引的一致提交。构造结束后,mInstaller.setWarnIfHeld(mLock) 开始警告运行期在持有包锁时调用 installd 的慢路径,说明启动期锁策略不能照搬到正常运行期。

5.3 阶段图 ​

这张图刻意把“读取旧状态”和“扫描磁盘事实”放在同一临界区:启动要把两类事实 reconcile 成新的 Settings、组件索引与包集合。它也解释了为什么 BOOT_PROGRESS_PMS_READY 不能简单理解为 systemReady():该事件发生在构造临界区内,表示启动扫描和 Settings 写回完成。

6. 恢复与扫描 ​

6.1 Settings恢复 ​

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

java
if (!SELinuxMMAC.readInstallPolicy()) {
    throw new RuntimeException("Unable to load SELinux MMAC policy");
}

mFirstBoot = !mSettings.readLPw(
        computer,
        mInjector.getUserManagerInternal().getUsers(
                false /* excludeDying */));

if (mFirstBoot) {
    try {
        mInstaller.setFirstBoot();
    } catch (InstallerException e) {
        // installd 通知失败会降级记录,不推翻 Settings 的 first-boot 判断。
        Slog.w(TAG, "Could not set First Boot: ", e);
    }
    DexOptHelper.requestCopyPreoptedFiles();
}

SELinux install policy 是硬前置:加载失败直接抛出,构造不能继续。Settings 则用返回值区分“没有文件的首次启动”和“读取到已有状态”。Installer.setFirstBoot() 失败被降级为 warning,说明 first-boot 状态 owner 仍是 PMS/Settings;通知 installd 失败不会把启动自动改判为非首次启动。

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

java
try {
    if (!readSettingsLPw(computer, users, originalFirstInstallTimes)) {
        return false; // 没有 Settings 文件,调用方据此认定 first boot。
    }
} finally {
    if (!mVersion.containsKey(StorageManager.UUID_PRIVATE_INTERNAL)) {
        Slog.wtf(PackageManagerService.TAG,
                "No internal VersionInfo found in settings, using current.");
        findOrCreateVersion(
                StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
    }
}

readLPw() 的 finally 会补齐缺失的 volume 版本信息,因此部分字段缺失不是立即终止启动。更底层的 ResilientAtomicFile 依次尝试 temporary backup、主文件和 reserve copy;XML 解析异常会删除损坏的当前副本并递归重试下一副本。这里存在“缺文件”“主文件损坏但可恢复”“所有副本都不可用”等不同语义,不能统称为 packages.xml 读取失败。

6.2 两段扫描 ​

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

java
EventLog.writeEvent(
        EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
        startTime);

final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !partitionsFingerprint.equals(ver.fingerprint);
mPriorSdkVersion = mIsUpgrade ? ver.sdkVersion : -1;

mInitAppsHelper = new InitAppsHelper(
        this, mApexManager, mInstallPackageHelper,
        mInjector.getSystemPartitions());

PackageParser2 packageParser =
        mInjector.getScanningCachingPackageParser();
mOverlayConfig = mInitAppsHelper.initSystemApps(
        packageParser, packageSettings, userIds, startTime);
mInitAppsHelper.initNonSystemApps(
        packageParser, userIds, startTime);
packageParser.close();

第一段扫描系统分区,第二段扫描非系统应用。升级由“当前分区 fingerprint 与 Settings 中记录值不同”决定,它影响旧包集合保存、权限重新授予、code cache 清理和默认 preferred app 等路径。PackageParser2.close() 位于两段扫描之后,表示这个扫描期 parser 的资源在进入 required-package 解析前已经释放。

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

java
EventLog.writeEvent(
        EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
        SystemClock.uptimeMillis());

if ((mScanFlags & SCAN_FIRST_BOOT_OR_UPGRADE)
        == SCAN_FIRST_BOOT_OR_UPGRADE) {
    fixInstalledAppDirMode();
}

scanDirTracedLI(
        mPm.getAppInstallDir(), 0,
        mScanFlags | SCAN_REQUIRE_KNOWN,
        packageParser, mExecutorService, null);

List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
    throw new IllegalStateException(
            "Not all tasks finished before calling close: "
                    + unfinishedTasks);
}

/data/app 扫描允许内部 executor 并行解析,但 initNonSystemApps() 返回前必须关闭 executor,并要求没有遗留任务。也就是说并行只优化阶段内部,不能越过 PMS 构造屏障。首次启动或升级时还会修正安装目录 mode;普通启动不会无条件执行该修复。

6.3 写回与异步任务 ​

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

java
EventLog.writeEvent(
        EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
        SystemClock.uptimeMillis());

// OTA 路径清理 code cache 后,再异步修复 system user 的应用数据。
mPrepareAppDataFuture = mAppDataHelper.fixAppsDataOnBoot();

writeSettingsLPrTEMP();
EventLog.writeEvent(
        EventLogTags.BOOT_PROGRESS_PMS_READY,
        SystemClock.uptimeMillis());

// 扫描后才能确定 installer/verifier 等系统包并创建 installer service。
mInstallerService = mInjector.getPackageInstallerService(
        mDeveloperVerificationServiceProvider);

// 扫描补齐了状态,重建 live 查询视图。
mLiveComputer = createLiveComputer();

fixAppsDataOnBoot() 返回 Future,构造函数不在这里等待。这样 SystemServer 可以继续启动其他服务,同时后台线程处理 app-data fixup;真正的消费屏障被推迟到第三方应用启动前。BOOT_PROGRESS_PMS_READY 因而只覆盖扫描与 Settings 写回,不覆盖该 Future 的完成时间,也不覆盖稍后的 systemReady()。

7. 发布后阶段 ​

7.1 Local消费者 ​

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

java
t.traceBegin("DexUseManagerLocal");
// PackageManagerLocal 已在 PMS 构造早期注册;这里创建它的首个重要消费者。
LocalManagerRegistry.addManager(
        DexUseManagerLocal.class,
        DexUseManagerLocal.createInstance(mSystemContext));
t.traceEnd();

if (!mRuntimeRestart && !isFirstBootOrUpgrade()) {
    FrameworkStatsLog.write(
            FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
            FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__
                    PACKAGE_MANAGER_INIT_READY,
            SystemClock.elapsedRealtime());
}

源码注释要求 DexUseManagerLocal 在 PackageManagerLocal 注册后、PMS 开始处理 notifyDexLoad Binder 调用前初始化。它揭示了一个细粒度顺序:Binder 服务虽已发布,但 SystemServer 紧接着补齐一个 Binder 调用会依赖的 local consumer。statsd 的 PACKAGE_MANAGER_INIT_READY 也在这个动作之后,并且仅在非 runtime restart、非 first boot、非 upgrade 时记录。

7.2 DexOpt与维护 ​

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

java
t.traceBegin("ArtManagerLocal");
DexOptHelper.initializeArtManagerLocal(
        context, mPackageManagerService);
t.traceEnd();

t.traceBegin("UpdatePackagesIfNeeded");
try {
    Watchdog.getInstance().pauseWatchingCurrentThread("dexopt");
    mPackageManagerService.updatePackagesIfNeeded();
} catch (Throwable e) {
    reportWtf("update packages", e);
} finally {
    Watchdog.getInstance().resumeWatchingCurrentThread("dexopt");
}
t.traceEnd();

mPackageManagerService.updateMetricsIfNeeded();

try {
    mPackageManagerService.performFstrimIfNeeded();
} catch (Throwable e) {
    reportWtf("performing fstrim", e);
}

这些动作发生在 startOtherServices(),不是 PMS 构造函数的一部分。dexopt upgrade 可能很慢,所以再次暂停当前线程 Watchdog;异常被 reportWtf 记录后继续启动。fstrim 同样是可降级维护动作。与之对比,构造函数的不变量失败没有局部 catch,会阻止 main() 返回和 Binder 发布。

8. systemReady屏障 ​

8.1 调用位置 ​

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

java
mSystemServiceManager.startService(PermissionPolicyService.class);

t.traceBegin("MakePackageManagerServiceReady");
mPackageManagerService.systemReady();
t.traceEnd();

// PMS ready 后才继续 CrashRecovery、DisplayManager 等服务的 ready 阶段。
mSystemServiceManager.startService(
        CRASHRECOVERY_MODULE_LIFECYCLE_CLASS);

PMS systemReady() 前,PermissionPolicyService 已启动;调用本身没有像 DisplayManager systemReady() 那样用局部 try/catch 包住。因此该方法抛出的异常会离开当前启动阶段,并最终进入 SystemServer.run() 的服务启动失败处理。这里要求 PMS 完成一组不可随意跳过的运行期接线。

8.2 状态切换 ​

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

java
public void systemReady() {
    PackageManagerServiceUtils.enforceSystemOrRoot(
            "Only the system can claim the system is ready");

    final ContentResolver resolver = mContext.getContentResolver();
    if (mReleaseOnSystemReady != null) {
        for (int i = mReleaseOnSystemReady.size() - 1; i >= 0; --i) {
            final File dstCodePath = mReleaseOnSystemReady.get(i);
            F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
        }
        mReleaseOnSystemReady = null;
    }

    mSystemReady = true;
    // 后续 observer 与子系统回调消费这个生命周期状态。

调用者必须是 system 或 root。切换状态前,PMS 释放延迟到 system-ready 的压缩块资源,并把列表置空,防止重复释放。mSystemReady = true 是运行期行为的开关,但不是方法终点;后面还有同步 I/O、user/app reconcile、权限授予与安装会话恢复。

8.3 子系统接线 ​

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

java
ContentObserver co = new ContentObserver(mHandler) {
    @Override
    public void onChange(boolean selfChange) {
        final boolean ephemeralFeatureDisabled =
                Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0;
        for (int userId : UserManagerService.getInstance().getUserIds()) {
            final boolean instantAppsDisabledForUser =
                    ephemeralFeatureDisabled || Secure.getIntForUser(
                            resolver, Secure.INSTANT_APPS_ENABLED, 1, userId) == 0;
            mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser);
        }
    }
};
mContext.getContentResolver().registerContentObserver(
        android.provider.Settings.Global.getUriFor(
                Global.ENABLE_EPHEMERAL_FEATURE),
        false, co, USER_ALL);
mContext.getContentResolver().registerContentObserver(
        android.provider.Settings.Secure.getUriFor(
                Secure.INSTANT_APPS_ENABLED),
        false, co, USER_ALL);
co.onChange(true);

mAppsFilter.onSystemReady(
        LocalServices.getService(PackageManagerInternal.class));
CarrierAppUtils.disableCarrierAppsUntilPrivileged(
        mContext.getOpPackageName(), UserHandle.USER_SYSTEM, mContext);

synchronized (mLock) {
    ArrayList<Integer> changed =
            mSettings.systemReady(mComponentResolver);
    for (int userId : changed) {
        mSettings.writePackageRestrictionsLPr(userId);
    }
}

mUserManager.systemReady();
final StorageManager storage =
        mInjector.getSystemService(StorageManager.class);
storage.registerListener(mStorageEventHelper);
mInstallerService.systemReady();

mUserManager.reconcileUsers(
        StorageManager.UUID_PRIVATE_INTERNAL);
mStorageEventHelper.reconcileApps(
        snapshotComputer(), StorageManager.UUID_PRIVATE_INTERNAL);
mPermissionManager.onSystemReady();

这一段把构造期 owner 转入运行期:Settings 清理或修正组件引用并写回 per-user restrictions;UserManager 与 StorageManager 建立用户和卷的变化通道;PackageInstallerService 读取会话、过期旧会话、清理孤立 stage/icon;PermissionManager 开启系统就绪后的策略。

mSettings.systemReady() 在 mLock 内返回发生变化的 userId,PMS 随即同步写回对应 restrictions。消费者不是一个抽象的“系统”,而是后续组件解析、启动决策和用户查询。Storage listener 则让启动后挂载/卸载卷的事件进入 StorageEventHelper。

8.4 权限与会话 ​

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

java
for (int i = 0; i < livingUserCount; i++) {
    final int userId = livingUsers.get(i).id;
    final boolean isPermissionUpgradeNeeded = !Objects.equals(
            mPermissionManager.getDefaultPermissionGrantFingerprint(userId),
            Build.FINGERPRINT);
    if (isPermissionUpgradeNeeded) {
        grantPermissionsUserIds = ArrayUtils.appendInt(
                grantPermissionsUserIds, userId);
    }
}

for (int userId : grantPermissionsUserIds) {
    mLegacyPermissionManager.grantDefaultPermissions(userId);
    mPermissionManager.setDefaultPermissionGrantFingerprint(
            Build.FINGERPRINT, userId);
}

if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
    mLegacyPermissionManager
            .scheduleReadDefaultPermissionExceptions();
}

// 放在最后,因为恢复 staged install 可能再次触发安装链路。
mInstallerService.restoreAndApplyStagedSessionIfNeeded();
mExistingPackages = null;

默认权限不是每次启动都重授:只有某用户保存的 grant fingerprint 与当前 build fingerprint 不同才进入同步授予;没有用户需要升级时,系统改为异步预读 default-permission exceptions。这里既有条件路径,也有不同生效时机。

staged session 恢复被源码明确要求放在最后,因为它可能触发原子 APK 安装,并反向依赖其他组件已 ready。mExistingPackages 只服务升级期比较,恢复完成后被清空,结束其启动期生命周期。

9. AppData屏障 ​

9.1 后台修复 ​

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

java
public Future<?> fixAppsDataOnBoot() {
    final @StorageManager.StorageFlags int storageFlags;
    if (StorageManager.isFileEncrypted()) {
        storageFlags = StorageManager.FLAG_STORAGE_DE;
    } else {
        storageFlags = StorageManager.FLAG_STORAGE_DE
                | StorageManager.FLAG_STORAGE_CE;
    }

    final List<String> deferPackages;
    try (PackageManagerTracedLock installLock =
            mPm.mInstallLock.acquireLock()) {
        deferPackages = reconcileAppsDataLI(
                StorageManager.UUID_PRIVATE_INTERNAL,
                UserHandle.USER_SYSTEM, storageFlags,
                true /* migrateAppData */,
                true /* onlyCoreApps */);
    }

    Future<?> prepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
        try {
            mInstaller.fixupAppData(
                    StorageManager.UUID_PRIVATE_INTERNAL,
                    StorageManager.FLAG_STORAGE_DE
                            | StorageManager.FLAG_STORAGE_CE);
        } catch (Installer.InstallerException e) {
            Slog.w(TAG, "Trouble fixing GIDs", e);
        }
        // 省略:对 deferPackages 建批次,并在 mInstallLock 下执行。
    }, "prepareAppData");
    return prepareAppDataFuture;
}

FBE 设备在早期只能保证 device-encrypted 数据,非 FBE 设备同时处理 DE/CE。同步部分先 reconcile system user 的 core apps,慢的全量 fixup 和 deferred batch 进入 SystemServerInitThreadPool。Installer 失败在任务内记录 warning;任务仍会走到结束,从而允许上层屏障获得确定的完成状态,而不是无限等待。

9.2 第三方放行 ​

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

java
// 网络等服务完成 ready 后,等待 PMS 构造期创建的 Future。
mPackageManagerService.waitForAppDataPrepared();

t.traceBegin("PhaseThirdPartyAppsCanStart");
if (webviewPrep != null) {
    ConcurrentUtils.waitForFutureNoInterrupt(
            webviewPrep, WEBVIEW_PREPARATION);
}
mSystemServiceManager.startBootPhase(
        t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
t.traceEnd();

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

java
public void waitForAppDataPrepared() {
    if (mPrepareAppDataFuture == null) {
        return;
    }
    ConcurrentUtils.waitForFutureNoInterrupt(
            mPrepareAppDataFuture, "wait for prepareAppData");
    mPrepareAppDataFuture = null;
}

Future 的 owner 是 PMS,消费者是 SystemServer 的第三方应用启动屏障。等待完成后字段置空:既释放引用,也使重复调用成为快速返回。这个细节说明“PMS systemReady 已完成”仍不足以放行第三方代码,应用数据目录的一致性还有独立异步生命周期。

10. 失败与清理 ​

启动代码有四类不同的失败语义。

硬不变量失败时,构造函数不返回,main() 后面的 ServiceManager 注册不会执行。Watchdog 仍由 SystemServer 的 finally 恢复。此时不能描述为“PMS 注册后不可用”,因为服务根本没有发布。

Settings 损坏优先恢复,而不是直接当作首次启动。ResilientAtomicFile.openRead() 先看 temporary backup,再看主文件,最后看 reserve copy;解析异常通过 failRead() 删除当前坏副本后重试。这里有一个重要边界:异常分支故意忽略递归重试的返回值,避免把“启动时原本存在但损坏”的 Settings 标成 first boot;初次打开就没有文件,或解析入口直接返回“无 start tag”时,才会把 false 传回 PMS。

Installer 的 first-boot 通知、app-data GID fixup、单包 app-data 创建、dexopt upgrade、fstrim 各自有局部降级逻辑。它们不能合并成“任何启动错误都会重启 system_server”,也不能反过来声称都不会影响启动。判断方法是看异常是否被当前 owner 捕获、是否有替代副本或重试、以及上层是否还有同步屏障。

11. 观测与测试 ​

11.1 时间埋点 ​

PMS 启动同时使用三类时间证据。

证据起点与终点适合回答的问题
Perfetto traceStartPackageManagerService、create package manager、各内部 trace哪个同步阶段占用 SystemServer 主线程
EventLog boot progressPMS_START、SYSTEM_SCAN_START、DATA_SCAN_START、SCAN_END、PMS_READYSettings/扫描各阶段的墙钟顺序与耗时
statsd boot eventPACKAGE_MANAGER_INIT_START/READY普通非升级启动的整体 PMS 初始化时间

statsd 的 READY 有条件过滤,EventLog 的 PMS_READY 又早于 systemReady(),所以分析日志时必须先确认事件语义,不能只按名字比较时间。首次启动和 OTA 应优先看 EventLog/trace,因为 SystemServer 明确不写普通启动的 PACKAGE_MANAGER_INIT_READY 事件。

下面的命令只读设备日志。输入是本次开机的 log buffer,输出是 PMS 五个阶段的时间戳;如果 buffer 已轮转,缺失事件不能反推代码没有执行。

bash
# 输入:当前设备的 events buffer。
# 输出:PMS 构造、system/data 扫描与构造 ready 的 EventLog 时间线。
adb logcat -b events -d \
  | rg 'boot_progress_pms_(start|system_scan_start|data_scan_start|scan_end|ready)'

# 输入:当前设备可用的服务名表。
# 输出:main() 返回后应出现的 Java 与 native Binder 服务。
adb shell service list \
  | rg '(^|[[:space:]])package(_native)?:'

11.2 Settings恢复测试 ​

源码文件:frameworks/base/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java

java
@Test
public void testReadMalformedPackagesXmlKeySetSettings() throws Exception {
    writeReserveCopyOldFiles();
    writeCorruptedPackagesXml();

    Settings settings = makeSettings();
    assertThat(settings.readLPw(
            computer, createFakeUsers()), is(true));
    verifyKeySetMetaData(settings);
}

测试输入是“有效 reserve copy + 损坏的主 packages.xml”。动作是调用启动期同一个 Settings.readLPw();断言不仅要求返回 true,还核对 keyset metadata,防止只恢复了空壳。它支持“主副本损坏时会回退并恢复状态”的结论,但没有覆盖整个 SystemServer 启动顺序,也没有证明任意磁盘损坏都可恢复。

更接近真实 I/O 的 host test 会先写出大于一页的 packages.xml,启用 fs-verity,再直接损坏块设备上的第一页或第二页,最后运行设备侧 testReadSettings。它分别覆盖 header 解析失败和 XML 中段解析失败,反向验证 reserve copy 不只是单元测试 mock 路径。

11.3 Installer测试 ​

源码文件:frameworks/base/services/tests/mockingservicestests/src/com/android/server/pm/PackageInstallerServiceTest.java

java
@Test
public void testWriteAndReadVerificationPolicyPerUser() {
    doReturn(new int[] {
            UserHandle.USER_SYSTEM, TEST_USER_ID, TEST_USER_ID_2
    }).when(mPms.mUserManager).getUserIds();

    final PackageInstallerService service = new PackageInstallerService(
            rule.mocks().getContext(), mPms, null,
            new ComponentName(mPackageName, this.getClass().getName()));
    service.systemReady();
    // 省略:为三个用户写入不同 policy。

    final PackageInstallerService service2 = new PackageInstallerService(
            rule.mocks().getContext(), mPms, null,
            new ComponentName(mPackageName, this.getClass().getName()));
    service2.systemReady();

    assertThat(service2.getDeveloperVerificationPolicy(
            UserHandle.USER_SYSTEM)).isEqualTo(policyForSystemUser);
    assertThat(service2.getDeveloperVerificationPolicy(
            TEST_USER_ID)).isEqualTo(policyForTestUser);
}

测试把第二个实例当作重启后的服务:第一次 systemReady() 建立 per-user 默认值并持久化修改,第二次 systemReady() 重新读取文件,断言各用户策略被保留。它支持“PackageInstallerService 的持久化会话/策略恢复属于 systemReady 阶段”这一生命周期判断;它没有覆盖 PMS 外层 systemReady() 的全部顺序,也不证明 staged APK 恢复成功。

当前源码测试更擅长分别验证 Settings 恢复、PackageInstallerService ready 行为和 PMS 子组件,不存在一个单元测试可以替代 SystemServer 真实启动时序。完整顺序仍要由调用方源码、trace/EventLog 与设备启动实验共同判断。

12. 源码复盘 ​

可以用下面三条路径检查自己是否真正掌握了启动主线。

第一,从 SystemServer.startBootstrapServices() 的 StartPackageManagerService 开始,指出默认显示、DomainVerificationService 和 Watchdog 各自提供什么边界,再追到 ServiceManager.addService("package", ...)。如果中间跳过 Settings 恢复和两段扫描,就还没有解释 Binder 发布前为何已有一致查询状态。

第二,给定“boot_progress_pms_ready 已出现,但第三方应用仍未启动”的现象,应继续检查 MakePackageManagerServiceReady、prepareAppData Future、WebView preparation 和 PHASE_THIRD_PARTY_APPS_CAN_START,而不是把 PMS_READY 当作系统可启动应用的最终事件。

第三,给定“主 packages.xml 损坏”的故障,应沿 Settings.readLPw()、readSettingsLPw()、ResilientAtomicFile.openRead()/failRead() 判断当前读取的是 temporary backup、主文件还是 reserve copy,并区分“初次就没有文件”与“已有文件解析异常后重试”。后者即使最终没有可用副本,外层也会保留非 first-boot 语义,避免把损坏恢复错误地当成新设备初始化。

后续“PMS构造函数”专题将按字段与 helper 展开构造内部阶段;系统包和 /data/app 的目录扫描、并行解析及冲突处理则由包扫描专题继续追踪。本文的边界是解释这些动作为什么必须发生在 Binder 发布和第三方应用放行之前。