Android-Framework(十二)-AMS与PMS的启动过程

文章内容

素材来源于“AMS 的启动过程分析”“PMS 的启动过程分析”“安装包的解析过程分析”三段视频。

一、system_server

应用进程里的 Application.onCreate() 已经处于启动链后段。此时系统早已知道包名对应哪个 UID、入口 Activity 是谁、它声明了哪些 intent-filter。这些数据若到 startActivity 时才从 APK 临时读取,每次组件解析都要重复访问压缩包和 XML,系统也无法在开机阶段建立统一的包状态。

因此,观察范围需要移到 system_server。它承载 AMS、PMS 等系统服务,开机主线程还要处理服务依赖、Binder 发布、目录扫描和 ready 阶段。线程等待、锁竞争、跨进程调用以及扫描期 I/O 都可能影响系统进入可交互状态而且应用侧的 onCreate() 优化无法覆盖这些耗时。

Application.onCreate 之外的启动耗时来源

SystemServer.main() 创建实例并进入 run(),run() 按引导服务、核心服务、其他服务三个阶段组织启动,随后让主线程进入消息循环。下面这段代码保留了这条外层控制链。

java
// 进程入口接收启动参数。public static void main(String[] args) {    // 创建 SystemServer 并进入主流程。    new SystemServer().run();// 结束进程入口。} // run 负责组织系统服务启动阶段。private void run() {    // 引导服务包含 Installer、AMS 和 PMS。    startBootstrapServices();    // 核心服务在引导服务之后启动。    startCoreServices();    // 其余系统服务在最后一个阶段启动。    startOtherServices();    // 主线程进入消息循环。    Looper.loop();    // Looper 意外退出时终止启动流程。    throw new RuntimeException("Main thread loop unexpectedly exited");// 结束 run。}

二、system_server 初始化

startBootstrapServices() 处理彼此存在早期依赖的服务。Installer 需要赶在其他服务之前连接 installd,以便准备 /data/user 等目录;AMS 随后通过 ActivityManagerService.Lifecycle 创建。
PowerManagerService 和默认显示准备完成后,vold.decrypt 决定 mOnlyCore,PMS 才进入构造与扫描。

system_server 的启动三阶段与 AMS/PMS 的位置

下面这段代码对应 SystemServer.java:322-367,它的输入包括 mSystemContext、Installer、工厂测试状态和 mOnlyCore,输出是保存于 SystemServer 字段中的 AMS、PMS 实例。

java
// 定义引导服务启动阶段。private void startBootstrapServices() {    // 启动 Installer 并取得实例。    Installer installer = mSystemServiceManager.startService(Installer.class);    // 通过 Lifecycle 创建并取得 AMS 本体。    mActivityManagerService = mSystemServiceManager.startService(            // 指定 AMS 的 SystemService 包装类。            ActivityManagerService.Lifecycle.class).getService();    // 把 SystemServiceManager 交给 AMS。    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);    // 把 Installer 交给 AMS。    mActivityManagerService.setInstaller(installer);    // 读取设备加密启动状态。    String cryptState = SystemProperties.get("vold.decrypt");    // 加密进行中时限制为 core app。    if (ENCRYPTING_STATE.equals(cryptState)) {        // 记录限制扫描的原因。        Slog.w(TAG, "Detected encryption in progress - only parsing core apps");        // 开启 core-only 模式。        mOnlyCore = true;    // 已加密设备也采用 core-only 模式。    } else if (ENCRYPTED_STATE.equals(cryptState)) {        // 记录设备已加密状态。        Slog.w(TAG, "Device encrypted - only parsing core apps");        // 开启 core-only 模式。        mOnlyCore = true;    // 结束加密状态判断。    }    // 记录 PMS 启动节点。    Slog.i(TAG, "Package Manager");    // 创建 PMS,并把扫描模式传入。    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,            // 传入工厂测试标志与 core-only 标志。            mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);    // 保存 PMS 判断出的首次启动状态。    mFirstBoot = mPackageManagerService.isFirstBoot();    // 取得面向 Context 的 PackageManager 门面。    mPackageManager = mSystemContext.getPackageManager();// 结束引导阶段。}

mOnlyCore 是字段名,其值来自 vold.decrypt。加密流程进行中或设备处于相应加密状态时,PMS 限制包解析范围,避免在此阶段处理普通第三方应用。

三、AMS 是怎么创建出来的

3.1 Lifecycle 包装层和依赖注入

startBootstrapServices 里创建 AMS 的四行代码

SystemServiceManager.startService(ActivityManagerService.Lifecycle.class) 操作的是符合 SystemService 生命周期的包装层。
下面这段代码中 PMS 走另一种入口:构造完成后以 package 为服务名注册到 ServiceManager。

java
// PMS 的静态入口接收启动所需依赖与模式。public static PackageManagerService main(Context context, Installer installer,        // 声明工厂测试与 core-only 参数。        boolean factoryTest, boolean onlyCore) {    // PMS 构造函数完成设置读取、扫描与解析。    PackageManagerService m = new PackageManagerService(context, installer,            // 将两个模式参数传入构造函数。            factoryTest, onlyCore);    // 发布名为 package 的 Binder 服务。    ServiceManager.addService("package", m);    // 返回同一个 PMS 实例。    return m;// 结束 PMS 静态入口。}

下面这段代码展示 Lifecycle 的职责边界。构造器创建 AMS 本体;SystemServiceManager 调用 onStart() 时,Lifecycle 才转入 AMS 的 start()。

java
// Lifecycle 接入统一的 SystemService 生命周期。public static final class Lifecycle extends SystemService {    // 保存 AMS 本体。    private final ActivityManagerService mService;    // Lifecycle 构造器接收系统上下文。    public Lifecycle(Context context) {        // 初始化 SystemService 基类。        super(context);        // 创建 AMS 本体。        mService = new ActivityManagerService(context);    // 结束 Lifecycle 构造器。    }    // 声明启动阶段回调。    @Override    // SystemServiceManager 调用该方法启动 AMS。    public void onStart() {        // 进入 AMS 自身的 start。        mService.start();    // 结束启动回调。    }    // 向 SystemServer 暴露 AMS 实例。    public ActivityManagerService getService() {        // 返回构造出的服务本体。        return mService;    // 结束实例访问方法。    }// 结束 Lifecycle 定义。}

3.2 构造函数建立运行骨架

下面这段代码是构造函数前半段。AMS 新建一条前台优先级 ServiceThread,MainHandler 使用该线程的 Looper;UiHandler 在这个版本的实现中也由 AMS 构造。两个 BroadcastQueue 分别承载前台与后台广播,ActiveServices、ProviderMap 则保存服务和 Provider 管理状态。

java
// AMS 构造器接收 system_server 的 Context。public ActivityManagerService(Context systemContext) {    // 保存系统上下文。    mContext = systemContext;    // 读取工厂测试模式。    mFactoryTest = FactoryTest.getMode();    // 保存当前系统 ActivityThread。    mSystemThread = ActivityThread.currentActivityThread();    // 创建 AMS 专用 ServiceThread。    mHandlerThread = new ServiceThread(TAG,            // 设置前台线程优先级并禁止常规 I/O。            android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);    // 启动 ServiceThread。    mHandlerThread.start();    // 将 MainHandler 绑定到 ServiceThread 的 Looper。    mHandler = new MainHandler(mHandlerThread.getLooper());    // 创建处理 UI 相关消息的 UiHandler。    mUiHandler = new UiHandler();    // 创建前台广播队列。    mFgBroadcastQueue = new BroadcastQueue(this, mHandler,            // 使用前台广播超时参数。            "foreground", BROADCAST_FG_TIMEOUT, false);    // 创建后台广播队列。    mBgBroadcastQueue = new BroadcastQueue(this, mHandler,            // 使用后台广播超时参数。            "background", BROADCAST_BG_TIMEOUT, true);    // 建立 Service 管理对象。    mServices = new ActiveServices(this);    // 建立 Provider 映射。    mProviderMap = new ProviderMap(this);// 省略构造函数后续对象初始化。}

AMS 构造函数建立的成员对象

下面这段代码补上 Activity 栈、最近任务、任务持久化和 CPU 采样线程之间的联系。ActivityStackSupervisor 管理 ActivityStack、TaskRecord 与 ActivityRecord;mProcessCpuThread 在构造阶段创建,启动动作留给 start()。

java
// 创建最近任务管理对象。mRecentTasks = new RecentTasks(this);// 让 ActivityStackSupervisor 持有 AMS 与最近任务。mStackSupervisor = new ActivityStackSupervisor(this, mRecentTasks);// 创建任务持久化对象。mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, mRecentTasks);// 创建 CPU 采样线程。mProcessCpuThread = new Thread("CpuTracker") {    // 覆盖线程执行入口。    @Override    // 定义采样循环。    public void run() {        // 线程持续进行周期性采样。        while (true) {            // 实际源码在此计算延迟并更新 CPU 统计。            updateCpuStatsNow();        // 结束采样循环。        }    // 结束线程入口。    }// 结束线程对象并保存到字段。};

3.3 start() 与电源管理

下面这段代码中,两个 setter 完成依赖注入,start() 清理继承自 zygote 的进程组状态、启动 CPU 线程、发布 BatteryStats 与 AppOps,并把 ActivityManagerInternal 放入 LocalServices。AMS 的 Activity Binder 注册不在该方法内;SystemServer 稍后调用 setSystemProcess() 完成相关注册。setSystemProcess() 里做的是 ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true),外加 processinfo、permission、meminfo、gfxinfo、dbinfo、cpuinfo 这几个调试与统计接口,最后把 system_server 自己作为一个持久进程登记进 AMS 的进程表。

java
// 注入 SystemServiceManager。public void setSystemServiceManager(SystemServiceManager mgr) {    // 保存管理器引用。    mSystemServiceManager = mgr;// 结束 setter。}// 注入 Installer。public void setInstaller(Installer installer) {    // 保存 Installer 引用。    mInstaller = installer;// 结束 setter。}// 启动 AMS 的运行期设施。private void start() {    // 清理 zygote 遗留的进程组设置。    Process.removeAllProcessGroups();    // 启动构造阶段创建的 CPU 线程。    mProcessCpuThread.start();    // 发布 BatteryStats 服务。    mBatteryStatsService.publish(mContext);    // 发布 AppOps 服务。    mAppOpsService.publish(mContext);    // 注册 system_server 内部接口。    LocalServices.addService(ActivityManagerInternal.class, new LocalService());// 结束 AMS 启动方法。}

下面这段代码在 PowerManagerService 启动后执行。它初始化栈和电池统计的电源管理能力,并创建语音场景使用的部分唤醒锁。

java
// 初始化 AMS 依赖的电源管理对象。public void initPowerManagement() {    // 初始化 ActivityStackSupervisor 的电源状态。    mStackSupervisor.initPowerManagement();    // 初始化 BatteryStatsService 的电源状态。    mBatteryStatsService.initPowerManagement();    // 取得 system_server 内部的 PowerManager 接口。    mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);    // 从 Context 取得公开 PowerManager。    PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);    // 创建语音功能使用的部分唤醒锁。    mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");    // 关闭该唤醒锁的引用计数。    mVoiceWakeLock.setReferenceCounted(false);// 结束电源管理初始化。}

四、AMS 的收尾:systemReady 拉起桌面和 SystemUI

4.1 ready 回调为什么位于启动尾部

startOtherServices() 已经建立窗口、输入等服务后,才调用 mActivityManagerService.systemReady(runnable)。AMS 在 systemReady() 内整理进程和用户状态,并在启动初始应用前执行回调。回调把 SystemService 推进到 PHASE_ACTIVITY_MANAGER_READY,准备 WebView,再启动 SystemUI。过早调用会让第三方代码或初始应用面对尚未 ready 的依赖服务。

startOtherServices 收尾:systemReady 与 SystemUI 启动时序

SystemServer 把收尾任务交给 AMS,AMS 达到允许初始应用启动的状态后回调 SystemServer。

java
// 告知 AMS 系统服务已接近可运行状态。mActivityManagerService.systemReady(new Runnable() {    // 声明 ready 回调入口。    @Override    // 执行 SystemServer 的剩余准备工作。    public void run() {        // 将 SystemService 推进到 AMS ready 阶段。        mSystemServiceManager.startBootPhase(                // 使用 Activity Manager Ready 阶段常量。                SystemService.PHASE_ACTIVITY_MANAGER_READY);        // 在 system_server 中准备 WebView。        WebViewFactory.prepareWebViewInSystemServer();        // 启动 SystemUI 服务。        startSystemUi(context);    // 结束 ready 回调。    }// 结束 systemReady 调用。});

4.2 SystemUI 的显式启动

下面这段代码用显式 ComponentName 指定 com.android.systemui.SystemUIService,并以 user 0 的 UserHandle.OWNER 身份调用 startServiceAsUser()。状态栏、导航栏、通知界面、近期任务界面和锁屏相关界面属于 SystemUI 在该版本中的主要界面职责;具体模块会随 Android 版本调整。

java
// 定义 SystemUI 启动辅助方法。static final void startSystemUi(Context context) {    // 创建显式服务 Intent。    Intent intent = new Intent();    // 指定 SystemUI 包名与服务类名。    intent.setComponent(new ComponentName("com.android.systemui",            // 填入 SystemUIService 的完整类名。            "com.android.systemui.SystemUIService"));    // 以 user 0 身份启动服务。    context.startServiceAsUser(intent, UserHandle.OWNER);// 结束 SystemUI 启动方法。}

SystemUI 的功能与启动路径

4.3 HOME Intent 如何变成桌面 Activity

mBooting 进入启动状态后,AMS 发起 HOME Activity 启动。

java
// 标记系统仍在启动阶段。mBooting = true;// 为当前用户启动 HOME Activity。startHomeActivityLocked(mCurrentUserId, "systemReady");

下面这段代码连接了 AMS 与 PMS。getHomeIntent() 加入 CATEGORY_HOME;resolveActivityInfo() 经 AppGlobals.getPackageManager() 查询 PMS,得到 ActivityInfo 后再交给 ActivityStackSupervisor。查询目标来自 PMS 已建立的数据结构。

java
// 构造用于寻找桌面的 Intent。Intent getHomeIntent() {    // 使用顶部 action 和 data 创建 Intent。    Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);    // 应用可能预设的顶部组件。    intent.setComponent(mTopComponent);    // 非低级工厂测试模式才寻找普通桌面。    if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {        // 加入 HOME 类别。        intent.addCategory(Intent.CATEGORY_HOME);    // 结束模式判断。    }    // 返回 HOME Intent。    return intent;// 结束 Intent 构造方法。}// 为指定用户启动桌面。boolean startHomeActivityLocked(int userId, String reason) {    // 低级工厂测试且未指定顶部 action 时不启动桌面。    if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL            // 同时检查顶部 action 是否为空。            && mTopAction == null) {        // 返回未启动状态。        return false;    // 结束工厂测试判断。    }    // 取得 HOME Intent。    Intent intent = getHomeIntent();    // 通过包管理接口解析目标 ActivityInfo。    ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);    // 找到目标组件后进入启动分支。    if (aInfo != null) {        // 把解析结果写成显式组件。        intent.setComponent(new ComponentName(                // 使用目标应用包名与 Activity 类名。                aInfo.applicationInfo.packageName, aInfo.name));        // 复制 ActivityInfo,避免修改 PMS 返回的共享对象。        aInfo = new ActivityInfo(aInfo);        // 将 ApplicationInfo 调整到目标用户。        aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);        // 查找目标应用的现有进程记录。        ProcessRecord app = getProcessRecordLocked(aInfo.processName,                // 使用目标应用 UID,并允许保留记录。                aInfo.applicationInfo.uid, true);        // 未运行或未被 instrumentation 接管时启动桌面。        if (app == null || app.instrumentationClass == null) {            // 为启动任务添加新任务栈标志。            intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);            // 交给栈管理器启动 HOME Activity。            mStackSupervisor.startHomeActivity(intent, aInfo, reason);        // 结束 instrumentation 状态判断。        }    // 结束目标存在判断。    }    // 表示 HOME 启动请求已处理。    return true;// 结束 HOME 启动方法。}

五、PMS 是怎么创建出来的

5.1 Settings 建立持久化边界

PMS 的 main() 调用构造函数时,大量准备工作已经在 ServiceManager.addService("package", m) 之前完成。下面的代码展示 Settings 的文件边界:packages.xml 保存包设置,packages-backup.xml 用于备份恢复,packages.list 提供包与 UID 等列表信息;packages-stopped.xml 及备份文件在该版本中用于迁移兼容。

Settings 在 /data/system 下建立的持久化文件

java
// Settings 构造器接收数据目录与共享锁。Settings(File dataDir, Object lock) {    // 保存并发访问使用的锁。    mLock = lock;    // 建立 /data/system 对应对象。    mSystemDir = new File(dataDir, "system");    // 创建目录及缺失的父目录。    mSystemDir.mkdirs();    // 指向主要包设置文件。    mSettingsFilename = new File(mSystemDir, "packages.xml");    // 指向包设置备份文件。    mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");    // 指向包列表文件。    mPackageListFilename = new File(mSystemDir, "packages.list");    // 设置包列表文件的权限与属主属组。    FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);    // 指向迁移所需的 stopped 状态文件。    mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");    // 指向 stopped 状态备份文件。    mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");// 结束 Settings 构造器。}

5.2 共享 UID 与 PMS 初始化

PackageManagerService.main() 与六个共享 UID

下面的代码在包扫描前把六组系统共享 UID 写入 Settings。解析带 android:sharedUserId 的包时,PMS 才能将它关联到已有的共享用户。

sharedUserId UID
android.uid.system 1000
android.uid.phone 1001
android.uid.bluetooth 1002
android.uid.log 1007
android.uid.nfc 1027
android.uid.shell 2000
java
// 注册 system 共享用户。mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,        // 系统共享用户同时具有 privileged 标志。        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 phone 共享用户。mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,        // phone 共享用户同时具有 privileged 标志。        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 log 共享用户。mSettings.addSharedUserLPw("android.uid.log", LOG_UID,        // log 共享用户同时具有 privileged 标志。        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 nfc 共享用户。mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,        // nfc 共享用户同时具有 privileged 标志。        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 bluetooth 共享用户。mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,        // bluetooth 共享用户同时具有 privileged 标志。        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 注册 shell 共享用户。mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,        // shell 共享用户同时具有 privileged 标志。        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

5.3 dexopt、工作线程与目录

下面的代码中,mLazyDexOpt 决定 dexopt 的 LRU 时间窗:为真时是 30 分钟,否则是 7 天。阈值算完、SystemConfig 读完之后,构造函数才建立 ServiceThread 与 PackageHandler,并把 PackageHandler 交给 Watchdog 监控;紧接着建立 /data/app、/data/app-private 等路径字段。/data/app-private 对应 mDrmAppPrivateInstallDir,不能写成其他字段名。

dexopt 的 LRU 阈值与 framework 目录优化流程

java
// 声明 dexopt 使用的分钟阈值。long dexOptLRUThresholdInMinutes;// 懒执行 dexopt 时使用较短时间窗。if (mLazyDexOpt) {    // eng 场景取最近 30 分钟。    dexOptLRUThresholdInMinutes = 30;// 普通分支使用七天时间窗。} else {    // 将七天换算为分钟。    dexOptLRUThresholdInMinutes = 7 * 24 * 60;// 结束阈值分支。}// 将分钟换算为毫秒并保存。mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;// 取得 /data 目录。File dataDir = Environment.getDataDirectory();// 建立第三方 APK 安装目录字段。mAppInstallDir = new File(dataDir, "app");// 建立前向锁定 APK 目录字段。mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

构造函数还会处理 /system/framework 的 dex 优化,并依次扫描 /system/framework、/system/priv-app、/system/app、/vendor/app 与数据分区中的应用目录。不同目录附带的 parse/scan flags 不同,决定系统包、privileged 包、vendor 包及 forward-lock 包的处理方式。

PMS 启动时扫描的目录清单

下面这段代码是普通第三方包进入扫描链的入口。mOnlyCore 为真时跳过这两次数据目录扫描;这适用于系统可用包范围已经受加密状态约束的启动场景,并不表示安装目录永久固定。

java
// 非 core-only 模式才扫描数据分区应用。if (!mOnlyCore) {    // 记录 PMS 数据扫描开始事件。    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,            // 写入开机以来的运行时间。            SystemClock.uptimeMillis());    // 扫描 /data/app 中的普通第三方包。    scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);    // 扫描 /data/app-private 中的前向锁定包。    scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,            // 保留已知包约束并传入当前时间参数。            scanFlags | SCAN_REQUIRE_KNOWN, 0);// 结束数据目录扫描条件。}

六、扫描:scanDirLI 与 scanPackageLI

6.1 scanDirLI 的筛选与失败处理

scanDirLI 的判定与失败处理

下面这段代码先读取目录。空目录立即返回;循环中只把 APK 或目录视为候选包,并过滤安装暂存名。普通文件命中 continue 后仍保留在原处,因为它没有进入包解析,也没有构成 INSTALL_FAILED_INVALID_APK。删除逻辑服务于“候选用户包解析后被判定为无效 APK”的情况,目录由 Installer 调用 installd 删除,单文件则调用 File.delete()。

java
// 扫描给定目录中的候选包。private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {    // 读取目录条目。    final File[] files = dir.listFiles();    // 空目录无需继续处理。    if (ArrayUtils.isEmpty(files)) {        // 记录空目录信息。        Log.d(TAG, "No files in app dir " + dir);        // 返回调用方。        return;    // 结束空目录判断。    }    // 遍历目录条目。    for (File file : files) {        // APK 或目录且名称不是 stage 时才是候选包。        final boolean isPackage = (isApkFile(file) || file.isDirectory())                // 过滤安装暂存目录名。                && !PackageInstallerService.isStageName(file.getName());        // 普通文件直接跳过。        if (!isPackage) {            // 处理下一个目录条目。            continue;        // 结束候选包判断。        }        // 捕获单个候选包的解析异常。        try {            // 要求输入必须符合 APK 包格式。            scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,                    // 传入扫描标志与时间。                    scanFlags, currentTime, null);        // 接收包管理异常。        } catch (PackageManagerException e) {            // 非系统无效 APK 才进入删除分支。            if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&                    // 同时检查 INVALID_APK 错误码。                    e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {                // 目录包交给 Installer 删除。                if (file.isDirectory()) {                    // 通过 installd 删除包目录。                    mInstaller.rmPackageDir(file.getAbsolutePath());                // 单文件走 File API。                } else {                    // 删除解析失败的 APK 文件。                    file.delete();                // 结束文件类型判断。                }            // 结束无效用户包判断。            }        // 结束异常处理。        }    // 结束目录遍历。    }// 结束目录扫描方法。}

6.2 scanPackageLI 进入解析器

下面这段代码为每次扫描新建 PackageParser,注入进程拆分、core-only 与显示参数。parsePackage() 的输出是 PackageParser.Package;异常被转换为 PMS 使用的 PackageManagerException。后续签名校验、Settings 关联和扫描结果登记不在本段摘录内。

java
// 扫描单个包文件或目录。private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,        // 接收扫描时间和目标用户。        long currentTime, UserHandle user) throws PackageManagerException {    // 合并 PMS 默认解析标志。    parseFlags |= mDefParseFlags;    // 为本次包扫描创建解析器。    PackageParser pp = new PackageParser();    // 传入独立进程配置。    pp.setSeparateProcesses(mSeparateProcesses);    // 传入 core-only 限制。    pp.setOnlyCoreApps(mOnlyCore);    // 传入显示参数供资源解析使用。    pp.setDisplayMetrics(mMetrics);    // 声明解析结果对象。    final PackageParser.Package pkg;    // 捕获底层解析异常。    try {        // 解析候选包并生成 Package。        pkg = pp.parsePackage(scanFile, parseFlags);    // 接收 PackageParser 异常。    } catch (PackageParserException e) {        // 转换为 PMS 统一异常类型。        throw PackageManagerException.from(e);    // 结束异常处理。    }    // 省略:签名校验、Settings 查找与扫描结果登记。

七、解析 AndroidManifest.xml

7.1 单 APK 与拆分包

/data/app 中既可能出现单个 APK,也可能出现装有 base.apk 和 split APK 的目录。下面的代码根据 File.isDirectory() 分流,目录进入 parseClusterPackage(),单文件进入 parseMonolithicPackage()。

从 /data/app 到 AndroidManifest.xml 解析

java
// 根据输入形态选择包解析路径。public Package parsePackage(File packageFile, int flags) throws PackageParserException {    // 目录表示 cluster 包布局。    if (packageFile.isDirectory()) {        // 解析 base 与 split 组成的包目录。        return parseClusterPackage(packageFile, flags);    // 单文件走 monolithic 路径。    } else {        // 解析单 APK 文件。        return parseMonolithicPackage(packageFile, flags);    // 结束输入形态判断。    }// 结束包解析入口。}

7.2 APK 如何进入 AssetManager

下面这段代码先检查 PARSE_MUST_BE_APK,再调用 AssetManager.addAssetPath()。返回的 cookie 标识 AssetManager 中的资源路径,后续打开清单 XML 时仍要使用它。cookie == 0 表示资源路径加载失败。

java
// 将 APK 资源路径加入 AssetManager。private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)        // 声明可能抛出的解析异常。        throws PackageParserException {    // 强制 APK 模式下校验路径后缀。    if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) {        // 抛出非 APK 错误。        throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,                // 附带无效路径。                "Invalid package file: " + apkPath);    // 结束路径校验。    }    // 把 APK 加入资源路径并取得 cookie。    int cookie = assets.addAssetPath(apkPath);    // cookie 为零表示加入失败。    if (cookie == 0) {        // 抛出清单加载错误。        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,                // 附带加载失败路径。                "Failed adding asset path: " + apkPath);    // 结束 cookie 校验。    }    // 返回资源路径 cookie。    return cookie;// 结束资源加载方法。}

从 APK 到 XML 解析器的调用链

下面这段代码创建 Resources,设置资源配置,再通过 openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME) 打开 APK 内的 AndroidManifest.xml。解析成功后,PMS 为 Package 补充卷标识和基础代码路径;finally 关闭 parser。

java
// 解析 base APK 文件。private Package parseBaseApk(File apkFile, AssetManager assets, int flags)        // 声明解析异常。        throws PackageParserException {    // 取得 APK 绝对路径。    final String apkPath = apkFile.getAbsolutePath();    // 声明可扩展存储的卷标识。    String volumeUuid = null;    // /mnt/expand/ 前缀表示 APK 位于可扩展存储卷。    if (apkPath.startsWith(MNT_EXPAND)) {        // 找卷 UUID 在路径中的结束位置。        final int end = apkPath.indexOf('/', MNT_EXPAND.length());        // 截出这一段卷 UUID。        volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);    }    // 把 APK 加入 AssetManager。    final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);    // 声明资源对象。    Resources res = null;    // 声明 XML 解析器。    XmlResourceParser parser = null;    // 捕获资源和 XML 解析异常。    try {        // 基于 AssetManager 创建 Resources。        res = new Resources(assets, mMetrics, null);        // 打开 APK 内的 AndroidManifest.xml。        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);        // 保存解析错误文本。        final String[] outError = new String[1];        // 进入基于 Resources 与 parser 的重载。        final Package pkg = parseBaseApk(res, parser, flags, outError);        // 返回空表示清单内容不合法。        if (pkg == null) {            // 把错误码与解析位置组成异常信息。            throw new PackageParserException(mParseError,                    apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);        }        // 记录 APK 所属的存储卷。        pkg.volumeUuid = volumeUuid;        // 写入 APK 的基础代码路径。        pkg.baseCodePath = apkPath;        // 签名留给后续的证书校验步骤填写。        pkg.mSignatures = null;        // 返回完整度更高的 Package 对象。        return pkg;    // 省略:PackageParserException 原样抛出,其余异常包装成 UNEXPECTED_EXCEPTION 再抛。    // 无论成功失败都执行资源清理。    } finally {        // 安静地关闭 XML 解析器。        IoUtils.closeQuietly(parser);    // 结束资源清理。    }// 结束 base APK 文件解析。}

7.3 Manifest 属性节点

下面这段代码进入 XML 内容解析后,先取得 <manifest> 的 package/split 名称。base APK 不接受非空 splitName;随后创建 Package(pkgName),读取 versionCode、revisionCode、versionName 与 sharedUserId。刚执行 new Package(pkgName) 时,其余组件字段还没有填充。

java
// 解析 base APK 的 manifest 内容。private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,        // 接收错误文本数组。        String[] outError) throws XmlPullParserException, IOException {    // 将 parser 作为属性集合使用。    AttributeSet attrs = parser;    // 声明包名。    final String pkgName;    // 声明 split 名称。    final String splitName;    // 捕获包名与 split 名称的解析异常。    try {        // 调用根标签名称解析。        Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);        // 取得 package 名称。        pkgName = packageSplit.first;        // 取得 split 名称。        splitName = packageSplit.second;    // 名称不合法时记下错误码并放弃这个包。    } catch (PackageParserException e) {        // 标记成坏包名错误。        mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;        // 返回空结果。        return null;    // 结束名称解析异常处理。    }    // base APK 不允许出现 split 名称。    if (!TextUtils.isEmpty(splitName)) {        // 写入可诊断错误。        outError[0] = "Expected base APK, but found split " + splitName;        // 返回空结果。        return null;    // 结束 split 校验。    }    // 用包名创建 Package 模型。    final Package pkg = new Package(pkgName);    // 取得 manifest 属性数组。    TypedArray sa = res.obtainAttributes(attrs,            // 使用内部 manifest styleable。            com.android.internal.R.styleable.AndroidManifest);    // 同时写入 Package 与 ApplicationInfo 的 versionCode。    pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(            // 缺省 versionCode 为零。            com.android.internal.R.styleable.AndroidManifest_versionCode, 0);    // 读取 base APK 的 revisionCode。    pkg.baseRevisionCode = sa.getInteger(            // 缺省 revisionCode 为零。            com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);    // 读取 versionName。    pkg.mVersionName = sa.getNonConfigurationString(            // 缺省值参数为零。            com.android.internal.R.styleable.AndroidManifest_versionName, 0);    // 读取可选 sharedUserId。    String str = sa.getNonConfigurationString(            // 使用 manifest 的 sharedUserId 属性。            com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);    // 省略:sharedUserId 的合法性校验分支、installLocation、coreApp,    // 以及 application、四大组件与 meta-data 的分发。

下面这段代码校验根标签必须为 <manifest>。android 包名跳过常规合法性校验,其他包名调用 validateName();仅当返回错误文本时才抛异常。if (!"android".equals(packageName)) 的含义是“包名不为 android 时执行合法性校验”,不能理解为条件成立就直接抛异常。

java
// 从 manifest 根标签解析 package 与 split 名称。private static Pair<String, String> parsePackageSplitNames(XmlPullParser parser,        // 接收属性与解析标志。        AttributeSet attrs, int flags) throws IOException, XmlPullParserException,        // 声明包解析异常。        PackageParserException {    // 根标签必须名为 manifest。    if (!parser.getName().equals("manifest")) {        // 根标签错误时抛出 malformed 异常。        throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,                // 写明缺少 manifest 标签。                "No <manifest> tag");    // 结束根标签校验。    }    // 读取 package 属性。    final String packageName = attrs.getAttributeValue(null, "package");    // android 以外的包名才执行常规合法性校验。    if (!"android".equals(packageName)) {        // 校验 Java 风格包名。        final String error = validateName(packageName, true, true);        // 有错误文本时拒绝该包名。        if (error != null) {            // 抛出坏包名异常。            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,                    // 附带校验错误文本。                    "Invalid manifest package: " + error);        // 结束错误判断。        }    // 结束特殊包名判断。    }    // 读取可选 split 属性。    String splitName = attrs.getAttributeValue(null, "split");    // 返回驻留字符串组成的名称对。    return Pair.create(packageName.intern(),            // split 为空时原样返回空值。            (splitName != null) ? splitName.intern() : splitName);// 结束名称解析。}

7.4 application 与组件公共字段

下面这段代码将 <application android:name> 交给 buildClassName(),相对类名会结合包名转换成完整类名,并写入 ApplicationInfo.className。应用进程创建 Application 时,ActivityThread 使用该信息寻找具体类。

java
// 解析 application 标签。private boolean parseBaseApplication(Package owner, Resources res,        // 接收 XML、属性、标志和错误数组。        XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)        // 声明 XML 相关异常。        throws XmlPullParserException, IOException {    // 取得 Package 持有的 ApplicationInfo。    final ApplicationInfo ai = owner.applicationInfo;    // 保存应用包名。    final String pkgName = owner.applicationInfo.packageName;    // 取得 application 属性数组。    TypedArray sa = res.obtainAttributes(attrs,            // 使用内部 application styleable。            com.android.internal.R.styleable.AndroidManifestApplication);    // 读取 android:name。    String name = sa.getNonConfigurationString(            // 指定 Application_name 属性。            com.android.internal.R.styleable.AndroidManifestApplication_name, 0);    // 声明了类名才做拼接。    if (name != null) {        // 转成完整 Application 类名。        ai.className = buildClassName(pkgName, name, outError);    // 结束类名处理。    }    // 省略:类名失败处理、其他属性与四大组件入口。

parseBaseApplication() 遍历 <application> 的子标签时按标签名分发,四个分支分别落到 parseActivity()、parseService()、parseProvider(),解析结果收进 Package 的四个列表。下面这段接在上一段代码之后。

java
// 读取当前子标签名,后续按名称分发。String tagName = parser.getName();// <activity> 走 parseActivity 的 activity 分支。if (tagName.equals("activity")) {    // 最后一个参数继承 application 层的硬件加速设置。    Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,            owner.baseHardwareAccelerated);    // 单个组件解析失败就终止整个包的解析。    if (a == null) {        // 标记清单格式错误。        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;        // 返回失败。        return false;    }    // 收进 Package 的 activities 列表。    owner.activities.add(a);// <receiver> 复用 parseActivity,只是 receiver 参数为 true。} else if (tagName.equals("receiver")) {    // 以 receiver 模式解析。    Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);    // 单个组件解析失败就终止整个包的解析。    if (a == null) {        // 标记清单格式错误。        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;        // 返回失败。        return false;    }    // 收进 Package 的 receivers 列表。    owner.receivers.add(a);// <service> 走 parseService。} else if (tagName.equals("service")) {    // 解析 service 标签。    Service s = parseService(owner, res, parser, attrs, flags, outError);    // 单个组件解析失败就终止整个包的解析。    if (s == null) {        // 标记清单格式错误。        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;        // 返回失败。        return false;    }    // 收进 Package 的 services 列表。    owner.services.add(s);// <provider> 走 parseProvider。} else if (tagName.equals("provider")) {    // 解析 provider 标签。    Provider p = parseProvider(owner, res, parser, attrs, flags, outError);    // 单个组件解析失败就终止整个包的解析。    if (p == null) {        // 标记清单格式错误。        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;        // 返回失败。        return false;    }    // 收进 Package 的 providers 列表。    owner.providers.add(p);// 其余标签走后面的分支。} else if (tagName.equals("activity-alias")) {    // 省略:activity-alias、library、uses-library 与 meta-data 的分支。}

Package 对象的字段来源

下面这段代码是不同组件共用的名称、图标和标签解析逻辑。组件必须声明 android:name,.MainActivity 这类相对名称在此补全;PackageItemInfo.packageName 最终指回所属包。

java
// 解析组件共有的 PackageItemInfo 字段。private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,        // 接收错误、标签、属性和资源索引。        String[] outError, String tag, TypedArray sa,        // 接收名称、标签、图标、徽标与横幅索引。        int nameRes, int labelRes, int iconRes, int logoRes, int bannerRes) {    // 读取组件 android:name。    String name = sa.getNonConfigurationString(nameRes, 0);    // 组件名缺失时解析失败。    if (name == null) {        // 写入缺少名称的错误。        outError[0] = tag + " does not specify android:name";        // 返回失败。        return false;    // 结束名称缺失判断。    }    // 将相对类名补全为完整名称。    outInfo.name = buildClassName(owner.applicationInfo.packageName, name, outError);    // 补全失败同样判定为解析失败。    if (outInfo.name == null) {        // 返回失败。        return false;    // 结束类名补全判断。    }    // 读取组件图标资源 id。    int iconVal = sa.getResourceId(iconRes, 0);    // 声明了图标就记下来。    if (iconVal != 0) {        // 写入图标资源 id。        outInfo.icon = iconVal;        // 图标优先,清掉可能存在的字面标签。        outInfo.nonLocalizedLabel = null;    }    // 读取组件 logo 资源 id。    int logoVal = sa.getResourceId(logoRes, 0);    // 声明了 logo 就记下来。    if (logoVal != 0) {        // 写入 logo 资源 id。        outInfo.logo = logoVal;    }    // 读取组件 banner 资源 id。    int bannerVal = sa.getResourceId(bannerRes, 0);    // 声明了 banner 就记下来。    if (bannerVal != 0) {        // 写入 banner 资源 id。        outInfo.banner = bannerVal;    }    // 读取标签,它可能是字面字符串也可能是资源引用。    TypedValue v = sa.peekValue(labelRes);    // 标签是字面字符串时转成非本地化标签。    if (v != null && (outInfo.labelRes=v.resourceId) == 0) {        // 记录字面标签文本。        outInfo.nonLocalizedLabel = v.coerceToString();    }    // 写入组件所属包名。    outInfo.packageName = owner.packageName;    // 返回公共字段解析结果。    return true;// 结束公共字段解析方法。}

7.5 Activity、launchMode 与 taskAffinity

下面这段代码中的 Activity 是 PackageParser.Activity 静态内部模型,不是运行时的 android.app.Activity 实例。它封装 ActivityInfo 和解析出的 intent 列表。receiver 共用部分解析代码,但其 launchMode 固定为 LAUNCH_MULTIPLE。

java
// 解析 activity 或 receiver 标签。private Activity parseActivity(Package owner, Resources res,        // 接收 XML、属性、标志和错误数组。        XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,        // 接收组件类型与硬件加速缺省值。        boolean receiver, boolean hardwareAccelerated)        // 声明 XML 解析异常。        throws XmlPullParserException, IOException {    // 取得 activity 属性数组。    TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestActivity);    // 首次解析组件时创建公共参数对象。    if (mParseActivityArgs == null) {        // 传入包、错误数组与各公共属性索引。        mParseActivityArgs = new ParseComponentArgs(owner, outError,                // 传入组件名称属性索引。                R.styleable.AndroidManifestActivity_name,                // 传入组件标签属性索引。                R.styleable.AndroidManifestActivity_label,                // 传入组件图标属性索引。                R.styleable.AndroidManifestActivity_icon,                // 传入组件徽标属性索引。                R.styleable.AndroidManifestActivity_logo,                // 传入组件横幅属性索引。                R.styleable.AndroidManifestActivity_banner,                // 传入独立进程配置。                mSeparateProcesses,                // 传入组件进程属性索引。                R.styleable.AndroidManifestActivity_process,                // 传入组件描述属性索引。                R.styleable.AndroidManifestActivity_description,                // 传入组件启用状态属性索引。                R.styleable.AndroidManifestActivity_enabled);    // 结束公共参数初始化。    }    // 根据解析对象类型设置标签名。    mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>";    // 将本次属性数组写入公共参数。    mParseActivityArgs.sa = sa;    // 将解析标志写入公共参数。    mParseActivityArgs.flags = flags;    // 创建清单层的 Activity 模型。    Activity a = new Activity(mParseActivityArgs, new ActivityInfo());    // 公共字段解析失败时停止处理。    if (outError[0] != null) {        // 回收 TypedArray。        sa.recycle();        // 返回空结果。        return null;    // 结束公共字段错误判断。    }    // activity 分支读取 launchMode。    if (!receiver) {        // 缺省值对应 standard 模式。        a.info.launchMode = sa.getInt(                // 读取 AndroidManifestActivity_launchMode。                R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);    // receiver 不具备 Activity 启动模式。    } else {        // 将 receiver 的字段固定为 LAUNCH_MULTIPLE。        a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;    // 结束组件类型分支。    }    // 省略:其余 Activity 属性与子标签解析。

在 Android 6.0.0_r1 中,ActivityInfo 里定义的是 LAUNCH_MULTIPLE=0、LAUNCH_SINGLE_TOP=1、LAUNCH_SINGLE_TASK=2、LAUNCH_SINGLE_INSTANCE=3。下面这段代码展示 Activity 的 taskAffinity 继承规则:Activity 没写该属性时,buildTaskAffinityName() 接收 Application 层的默认值;Application 也没有单独指定时,其默认值与包名相关。

java
// 读取 Activity 的 taskAffinity 属性。str = sa.getNonConfigurationString(        // 使用对应 styleable 索引。        R.styleable.AndroidManifestActivity_taskAffinity,        // 使用 Android 6.0 源码中的配置版本参数。        Configuration.NATIVE_CONFIG_VERSION);// 结合包名、Application 默认值和组件值生成最终 affinity。a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,        // 将 Application 默认值、Activity 声明值与错误数组传入。        owner.applicationInfo.taskAffinity, str, outError);

四大组件在 parseBaseApplication 中的分发

下面这段代码先处理 <intent-filter> 标签本身:新建 ActivityIntentInfo,再把它交给 parseIntent() 填充。过滤结果只有在至少有一个 action 时才进入 Activity 的 intents 列表,action 为空的 filter 只打一条警告。同一段分支还顺带处理了 <preferred> 与 <meta-data>。

java
// intent-filter 子标签进入过滤器解析分支。if (parser.getName().equals("intent-filter")) {    // 为当前组件新建一个过滤器模型。    ActivityIntentInfo intent = new ActivityIntentInfo(a);    // 填充 action、category 与 data。    if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {        // 过滤器解析失败,整个 activity 作废。        return null;    }    // 没有任何 action 的过滤器只记警告。    if (intent.countActions() == 0) {        // 输出警告并附带源码位置。        Slog.w(TAG, "No actions in intent filter at "                + mArchiveSourcePath + " "                + parser.getPositionDescription());    // 有效的过滤器才登记。    } else {        // 收进 Activity 的 intents 列表。        a.intents.add(intent);    }// receiver 之外还允许 preferred 过滤器。} else if (!receiver && parser.getName().equals("preferred")) {    // 省略:preferred 过滤器的处理分支。// meta-data 单独走自己的解析方法。} else if (parser.getName().equals("meta-data")) {    // 解析 meta-data 并挂到组件上。    if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,            outError)) == null) {        // meta-data 解析失败同样作废。        return null;    }}

过滤器内部的 action 与 category 由 parseIntent() 逐个读取,字符串分别进入 ActivityIntentInfo 的两个集合。

java
// 读取 intent-filter 的当前子标签名。String nodeName = parser.getName();// action 标签进入动作分支。if (nodeName.equals("action")) {    // 读取 android:name 属性。    String value = attrs.getAttributeValue(ANDROID_RESOURCES, "name");    // 拒绝缺少名称或名称为空的 action。    if (value == null || value == "") {        // 写入缺少 android:name 的错误。        outError[0] = "No value supplied for <android:name>";        // 返回解析失败。        return false;    // 结束 action 名称校验。    }    // 跳过当前 XML 标签余下内容。    XmlUtils.skipCurrentTag(parser);    // 将动作字符串加入 filter。    outInfo.addAction(value);// category 标签进入类别分支。} else if (nodeName.equals("category")) {    // 读取 android:name 属性。    String value = attrs.getAttributeValue(ANDROID_RESOURCES, "name");    // 拒绝缺少名称或名称为空的 category。    if (value == null || value == "") {        // 写入缺少 android:name 的错误。        outError[0] = "No value supplied for <android:name>";        // 返回解析失败。        return false;    // 结束 category 名称校验。    }    // 跳过当前 XML 标签余下内容。    XmlUtils.skipCurrentTag(parser);    // 将类别字符串加入 filter。    outInfo.addCategory(value);// data 标签进入数据匹配分支。} else if (nodeName.equals("data")) {    // 省略:mimeType、scheme、host 与 port 的原始解析分支。

7.6 manifest示例解析

PMS 读到 package="com.example.myapplication" 后创建 Package;android:name="BaseApplication" 进入 application 类名解析;.MainActivity 经公共组件逻辑补全类名,singleTask 写入 ActivityInfo.launchMode,MAIN/LAUNCHER 则进入 ActivityIntentInfo。

xml
<!-- 声明 XML 版本与字符编码。 --><?xml version="1.0" encoding="utf-8"?><!-- 声明 Android 命名空间与应用包名。 --><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication">    <!-- 声明读取外部存储权限。 -->    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />    <!-- 声明 Application 及素材中出现的基础属性。 -->    <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:name="BaseApplication" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">        <!-- 声明使用相对类名且启动模式为 singleTask 的主 Activity。 -->        <activity android:name=".MainActivity" android:launchMode="singleTask">            <!-- 声明入口 Intent 过滤器。 -->            <intent-filter>                <!-- 声明 MAIN 动作。 -->                <action android:name="android.intent.action.MAIN" />                <!-- 声明 LAUNCHER 类别。 -->                <category android:name="android.intent.category.LAUNCHER" />            <!-- 结束入口过滤器。 -->            </intent-filter>        <!-- 结束 Activity 声明。 -->        </activity>    <!-- 结束 Application 声明。 -->    </application><!-- 结束 manifest。 --></manifest>

示例中的 BaseApplication 只覆盖 onCreate() 与 attachBaseContext()。

java
// 声明示例包名。package com.example.myapplication;// 导入 Application 基类。import android.app.Application;// 导入 Context 类型。import android.content.Context;// 定义自定义 Application。public class BaseApplication extends Application {    // 覆盖 Application.onCreate。    @Override    // 接收应用创建回调。    public void onCreate() {        // 调用父类实现。        super.onCreate();    // 结束创建回调。    }    // 覆盖基础 Context 附加方法。    @Override    // 接收基础 Context。    protected void attachBaseContext(Context base) {        // 调用父类实现。        super.attachBaseContext(base);    // 结束 Context 附加方法。    }// 结束自定义 Application。}

八、为什么需要缓存:AMS 与 PMS 的分工

AMS 与 PMS 的分工:ActivityInfo 的缓存与查询

整个过程可以归纳为一条跨服务链:PMS 在扫描期把清单转成 Package 及其中的 ApplicationInfo、Activity、Service、Receiver、Provider、permission 和 intent 信息;AMS 处理 Activity 启动时,经包管理 Binder 接口查询具体 ActivityInfo。Binder 是这里的进程通信接口边界,不过 Android 6.0 的 AMS 与 PMS 同处 system_server;PMS 还通过 Installer 与 installd 处理 dexopt、目录创建或删除等文件系统操作。

java
// 启动 Installer 并取得实例。Installer installer = mSystemServiceManager.startService(Installer.class);// 通过 Lifecycle 创建并取得 AMS 本体。mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();// 将 Installer 依赖交给 AMS。mActivityManagerService.setInstaller(installer);// AMS ready 后发起桌面启动。startHomeActivityLocked(mCurrentUserId, "systemReady");// SystemServer 的 ready 回调启动 SystemUI。startSystemUi(context);// PMS 扫描普通第三方包安装目录。scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);// 单包扫描进入 PackageParser。pkg = pp.parsePackage(scanFile, parseFlags);

上段代码分散在不同方法中,用于对应素材总结中的节点,不表示它们在同一个方法内连续执行。HOME 路径可直接观察同一分工:resolveActivityInfo() 查询 PMS,mStackSupervisor.startHomeActivity() 使用返回的 ActivityInfo。

内存缓存让 startActivity 避开重复的 APK/XML 解析。Settings 管理的 packages.xml 等文件形成持久化边界,用于恢复已知包设置并辅助后续扫描;“下次开机完全不解析所有 APK”会超过本文代码片段能支持的范围。准确的说法是 PMS 会读取已有设置,并结合目录内容、文件状态与扫描策略更新内存包状态。

九、小结与动手验证

一个已安装 APK 从 /data/app 进入可启动状态,负责加工清单数据的是 PMS,时机主要位于 system_server 的引导服务阶段。PackageManagerService 构造期间扫描应用目录,scanDirLI() 过滤候选项,scanPackageLI() 创建 PackageParser;解析器把 AndroidManifest.xml 的 application、组件与 intent-filter 写入 Package 模型。AMS 在运行期接收启动请求并查询 PMS 返回的 ActivityInfo,随后由 Activity 栈管理链执行启动。

AMS 与 PMS 均由 startBootstrapServices() 创建。AMS 的 systemReady() 位于 startOtherServices() 尾部,负责发起 HOME Activity 启动;其回调推进 boot phase 并启动 SystemUI。这解释了包信息准备和界面拉起为何处在不同阶段。

可在具备相应权限的 Android 6.0 测试设备或模拟器上检查包管理状态:

shell
adb shell dumpsys package com.example.myapplicationadb shell su -c 'cat /data/system/packages.xml'adb logcat -b events | grep BOOT_PROGRESS_PMS

dumpsys package 用于观察 PMS 已缓存的包与组件信息;读取 /data/system/packages.xml 通常需要 root 或调试系统权限;events 日志中的 BOOT_PROGRESS_PMS_* 可定位 PMS 扫描阶段。


本文由 AgentWorkFlow 生成

觉得不错?给作者点个赞吧

分类:Android系统

评论 (0)

无需注册,留言经站长审核后公开。

加载评论中…

Android-Framework(十二)-AMS与PMS的启动过程 · Yinchen 的博客