PMS源码解析

5 阅读20分钟

PMS的使用

通过以下代码可以获取安卓手机中已安装程序的 PackageInfo:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        PackageManager packageManager = getPackageManager();
        // 获取所有已安装程序的 PackageInfo
        List<PackageInfo> packages = packageManager.getInstalledPackages(0);
        for(PackageInfo packageInfo : packages){
            Log.d("MainActivity", "package name:" + packageInfo.packageName);
        }
    }
}

在模拟器上运行后,打印如下:

MainActivity com.example.test D package name:com.google.android.networkstack.tethering
MainActivity com.example.test D package name:com.google.android.ext.services
MainActivity com.example.test D package name:com.android.providers.telephony
MainActivity com.example.test D package name:com.android.dynsystem
MainActivity com.example.test D package name:com.google.android.cellbroadcastservice
MainActivity com.example.test D package name:com.android.providers.calendar
...

里面究竟是怎么获取的呢?我们来看看源码,本文源码基于 Android 11.0。

由于 Activity 继承自 ContextWrapper,最终会委托给 ContextImpl 调用其 getPackageManager() 方法:

class ContextImpl extends Context {

    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }

        final IPackageManager pm = ActivityThread.getPackageManager();
        final IPermissionManager permissionManager = ActivityThread.getPermissionManager();
        if (pm != null && permissionManager != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm, permissionManager));
        }

        return null;
    }
}

可以看到 getPackageManager() 实际返回的是 ApplicationPackageManager 的实例,这里 ActivityThread.getPackageManager() 拿到的 IPackageManager 对象传给了 ApplicationPackageManager 实例。ApplicationPackageManager 代码如下:

public class ApplicationPackageManager extends PackageManager {

    private final ContextImpl mContext;
    private final IPackageManager mPM;
    private final IPermissionManager mPermissionManager;

    protected ApplicationPackageManager(ContextImpl context, IPackageManager pm,
                                        IPermissionManager permissionManager) {
        mContext = context;
        mPM = pm;
        mPermissionManager = permissionManager;
    }

    public List<PackageInfo> getInstalledPackages(int flags) {
        return getInstalledPackagesAsUser(flags, getUserId());
    }

    public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
        try {
            // 调用 mPM 的 getInstalledPackages() 方法
            ParceledListSlice<PackageInfo> parceledList =
                    mPM.getInstalledPackages(updateFlagsForPackage(flags, userId), userId);
            if (parceledList == null) {
                return Collections.emptyList();
            }
            return parceledList.getList();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
} 

里面调用了 mPM 的 getInstalledPackages() 方法,mPM 是通过 ActivityThread 的 getPackageManager() 获取的, 代码如下:

public final class ActivityThread extends ClientTransactionHandler {

    public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            return sPackageManager;
        }
        final IBinder b = ServiceManager.getService("package");
        sPackageManager = IPackageManager.Stub.asInterface(b);
        return sPackageManager;
    }

}

这里出现了 IBinder,很明显是跨进程通信,实际调用的是 SystemServer 进程的 PackageManagerService 的 getInstalledPackages() 方法。类图如下:

image.png

PackageManagerService 的 getInstalledPackages() 方法代码如下:

public class PackageManagerService extends IPackageManager.Stub implements PackageSender {

    // Keys are String (package name), values are Package.
    final ArrayMap<String, AndroidPackage> mPackages = new ArrayMap<>();

    final Settings mSettings;

    @Override
    public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
        ...
        flags = updateFlagsForPackage(flags, userId);
        // listUninstalled 包含所有系统已知的包(包括已卸载但有记录的)
        final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
        final boolean listApex = (flags & MATCH_APEX) != 0;
        final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
        ...
        synchronized (mLock) {
            ArrayList<PackageInfo> list;
            if (listUninstalled) {
                // mSettings.mPackages 包含所有曾经安装过,且在 packages.xml 中有记录的包
                list = new ArrayList<>(mSettings.mPackages.size());
                for (PackageSetting ps : mSettings.mPackages.values()) {
                    if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                        continue;
                    }
                    if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                        continue;
                    }
                    final PackageInfo pi = generatePackageInfo(ps, flags, userId);
                    if (pi != null) {
                        list.add(pi);
                    }
                }
            } else {
                // 否则,从 mPackages 中获取
                list = new ArrayList<>(mPackages.size());
                for (AndroidPackage p : mPackages.values()) {
                    final PackageSetting ps = getPackageSetting(p.getPackageName());
                    if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                        continue;
                    }
                    if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                        continue;
                    }
                    final PackageInfo pi = generatePackageInfo(ps, flags, userId);
                    if (pi != null) {
                        list.add(pi);
                    }
                }
            }
            if (listApex) {
                if (listFactory) {
                    list.addAll(mApexManager.getFactoryPackages());
                } else {
                    list.addAll(mApexManager.getActivePackages());
                }
                if (listUninstalled) {
                    list.addAll(mApexManager.getInactivePackages());
                }
            }
            return new ParceledListSlice<>(list);
        }
    }
}

这里根据传入的 flags 参数来判断 listUninstalled 是否为 true,前面我们传的 flags 是 0 ,所以这里 listUninstalled 是 false 。代码中判断如果 listUninstalled 是 true,从 mSettings.mPackages 中获取 PackageInfo 列表,否则从 mPackages 中获取。mSettings 和 mPackages 都是 PMS 中的成员。

mSettings.mPackages 和 mPackages 中的数据又是从哪里来的呢?我们来看看 PMS 的初始化过程。

PMS的初始化

PackageManagerService 的初始化在 SystemServer 的 startBootstrapServices() 方法中:

private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
    ...
    // Installer 
    Installer installer = mSystemServiceManager.startService(Installer.class);
    ...
    // AMS
    mActivityManagerService = ActivityManagerService.Lifecycle.startService(mSystemServiceManager, atm);
    ...
    mActivityManagerService.setInstaller(installer);
    ...
    // PMS
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    ...
    mFirstBoot = mPackageManagerService.isFirstBoot();
    mPackageManager = mSystemContext.getPackageManager();
}

在这里启动了 AMS、PMS,并且都传入了 Installer 的实例。mFirstBoot 表示 PMS 是否首次被启动,mFirstBoot 是后续 WMS 创建时所需的参数,由此可以看出系统服务之间是有依赖关系的,启动顺序不能随意更改。PMS 的 main() 方法代码如下:

public static PackageManagerService main(Context context, Installer installer,
                                         boolean factoryTest, boolean onlyCore) {
    ...
    PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
    ...
    ServiceManager.addService("package", m);
    final PackageManagerNative pmn = m.new PackageManagerNative();
    ServiceManager.addService("package_native", pmn);
    return m;
}

这里新建了 PackageManagerService 的实例,并将 PMS 注册到 ServiceManager 中。

下面来看看 PMS 的构造方法,PMS 的构造方法大概有 800 多行代码,分为 5 个阶段,每个阶段会打印出相应的 EventLog(EventLog 用于打印 Android 系统的事件日志),如下所示。

  1. BOOT_PROGRESS_PMS_START(开始阶段)。
  2. BOOT_PROGRESS_PMS_SYSTEM_SCAN_START(扫描系统分区阶段)。
  3. BOOT_PROGRESS_PMS_DATA_SCAN_START(扫描 Data 分区阶段)。
  4. BOOT_PROGRESS_PMS_SCAN_END(扫描结束阶段)。
  5. BOOT_PROGRESS_PMS_READY(准备就绪阶段)。

下面对每个阶段进行分析。

开始阶段

public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
    ...
    // 打印开始阶段日志
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
            SystemClock.uptimeMillis());
    ...
    // 屏幕的相关信息
    mMetrics = new DisplayMetrics();
    mInstaller = injector.getInstaller();
    // PackageManagerInternalImpl 是 PackageManager 的本地服务
    mPmInternal = new PackageManagerInternalImpl();
    // 加入 LocalServices
    LocalServices.addService(PackageManagerInternal.class, mPmInternal);
    ...
    // 初始化 mSettings
    mSettings = injector.getSettings();
    ...
    // 在 mSettings 中添加多个默认 sharedUserId
    mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); // 注释 1
    mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    ...
    // 创建 Dex 优化工具类
    mPackageDexOptimizer = new PackageDexOptimizer(mInstaller, mInstallLock, mContext,
            "*dexopt*");
    mDexManager =
            new DexManager(mContext, this, mPackageDexOptimizer, mInstaller, mInstallLock);
    ...
    // 创建后台线程 ServiceThread
    mHandlerThread = new ServiceThread(TAG,
            Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
    mHandlerThread.start();
    // 创建 PackageHandler,绑定到 ServiceThread 的消息队列
    mHandler = new PackageHandler(mHandlerThread.getLooper());
    // 添加到 Watchdog 中检测
    Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
    ...
    // 读取 /data/system/packages.xml 中存储的包信息,
    // 解析后将数据存放到 mSettings 中
    mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(
            /* excludePartial= */ true,
            /* excludeDying= */ false,
            /* excludePreCreated= */ false)); 
    ... 
}

在开始阶段创建了多个对象,下面来简单介绍一下其中几个比较关键的对象:

  • mSettings:作用是持久化并管理所有已安装应用的运行时状态、权限授予、启用/禁用状态、sharedUserId 映射等元数据,确保系统重启后能准确恢复应用的安装与配置状态。注释 1 处添加默认 sharedUserId(如 android.uid.system)是为了让系统核心 APK(如 framework-res.apk、SystemUI.apk)能以 SYSTEM_UID 运行,并共享同一个 Linux UID,从而共享进程或访问彼此的数据。这些 sharedUserId 必须在读取 packages.xml 之前注册,否则解析到使用该 sharedUserId 的包时会失败。
  • mInstaller:Installer 继承自 SystemService,和 PMS、AMS 一样是系统服务,PMS 的很多操作都是由 Installer 来完成的,比如 apk 的安装和卸载。Installer 是 Java 层的客户端,通过 IInstalld AIDL 接口与 native 层的 installd 守护进程进行通信,由 installd 来完成具体的操作。
  • mPackageDexOptimizer:Dex 优化的工具类。
  • mHandler(PackageHandler类型):PackageHandler 继承自 Handler,它绑定了后台线程 ServiceThread 的消息队列。PMS 通过 PackageHandler 驱动 apk 的复制和安装工作。
  • Watchdog:用于检测系统关键线程是否发生死锁或长时间无响应。将 PackageHandler 加入 Watchdog,是为了监控其消息处理是否被阻塞(例如因 I/O 卡住)。若超时未响应,Watchdog 会记录日志并在必要时重启 SystemServer,防止系统僵死。

上面的代码最后调用了 mSettings 的 readLPw() 方法读取 packages.xml(或其备份)文件,里面记录了每个包的权限、签名、组件、sharedUserId、权限授予等信息,对里面的信息进行解析后,添加到 mSettings 中(将 <package> 标签中的信息转换为内存中的 PackageSetting 对象)

如果成功地从磁盘上的配置文件(packages.xml 或其备份)中完整读取并解析了 PackageManager 的持久化状态,则 mFirstBoot 为 false;如果系统是首次启动,则 packages.xml 和 packages-backup.xml 都不存在,此时 mFirstBoot 将为 true。

packages.xml 在设备的 /data/system/ 目录下,内容如下所示:

...
<package name="com.google.android.calendar" codePath="/product/app/CalendarGooglePrebuilt" nativeLibraryPath="/product/app/CalendarGooglePrebuilt/lib" publicFlags="810270277" privateFlags="-1945628656" ft="187e77be448" it="187e77be448" ut="187e77be448" version="2016607548" sharedUserId="10131" isOrphaned="true">
    <sigs count="1" schemeVersion="3">
        <cert index="6" />
    </sigs>
    <perms>
        <item name="com.google.android.c2dm.permission.RECEIVE" granted="true" flags="0" />
        <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
    </perms>
    <proper-signing-keyset identifier="8" />
    <domain-verification packageName="com.google.android.calendar" status="0">
        <domain name="www.google.com" />
        <domain name="calendar.google.com" />
    </domain-verification>
</package>
<package name="com.google.android.deskclock" codePath="/product/app/PrebuiltDeskClockGoogle" nativeLibraryPath="/product/app/PrebuiltDeskClockGoogle/lib" publicFlags="810270277" privateFlags="-1945628416" ft="187e77bb568" it="187e77bb568" ut="187e77bb568" version="62000712" userId="10144" isOrphaned="true">
    <sigs count="1" schemeVersion="3">
        <cert index="6" />
    </sigs>
    <perms>
        <item name="android.permission.FOREGROUND_SERVICE" granted="true" flags="0" />
        <item name="android.permission.VIBRATE" granted="true" flags="0" />
        <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
    </perms>
    <proper-signing-keyset identifier="8" />
</package>
...

其中, package 表示包信息

  • name 表示包名
  • codePath 表示的是 APK 文件的路径
  • nativeLibraryPath 表示应用的 native 库的存储路径
  • it 表示首次安装时间戳(十六进制)
  • ut 表示最后更新时间
  • version 表示应用的版本号

sign 表示应用的签名 perms 表示应用声明使用的权限,每一个子标签代表一项权限

扫描系统分区阶段

// 打印扫描系统分区阶段日志
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
        startTime); 
... 
// /sytem/framework
File frameworkDir = new File(Environment.getRootDirectory(), "framework"); 
...
// 创建包解析器
PackageParser2 packageParser = new PackageParser2(mSeparateProcesses, mOnlyCore,
        mMetrics, mCacheDir, mPackageParserCallback);
// 线程池
ExecutorService executorService = ParallelPackageParser.makeExecutorService();
// 扫描 apks 之前准备 apex 的包信息,在 apex 中扫描 apk 的时候需要这些信息
mApexManager.scanApexPackagesTraced(packageParser, executorService);
// 扫描 sytem、vendor、odm、oem、product、system_ext 这些目录下的 overlay 目录
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);
}
// 扫描 /sytem/framework 目录
scanDirTracedLI(frameworkDir, systemParseFlags,
        systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
        packageParser, executorService);
if (!mPackages.containsKey("android")) {
    throw new IllegalStateException(
            "Failed to load frameworks package; check log for warnings");
}
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
    final ScanPartition partition = mDirsToScanAsSystem.get(i);
    if (partition.getPrivAppFolder() != null) {
        // 扫描 sytem、vendor、odm、oem、product、system_ext 这些目录下的 priv-app 目录
        scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
                systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
                packageParser, executorService);
    }
    // 扫描 sytem、vendor、odm、oem、product、system_ext 这些目录下的 app 目录
    scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
            systemScanFlags | partition.scanFlag, 0,
            packageParser, executorService);
}
...
// 用于删除不存在的系统 App
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
...
if (!mOnlyCore) {
    ...
    final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
    while (psit.hasNext()) {
        PackageSetting ps = psit.next();

        // 如果不是系统 App,跳过
        if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
            continue;
        }

        final AndroidPackage scannedPkg = mPackages.get(ps.name);
        if (scannedPkg != null) {
            // 如果这个 App 在 mSettings.mDisabledSysPackages 中
            if (mSettings.isDisabledSystemPackageLPr(ps.name)) { // 注释 1
                // 从 mPackages 中移除
                removePackageLI(scannedPkg, true);
                // 把 apk 路径添加到 mExpectingBetter 列表中
                mExpectingBetter.put(ps.name, ps.codePath);
            }

            continue;
        }

        if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
        ...
        } else {
            // 如果这个 App 还在 mSettings.mDisabledSysPackages 中
            final PackageSetting disabledPs =
                    mSettings.getDisabledSystemPkgLPr(ps.name);
            // 没有找到该 apk 
            if (disabledPs.codePath == null || !disabledPs.codePath.exists()
                    || disabledPs.pkg == null) { // 注释 2
                possiblyDeletedUpdatedSystemApps.add(ps.name);
            } else {
                // 添加到 mExpectingBetter 列表中,如果扫描 Data 分区的时候找不到这个应用,
                // 后面可以从 mExpectingBetter 中恢复这个应用
                mExpectingBetter.put(disabledPs.name, disabledPs.codePath);
            }
        }
    }
}

这里调用了 scanDirTracedLI() 方法扫描系统分区(/vendor/app、/sytem/framework、 /sytem/priv-app、/sytem/app 等目录)下的 apk 文件。

  • /vendor/app:保存设备厂商提供的 App
  • /sytem/framework:保存的是资源型的 App,它们用来打包资源文件;
  • /sytem/priv-app:用来存放特权 App;
  • /sytem/app:用于存放系统 App;

有可能出现这种情况,设备上有一个非系统应用,现在进行 OTA 升级,发现升级的是同一个应用,但是是系统应用。这时候需要判断两个应用的版本,如果非系统应用的版本更加新,系统应用需要被隐藏。这时候 PMS 会将该系统应用的 PackageSetting 存储到 mSettings 的 mDisabledSysPackages 列表中,见 PMS 的 addForInitLI()方法(scanDirTracedLI() 中会调用 addForInitLI()):

private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
        @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
        @Nullable UserHandle user) throws PackageManagerException {
    ...
    // 是否要隐藏系统应用
    if (shouldHideSystemApp) {
        synchronized (mLock) {
            // 添加到 mSettings 的 mDisabledSysPackages 列表中
            mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
        }
    }
} 

mDisabledSysPackages 中的信息会被 PMS 保存到 packages.xml 中的 <updated-package 标签下,具体见 Settings 的 writeDisabledSysPackageLPr() 方法。

扫描系统分区阶段代码的注释 1 处就是上面说的这种情况,需要将该 App 从 mPackages 中移除,并将 apk 路径添加到 mExpectingBetter 列表中,等待后续处理。mExpectingBetter 的作用是:如果后续扫描 Data 分区的时候找不到这个应用,可以从 mExpectingBetter 中恢复这个应用。在注释 2 处,如果找不到这个系统应用的 apk 的路径,会将它加入 possiblyDeletedUpdatedSystemApps 列表中,意为 “可能已经删除的升级的系统App” ,之所以是 “可能”,是因为系统还没有扫描 Data 分区。

扫描 Data 分区阶段

if (!mOnlyCore) {
    // 打印扫描 Data 分区阶段日志
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
            SystemClock.uptimeMillis());
    // 扫描 /data/app/ 目录 
    scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
            packageParser, executorService);

}
// 关闭解析器
packageParser.close();
// 关闭线程池
List<Runnable> unfinishedTasks = executorService.shutdownNow();
...
if (!mOnlyCore) {
    // 扫描完 Data 分区后,处理 possiblyDeletedUpdatedSystemApps 列表
    for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
        final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
        final AndroidPackage pkg = mPackages.get(packageName);
        final String msg;

        // 从 mSetting.mDisabledSysPackages 中移除此应用
        mSettings.removeDisabledSystemPackageLPw(packageName);
        // 如果这个系统 App 的包信息不在 mPackages 中,
        // 则说明其是残留的 App 信息,后续会在 reconciliation 这一步中删除它的数据
        if (pkg == null) {
            msg = "Updated system package " + packageName
                    + " no longer exists; removing its data";
        } else {
            // 如果这个系统 App 在 mPackages 中,则说明其存在于 Data 分区中,
            // 不属于系统 App, 那么移除其系统权限
            msg = "Updated system package " + packageName
                    + " no longer exists; rescanning package on data";
            removePackageLI(pkg, true);
            try {
                final File codePath = new File(pkg.getCodePath());
                scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
            } catch (PackageManagerException e) {
                Slog.e(TAG, "Failed to parse updated, ex-system package: "
                        + e.getMessage());
            }
        }

        // 如果这个系统 App 的包信息不在 mPackages 中,但是它在 mSettings 
        // 的 mPackages 中,可能是之前系统扫描拿到的,但是不存在于 Data 分区中,需要
        // 移除包数据
        final PackageSetting ps = mSettings.mPackages.get(packageName);
        if (ps != null && mPackages.get(packageName) == null) {
            removePackageDataLIF(ps, null, null, 0, false);

        }
        logCriticalInfo(Log.WARN, msg);
    }

    // 遍历 mExpectingBetter 列表
    for (int i = 0; i < mExpectingBetter.size(); i++) {
        final String packageName = mExpectingBetter.keyAt(i);
        // 如果在 Data 分区没有扫描到这个应用
        if (!mPackages.containsKey(packageName)) {
            // 拿到系统 App 的升级包路径
            final File scanFile = mExpectingBetter.valueAt(i);

            logCriticalInfo(Log.WARN, "Expected better " + packageName
                    + " but never showed up; reverting to system");

            @ParseFlags int reparseFlags = 0;
            @ScanFlags int rescanFlags = 0;
            // 根据系统 App 所在的目录设置扫描的解析参数
            for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
                final ScanPartition partition = mDirsToScanAsSystem.get(i1);
                if (partition.containsPrivApp(scanFile)) {
                    reparseFlags = systemParseFlags;
                    rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
                            | partition.scanFlag;
                    break;
                }
                if (partition.containsApp(scanFile)) {
                    reparseFlags = systemParseFlags;
                    rescanFlags = systemScanFlags | partition.scanFlag;
                    break;
                }
            }
            // 将 packageName 对应的包配置(PackageSetting)添加
            // 到 mSettings.mPackages 中 
            mSettings.enableSystemPackageLPw(packageName);

            // 重新扫描该系统 App
            scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
        }
    }
    ...
}
// 清空 mExpectingBetter
mExpectingBetter.clear();
...

扫描 Data 分区阶段的主要工作有以下3点:

  1. 调用 scanDirTracedLI() 方法扫描 /data/app 目录下的 apk 文件;
  2. 遍历 possiblyDeletedUpdatedSystemApps 列表,如果这个系统 App 的包信息不在 mPackages 中,则说明其是残留的 App 信息,后续会删除它的数据。如果这个系统 App 的包信息在 mPackages 中,则说明其存在于 Data 分区中,不属于系统 App,那么移除其系统权限。
  3. 遍历 mExpectingBetter 列表,如果在 Data 分区没有扫描到这个系统 App,根据系统 App 所在的目录设置扫描的解析参数,将 packageName 对应的包配置添加到 mSettings.mPackages 中。调用 scanPackageTracedLI() 扫描该系统 App,最后清理 mExpectingBetter 列表。

扫描结束阶段

// 打印扫描结束阶段日志 
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
        SystemClock.uptimeMillis());

// 如果当前平台的 SDK 版本跟上次启动时的 SDK 版本不同,则更新 apk 的授权
final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
mPermissionManager.updateAllPermissions(
        StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated);
ver.sdkVersion = mSdkVersion;

// 如果是第一次启动或者是 Android M 升级后的第一次启动,
// 则需要初始化所有用户定义的默认首选 App
if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
    for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
        mSettings.applyDefaultPreferredAppsLPw(user.id);
        primeDomainVerificationsLPw(user.id);
    }
}
...
// OTA 升级后的第一次启动,且非“仅核心模式”启动时,
// 清除所有内部存储上已安装应用的代码缓存(如 dexopt 生成的 oat/art 文件),
// 以确保新系统环境下运行的是与当前系统兼容的优化代码。
if (mIsUpgrade && !mOnlyCore) {
    Slog.i(TAG, "Build fingerprint changed; clearing code caches");
    for (int i = 0; i < mSettings.mPackages.size(); i++) {
        final PackageSetting ps = mSettings.mPackages.valueAt(i);
        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
            // No apps are running this early, so no need to freeze
            clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
                    FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
                            | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
                            | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
        }
    }
    ver.fingerprint = Build.FINGERPRINT;
}
...
// 把 mSettings 的内容保存到 packages.xml 中
mSettings.writeLPr();

扫描结束阶段的主要工作有以下几点。

  1. 如果当前平台的 SDK 版本和上次启动时的 SDK 版本不同,则更新 apk 的授权。
  2. 如果是第一次启动或者是 Android M 升级后的第一次启动,则需要初始化所有用户定义的默认首选 App。
  3. OTA 升级后的第一次启动,且非“仅核心模式”启动时,清除所有内部存储上已安装应用的代码缓存(如 dexopt 生成的 oat/art 文件),以确保新系统环境下运行的是与当前系统兼容的优化代码。
  4. 调用 mSettings 的 writeLPr() 方法把 mSettings 中的内容保存到 packages.xml 中,这样以后 PMS 再创建时会读到此前保存的内容。

准备就绪阶段

// 打印准备就绪阶段的日志 
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
        SystemClock.uptimeMillis());
...
// 创建 PackageInstallerService
mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier);
...
// 垃圾回收
Runtime.getRuntime().gc();
...

// The initial scanning above does many calls into installd while
// holding the mPackages lock, but we're mostly interested in yelling
// once we have a booted system.
mInstaller.setWarnIfHeld(mLock);
...

这里创建了 PackageInstallerService,PackageInstallerService 是用于管理安装会话的服务,它会为每次的安装过程分配一个 Sessionld,这里还进行了一次垃圾收集。

apk的解析过程

扫描系统分区和扫描 Data 分区都会调用 scanDirTracedLI() 方法扫描对应的目录,代码如下:

private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
        long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
    ...
    scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
    ...
}

里面调用了scanDirLI():

private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
        PackageParser2 packageParser, ExecutorService executorService) {
    final File[] files = scanDir.listFiles();
    ...
    // ParallelPackageParser 中传入了 executorService
    ParallelPackageParser parallelPackageParser =
            new ParallelPackageParser(packageParser, executorService);

    int fileCount = 0;
    for (File file : files) {
        // 把解析任务提交给线程池处理
        parallelPackageParser.submit(file, parseFlags);
        fileCount++;
    }
    ...
    // 处理解析结果
    for (; fileCount > 0; fileCount--) {
        ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
        ...
        addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
                currentTime, null);
    }
    ...
}

在这里调用了 ParallelPackageParser 的 submit() 方法,然后使用 addForInitLI() 处理解析结果。ParallelPackageParser 的 submit() 方法代码如下:

class ParallelPackageParser {

    private final PackageParser2 mPackageParser;

    private final ExecutorService mExecutorService;

    ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) {
        mPackageParser = packageParser;
        mExecutorService = executorService;
    }

    /**
     * Submits the file for parsing
     */
    public void submit(File scanFile, int parseFlags) {
        mExecutorService.submit(() -> {
            ParseResult pr = new ParseResult();
            ...
            pr.scanFile = scanFile;
            pr.parsedPackage = parsePackage(scanFile, parseFlags);
            ...
            mQueue.put(pr);
            ...
        });
    }
    
    protected ParsedPackage parsePackage(File scanFile, int parseFlags)
            throws PackageParser.PackageParserException {
        return mPackageParser.parsePackage(scanFile, parseFlags, true);
    }
}

在 ParallelPackageParser 中使用了线程池来处理解析任务,调用了 PackageParser2 的 parsePackage() 方法:

public class PackageParser2 implements AutoCloseable {

    private ParsingPackageUtils parsingUtils;

    public PackageParser2(String[] separateProcesses, boolean onlyCoreApps,
            DisplayMetrics displayMetrics, @Nullable File cacheDir, @NonNull Callback callback) {
        ...
        parsingUtils = new ParsingPackageUtils(onlyCoreApps, separateProcesses, displayMetrics,
                callback);
        ...
    }

    public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {
        ...
        ParseInput input = mSharedResult.get().reset();
        ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
        ...
        ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
        ...
        return parsed;
    }

} 

parsingUtils 是 ParsingPackageUtils 的实例,继续看 ParsingPackageUtils 的 parsePackage() 方法:

public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
        int flags)
        throws PackageParserException {
    if (packageFile.isDirectory()) {
        return parseClusterPackage(input, packageFile, flags);
    } else {
        return parseMonolithicPackage(input, packageFile, flags);
    }
}

上面的代码通过判断 packageFile 是否是目录走向了两个分支。

Android 5.0 引人了 SplitAPK 机制其目的是解决 65536 上限及 apk 安装包越来越大的问题。SplitAPK 机制可以将一个 apk 拆分成多个独立的 apk。在引入了 SplitAPK 机制后,APK 有以下两种分类。

  • SingleAPK:一个完整的 apk 安装文件,即 baseAPK。Android 称其为 Monolithic;
  • MutipleAPK:在一个文件目录中安装文件,其内部有多个被拆分的 apk,这些 apk 由一个 baseAPK 和一个或多个 splitAPK 组成。Android 称其为 Cluster;

如果 packageFile 是一个目录,说明 apk 的种类是 MutipleAPK,需要调用 parseClusterPackage() 方法来解析,否则调用 parseMonolithicPackage() 方法。parseClusterPackage() 方法更加复杂,我们来看看这个方法:

private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
        int flags) {
    // 轻量级解析目录文件 
    ParseResult<PackageParser.PackageLite> liteResult =
            ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
    if (liteResult.isError()) {
        return input.error(liteResult);
    }

    final PackageParser.PackageLite lite = liteResult.getResult();
    // mOnlyCoreApps 用于指示是否只解析核心应用
    // lite.coreApp 表示当前包是否包含核心应用
    if (mOnlyCoreApps && !lite.coreApp) {
        return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
                "Not a coreApp: " + packageDir);
    }
    ...
    try {
        final AssetManager assets = assetLoader.getBaseAssetManager();
        final File baseApk = new File(lite.baseCodePath);
        // 解析 baseApk 
        ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
                lite.codePath, assets, flags);
        if (result.isError()) {
            return input.error(result);
        }

        ParsingPackage pkg = result.getResult();
        if (!ArrayUtils.isEmpty(lite.splitNames)) {
            pkg.asSplit(
                    lite.splitNames,
                    lite.splitCodePaths,
                    lite.splitRevisionCodes,
                    splitDependencies
            );
            final int num = lite.splitNames.length;

            for (int i = 0; i < num; i++) {
                final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
                parseSplitApk(input, pkg, i, splitAssets, flags);
            }
        }
        ...
        return input.success(pkg);
    }
}

这里先调用了 parseClusterPackageLite() 方法,用于轻量级解析目录文件,之所以要轻量级解析,是因为解析 apk 是一个复杂且耗时的工作,这里的逻辑并不需要 apk 的所有信息。parseClusterPackageLite() 方法内部会通过 parseApkLite() 方法解析每个 MutipleAPK,得到每个 MutipleAPK 对应的 ApkLite(轻量级apk信息),然后将这些 ApkLite 封装为一个 PackageLite(轻量级包信息)并返回。

mOnlyCoreApps 用于指示是否只解析“核心”应用,“核心”应用指的是 AndroidManifest.xml 中属性 coreApp 的值为 true,只解析“核心”应用是为了创建一个极简的启动环境。它是在创建 PMS 时 mOnlyCoreApps 一路传递过来,如果我们加密了设备,mOnlyCoreApps的值就为 true,另外可以通过 PackageParser 的 setOnlyCoreApps() 方法来设置 mOnlyCoreApps 的值。lite.coreApp 表示当前包是否包含“核心”应用。

然后调用了 parseBaseApk() 方法用于解析 baseApk,最后获取了 splitAPK 的数量,根据这个数量遍历调用 parseSplitApk() 方法来解析每个 splitAPK。这里主要查看 parseBaseApk() 方法,parseMonolithicPackage() 也会走到 parseBaseApk() 方法,其代码如下:

private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
        String codePath, AssetManager assets, int flags) { 
    final String apkPath = apkFile.getAbsolutePath();

    String volumeUuid = null;
    if (apkPath.startsWith(PackageParser.MNT_EXPAND)) { // 注释 1
        final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
        volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
    }
    ...
    XmlResourceParser parser = assets.openXmlResourceParser(cookie,
            PackageParser.ANDROID_MANIFEST_FILENAME);
    final Resources res = new Resources(assets, mDisplayMetrics, null);

    ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res, parser, flags); 
    final ParsingPackage pkg = result.getResult();
    ...
    pkg.setVolumeUuid(volumeUuid); // 注释 2
    ... 
    return input.success(pkg);
}

在注释 1 处,如果 apk 的路径以 /mnt/expand/ 开头,就截取该路径来获取 volumeUuid。注释 2 处的代码用于以后标识这个解析后的 Package。这里创建了 XmlResourceParser 实例,用于解析 apk 中的 AndroidManifest.xml 文件,然后调用了重载的 parseBaseApk() 方法:

private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
        String codePath, Resources res, XmlResourceParser parser, int flags)
        throws XmlPullParserException, IOException, PackageParserException {
    ...
    // 从资源中提取自定义属性集 com.android.internal.R.styleable.AndroidManifest
    // 并得到 TypedArray
    final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest); 
    // 创建 ParsingPackage 实例用于承载解析结果
    final ParsingPackage pkg = mCallback.startParsingPackage(
            pkgName, apkPath, codePath, manifestArray, isCoreApp);
    final ParseResult<ParsingPackage> result =
            parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
    ...
    return input.success(pkg);
}

这里先从资源中提取了自定义属性集 com.android.internal.R.styleable.AndroidManifest 并得到 TypedArray,这个属性集所在的源码位置为:frameworks/base/core/res/res/values/ attrs_manifest.xml。然后调用了 parseBaseApkTags() :

private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
        TypedArray sa, Resources res, XmlResourceParser parser, int flags)
        throws XmlPullParserException, IOException {
    ...
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG
            || parser.getDepth() > depth)) {
        if (type != XmlPullParser.START_TAG) {
            continue;
        }

        String tagName = parser.getName();
        final ParseResult result;

        // 查找 <application> 标签
        if (PackageParser.TAG_APPLICATION.equals(tagName)) {
            if (foundApp) {
                if (PackageParser.RIGID_PARSER) {
                    result = input.error("<manifest> has more than one <application>");
                } else {
                    Slog.w(TAG, "<manifest> has more than one <application>");
                    result = input.success(null);
                }
            } else {
                foundApp = true;
                result = parseBaseApplication(input, pkg, res, parser, flags);
            }
        } else {
            result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
        }
    }
    ...
    return input.success(pkg);
}

这里使用 XmlResourceParser 查找 AndroidManifest.xml 中的 <application> 标签,然后调用 parseBaseApplication() 来解析 apk 中的基础 Application 节点树:

private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
        ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
        throws XmlPullParserException, IOException {
    ...
    // 解析 <application> 标签中的属性:theme 这些
    parseBaseAppBasicFlags(pkg, sa);
    ...
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG
            || parser.getDepth() > depth)) {
        ...
        final ParseResult result;
        String tagName = parser.getName();
        boolean isActivity = false;
        switch (tagName) {
            case "activity":
                isActivity = true;
                // fall-through
            case "receiver":
                ParseResult<ParsedActivity> activityResult =
                        ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                                res, parser, flags, PackageParser.sUseRoundIcon, input);

                if (activityResult.isSuccess()) {
                    ParsedActivity activity = activityResult.getResult();
                    // 判断是 Activity
                    if (isActivity) {
                        hasActivityOrder |= (activity.getOrder() != 0);
                        pkg.addActivity(activity); // 添加 Activity
                    } else {
                        hasReceiverOrder |= (activity.getOrder() != 0);
                        pkg.addReceiver(activity); // 添加 Receiver
                    }
                }

                result = activityResult;
                break;
            case "service":
                ParseResult<ParsedService> serviceResult =
                        ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
                                flags, PackageParser.sUseRoundIcon, input);
                if (serviceResult.isSuccess()) {
                    ParsedService service = serviceResult.getResult();
                    hasServiceOrder |= (service.getOrder() != 0);
                    pkg.addService(service); // 添加Service
                }

                result = serviceResult;
                break;
            case "provider":
                ParseResult<ParsedProvider> providerResult =
                        ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
                                flags, PackageParser.sUseRoundIcon, input);
                if (providerResult.isSuccess()) {
                    pkg.addProvider(providerResult.getResult()); // 添加Provider
                }

                result = providerResult;
                break;
            case "activity-alias":
                activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
                        parser, PackageParser.sUseRoundIcon, input);
                if (activityResult.isSuccess()) {
                    ParsedActivity activity = activityResult.getResult();
                    hasActivityOrder |= (activity.getOrder() != 0);
                    pkg.addActivity(activity);
                }

                result = activityResult;
                break;
            default:
                result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
                break;
        }

        if (result.isError()) {
            return input.error(result);
        }
    }
    ...
    return input.success(pkg);
}

这里就是解析 Application 的子节点——四大组件,并把信息存入 ParsingPackage 实例 pkg 中,最后转成 ParallelPackageParser.ParseResult。然后调用了 addForInitLI() 方法,这个方法逻辑比较复杂,有很多对系统应用的校验(包名修改、版本升级、签名收集和校验),但我们只关注下面内容:

// 在系统启动期间,将新安装的包添加到其内部的数据结构中。
// 添加后,该包对于系统就是可见的,可以用于查询。
private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
        @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
        @Nullable UserHandle user)
                throws PackageManagerException {
    ...
    // 继续扫描
    final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
            | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
    if (scanResult.success) {
        synchronized (mLock) {
            boolean appIdCreated = false;
            final String pkgName = scanResult.pkgSetting.name;
            final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
                    new ReconcileRequest(
                            Collections.singletonMap(pkgName, scanResult),
                            mSharedLibraries,
                            mPackages,
                            Collections.singletonMap(pkgName, getSettingsVersionForPackage(parsedPackage)),
                            Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))),
                    mSettings.mKeySetManagerService);
            appIdCreated = optimisticallyRegisterAppId(scanResult);
            // 提交扫描结果,更新系统状态
            commitReconciledScanResultLocked(
                    reconcileResult.get(pkgName), mUserManager.getUserIds());

        }
    }
    ...
    return scanResult.pkgSetting.pkg;
}

里面调用 scanPackageNewLI() 继续扫描,主要是更新 mSettings 中的成员变量 mPackages。然后调用 reconcilePackagesLocked() 进行一致化处理,再调用 commitReconciledScanResultLocked() 提交包扫描结果、更新系统状态,内部又调用了commitPackageSettings():

// 添加扫描完成的包到系统,这个方法执行完后,包可以用于查询、解析
private void commitPackageSettings(AndroidPackage pkg,
        @Nullable AndroidPackage oldPkg, PackageSetting pkgSetting,
        final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
    ...
    synchronized (mLock) {
        // 添加到 mSettings.mPackages 中
        mSettings.insertPackageSettingLPw(pkgSetting, pkg);
        // 添加到 mPackages 中
        mPackages.put(pkg.getPackageName(), pkg);
        ...
        // 添加组件到 mComponentResolver:所有的四大组件信息添加到内部数据结构中
        mComponentResolver.addAllComponents(pkg, chatty);
        ...
        // 添加权限到 mPermissionManager 中
        mPermissionManager.addAllPermissionGroups(pkg, chatty);
        ...
        for (i = 0; i < collectionSize; i++) {
            ParsedInstrumentation a = pkg.getInstrumentations().get(i);
            a.setPackageName(pkg.getPackageName());
            mInstrumentation.put(a.getComponentName(), a);
        }
    }
}

在这里,把 PackageSetting 添加到 mSettings.mPackages 中,把 AndroidPackage 添加到 mPackages 中,四大组件信息添加到 mComponentResolver 中,应用的权限添加到 mPermissionManager 中。这样就把包信息记录在了 PMS 管理的内存中。

总结一下:

  1. 系统启动后会启动 SystemServer,在 SystemServer 的 startBootstrapServices() 中会调用 PMS 的构造方法,PMS 的构造方法共分为 5 个阶段:开始阶段、扫描系统分区阶段、扫描 Data 分区阶段、扫描结束阶段、准备就绪阶段。
  2. 在开始阶段,初始化关键对象(如 mSettings、mInstaller、PackageHandler),并读取 /data/system/packages.xml 文件,将其内容解析为 PackageSetting 对象存入 mSettings.mPackages,但尚未构建运行时包对象 AndroidPackage。
  3. 在扫描系统分区阶段,扫描 /system、/vendor 等目录下的 APK,解析后构建 AndroidPackage 对象,并创建或更新其对应的 PackageSetting,确保其存在于 mSettings.mPackages 中,并将 AndroidPackage 加入 PMS 的 mPackages,同时注册四大组件和权限。
  4. 在扫描 Data 分区阶段,扫描 /data/app 目录,执行类似的解析与注册流程(使用不同扫描标志),并处理 OTA 升级后可能被 disable 的系统应用(如从 mExpectingBetter 恢复)。
  5. 在扫描结束阶段,将 mSettings 中的最新包状态(包括新安装、权限变更等)写回 packages.xml,实现持久化。
  6. 在准备就绪阶段,创建 PackageInstallerService(用于管理安装会话),执行 GC,并完成 PMS 初始化。