第 26 章:PackageManagerService 📅 发布时间:2026/8/19 11:34:12 👁 浏览次数: PackageManagerService(PMS)是 Android 中负责应用生命周期管理最重要的系统服务。它负责设备上每一个 APK 的发现、解析、校验、安装、更新与卸载。它维护已安装软件包的权威数据库,强制执行权限策略,将 Intent 解析到对应组件,编排 Overlay 系统,是整个应用生态的底层支撑。其模块树包含超过 6000 个源文件,可以说是整个 Android 框架中最复杂的子系统。本章由底向上剖析 PMS:从 APK 本身的结构讲起,再到服务架构、开机扫描、安装流水线、权限模型、Intent 解析、Split APK 以及运行时资源覆盖(RRO)系统。26.1 APK 结构Android 包(APK)是具备规范内部布局的 ZIP 归档文件。设备上每一个应用、系统服务、Overlay 包、共享库都以 APK(或者一组 Split APK)形式分发。理解 APK 结构是弄懂 PMS 所有行为的前提。26.1.1 APK 内部构成APK 文件本质是后缀为.apk的 ZIP 文件。解压普通 APK 可以看到如下顶层条目:my‑app.apk +-- AndroidManifest.xml (二进制XML,必需) +-- classes.dex (DEX字节码,必需) +-- classes2.dex (可选,附加DEX文件) +-- resources.arsc (编译后的资源表) +-- res/ (编译完成资源:布局、图片等) +-- lib/ (原生共享库) | +-- armeabi‑v7a/ | +-- arm64‑v8a/ | +-- x86/ | +-- x86_64/ +-- assets/ (原始资源文件,通过AssetManager访问) +-- META‑INF/ (v1签名方案签名信息) | +-- MANIFEST.MF | +-- CERT.SF | +-- CERT.RSA +-- kotlin/ (Kotlin元数据,可选) +-- stamp‑cert‑sha256 (源证书戳记,可选)以上每一个组件都承担关键作用。26.1.2 AndroidManifest.xmlManifest 是 APK 中最重要的文件。它声明应用包名、版本、组件(Activity、Service、Receiver、Provider)、权限、最低 / 目标 SDK 版本、软硬件特性需求等等。PMS 解析该文件来填充内部数据结构。Manifest 存储为二进制 XML 格式,并非开发者编写的文本 XML。构建阶段 aapt2 工具将文本 XML 编译为紧凑二进制表示。二进制格式使用整数池索引引用字符串,采用扁平化树结构,无需构建完整 DOM 树即可完成解析。与 PMS 强相关的 Manifest 属性:属性作用package唯一包标识符,PMS 中的主键android:versionCode用于版本对比的整型版本号android:versionName用户可读版本字符串android:minSdkVersion安装所需最低 API 等级android:targetSdkVersion应用测试所针对的 API 等级android:sharedUserId(已废弃)多包之间 UID 共享uses‑permission申请运行时 / 安装时权限permission自定义权限定义application应用元数据与组件容器activity,service,receiver,provider组件声明intent‑filter组件 Intent 匹配规则uses‑library共享库依赖uses‑splitSplit APK 依赖声明PMS 内部常量MIN_INSTALLABLE_TARGET_SDK控制软件包可安装的最低 targetSdk 版本。public static final int MIN_INSTALLABLE_TARGET_SDK = Flags.minTargetSdk24() ? Build.VERSION_CODES.N : Build.VERSION_CODES.M;该安全机制阻止恶意软件通过指定旧 SDK 版本来规避现代隐私与安全 API 强制校验。26.1.3 classes.dexDalvik 可执行文件(DEX)包含编译完成的应用字节码。当单个 DEX 超出 65536 方法上限时,APK 可以包含多个 DEX 文件(classes.dex、classes2.dex……classesN.dex),即 Multidex。DEX 格式官方文档定义,包含:Header:魔数、校验和、SHA‑1 签名、文件大小String IDs:文件全部字符串池Type IDs:全部类型(类、基础类型)引用Proto IDs:方法原型(返回类型 + 参数类型)Field IDs:字段引用Method IDs:方法引用Class Defs:类定义,访问标记、父类、接口Data:实际字节码、调试信息、注解安装阶段,PMS 与 ART 运行时协同完成 DEX 编译,该过程叫 dexopt,将 DEX 字节码转换为优化后的原生代码。PMS 中DexOptHelper负责该交互逻辑。/** Helper class for dex optimization operations in PackageManagerService. */ public final class DexOptHelper { @NonNull private static final ThreadPoolExecutor sDexoptExecutor = new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */, 60 /* keepAliveTime */, TimeUnit.SECONDS, new LinkedBlockingQueueRunnable());26.1.4 resources.arsc编译后的资源表,完成资源 ID 到实际值的映射。res 下每一份资源分配整型 ID,例如R.layout.activity_main = 0x7f0b001a。resources.arsc保存 ID 到字符串、尺寸、颜色或者 APK 内部文件路径的映射关系。资源表结构:String Pool:全部资源引用字符串Package chunks:每个包对应一块(通常一个)Type spec chunks:与配置无关的类型信息Type chunks:区分配置的资源数值该文件是 26.8 节运行时资源覆盖 RRO 系统的核心,RRO 就是对该表条目做覆盖替换。26.1.5 lib / 目录lib 目录存放按 ABI(应用二进制接口)分类存放原生.so共享库。PMS 安装时将原生库解压拷贝到应用原生库目录。标准 ABI 子目录:目录架构说明armeabi‑v7a/32 位 ARM最通用 32 位架构arm64‑v8a/64 位 ARM最通用 64 位架构x86/32 位 x86模拟器、部分平板x86_64/64 位 x86模拟器、部分 Chromebookriscv64/64 位 RISC‑V新兴架构PMS 使用NativeLibraryHelper在安装阶段拷贝原生库到文件系统;ScanPackageUtils中的 ABI 选择逻辑结合设备支持 ABI 与 APK 提供的 ABI,选定最终使用的 ABI 目录。26.1.6 META‑INF / 与 APK 签名META‑INF 目录存放 JAR 签名信息(v1 签名方案)。现代 Android 有多套迭代演进的签名方案。APK 签名方案 v1(JAR 签名)继承 Java JAR 签名的原始签名方案:计算 ZIP 内部每一个文件的摘要将摘要写入META‑INF/MANIFEST.MF使用开发者密钥对 MANIFEST.MF 签名,生成 CERT.SF将证书与签名嵌入 CERT.RSA(或.DSA/.EC)缺陷:v1 签名不保护 ZIP 元数据(本地文件头、中央目录)。攻击者可以修改 ZIP 结构而不破坏签名,典型漏洞为 Janus(CVE‑2017‑13156)。APK 签名方案 v2(Android7.0+)为修复 v1 缺陷引入,将整个 APK 当作二进制 Blob 签名: 将 APK 划分为 4 个部分:ZIP 中央目录之前的内容ZIP 中央目录中央目录末尾签名块(插入 1、2 之间)对除签名块以外全部内容计算摘要;摘要签名后放入 APK 签名块。修改 APK 任意部分都会导致签名校验失败。APK 签名方案 v3(Android9.0+)在 v2 基础上增加密钥轮换支持。v3 引入 “轮换证明” 结构,将新旧签名证书链式串联。开发者可以轮换签名密钥,同时不丢失已有应用的更新能力。 轮换证明是证书链表,链内每个证书对下一个证书签名,建立从原始签名证书到当前证书的信任链。APK 签名方案 v4(Android11+)面向增量文件系统 IncFS 增量安装。v4 生成独立.idsig文件,内部保存 APK 内容 Merkle 树哈希。允许边下载边校验 APK 数据块,未完整下载完成即可开始安装。private static final boolean DEFAULT_VERIFY_ENABLE = true; private static final long DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT = 30 * 1000;PMS 使用ApkSignatureVerifier在安装阶段校验签名;校验器优先使用最新签名方案,降级到老版本。APK 中存在的所有签名方案都必须校验通过,不能剥离 v2 签名只依赖 v1。26.1.7 APK 对齐APK 未压缩条目需要 4 字节边界对齐。zipalign 工具完成对齐,系统可以直接 mmap 资源文件,无需解压。Android15 开始强制原生库使用 16KB 页大小对齐。public static final int PAGE_SIZE_16KB = 16384;构建阶段 zipalign、安装阶段 PMS 都会强制校验对齐。对齐异常的 APK 要么安装失败,要么性能下降,系统必须把资源解压到独立文件,无法直接 mmap 映射 APK 内部资源。26.1.8 APK 构建流水线理解 APK 构建流程有助于理解 APK 内部结构:工具输入输出对照表:工具输入输出APK 组件javac / kotlinc.java / .kt.class中间产物D8 / R8.classclasses.dexDEX 字节码aapt2 compileres/.flat中间产物aapt2 link.flat + manifestresources.arsc + 二进制 XML资源ndk‑build / cmakeC/C++ 源码.so原生库zipalign未对齐 APK对齐 APK文件对齐修复apksigner对齐 APK签名 APKMETA‑INF / 签名块26.1.9 APK 压缩与 Stub 包系统分区空间有限,部分系统 APK 会被压缩。PMS 支持.gz后缀压缩包。public final static String COMPRESSED_EXTENSION = ".gz"; /** Suffix of stub packages on the system partition */ public final static String STUB_SUFFIX = "-Stub";Stub 包是放置在系统分区最小占位 APK。设备首次启动或者 OTA 升级之后,PMS 把完整包解压到/data分区。既保持系统分区体积小,又保留完整应用能力。InitAppsHelper跟踪 Stub 包:// Tracks of stub packages that must either be replaced with full // versions in the /data partition or be disabled. private final ListString mStubSystemApps = new ArrayList();系统扫描阶段:private void updateStubSystemAppsList(ListString stubSystemApps) { final int numPackages = mPm.mPackages.size(); for (int index = 0; index numPackages; index++) { final AndroidPackage pkg = mPm.mPackages.valueAt(index); if (pkg.isStub()) { stubSystemApps.add(pkg.getPackageName()); } } }26.1.10 APK 校验和PMS 支持计算、校验 APK 文件校验和,应用商店、企业设备管理会用到该接口。public void requestFileChecksums(@NonNull File file, @NonNull String installerPackageName, @Checksum.TypeMask int optional, @Checksum.TypeMask int required, @Nullable List trustedInstallers, @NonNull IOnChecksumsReadyListener onChecksumsReadyListener) throws FileNotFoundException {支持 MD5、SHA‑1、SHA‑256、SHA‑512 以及 Merkle 根哈希;ApkChecksums类完成实际计算。26.2 PackageManagerService 架构PMS 是 Android 规模最大、架构最复杂系统服务之一。经过多轮重构,从单体大类拆分为一组辅助类,使用基于快照的并发模型。26.2.1 服务注册与入口SystemServer 开机流程启动 PMS,向 ServiceManager 注册名为package服务。应用通过Context.getSystemService(Context.PACKAGE_SERVICE)或者PackageManager.getPackageManager()访问。类层级关系:26.2.2 锁层级PMS 三把核心锁,锁获取顺序至关重要,防止死锁。/** * Internally there are three important locks: * ul * li{@link #mLock} 保护内存中解析后的包信息以及相关状态。细粒度锁,持有时间要尽可能短,系统竞争最激烈锁之一。 * li{@link #mInstallLock} 保护所有installd调用,磁盘上应用数据繁重操作。installd单线程,操作可能很慢;**持有mLock时禁止获取mInstallLock**;持有mInstallLock期间可以短暂获取mLock。 * li{@link #mSnapshotLock} 保护快照对象与快照失效标记。 * /ul */方法命名约定强制锁规范:后缀必须持有的锁LI调用方持有 mInstallLockLIF持有 mInstallLock,包处于冻结状态LPr读 mLockLPw写 mLock26.2.3 Computer 快照模式现代 PMS 最重要架构特性,用来解决严重锁竞争:旧版本mLock是系统竞争最激烈锁,容易引发卡顿、ANR。核心思路:绝大多数 PMS 操作为只读查询,不修改状态。快照模式把读、写操作分离。Computer 接口:定义全部只读查询方法ComputerEngine:基于 PMS 状态快照实现接口ComputerLocked:封装 synchronized (mLock) 访问实时内存Snapshot 内部类:保存整套 PMS 状态,可以是实时引用或者深拷贝副本snapshotComputer()管理快照创建:public Computer snapshotComputer() { return snapshotComputer(true /*allowLiveComputer*/); } public Computer snapshotComputer(boolean allowLiveComputer) { var isHoldingPackageLock = Thread.holdsLock(mLock); if (allowLiveComputer) { if (isHoldingPackageLock) { // 当前线程持有mLock,可能已经修改状态但快照尚未失效,直接返回live对象 return mLiveComputer; } } var oldSnapshot = sSnapshot.get(); var pendingVersion = sSnapshotPendingVersion.get(); if (oldSnapshot != null oldSnapshot.getVersion() == pendingVersion) { return oldSnapshot.use(); } // ... 在mSnapshotLock下重建快照 }Snapshot 内部类保存全部可变状态:class Snapshot { public static final int LIVE = 1; public static final int SNAPPED = 2; public final Settings settings; public final WatchedSparseIntArray isolatedOwners; public final WatchedArrayMapString, AndroidPackage packages; public final WatchedArrayMapComponentName, ParsedInstrumentation instrumentation; public final WatchedSparseBooleanArray webInstantAppsDisabled; public final ComponentName resolveComponentName; public final ActivityInfo resolveActivity; public final ActivityInfo instantAppInstallerActivity; public final ResolveInfo instantAppInstallerInfo; public final InstantAppRegistry instantAppRegistry; public final ApplicationInfo androidApplication; public final String appPredictionServicePackage; public final AppsFilterSnapshot appsFilter; public final ComponentResolverApi componentResolver; public final PackageManagerService service; public final WatchedArrayMapString, Integer frozenPackages; public final SharedLibrariesRead sharedLibraries; // ... }版本机制使用两个原子变量:sSnapshot当前快照;sSnapshotPendingVersion每次发生状态变更就递增。查询逻辑:如果调用线程已经持有 mLock,直接返回 live computer如果快照版本等于 pending 版本,返回缓存快照否则获取 mSnapshotLock,执行快照重建返回新快照绝大多数调用方实现无锁读。26.2.4 核心数据结构全部打上@Watched注解,修改自动触发快照失效。// Keys are String (package name), values are Package. @Watched @GuardedBy("mLock") final WatchedArrayMapString, AndroidPackage mPackages = new WatchedArrayMap(); // Package settings (persistent per‑package state) @Watched @GuardedBy("mLock") final Settings mSettings; // Component resolver for intent matching @Watched final ComponentResolver mComponentResolver; // App visibility filtering @Watched final AppsFilterImpl mAppsFilter; // Shared libraries @Watched private final SharedLibrariesImpl mSharedLibraries; // Frozen packages (undergoing surgery) @GuardedBy("mLock") final WatchedArrayMapString, Integer mFrozenPackages = new WatchedArrayMap();@Watched配合Watchable/Watcher观察者模式,修改任意集合自动递增sSnapshotPendingVersion,使缓存快照失效。26.2.5 PackageSettingPackageSetting是每个软件包持久化状态记录,实现PackageStateInternal,保存跨重启需要保留的数据: 包名、代码路径、资源路径;versionCode、签名信息;安装时间、更新时间;安装来源;主 / 次要 ABI;每用户状态启用 / 禁用 / 冻结;权限授予状态;域名校验状态。Settings类持有全部 PackageSetting 实例,负责序列化、反序列化/data/system/packages.xml,这是包元数据持久化存储。26.2.6 辅助类拆分现代 PMS 拆分为大量独立职责辅助类,主服务持有全部实例引用:private final BroadcastHelper mBroadcastHelper; private final RemovePackageHelper mRemovePackageHelper; private final DeletePackageHelper mDeletePackageHelper; private final InitAppsHelper mInitAppsHelper; private final AppDataHelper mAppDataHelper; @NonNull private final InstallPackageHelper mInstallPackageHelper; private final PreferredActivityHelper mPreferredActivityHelper; private final ResolveIntentHelper mResolveIntentHelper; private final DexOptHelper mDexOptHelper; private final SuspendPackageHelper mSuspendPackageHelper; private final DistractingPackageHelper mDistractingPackageHelper; private final StorageEventHelper mStorageEventHelper; private final FreeStorageHelper mFreeStorageHelper;拆分收益:可读性:每个文件 500‑2000 行,避免单个文件 15000 行以上可测试性:辅助类可以独立单元测试锁规范:每个辅助类明确标注需要哪些锁代码归属:OWNERS 文件分配不同团队维护不同模块26.2.7 Handler 消息PMS 运行在独立 ServiceThread,Handler 处理异步任务。消息常量定义:static final int SEND_PENDING_BROADCAST = 1; static final int POST_INSTALL = 9; static final int WRITE_SETTINGS = 13; static final int WRITE_DIRTY_PACKAGE_RESTRICTIONS = 14; static final int PACKAGE_VERIFIED = 15; static final int CHECK_PENDING_VERIFICATION = 16; static final int WRITE_PACKAGE_LIST = 19; static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20; static final int ENABLE_ROLLBACK_STATUS = 21; static final int ENABLE_ROLLBACK_TIMEOUT = 22; static final int DEFERRED_NO_KILL_POST_DELETE = 23; static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24; static final int DOMAIN_VERIFICATION = 27; static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28; static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER = 29; static final int WRITE_USER_PACKAGE_RESTRICTIONS = 30;Handler 线程看门狗超时设置 10 分钟,适配超大应用安装这类长时间任务。static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes26.2.8 Watcher / Watchable 观察者模式@Watched注解是快照失效的核心,属于com.android.server.utils自定义观察者模式当 PackageManagerService(PMS)中任意被@Watched标记的字段发生变更(例如向mPackages新增一个应用包),就会触发 Watchable 观测模式:WatchedArrayMap检测到数据发生变更向自身的观察者列表调用dispatchChange(this)PMS 的总观测器接收回调调用PackageManagerService.onChange(what)该方法会递增sSnapshotPendingVersion下一次执行snapshotComputer()时会检测到版本已过期,随即重新构建快照private final Watcher mWatcher = new Watcher() { @Override public void onChange(@Nullable Watchable what) { PackageManagerService.onChange(what); } };该设计模式具备多项优势:自动失效:无需手动维护记账逻辑;任意数据变更都会自动触发失效细粒度:各个字段可以独立触发失效逻辑便于调试:what参数可以标识出具体是哪一个字段发生改动开销低:对 AtomicInteger 做自增操作几乎没有性能损耗26.2.9 快照重建性能快照重建会深拷贝所有被 @Watched 标记字段。@GuardedBy("mLock") private Computer rebuildSnapshot(@Nullable Computer oldSnapshot, int newVersion) { var now = SystemClock.currentTimeMicro(); var hits = oldSnapshot == null ? -1 : oldSnapshot.getUsed(); var args = new Snapshot(Snapshot.SNAPPED); var newSnapshot = new ComputerEngine(args, newVersion); var done = SystemClock.currentTimeMicro(); if (mSnapshotStatistics != null) { mSnapshotStatistics.rebuild(now, done, hits, newSnapshot.getPackageStates().size()); } return newSnapshot; }SnapshotStatistics统计重建频率与耗时,定位性能退化。普通设备 300 + 应用,快照重建耗时约 1‑5ms。snapshotComputer()三层策略:public Computer snapshotComputer(boolean allowLiveComputer) { var isHoldingPackageLock = Thread.holdsLock(mLock); // Tier 1: Caller already holds mLock, use live computer if (allowLiveComputer isHoldingPackageLock) { return mLiveComputer; } // Tier 2: Cached snapshot is still valid var oldSnapshot = sSnapshot.get(); var pendingVersion = sSnapshotPendingVersion.get(); if (oldSnapshot != null oldSnapshot.getVersion() == pendingVersion) { return oldSnapshot.use(); } // Tier 3: Need to rebuild under mSnapshotLock synchronized (mSnapshotLock) { // Double-check: another thread may have rebuilt while we waited var rebuildSnapshot = sSnapshot.get(); var rebuildVersion = sSnapshotPendingVersion.get(); if (rebuildSnapshot != null rebuildSnapshot.getVersion() == rebuildVersion) { return rebuildSnapshot.use(); } // Rebuild under mLock synchronized (mLock) { var newSnapshot = rebuildSnapshot(rebuildSnapshot, rebuildVersion); sSnapshot.set(newSnapshot); return newSnapshot.use(); } } }26.2.10 PackageManagerServiceInjectorPMS 使用注入器做依赖注入,方便单元测试、模块化初始化。注入器提供Context and system servicesABI helpersIncremental managerAPEX managerBackground executorsComponent resolver factoryShared library implementation测试代码可以替换 mock 对象,隔离测试 PMS 组件。26.2.11 系统分区PMS 维护有序系统分区列表:public static final ListScanPartition SYSTEM_PARTITIONS = Collections.unmodifiableList( PackagePartitions.getOrderedPartitions(ScanPartition::new));分区按特异性升序:每个分区对应扫描标记与权限等级:分区扫描标记权限等级/system/appSCAN_AS_SYSTEMSystem/system/priv‑appSCAN_AS_SYSTEM | SCAN_AS_PRIVILEGEDPrivileged/vendor/appSCAN_AS_SYSTEM | SCAN_AS_VENDORVendor/product/appSCAN_AS_SYSTEM | SCAN_AS_PRODUCTProduct/system_ext/appSCAN_AS_SYSTEM | SCAN_AS_SYSTEM_EXTSystem Ext/odm/appSCAN_AS_SYSTEM | SCAN_AS_ODMODM26.3 包扫描开机阶段 PMS 发现并解析设备上全部 APK。扫描成千上万个包会耗费数十秒,直接影响开机到可用的时间。扫描逻辑由InitAppsHelper编排。26.3.1 开机扫描总流程应用扫描流程由InitAppsHelper(frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java)统一调度执行:final class InitAppsHelper { private final PackageManagerService mPm; private final ListScanPartition mDirsToScanAsSystem; private final int mScanFlags; private final int mSystemParseFlags; private final int mSystemScanFlags; private final InstallPackageHelper mInstallPackageHelper; private final ApexManager mApexManager; private final ExecutorService mExecutorService; private long mSystemScanTime; private int mCachedSystemApps; private int mSystemPackagesCount; private final boolean mIsDeviceUpgrading; private final ListScanPartition mSystemPartitions;扫描目录顺序:26.3.2 扫描标记PMS 定义了一整套扫描标志位,用于控制应用扫描行为。这些是在PackageManagerService.java中以常量形式定义的位掩码标志:static final int SCAN_NO_DEX = 1 0; static final int SCAN_UPDATE_SIGNATURE = 1 1; static final int SCAN_NEW_INSTALL = 1 2; static final int SCAN_UPDATE_TIME = 1 3; static final int SCAN_BOOTING = 1 4; static final int SCAN_REQUIRE_KNOWN = 1 7; static final int SCAN_MOVE = 1 8; static final int SCAN_INITIAL = 1 9; static final int SCAN_DONT_KILL_APP = 1 10; static final int SCAN_IGNORE_FROZEN = 1 11; static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1 12; static final int SCAN_AS_INSTANT_APP = 1 13; static final int SCAN_AS_FULL_APP = 1 14; static final int SCAN_AS_VIRTUAL_PRELOAD = 1 15; static final int SCAN_AS_SYSTEM = 1 16; static final int SCAN_AS_PRIVILEGED = 1 17; static final int SCAN_AS_OEM = 1 18; static final int SCAN_AS_VENDOR = 1 19; static final int SCAN_AS_PRODUCT = 1 20; static final int SCAN_AS_SYSTEM_EXT = 1 21; static final int SCAN_AS_ODM = 1 22; static final int SCAN_AS_APK_IN_APEX = 1 23; static final int SCAN_DROP_CACHE = 1 24; static final int SCAN_AS_FACTORY = 1 25; static final int SCAN_AS_APEX = 1 26; static final int SCAN_AS_STOPPED_SYSTEM_APP = 1 27;开机启动阶段,初始扫描标志位由InitAppsHelper计算得出:int scanFlags = SCAN_BOOTING | SCAN_INITIAL; if (mIsDeviceUpgrading || mPm.isFirstBoot()) { mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE; } else { mScanFlags = scanFlags; } mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; mSystemScanFlags = mScanFlags | SCAN_AS_SYSTEM;26.3.3 APEX 包扫描现代 Android 使用 APEX(Android Pony EXpress)实现可更新系统组件。APEX 内部可以包含 APK 包,PMS 需要对其执行扫描。扫描顺序:先扫描 APEX 包,之后扫描系统分区,最后扫描 data 分区。public OverlayConfig initSystemApps(PackageParser2 packageParser, WatchedArrayMapString, PackageSetting packageSettings, int[] userIds, long startTime) { // 在扫描APK之前准备apex包信息 final ListApexManager.ScanResult apexScanResults = scanApexPackagesTraced(packageParser); mApexManager.notifyScanResult(apexScanResults); scanSystemDirs(packageParser, mExecutorService); // ... }APEX 的扫描分区由当前处于激活状态的 APEX 模块推导得到:private ListScanPartition getApexScanPartitions() { final ListScanPartition scanPartitions = new ArrayList(); final ListApexManager.ActiveApexInfo activeApexInfos = mApexManager.getActiveApexInfos(); for (int i = 0; i activeApexInfos.size(); i++) { final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i)); if (scanPartition != null) { scanPartitions.add(scanPartition); } } return scanPartitions; }26.3.4 PackageParser2APK 文件的实际解析