1. 起点:SystemServer
PMS(PackageManagerService) 的启动始于 SystemServer 的 startBootstrapServices() 方法。这个方法用于启动一系列关键的基础服务。
csharp
// 文件:frameworks/base/services/java/com/android/server/SystemServer.java
private void startBootstrapServices() {
// 1. 启动 Installer 服务
Installer installer = mSystemServiceManager.startService(Installer.class);
// 2. 检查设备加密状态,决定是否只加载核心应用
// Only run "core" apps if we're encrypting the device.
String cryptState = VoldProperties.decrypt().orElse("");
if (ENCRYPTING_STATE.equals(cryptState)) {
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core apps");
mOnlyCore = true;
}
// 3. 调用 PMS 的 main() 方法,创建 PMS 实例
try {
Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
mOnlyCore);
} finally {
Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
}
// 4. 首次启动标记
mFirstBoot = mPackageManagerService.isFirstBoot();
// ...
}
这段代码清晰地展示了PMS启动前的三个关键动作:
- 启动
Installer服务 :Installer是 PMS 与底层installd守护进程通信的桥梁,负责执行创建应用目录、文件拷贝等实际操作。PMS 依赖于它,因此必须率先启动。 - 检查加密状态 :通过读取
vold.decrypt系统属性来判断设备是否处于加密或正在加密的状态。如果是,则设置mOnlyCore = true,这意味着 PMS 只会解析系统核心应用,以在解密完成前快速启动系统。 - 调用
main()方法 :这是启动 PMS 的核心步骤,传入了Context、Installer实例、工厂测试模式和加密状态标志。
2. 入口与注册:PMS.main() 方法
PackageManagerService.main() 方法是 PMS 的静态入口,它的逻辑相对直接。
java
java
// 文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 1. 调用 PMS 的构造函数,完成核心初始化
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT,
Build.VERSION.INCREMENTAL);
// 2. 向 ServiceManager 注册 Binder 服务 "package"
ServiceManager.addService("package", m);
// 3. 注册另一个服务 "package_native"
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
main 方法承担了两个核心职责:
- 创建 PMS 实例 :通过
new PackageManagerService(...)调用其构造函数。所有核心的、复杂的初始化逻辑都在构造函数中完成。 - 注册 Binder 服务 :将创建好的 PMS 实例注册到
ServiceManager中。注册后,其他系统服务和应用程序就能通过"package"这个字符串获取到它的 Binder 代理,从而进行跨进程通信(IPC)。此外,还会注册一个"package_native"服务,用于提供原生接口。
3. 核心:PMS 构造函数中的复杂初始化
PMS 的构造函数是其启动流程中最复杂、最耗时的部分。它主要按顺序完成以下工作。
3.1. 第一阶段:准备工作与加载持久化信息
构造函数的开头会进行一系列初始化,并加载之前持久化的包信息。
java
ini
// 文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// ... 初始化成员变量 ...
// 1. 创建 Settings 对象,它管理 packages.xml 等文件
mSettings = injector.getSettings();
// 添加系统共享用户 ID
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PRIVILEGED);
...
// 2. 从磁盘读取持久化的包信息
mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(
/* excludePartial= */ true,
/* excludeDying= */ false,
/* excludePreCreated= */ false));
t.traceEnd();
mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
if (!mOnlyCore && mFirstBoot) {
requestCopyPreoptedFiles(mInjector);
}
// ...
}
- 初始化
Settings:Settings类虽然名字和系统设置无关,但它负责管理所有已安装应用的核心信息,并与/data/system/packages.xml等关键文件交互。 - 加载持久化数据 :
mSettings.readLPw()方法会解析packages.xml等文件,将之前系统已知的应用信息(如包名、版本、UID、权限等)加载到内存中。这为后续的扫描工作提供了基础。
3.2. 第二阶段:扫描 APK 文件
这是PMS启动过程中最核心、最耗时的阶段,即扫描文件系统中的APK文件,解析其内容。
java
scss
// 文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// ...
// 1. 扫描系统目录system/framework,system/app,system/priv-app
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
scanDirTracedLI(frameworkDir, systemParseFlags,
systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
packageParser, executorService);
...
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
packageParser, executorService);
}
scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
// ... 扫描其他系统分区目录(如 /vendor, /product 等)...
// 2. 如果 mOnlyCore == false,扫描用户数据目录
if (!onlyCore) {
scanDirTracedLI(new File(Environment.getDataDirectory(), "app"), ...);
// ... 扫描其他用户数据目录 ...
}
// ...
}
// 文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void scanDirTracedLI(File dir, int parseFlags, int scanFlags, long currentTime) {
// 1. 开始追踪,标签带有目录名,如 "scanDirTracedLI_app"
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDirTracedLI_" + dir.getName());
try {
// 2. 核心工作委托给 scanDirLI 执行
scanDirLI(dir, parseFlags, scanFlags, currentTime);
} finally {
// 3. 确保追踪结束
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- 扫描系统目录 :PMS 会首先扫描一系列系统分区下的目录,例如
/system/framework(框架资源)、/system/app(系统应用)、/system/priv-app(特权应用)等。 - 扫描用户目录 :如果设备未加密(
mOnlyCore == false),PMS 会继续扫描用户数据目录,主要是/data/app,这里面存放着用户安装的所有第三方应用。
arduino
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
// 1. 获取目录下的所有文件
final File[] files = dir.listFiles();
// 2. 创建并发解析器(内部持有线程池)
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(...)) {
// 3. 遍历文件,提交解析任务
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
continue; // 跳过非APK文件
}
// 将 APK 解析任务提交到线程池
parallelPackageParser.submit(file, parseFlags);
}
// 4. 准备收集结果(进入下一阶段)
// ... (见阶段三)
}
}
ParallelPackageParser:它是并发解析引擎,在构造时创建了一个固定大小的线程池(通常为 CPU 核心数)。parallelPackageParser.submit():内部会将 APK 文件包装成任务,最终调用PackageParser2.parsePackage()来解析AndroidManifest.xml,生成一个临时的PackageParser.Package对象。此阶段不阻塞,会快速遍历完所有文件
java
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(...)) {
// ... 提交所有任务 (阶段二) ...
// 1. 循环获取已完成的解析结果
for (int i = 0; i < fileCount; i++) {
// take() 是一个阻塞方法,等待下一个任务完成
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
// 2. 如果解析失败(如APK损坏),记录错误并跳过
if (parseResult.pkg == null) {
// ... 错误处理 ...
continue;
}
// 3. 核心:将解析结果交给 scanPackageChildLI 进行后续处理
scanPackageChildLI(parseResult.pkg, parseResult.scanFile,
parseFlags, scanFlags, currentTime, null);
}
}
}
parallelPackageParser.take():这是一个阻塞队列的取出操作。它会等待线程池中任何一个任务完成,然后立即返回结果,而不是等所有任务全部完成。scanPackageChildLI():这里是解析后处理 的真正入口,它接收的是已经解析好的PackageParser.Package对象(包含了四大组件等全部信息)
scanPackageChildLI 负责将解析出的 Package 对象进行系统级验证、资源分配和最终注册。这是 APK 从"文件"变为"系统可识别应用"的关键一步。
java
ruby
// 文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private PackageSetting scanPackageChildLI(PackageParser.Package pkg, ...) {
// 1. 处理 Split APK 逻辑(在 user 版本通常无效)
// ...
// 2. 核心:调用 addForInitLI,将应用信息纳入 PMS 管理
addForInitLI(pkg, ...);
// 3. 返回该应用的设置信息
return pkg.mScanResult;
}
addForInitLI 内部会执行以下最重要的系统操作:
java
csharp
// addForInitLI 方法中的核心逻辑(伪代码示意)
private void addForInitLI(PackageParser.Package pkg, ...) {
// 1. 分配或复用 Linux UID(用户ID)
final SharedUserSetting sharedUser = mSettings.getSharedUserLPw(...);
if (sharedUser != null) {
pkg.mSharedUserId = sharedUser.userId;
} else {
// 创建新的 UID
}
// 2. 将四大组件注册到 PMS 的全局索引中
// 注册 Activity
mActivities.add(pkg.activities);
// 注册 Service
mServices.add(pkg.services);
// 注册 Receiver
mReceivers.add(pkg.receivers);
// 注册 Provider
mProviders.add(pkg.providers);
// 3. 将包名与 Package 对象存入全局 mPackages 映射
mPackages.put(pkg.packageName, pkg);
}
关键点:
- UID 分配:确保每个应用有唯一的 Linux 身份,是应用沙箱隔离的基础。
- 组件注册 :将四大组件信息分别存入
mActivities、mServices、mReceivers、mProviders这四个ArrayList中。这就是为什么我们调用queryIntentActivities()等 API 时,PMS 能快速返回匹配的组件列表。 - 全局索引 :
mPackages是一个ArrayMap<String, PackageParser.Package>,通过包名可以 O(1) 获取应用完整信息。
3.3. 第三阶段:收尾与后续优化
扫描完成后,PMS 会进行一些收尾工作。
java
scss
// 文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// ...
// 1. 根据扫描结果更新应用的依赖关系,清理无效数据(如被卸载的应用残留)
updateAllSharedLibrariesLPw(null);
pruneSharedLibrariesLPw();
// 2. 将内存中的最新状态写回磁盘(持久化)
mSettings.writeLPr();
}
- 更新与清理:根据扫描到的最新APK信息,更新内存中的数据,处理应用依赖,并清理掉不再存在的应用数据。
- 持久化状态 :最后,
mSettings.writeLPr()方法会将内存中的包信息重新写回到packages.xml等文件中,确保下次开机时能快速加载。