PackageManagerService启动详解(八)之扫描data分区应用安装目录阶段流程分析

本文主要是介绍PackageManagerService启动详解(八)之扫描data分区应用安装目录阶段流程分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

PKMS启动详解(八)之BOOT_PROGRESS_PMS_DATA_SCAN_START阶段流程分析


Android PackageManagerService系列博客目录:

PKMS启动详解系列博客概要
PKMS启动详解(一)之整体流程分析
PKMS启动详解(二)之怎么通过packages.xml对已安装应用信息进行持久化管理?
PKMS启动详解(三)之BOOT_PROGRESS_PMS_START流程分析
PKMS启动详解(四)之Android包信息体和包解析器(上)
PKMS启动详解(五)之Android包信息体和包解析器(中)
PKMS启动详解(六)之Android包信息体和包解析器(下)
PKMS启动详解(七)之扫描系统应用安装目录阶段流程分析
PKMS启动详解(八)之BOOT_PROGRESS_PMS_DATA_SCAN_START阶段流程分析



本篇博客编写思路总结和关键点说明:

在这里插入图片描述

为了更加方便的读者阅读博客,通过导读思维图的形式将本博客的关键点列举出来,从而方便读者取舍和阅读!



引言

  欢迎回来继续PKMS之旅,在我们上一期的博客PKMS启动详解(七)之系统应用安装目录扫描阶段流程分析之中我们重点分析了系统应用安装目录扫描流程的相关相关核心步骤和事宜!其中伤害性不大,但是侮辱性极强的相关源码处理逻辑估计够读者喝了一壶的了!总之通过上篇的处理逻辑此时我们得到了如下的核心收获:

  • 通过扫描系统应用安装目录,得到目录下相关的应用对应的Packages信息

  • 将安装应用信息添加到PKMS中进行管理,同时将其对应的相关组件注册到PKMS中,以供后续第三方查询和使用

难道我们PKMS启动的千秋大业就此整个完结了吗,如果读者是这么认为那么就是太小瞧和不理解它了!虽然系统应用安装目录扫描结束了,但是还有data分区应用安装目录以及其它的相关处理逻辑依然还没有开始,那么在今天的博客中我们就将重点分析PKMS启动BOOT_PROGRESS_PMS_DATA_SCAN_START阶段的相关处理事宜。此时的读者是不是已经非常好奇PKMS是怎么扫描data分区应用安装目录的呢?不用着急,让我们一起来开启这段奇妙的探索之旅,相信经过我们的一起努力BOOT_PROGRESS_PMS_DATA_SCAN_START扫描data分区应用安装目录阶段会很轻松的拿下的。并且对于扫描data分区应用安装目录的阶段的整体流程,在本博客中我们将会分为两大部分来分析:

  • 扫描阶段
  • 扫描收尾阶段

    主要是处理date分区中扫描后:
    1.发现上次安装存在,但是此次扫描某些不存在的非系统应用
    2.处理覆盖升级的应用(譬如覆盖升级的应用不存在了,必须恢复位于系统分区的应用)
    等相关的逻辑处理流程!

好了不多说啥了,要得开干!

如果读者已经吃透了前面扫描系统应用安装目录的相关处理逻辑,那么这里对于处理data分区应用的扫描逻辑,那真的可以说的上是手到擒来毫不费工夫了。

因为处理的套路基本不变,处理的方法基本都一致,只是通过不同的参数走入了不同的分支而已。

并且此时读者是不是有一个好奇心,为啥我们的标题不叫扫描非系统应用安装目录呢,而是说data分区应用安装目录呢,这个先买个管子,留给读者自行体会!

注意:本篇的介绍是基于Android 7.xx平台为基础的(并且为了书写简便后续PackageManagerService统一简称为PKMS),其中涉及的代码路径如下:

--- frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.javaPackageSetting.java Settings.javaPackageInstallerService.java--- frameworks/base/core/java/android/content/pm/PackageParser.java--- frameworks/native/cmds/installd/install.cpp



一.开始data分区应用安装目录扫描

  这里我们可以看到PKMS对于data分区应用安装目录扫描的处理不似对于系统应用安装目录扫描的小心翼翼和前期准备(可能此时PKMS已经是情场老手,经验老道了),此时它是单刀直入直捣黄龙。我们来先来看下该阶段的整体流程概括:

// 【 PackageManagerService.java 】//设置扫描的参数final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;...//开始处理非系统应用if (!mOnlyCore) {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,SystemClock.uptimeMillis());// 扫描/data/app和/data/priv-appscanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags| PackageParser.PARSE_FORWARD_LOCK,scanFlags | SCAN_REQUIRE_KNOWN, 0);scanDirLI(mEphemeralInstallDir, mDefParseFlags| PackageParser.PARSE_IS_EPHEMERAL,scanFlags | SCAN_REQUIRE_KNOWN, 0);.../*** Remove disable package settings for any updated system* apps that were removed via an OTA. If they're not a* previously-updated app, remove them completely.* Otherwise, just revoke their system-level permissions.*/                 /* 进行最后的data分区扫描的收尾工作放在possiblyDeletedUpdatedSystemApps中的应用是在packge.xml中被标记成了待升级的系统应用但是文件却不存在了,因此这里检查用户目录下升级文件是否还存在,然后进行处理处理那些不存在的系统app!*/for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {PackageParser.Package deletedPkg = mPackages.get(deletedAppName);// 从mSettings.mDisabledSysPackages变量中移除去此应用mSettings.removeDisabledSystemPackageLPw(deletedAppName);String msg;if (deletedPkg == null) {// 用户目录中也没有升级包,则肯定是残留的应用信息,则把它的数据目录删除掉// 此时无任何扫描结果,表明这个系统中已经没有改apk了,那就删掉它msg = "Updated system package " + deletedAppName+ " no longer exists; it's data will be wiped";// Actual deletion of code and data will be handled by later// reconciliation step} else {msg = "Updated system app + " + deletedAppName+ " no longer present; removing system privileges for "+ deletedAppName;deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;}//报告系统发生了不一致的情况logCriticalInfo(Log.WARN, msg);}/*** Make sure all system apps that we expected to appear on* the userdata partition actually showed up. If they never* appeared, crawl back and revive the system version.*//**确保所有在用户data分区的应用都显示出来了,如果data分区的无法显示,就显示system分区的现在来处理mExpectingBetter列表,这个列表的应用是带有升级包的系统的应用,前面把他们从mPackages列表中清除了并放到mExpectingBetter列表最后也对它们进行扫描处理*/for (int i = 0; i < mExpectingBetter.size(); i++) {final String packageName = mExpectingBetter.keyAt(i);/* 如果PMS仍然没有扫描到mExpectingBetter列表中的apk,说明data分区的apk无法显示出现这种情况的原因,可能是由于OTA或者异常导致data分区的覆盖安装的应用已经丢失了那就要显示原来system分区的apk!*/if (!mPackages.containsKey(packageName)) {final File scanFile = mExpectingBetter.valueAt(i);logCriticalInfo(Log.WARN, "Expected better " + packageName+ " but never showed up; reverting to system");int reparseFlags = mDefParseFlags;//确保应用位于下面几个系统应用目录,如果不在,则不需要处理if (FileUtils.contains(privilegedAppDir, scanFile)) {reparseFlags = PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR| PackageParser.PARSE_IS_PRIVILEGED;} else if (FileUtils.contains(systemAppDir, scanFile)) {reparseFlags = PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR;} else if (FileUtils.contains(vendorAppDir, scanFile)) {reparseFlags = PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR;} else if (FileUtils.contains(oemAppDir, scanFile)) {reparseFlags = PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR;} else {Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);continue;}//现在把这个apk标示为系统应用,从mSettings.mDisabledSysPackages中删除,//因为在scanDirLI->scanPackageLI中会执行mSettings.disableSystemPackageLPw//所以此时包名的标签是只有<update-package>,执行到这步之后变成<package>标签,//在下面的scanPackageLI中又会添加一个<update-package>标签的mSettings.enableSystemPackageLPw(packageName);try {// 重新扫描一下这个文件,会添加一个<update-package>标签scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to parse original system package: "+ e.getMessage());}}}}//清空目录mExpectingBetter.clear();// Resolve the storage manager.// 获得存储管理对象!mStorageManagerPackage = getStorageManagerPackageName();// Resolve protected action filters. Only the setup wizard is allowed to// have a high priority filter for these actions.// 获得开机向导应用mSetupWizardPackage = getSetupWizardPackageName();if (mProtectedFilters.size() > 0) {if (DEBUG_FILTERS && mSetupWizardPackage == null) {Slog.i(TAG, "No setup wizard;"+ " All protected intents capped to priority 0");}for (ActivityIntentInfo filter : mProtectedFilters) {if (filter.activity.info.packageName.equals(mSetupWizardPackage)) {if (DEBUG_FILTERS) {Slog.i(TAG, "Found setup wizard;"+ " allow priority " + filter.getPriority() + ";"+ " package: " + filter.activity.info.packageName+ " activity: " + filter.activity.className+ " priority: " + filter.getPriority());}// skip setup wizard; allow it to keep the high priority filtercontinue;}Slog.w(TAG, "Protected action; cap priority to 0;"+ " package: " + filter.activity.info.packageName+ " activity: " + filter.activity.className+ " origPrio: " + filter.getPriority());filter.setPriority(0);}}mDeferProtectedFilters = false;mProtectedFilters.clear();// Now that we know all of the shared libraries, update all clients to have// the correct library paths.// 更新所有应用的动态库路径,保证他们有正确的共享库路径updateAllSharedLibrariesLPw();// 调整所有共享 uid 的 package 的指令集!for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {// NOTE: We ignore potential failures here during a system scan (like// the rest of the commands above) because there's precious little we// can do about it. A settings error is reported, though.adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,false /* boot complete */);}// Now that we know all the packages we are keeping,// read and update their last usage times.// 更新所有 package 的最新使用时间!mPackageUsage.read(mPackages);mCompilerStats.read();

在正式开始data分区相关应用安装目录扫描具体分析前,我们先来对于data分区可能存在的两种类型的apk来简单概括一下:

  • 一种就是最最普通的非系统apk
  • 另外一种是系统apk通过覆盖安装升级的方式,被安装到了data分区的特许的系统应用apk

好了有了对于上面两种的apk的了解,那么我们开始分析PKMS是怎么处理非系统应用的扫描的。并且这里需要特别注意的几点就是通过前面扫描系统应用安装目录的分区,我们得到了如下几个非常重要的集合:

  • mExpectingBetter:用来存放那些在data分区可能有更高版本的系统app!

    // 存放在系统应用安装目录可能存在覆盖升级包的系统应用相关信息,其中key是安装包包名,value是具体路径
    final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
    
  • possiblyDeletedUpdatedSystemApps:用来存储那些不存在的被更新过的系统app!

            // 删除任何不再存在的系统包,这类List表示的是有可能有升级包的系统应用final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
    

好了,我们接着往下开撸!在正式开撸前,我们先来看下扫描阶段的整体时序图:

在这里插入图片描述


1.1 扫描data分区非系统应用安装目录

好了,下面我们直接来看看PKMS对于data分区的扫描过程,如下:

// 【 PackageManagerService.java 】final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;// 同理,扫描 /data/app 目录和 /data/app-private 目录!scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags| PackageParser.PARSE_FORWARD_LOCK,scanFlags | SCAN_REQUIRE_KNOWN, 0);scanDirLI(mEphemeralInstallDir, mDefParseFlags| PackageParser.PARSE_IS_EPHEMERAL,scanFlags | SCAN_REQUIRE_KNOWN, 0);

通过上述的源码我们可以看到在该阶段最最最重要的核心诉求点只有一个按照特定循序调用scanDirTracedLI方法扫描应用安装目录,被扫描的目录循序如下:

  • /data/app;

    • parseFlags:0
    • scanFlags:scanFlags | SCAN_REQUIRE_KNOWN
  • /data/app-private;
    - parseFlags:PackageParser.PARSE_FORWARD_LOCK

    • scanFlags:scanFlags | SCAN_REQUIRE_KNOWN
  • /data/app-ephemeral;

    • parseFlags:PackageParser.PARSE_IS_EPHEMERAL
    • scanFlags:scanFlags | SCAN_REQUIRE_KNOWN

这里的步骤和扫描system分区是一样的,但是因为只是parseFlags和scanFlags的取值有所不同,所以会涉及不同的逻辑,所以一定要有对比思想,看看它和系统应用安装目录扫描的区别和联系。这里我们以扫描/data/app目录为例来展开相关的说明!


1.2 PKMS.scanDirTracedLI(…)

从此方法开始开始真正开启扫描流程,从此天涯是路人!我们先来看下此方法:

// 【 PackageManagerService.java 】private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir");try {scanDirLI(dir, parseFlags, scanFlags, currentTime);// 详见章节 【 1.3 】} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}

可以看到此方法平淡无奇,只是接着调用另一个方法scanDirLI继续处理,但是我这里有必要对其中它的两个入参parseFlags和scanFlags捯饬捯饬一下:

  • parseFlags:此参数的取值表明的是对于被扫描目录的处理逻辑的flag值,影响的是扫描目录源码的分支走向

  • scanFlags:此参数的取值表明的是对于扫描目录中的应用安装包解析处理逻辑的flag值,影响的是对具体的安装包的解析逻辑,即最终PackageParser.parsePackagesparsePackages的处理逻辑

好了我们接着往下看!


1.3 PKMS.scanDirLI(…)

没有啥前提要交代的,继续接着干!

// 【 PackageManagerService.java 】private void scanDirLI(File dir, final 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;}if (DEBUG_PACKAGE_SCANNING) {Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags+ " flags=0x" + Integer.toHexString(parseFlags));}Log.d(TAG, "start scanDirLI:"+dir);// use multi thread to speed up scanning// 通过线程加速扫描int iMultitaskNum = SystemProperties.getInt("persist.pm.multitask", 6);Log.d(TAG, "max thread:" + iMultitaskNum);final MultiTaskDealer dealer = (iMultitaskNum > 1) ? MultiTaskDealer.startDealer(MultiTaskDealer.PACKAGEMANAGER_SCANER, iMultitaskNum) : null;for (File file : files) {// 判断file是否是Package,必须要同时满足下面2个条件// 1、file 以 ".apk" 结尾或者file是一个目录;// 2、file不是安装阶段临时的apk类型的文件;final boolean isPackage = (isApkFile(file) || file.isDirectory())&& !PackageInstallerService.isStageName(file.getName());/*********** 这里为了方便演示直接展开了 ****************************/public static boolean isStageName(String name) {final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");final boolean isLegacyContainer = name.startsWith("smdl2tmp");return isFile || isContainer || isLegacyContainer;}     /*********** 这里为了方便演示直接展开了 ****************************/      if (!isPackage) {continue;}if (RegionalizationEnvironment.isSupported()) {if (RegionalizationEnvironment.isExcludedApp(file.getName())) {Slog.d(TAG, "Regionalization Excluded:" + file.getName());continue;}}final File ref_file = file;final int ref_parseFlags = parseFlags;final int ref_scanFlags = scanFlags;final long ref_currentTime = currentTime;Runnable scanTask = new Runnable() {public void run() {try {// 注意此时增加了一个解析参数就是PARSE_MUST_BE_APKscanPackageTracedLI(ref_file, ref_parseFlags | PackageParser.PARSE_MUST_BE_APK,ref_scanFlags, ref_currentTime, null);// 详见章节 【 1.4 】} catch (PackageManagerException e) {Slog.w(TAG, "Failed to parse " + ref_file + ": " + e.getMessage());// 如果扫描data分区的Apk失败,则删除data分区扫描失败的文件if ((ref_parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {logCriticalInfo(Log.WARN, "Deleting invalid package at " + ref_file);removeCodePathLI(ref_file);}}}};if (dealer != null)dealer.addTask(scanTask);elsescanTask.run();// 开始扫描}if (dealer != null)dealer.waitAll();Log.d(TAG, "end scanDirLI:"+dir);}

此处源码的逻辑清晰明了,主要分为两大步骤:

  • 首先判断被扫描目录下各个安装包路径的合法性,它必须满足如下的两个条件:

    • 首先各个安装包路径必须是apk文件或者是一个文件夹

    • 然后各个安装包文件名称不能是应用安装过程的临时存储类型的文件

      文件名称不可以是:
      1.不能以vmdl开头且不以 .tmp结尾;
      2.不能以smdl开头且不以 .tmp结尾;
      3.不能以smdl2tmp开头;

      读者是不是很好奇,什么时候会产生上述类型的安装包文件呢,在应用的安装过程中在/data目录下会产生上述类型的临时文件/data/app/vmdl1611774040.tmp/base.apk。关于这块在后续的博客应用的安装流程中会涉及到。

  • 然后通过多线程的方式继续调用scanPackageTracedLI进行下一步的扫描流程

    关于此处有两点需要注意:

    1、在Android AOSP的源码中,scanPackageTracedLI方法并不是在多线程中进行的,这是方案厂为了优化开机速度慢而进行优化的测量,我想这个手段很多做ROM开发的都有使用过(因为PKMS的扫描时很耗时间的)

    2.注意这里的try catch语句,后面方法抛出的异常都是在这个进 catch的,并且如果是扫描的/data分区的安装应用目录,在catch代码执行逻辑中会,若扫描失败则删除data分区扫描失败的文件。

好了我们接着往下看!


1.4 PKMS.scanPackageTracedLI(File…)

此处有一点需要注意的是scanPackageTracedLI方法存在重载,所以要认清具体调用的是哪个,继续接着干!

// 【 PackageManagerService.java 】private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");try {			return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);// 详见章节 【 1.5 】} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}

此代码比较懒,啥也没有干,直接当了个传递手调用另外一个方法scanPackageLI继续执行扫描逻辑。


1.5 PKMS.scanPackageLI(File …)

还记得为啥这一篇和前面分析PKMS中间差了三个篇章吗,这里就是原因了,因为这其中涉及到了解析Android安装包的流程,并其解析流程parsePackage是一个比较繁琐的,所以特意花了三个篇章来分析。

并且由于前面已经重点分析过了解析安装包的流程,这里就不需要大费周章的重复了,具体的就可以参见PKMS启动详解(四)之Android包信息体和包解析器了。

此处有一点需要注意的是scanPackageLI方法存在重载,所以要认清具体调用的是哪个,继续接着干,一直撸一直爽!

// 【 PackageManagerService.java 】/*此时parseFlags的取值为: 0 | PackageParser.PARSE_MUST_BE_APK此时scanFlags的取值是SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL | SCAN_REQUIRE_KNOWN*/private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,long currentTime, UserHandle user) throws PackageManagerException {if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);// 构建一个PackagePares包解析器对象实例,用于解析包// 并且填充前面PKMS构造方法中的相关成员信息PackageParser pp = new PackageParser(mContext);pp.setSeparateProcesses(mSeparateProcesses);pp.setOnlyCoreApps(mOnlyCore);pp.setOnlyPowerOffAlarmApps(mOnlyPowerOffAlarm);pp.setDisplayMetrics(mMetrics);//判断扫描模式if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;}Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");// 构建PackageParser.Package实例对象pkg用来保存解析结果final PackageParser.Package pkg;try {/*这里解析具体某个App中的AndroidManifestxml文件解析出来的数据放在PackageParser的内部类Package的实例pkg中,这里的parseFlags也是重点,系统(system)和非系统文件夹(/data/app)下扫描的tag是不一样的*/pkg = pp.parsePackage(scanFile, parseFlags);} catch (PackageParserException e) {throw PackageManagerException.from(e);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}// 然后需要将pkg中的信息传递给PKMS服务,继续进行相关的扫描处理return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);// 详见章节 【 1.6 】}

此处scanPackageLI方法的处理逻辑层次分明,主要分为三大部分:

  • 构建PackageParser安装包解析实例对象,并且使用PKMS中相关的成员信息填充它

    关于PackageParser我想就不用过大的介绍了,它是一个用于解析Android安装包的工具类,通过它可以得到完整的一个安装包的所有信息。

  • 接着调用PackageParser解析器的parsePackage方法正式开始解析安装包,得到安装包信息对象Package实例对象

    关于此处具体的就可以参见PKMS启动详解(四)之Android包信息体和包解析器,里面有特别详尽的对于Android包解析器和安装包信息体从设计到实施的全方位解读!

  • 将前面解析安装包得到的Package安装包信息对象继续交由重载的scanPackageLI进行下一步处理,然后返回得到继续处理的Pakcage实例对象

    虽然通过parsePackage方法解析得到了安装包的信息,但是这个不是最终的还必须进过进一步的加工和验证才可以!

我们接着继续往下一阶段进军!


1.6 PKMS.scanPackageLI(PackageParser.Package …,6)

该方法的参数入参是六个,不要和后面2.7章节的搞混淆了!注意,注意!

此处无声胜有声,接着往下看!

// 【 PackageManagerService.java 】/*此时policyFlags的取值为:0 | PackageParser.PARSE_MUST_BE_APK它的值来源于前面的parseFlags此时scanFlags的取值是SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL*/private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,final int policyFlags, int scanFlags, long currentTime, UserHandle user)throws PackageManagerException {// SCAN_CHECK_ONLY标签是为了检测是否所有的包(parent 和child)都可以被成功的扫描到if ((scanFlags & SCAN_CHECK_ONLY) == 0) {if (pkg.childPackages != null && pkg.childPackages.size() > 0) {scanFlags |= SCAN_CHECK_ONLY;}} else {scanFlags &= ~SCAN_CHECK_ONLY;}// 继续处理当前解析得到的package信息,然后得到最终扫描处理的scannedPkg信息为扫描的包信息!PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, policyFlags,scanFlags, currentTime, user);// 详见章节 【 2.6 】// 扫描当前package的子pacakge(如果有),我们不管子包存在的情况final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageParser.Package childPackage = pkg.childPackages.get(i);scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,currentTime, user);}// 如果设置SCAN_CHECK_ONLY标志位, 就调用自身,再次处理!,当前扫描条件不会进入此分支if ((scanFlags & SCAN_CHECK_ONLY) != 0) {return scanPackageLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user);}return scannedPkg;}

关于此处对于被扫描的package有child package我们不予考虑,最终该方法会继续调用scanPackageInternalLI执行余下的扫描处理逻辑。

如果被扫描的package有child package,并且是第一次进入该方法的话,就需要检测是否所有的包(parent和child)是否都可以被成功的扫描到。

对于有child package的情况的scanFlags本来是没有SCAN_CHECK_ONLY位的,所以这里会将其SCAN_CHECK_ONLY置为1,这样在方法的最后,又会调用自身,这次又会将 SCAN_CHECK_ONLY 位置为 0!


1.7 PKMS.scanPackageInternalLI(…)

如果说前面的逻辑读者理解起来毫不费力,那么从此处开始我想读者将不得不打气十二分精神了!

因为此处源码的逻辑牵涉到很多关于应用安装包处理的情况,如果是刚开始肯定是一遍过不了的,没有关系多整几遍,捋顺了关系就可以了。当然如果读者能一遍给整清楚那是最好不过的了(反正当初我学习的时候,是没有做到一次就OVER了)。

我们继续来看,通过前面的处理逻辑,我们解析获得了应用程序的PackageParser.Package对象,同时,我们也已经获得了上一次的应用的安装信息(相关信息存储在Settings.mPackages中),接下来,就是通过上述的相关数据继续处理解析获得的数据:

// 【 PackageManagerService.java 】/*通过前面的扫描解析,我们获得了应用程序的PackageParser.Package对象同时,我们也已经获得了上一次的安装信息,接下来,就是要处理解析获得的数据此时policyFlags的取值为:0 | PackageParser.PARSE_MUST_BE_APK它的值来源于前面的parseFlags此时scanFlags的取值是SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL*/private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,int policyFlags, int scanFlags, long currentTime, UserHandle user)throws PackageManagerException {// 这里传入的参数pkg表示解析扫描App应用的AndroidManifest.xml得到的结果/********************** 第一步 **********************//*判断非系统app是否需要更新*///这里的ps用来存储保存在Settings中的扫描的APK信息PackageSetting ps = null;PackageSetting updatedPkg;//表示需要升级的pkg// readersynchronized (mPackages) {// 对于系统apk来说,才有源包,此处逻辑不会执行String oldName = mSettings.mRenamedPackages.get(pkg.packageName);if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) 	{...}// 尝试获得PackageSetting对象if (ps == null) {ps = mSettings.peekPackageLPr(pkg.packageName);}/* 对于非系统的apk,updatePkg是为null的!但对于覆盖安装的系统apk来说,updatePkg不为null*/updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);// 因为是扫描的data分区不会进入此分支if ((policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {...}}boolean updatedPkgBetter = false;// 扫描data分区,不会进入此分支if (updatedPkg != null && (policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {...}// 普通的非系统apk,不进入这个分支!// 但是对于覆盖安装的系统apk,updatedPkg不为null,所以要添加指定的扫描位,表示当前解析的是一个系统 apk!if (updatedPkg != null) {// 对于被更新的系统app的flag进行设置!// An updated system app will not have the PARSE_IS_SYSTEM flag set// initially// 更新的系统应用程序最初不会设置PARSE_IS_SYSTEM的flagpolicyFlags |= PackageParser.PARSE_IS_SYSTEM;// An updated privileged app will not have the PARSE_IS_PRIVILEGED// flag set initially// 已经更新的应用不设置PARSE_IS_PRIVILEGED的flagif ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {policyFlags |= PackageParser.PARSE_IS_PRIVILEGED;}}/********************** 第二步**********************//*前面构建Package实例对象是还没有解析并填充APK的签名信息,现在正是要把签名信息填进去的时候,因为到这一步已经确认要安装APK了,APK能安装的前提就是一定要有签名信息,如果对已有的APK进行升级,则签名信息必须与已有APK相匹配(不然就乱套了),collectCertificatesLI方法就是从Apk的包中的META-INF目录中读取签名信息,*/// Verify certificates against what was last scanned// 安装包检验collectCertificatesLI(ps, pkg, scanFile, policyFlags);// 同样的对于非系统应用安装目录扫描,policyFlags 并没有置 PackageParser.PARSE_IS_SYSTEM_DIR 位为 1,// 所以不进入改分支!boolean shouldHideSystemApp = false;if (updatedPkg == null && ps != null&& (policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {...}// 对于非系统的apk,可能存在apk路径和data路径不一样的情况!这个不在本分支考虑范围之内if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {if (ps != null && !ps.codePath.equals(ps.resourcePath)) {// 如果不一样,就加上PackageParser.PARSE_FORWARD_LOCK位!policyFlags |= PackageParser.PARSE_FORWARD_LOCK;}}// 下面是处理data apk的apk路径和资源路径!String resourcePath = null;String baseResourcePath = null;// 设置resourcePath和baseResourcePath的值if ((policyFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {if (ps != null && ps.resourcePathString != null) {resourcePath = ps.resourcePathString;baseResourcePath = ps.resourcePathString;} else {// Should not happen at all. Just log an error.Slog.e(TAG, "Resource path not set for package " + pkg.packageName);}} else {resourcePath = pkg.codePath;baseResourcePath = pkg.baseCodePath;}// Set application objects path explicitly.// 设置Package.ApplicationInfo对象的path属性pkg.setApplicationVolumeUuid(pkg.volumeUuid);pkg.setApplicationInfoCodePath(pkg.codePath);pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);pkg.setApplicationInfoResourcePath(resourcePath);pkg.setApplicationInfoBaseResourcePath(baseResourcePath);pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);// Note that we invoke the following method only if we are about to unpack an application// 调用另外一个scanPackageLI()方法,对包进行扫描PackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags| SCAN_UPDATE_SIGNATURE, currentTime, user);// 详见章节 【 1.8 】// 对于非系统app不会进入此分支if (shouldHideSystemApp) {...}return scannedPkg;}

各位看官朋友,是不是被前面的一大坨的源码给唬住了!是的面对着这么一大堆的东东,如果不仔细将逻辑理清楚,上去就是一顿硬干,结局只有一个就是搞得云里雾里的(当然刚开始的我也是如此)。阅读源码都是一个痛苦的过程,但是当你攻克了它之后,你会发现原来它是那么美!

我们可以将扫描非系统应用安装目录scanPackageInternalLI方法的执行逻辑,大概归纳为如下几点,且听我慢慢道来:

  • 判断当前被扫描到的非系统应用是否是通过覆盖升级而来的,如果是则进行对应的处理

    在此阶段有几个大的知识点读者一定需要攻克:
    1.对于非系统APK,它的来源可能有两种情况:

    • 首先apk之前已经安装到了data分区,所以肯定packages.xml中肯定有记录, 所以会有对应的PackageSetting对象;
    • apk之前并没有安装,这次通过OTA升级的方式,通过data分区内置app的方式安装apk(这个就属于预置可卸载应用的方式);

    2.然后就是要理解mSettings.mDisabledSysPackages的含义,其实我已经在前面的博客中已经有多次重点提过了,这里mDisabledSysPackages指向的是被覆盖安装的系统应用信息。关于二者详细的解释请参见博客PKMS启动详解(二)之怎么通过packages.xml对已安装应用信息进行持久化管理?和博客PKMS启动详解(三)之BOOT_PROGRESS_PMS_START流程分析。

    3.最后另外的一个就是Android是怎么判断当前的非系统应用是覆盖升级而来呢?这里最最主要利用的就是前面我们获取的已经安装安装包信息大管家Settings中的信息来对比了,我们首先通过PKMS中Settings来获取保存在PKMS中的的APK安装信息对象(即ps对象),然后和前面扫描得到的APK信息pkg对象进行相关的对比(对比的逻辑是判断当前的APK是否是被覆盖升级而来的,如果是覆盖升级而来则会在mDisabledSysPackages知道对应的历史记录。

  • 获取扫描到的安装包签名情况

    在前面的流程中调用包解析器解析安装包获取Package对象时,并没有获取APK的签名信息,现在正是要把APK签名信息填进去的时候,因为到这一步已经确认要安装APK了,APK能安装的前提就是一定要有签名信息。如果对已有APK进行升级,则签名必须与已有APK相匹配(PKMS.collectCertificatesLI()方法就是从APK包中的META-INF目录中读取签名信息,它最后还是调用的包解析器处理相关的逻辑)

  • 继续调用scanPackageLI(PackageParser.Package, int, int, long, UserHandle)方法对解析出来的PackageParse.Package继续进行下一步的处理

至此关于scanPackageInternalLI的主线逻辑分析完毕了,这里我们还是简单看下Settings大管家中关于已安装包安装信息的相关操作的几个方法,如下:

// 【 Settings.java 】//被安装应用的包名为key,以安装应用相关信息为valuefinal ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();PackageSetting peekPackageLPr(String name) {return mPackages.get(name);}/*对应packages.xml中的<updated-package>标签被覆盖升级的系统应用其中key为被覆盖升级的应用包名,value为被覆盖升级前的安装信息*/private final ArrayMap<String, PackageSetting> mDisabledSysPackages =new ArrayMap<String, PackageSetting>();public PackageSetting getDisabledSystemPkgLPr(String name) {PackageSetting ps = mDisabledSysPackages.get(name);return ps;}void removeDisabledSystemPackageLPw(String name) {mDisabledSysPackages.remove(name);}PackageSetting enableSystemPackageLPw(String name) {//从 mDisabledSysPackages中获得被更新前的PackageSettingsPackageSetting p = mDisabledSysPackages.get(name);if(p == null) {Log.w(PackageManagerService.TAG, "Package " + name + " is not disabled");return null;}// Reset flag in ApplicationInfo object// 去掉ApplicationInfo.FLAG_UPDATED_SYSTEM_APP标志位!if((p.pkg != null) && (p.pkg.applicationInfo != null)) {p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;}// 复用数据,创建一个新的PackageSetting,并根据uid添加到指定集合中!PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,p.legacyNativeLibraryPathString, p.primaryCpuAbiString,p.secondaryCpuAbiString, p.cpuAbiOverrideString,p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,p.parentPackageName, p.childPackageNames);// 从mDisabledSysPackages移除这个packagemDisabledSysPackages.remove(name);return ret;}

1.8 PKMS.scanPackageLI(PackageParser.Package …,5)

这里可以看到PKMS对于解析得到的Package应用的处理非常谨慎,需要经过多次校验和验证最终确定它是否y一个合格并且可以使用的安装包信息!

注意该方法的入参参数是五个,不要和章节2.5的重载搞混淆了!

好了,我们接着继续往下看PKMS继续对扫描结果的进一步处理逻辑!

// 【 PackageManagerService.java 】private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {boolean success = false;try {// 调用scanPackageDirtyLI开始解析PackageParser.Packagefinal PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,currentTime, user);// 详见章节 【 1.9 】success = true;return res;} finally {if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {// 如果解析失败,则删除相应目录// DELETE_DATA_ON_FAILURES is only used by frozen pathsdestroyAppDataLIF(pkg, UserHandle.USER_ALL,StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);}}}

此方法也是一个传球手,它的内部继续调用scanPackageDirtyLI方法进行下一步的处理。


1.9 PKMS.scanPackageDirtyLI(PackageParser.Package …)

各位父老相亲们,如果在章节1.7 PKMS.scanPackageInternalLI方法中读者朋友还是没有吃饱喝足的话,那么在scanPackageDirtyLI(PackageParser.Package …)我想肯定够喝饱吃足的了。

墙裂建议读者在开始分析此章节之前,该上厕所的上厕所,该干啥的先干啥,然后再集中火力争取一次性干过!

如果将扫描非系统应用安装目录比喻为万里长征的话,那么此时就相当于进入了最后的关键时刻,成败就此一举了。我们看下扫描的后期的相关处理逻辑!

// 【 PackageManagerService.java 】/*继续处理前面扫描得到的数据并且源码中的英文注释,有保留了下来主要是为了读者能查看原文注释更加体会设计者的意图可能本人有些理解并没有完全吃透此时policyFlags的取值为:0 | PackageParser.PARSE_MUST_BE_APK它的值来源于前面的parseFlags此时scanFlags的取值是SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL*/private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,final int policyFlags, final int scanFlags, long currentTime, UserHandle user)throws PackageManagerException {//PackageParser.Package pkg 封装了扫描的结果信息/****************** 第一步 ***********/final File scanFile = new File(pkg.codePath);//非空路径判断if (pkg.applicationInfo.getCodePath() == null ||pkg.applicationInfo.getResourcePath() == null) {// Bail out. The resource and code paths haven't been set.throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,"Code and resource paths haven't been set correctly");}// Apply policy/****************** 第二步 ***********/// 根据policyFlags设置package以及其中applicationInfo等成员信息// 对非系统应用设置pkg.coreApp = false// 扫描data分区不会进入这里,那么package也就不会被设置ApplicationInfo.FLAG_SYSTEM标志位!if ((policyFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {...} else {// Only allow system apps to be flagged as core apps.// data分区apk进入这个分支,coreApp的值为false,并清除一些flagpkg.coreApp = false;// clear flags not applicable to regular appspkg.applicationInfo.privateFlags &=~ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;pkg.applicationInfo.privateFlags &=~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;}pkg.mTrustedOverlay = (policyFlags&PackageParser.PARSE_TRUSTED_OVERLAY) != 0;// 判断扫描传入的参数是不是携带了PARSE_IS_PRIVILEGED// 如果设置了,则设置对应pkg.applicationInfo.privateFlags,说明这个apk是特权apk// 扫描/data/app目录不会进入此分支if ((policyFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;}if ((policyFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {enforceCodePolicy(pkg);}//mCustomResolverComponentName是从系统资源中读出的,可以配置if (mCustomResolverComponentName != null &&mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {// 这里的用途和下面判断packageName是否为"android"有联系,// 因为调用setUpCustomResolverActivity(pkg)后mResolverReplaced为true。setUpCustomResolverActivity(pkg);}// 针对包名为“android”的APK进行处理,也就是framework-res.apk,属于系统平台包!// 所以扫描data分区也不会进入此分支if (pkg.packageName.equals("android")) {...}if (DEBUG_PACKAGE_SCANNING) {if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)Log.d(TAG, "Scanning package " + pkg.packageName);}synchronized (mPackages) {// *********** 第三步 ******************// 如果这个安装包的包名存在已经安装的列表中,说明该APP已经安装了,则不能重复安装,则抛出异常// mPackage用于保存系统内所有Package,以packageName为keyif (mPackages.containsKey(pkg.packageName)|| mSharedLibraries.containsKey(pkg.packageName)) {throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,"Application package " + pkg.packageName+ " already installed.  Skipping duplicate.");}// If we're only installing presumed-existing packages, require that the// scanned APK is both already known and at the path previously established// for it.  Previously unknown packages we pick up normally, but if we have an// a priori expectation about this package's install presence, enforce it.// With a singular exception for new system packages. When an OTA contains// a new system package, we allow the codepath to change from a system location// to the user-installed location. If we don't allow this change, any newer,// user-installed version of the application will be ignored.// 如果我们只安装已经存在的APP的包,因为是已经存在APP,// 所以可以通过PackageSetting获取它的路径,如果路径不一致,则抛异常// 系统apk不进入这个分支,SCAN_REQUIRE_KNOWN只有在扫描data分区是才会被设置!!// 扫描data分区,会进入此分支// *********** 第四步 ******************if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {// 这个分支扫描/data/app目录会走/* 扫描data分区时,会判断mExpectingBetter中是否包含该package!如果mExpectingBetter中有该应用的包名,说明该应用是覆盖system apk这种情况,我们后续会选择versionCode更高的显示出来此处不进行相关的逻辑处理*/if (mExpectingBetter.containsKey(pkg.packageName)) {logCriticalInfo(Log.WARN,"Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);} else {//获得上一次安装的信息PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);if (known != null) {/*如果本次扫描和之前安装后的codePath或者ResourcePath 不一样,抛出异常,结束处理这种情况,我们会忽视这个apk*/if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)|| !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,"Application package " + pkg.packageName+ " found at " + pkg.applicationInfo.getCodePath()+ " but expected at " + known.codePathString+ "; ignoring.");}}}}}// Initialize package source and resource directories        // *********** 第五步 ******************// Initialize package source and resource directories// 获得扫描到的 apk 和资源的路径,下面会用到!File destCodeFile = new File(pkg.applicationInfo.getCodePath());File destResourceFile = new File(pkg.applicationInfo.getResourcePath());// 代表Package的SharedUserSettings对象SharedUserSetting suid = null;// 代表Pacakge的PacakgeSettings对象PackageSetting pkgSetting = null;/*  非系统apk,要取消以下属性,判断是否是system app的依据是这个Apk的ApplicationInfo.FLAG_SYSTEM标志位只有系统APK才配拥有下面的三个属性*/if (!isSystemApp(pkg)) {// Only system apps can use these features.pkg.mOriginalPackages = null;pkg.mRealPackage = null;pkg.mAdoptPermissions = null;}// Getting the package setting may have a side-effect, so if we// are only checking if scan would succeed, stash a copy of the// old setting to restore at the end.PackageSetting nonMutatedPs = null;// writer// *********** 第六步 ******************// 锁上mPackages对象,意味着要对这个数据结构进行写操作,里面保存的就是解析出来的包信息synchronized (mPackages) {//如果apk是共享uid的if (pkg.mSharedUserId != null) {// 如果已经定义ShareUserId,则创建Package对应的ShareduserSetting// 然后加入到PackageManangerService中的Settings对象维护的数据结构中suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, 0, true);if (suid == null) {// 创建ShareduserSetting失败,抛异常throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,"Creating application package " + pkg.packageName+ " for shared user failed");}if (DEBUG_PACKAGE_SCANNING) {if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId+ "): packages=" + suid.packages);}}// Check if we are renaming from an original package name./* 对于系统package,如果有源包,那就要尝试将 package 的名字改为源包的名字!需要找到一个合适的源包,用来改名!这里有一点需要注意,非系统apk无源码,不进入这个分支覆盖安装的系统apk,可以进入这个分支 这里的目的是找到合适的源包,用来设置包名,并且复用源包的数据!*/ PackageSetting origPackage = null;//<original-package>:应用源码所在包//<manifest package=...>:应用运行时的进程名,同样也是R.java所在包名//关于这部分可以参见博客https://my.oschina.net/u/859753/blog/701727String realName = null;// 如果存在重命名前的包名if (pkg.mOriginalPackages != null) {// This package may need to be renamed to a previously// installed name.  Let's check on that...// 获取重命名的包名final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);// 如果当前package被重命名过,并且有源包的名字是重命名前的名字,就将package的名字改为以前的!if (pkg.mOriginalPackages.contains(renamed)) {// This package had originally been installed as the// original name, and we have already taken care of// transitioning to the new one.  Just update the new// one to continue using the old name.// 如果这个包原来是使用原始的名字,后面变更为新的名字,所以我们只需要更新到新的名字。realName = pkg.mRealPackage;//进行重命名操作if (!pkg.packageName.equals(renamed)) {// Callers into this function may have already taken// care of renaming the package; only do it here if// it is not already done.pkg.setPackageName(renamed);}} else {// 如果不包含在mOriginalPackages中// 遍历mOriginalPackagesfor (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {// 判断pkg的原始包的某一个是否出现在mSettings里面,如果出现过,则说明之前有过if ((origPackage = mSettings.peekPackageLPr(pkg.mOriginalPackages.get(i))) != null) {// We do have the package already installed under its// original name...  should we use it?/* 对当前 package 和其源包进行校验如果源包是非系统package,不是同一分区,无法重命名为源包名,返回false;或者源包是系统package,但是PMS.mPackage中仍然有其扫描数据,源包仍然存在,返回false;*/if (!verifyPackageUpdateLPr(origPackage, pkg)) {// New package is not compatible with original.origPackage = null;continue;} else if (origPackage.sharedUser != null) {// Make sure uid is compatible between packages./* 源包是系统 package,并且共享用户 uid;如果当前的 package 和源包的共享 uid 不匹配,也会返回 false!表示无法迁移数据;*/if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {Slog.w(TAG, "Unable to migrate data from " + origPackage.name+ " to " + pkg.packageName + ": old uid "+ origPackage.sharedUser.name+ " differs from " + pkg.mSharedUserId);origPackage = null;continue;}// TODO: Add case when shared user id is added [b/28144775]} else {if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "+ pkg.packageName + " to old name " + origPackage.name);}break;}}}}// mTransferedPackages用于保存那些自身数据已经被转移到其他 package 的 package!if (mTransferedPackages.contains(pkg.packageName)) {Slog.w(TAG, "Package " + pkg.packageName+ " was transferred to another, but its .apk remains");}// See comments in nonMutatedPs declarationif ((scanFlags & SCAN_CHECK_ONLY) != 0) {PackageSetting foundPs = mSettings.peekPackageLPr(pkg.packageName);if (foundPs != null) {nonMutatedPs = new PackageSetting(foundPs);}}// Just create the setting, don't add it yet. For already existing packages// the PkgSetting exists already and doesn't have to be created./* 获得当前扫描的这个package对应的packageSetting对象,如果已经存在就直接返回,不存在就创建!如果origPackage不为null,创建新的需要重命名为源包的名字!生成PackageSetting对象,对应的数据结构将序列化在/data/system/packages.xml文中中*/pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,pkg.applicationInfo.primaryCpuAbi,pkg.applicationInfo.secondaryCpuAbi,pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,user, false);// 详见章节 【 1.9.1 】if (pkgSetting == null) {//如果失败,则抛出异常throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,"Creating application package " + pkg.packageName + " failed");}/* 如果是有原始包的,则需要进进行原始包操作非系统apk不会进入此分支对于系统package,如果origPackage不为null,改名为源包的名字*/if (pkgSetting.origPackage != null) {// If we are first transitioning from an original package,// fix up the new package's name now.  We need to do this after// looking up the package under its new name, so getPackageLP// can take care of fiddling things correctly.// 设置这个APP的包名为原始包名,并设置origPackage为nullpkg.setPackageName(origPackage.name);// File a report about this.String msg = "New package " + pkgSetting.realName+ " renamed to replace old package " + pkgSetting.name;reportSettingsProblem(Log.WARN, msg);// Make a note of it.// 如果没有设置SCAN_CHECK_ONLY,并且源包是存在的,就将源包添加到mTransferedPackages中!if ((scanFlags & SCAN_CHECK_ONLY) == 0) {mTransferedPackages.add(origPackage.name);}// No longer need to retain this.// 清空originPackage属性!pkgSetting.origPackage = null;}// 如果真实的名字不为空,即有过重命名的,则添加进去if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realName != null) {// Make a note of it.mTransferedPackages.add(pkg.packageName);}/* 如果是这个安装包在mDisabledSysPackages列表中,则设置其flag为FLAG_UPDATED_SYSTEM_APPmSetting.mDisabledSysPackages中保存了所有已替换的安装包如果当前的系统apk被覆盖更新过,就添加 ApplicationInfo.FLAG_UPDATED_SYSTEM_APP 标签!*/if (mSettings.isDisabledSystemPackageLPr(pkg.packageName)) {// 如果当前安装的APK在mDisabledSysPackages列表中表示当前正在升级的system apkpkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;}// *********** 第七步 ******************	// 创建共享库// 如果Package声明了需要library或option-libaray,// PackageManagerService需要确保这些library已经被加载到mSharedLibraries中if ((policyFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {//如果不是系统目录// Check all shared libraries and map to their actual file path.// We only do this here for apps not on a system dir, because those// are the only ones that can fail an install due to this.  We// will take care of the system apps by updating all of their// library paths after the scan is done.// 检查所有共享库并映射到其具体的文件路径。我们只为非系统目录下的应用进行如下操作为,因为只有他们才需要如此做// 扫描完成后,我们将通过更新所有库路径来处理系统应用程序updateSharedLibrariesLPw(pkg, null);}// *********** 第八步 ****************** if (mFoundPolicyFile) {// 根据policy文件,找到Pacakge对应的seinfo,然后存入Package的applicationInfo中SELinuxMMAC.assignSeinfoValue(pkg);}// 处理keySet更新和Package的签名信息,还包括更新和验证pkg.applicationInfo.uid = pkgSetting.appId;// 将pkgSetting保存到pkg.mExtras中pkg.mExtras = pkgSetting;// shouldCheckUpgradeKeySetLP方法进行秘钥检查,是否一致if (shouldCheckUpgradeKeySetLP(pkgSetting, scanFlags)) {//详见章节【 1.9.2】if (checkUpgradeKeySetLP(pkgSetting, pkg)) {// 详见章节 【 1.9.3 】// 签名正确,更新本地的签名信息// We just determined the app is signed correctly, so bring// over the latest parsed certs.pkgSetting.signatures.mSignatures = pkg.mSignatures;} else {if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"Package " + pkg.packageName + " upgrade keys do not match the "+ "previously installed version");} else {// 签名错误pkgSetting.signatures.mSignatures = pkg.mSignatures;String msg = "System package " + pkg.packageName+ " signature changed; retaining data.";reportSettingsProblem(Log.WARN, msg);}}} else {// 如果不检查KetSet更新的话,就直接校验签名!try {// 重新验证签名verifySignaturesLP(pkgSetting, pkg);// We just determined the app is signed correctly, so bring// over the latest parsed certs.// 如果签名校验出现问题,这里会先恢复成本次解析的签名pkgSetting.signatures.mSignatures = pkg.mSignatures;} catch (PackageManagerException e) {// 验证错误处理流程if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {throw e;}// The signature has changed, but this package is in the system// image...  let's recover!pkgSetting.signatures.mSignatures = pkg.mSignatures;// However...  if this package is part of a shared user, but it// doesn't match the signature of the shared user, let's fail.// What this means is that you can't change the signatures// associated with an overall shared user, which doesn't seem all// that unreasonable.// 但是如果是sharedUser的情况,就会报错if (pkgSetting.sharedUser != null) {if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {throw new PackageManagerException(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,"Signature mismatch for shared user : "+ pkgSetting.sharedUser);}}// File a report about this.String msg = "System package " + pkg.packageName+ " signature changed; retaining data.";reportSettingsProblem(Log.WARN, msg);}}// Verify that this new package doesn't have any content providers// that conflict with existing packages.  Only do this if the// package isn't already installed, since we don't want to break// things that are installed.// *********** 第九步 ****************** /*应用安装时候才会进入这个分支判断这个package使用的content  providers是否和已经存在的package冲突此处扫描不会进入此分支*/if ((scanFlags & SCAN_NEW_INSTALL) != 0) {// 获取安装包中的provider的数量final int N = pkg.providers.size();int i;for (i=0; i<N; i++) {PackageParser.Provider p = pkg.providers.get(i);if (p.info.authority != null) {String names[] = p.info.authority.split(";");for (int j = 0; j < names.length; j++) {// 如果包含同样的provider,其中mProvidersByAuthority是系统中已有的providerif (mProvidersByAuthority.containsKey(names[j])) {PackageParser.Provider other = mProvidersByAuthority.get(names[j]);final String otherPackageName =((other != null && other.getComponentName() != null) ?other.getComponentName().getPackageName() : "?");throw new PackageManagerException(INSTALL_FAILED_CONFLICTING_PROVIDER,"Can't install because provider name " + names[j]+ " (in package " + pkg.applicationInfo.packageName+ ") is already used by " + otherPackageName);}}}}}// *********** 第十步 ****************** // 是否需要获取其他包的权限,同样的,只有系统apk才能进入该分支,用于权限继承!if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {// This package wants to adopt ownership of permissions from// another package.// 如果设置mAdoptPermissions属性,对应的AndroidManifest里面的"android:adopt-permissions",// 则对应设置对应的权限!该处有不明白的,请百度"android:adopt-permissions"。就明白了for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {final String origName = pkg.mAdoptPermissions.get(i);final PackageSetting orig = mSettings.peekPackageLPr(origName);if (orig != null) {// 校验要被继承权限的package一是否存在,二是否是系统应用!// 条件不满足,无法权限继承!if (verifyPackageUpdateLPr(orig, pkg)) {Slog.i(TAG, "Adopting permissions from " + origName + " to "+ pkg.packageName);//将origName的权限转给pkgmSettings.transferPermissionsLPw(origName, pkg.packageName);}}}}}//确定进程的名称,一般为packageNamefinal String pkgName = pkg.packageName;// 从base.apk或者split.apk(如果有)中选择修改时间最晚的作为扫描时间!final long scanFileTime = getLastModifiedTime(pkg, scanFile);final boolean forceDex = (scanFlags & SCAN_FORCE_DEX) != 0;pkg.applicationInfo.processName = fixProcessName(pkg.applicationInfo.packageName,pkg.applicationInfo.processName,pkg.applicationInfo.uid);if (pkg != mPlatformPackage) {// Get all of our default paths setuppkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);}// *********** 第十一步 ****************** //设置native相关属性final String path = scanFile.getPath();//获取被扫描的应用支持的平台final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);/* 扫描系统安装目录不会进入此分支设置本地库和系统平台相关的属性只有安装时候才会进入此分支*/if ((scanFlags & SCAN_NEW_INSTALL) == 0) {// 在/data/data/packageName/lib下建立和CPU类型对应的目录,例如ARM平台 arm,MIP平台 mips/derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);// Some system apps still use directory structure for native libraries// in which case we might end up not detecting abi solely based on apk// structure. Try to detect abi based on directory structure.// 如果是系统APP,系统APP的native库统一放到/system/lib下// 所以系统不会提取系统APP目录apk包中native库if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&pkg.applicationInfo.primaryCpuAbi == null) {setBundledAppAbisAndRoots(pkg, pkgSetting);setNativeLibraryPaths(pkg);}} else {// 如果是移动if ((scanFlags & SCAN_MOVE) != 0) {// We haven't run dex-opt for this move (since we've moved the compiled output too)// but we already have this packages package info in the PackageSetting. We just// use that and derive the native library path based on the new codepath.//设置支持的类型pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;}// Set native library paths again. For moves, the path will be updated based on the// ABIs we've determined above. For non-moves, the path will be updated based on the// ABIs we determined during compilation, but the path will depend on the final// package path (after the rename away from the stage path).setNativeLibraryPaths(pkg);}// This is a special case for the "system" package, where the ABI is// dictated by the zygote configuration (and init.rc). We should keep track// of this ABI so that we can deal with "normal" applications that run under// the same UID correctly./* "系统"安装包有特殊情况,其中ABI是由zygote配置(init.rc)指定。我们应该跟踪这个ABI。这样我们就可以在相同的UID下,正确处理"正常"的应用程序。此处处理的是framework-res.apk*/if (mPlatformPackage == pkg) {pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];}// If there's a mismatch between the abi-override in the package setting// and the abiOverride specified for the install. Warn about this because we// would've already compiled the app without taking the package setting into// account.if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {// 如果程序中设置的abi-override和为安装指定的abiOverride之间不匹配if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +" for package " + pkg.packageName);}}// 初始化abi属性pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;pkgSetting.cpuAbiOverrideString = cpuAbiOverride;// Copy the derived override back to the parsed package, so that we can// update the package settings accordingly.//赋值abi属性到pkg中pkg.cpuAbiOverride = cpuAbiOverride;if (DEBUG_ABI_SELECTION) {Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName+ " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="+ pkg.applicationInfo.nativeLibraryRootRequiresIsa);}// Push the derived path down into PackageSettings so we know what to// clean up at uninstall time.//保存lib路径,方便卸载时清理pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;if (DEBUG_ABI_SELECTION) {Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +" primary=" + pkg.applicationInfo.primaryCpuAbi +" secondary=" + pkg.applicationInfo.secondaryCpuAbi);}if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {// We don't do this here during boot because we can do it all// at once after scanning all existing packages.//// We also do this *before* we perform dexopt on this package, so that// we can avoid redundant dexopts, and also to make sure we've got the// code and package path correct.// 调整共享用户的abiadjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,pkg, true /* boot complete */);}if (mFactoryTest && pkg.requestedPermissions.contains(android.Manifest.permission.FACTORY_TEST)) {pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;}if (isSystemApp(pkg)) {pkgSetting.isOrphaned = true;}ArrayList<PackageParser.Package> clientLibPkgs = null;if ((scanFlags & SCAN_CHECK_ONLY) != 0) {if (nonMutatedPs != null) {synchronized (mPackages) {mSettings.mPackages.put(nonMutatedPs.name, nonMutatedPs);}}return pkg;}// *********** 第十二步 ****************** // Only privileged apps and updated privileged apps can add child packages.// 处理特权apk的子包,只有特权apk才能添加子包;// 特权apk包括两部分:// 1、特定uid的app// 2、framework-res.apk 和system/priv-app目录下的apk!if (pkg.childPackages != null && !pkg.childPackages.isEmpty()) {if ((policyFlags & PARSE_IS_PRIVILEGED) == 0) {throw new PackageManagerException("Only privileged apps and updated "+ "privileged apps can add child packages. Ignoring package "+ pkg.packageName);}final int childCount = pkg.childPackages.size();for (int i = 0; i < childCount; i++) {PackageParser.Package childPkg = pkg.childPackages.get(i);if (mSettings.hasOtherDisabledSystemPkgWithChildLPr(pkg.packageName,childPkg.packageName)) {throw new PackageManagerException("Cannot override a child package of "+ "another disabled system app. Ignoring package " + pkg.packageName);}}}// writer/*  处理系统APK更新时,链接库的改变如果是系统package,并且他之前更新过,就要尝试对共享库lib进行更新!只有系统package才能添加共享libdata分区目录扫描不会进入此分支*/synchronized (mPackages) {if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {...}}// 处理和冻结相关的逻辑if ((scanFlags & SCAN_BOOTING) != 0) {// 如果是开机扫描,不需要冻结,因为没有应用在此时可以运行// No apps can run during boot scan, so they don't need to be frozen} else if ((scanFlags & SCAN_DONT_KILL_APP) != 0) {// 如果扫描过程中不允许 kill app,那就不会冻结!// Caller asked to not kill app, so it's probably not frozen} else if ((scanFlags & SCAN_IGNORE_FROZEN) != 0) {// Caller asked us to ignore frozen check for some reason; they// probably didn't know the package name// 如果扫描过程中显式指定忽略冻结,那就不会冻结!} else {// We're doing major surgery on this package, so it better be frozen// right now to keep it from launching// 其他情况,我们会默认冻结该应用,防止其启动!checkPackageFrozen(pkgName);}// Also need to kill any apps that are dependent on the library.// 杀掉所有依赖于库文件的应用,因为库发生了更新!if (clientLibPkgs != null) {for (int i=0; i<clientLibPkgs.size(); i++) {PackageParser.Package clientPkg = clientLibPkgs.get(i);killApplication(clientPkg.applicationInfo.packageName,clientPkg.applicationInfo.uid, "update lib");}}// Make sure we're not adding any bogus keyset info// 添加秘钥集信息KeySetManagerService ksms = mSettings.mKeySetManagerService;ksms.assertScannedPackageValid(pkg);// writerTrace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");//记录trace事件// *********** 第十三步 ****************** boolean createIdmapFailed = false;synchronized (mPackages) {// We don't expect installation to fail beyond this pointif (pkgSetting.pkg != null) {// Note that |user| might be null during the initial boot scan. If a codePath// for an app has changed during a boot scan, it's due to an app update that's// part of the system partition and marker changes must be applied to all users.maybeRenameForeignDexMarkers(pkgSetting.pkg, pkg,(user != null) ? user : UserHandle.ALL);}// Add the new setting to mSettings// 把pkgSetting保存到Settings的变量mPackages中,String对应此包名,mSettings.insertPackageSettingLPw(pkgSetting, pkg);// 插入扫描到的安装包信息// Add the new setting to mPackages// 将pkg保存到PackageManagerService的成员变量mPackages中,key为包名mPackages.put(pkg.applicationInfo.packageName, pkg);// Make sure we don't accidentally delete its data.//清理空间 删除 已经的卸载的、但还占用存储空间的软件final Iterator<PackageCleanItem> iter = mSettings.mPackagesToBeCleaned.iterator();while (iter.hasNext()) {PackageCleanItem item = iter.next();if (pkgName.equals(item.packageName)) {iter.remove();}}// Take care of first install / last update times.            // 更新安装时间:即首次安装或者最后一次更新时间// 如果有当前时间          if (currentTime != 0) {// 若有没有首次安装时间,则说明是首次安装,即设置安装时间if (pkgSetting.firstInstallTime == 0) {pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;} else if ((scanFlags&SCAN_UPDATE_TIME) != 0) {// 如果有首次安装时间,则说明是更新安装,则设置最后更新时间pkgSetting.lastUpdateTime = currentTime;}} else if (pkgSetting.firstInstallTime == 0) {// We need *something*.  Take time time stamp of the file.// 如果没有当前时间且没有首次安装时间,则设置首次时间和最后更新时间等于当前扫描时间pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;} else if ((policyFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {if (scanFileTime != pkgSetting.timeStamp) {// A package on the system image has changed; consider this// to be an update.pkgSetting.lastUpdateTime = scanFileTime;}}// Add the package's KeySets to the global KeySetManagerService// 添加安装包的到全局的KeySetManagerService里面ksms.addScannedPackageLPw(pkg);// *********** 第十四步 ****************** /* 在此之前,四大组件的信息都是Package对象的私有变量,通过后续的代码,将他们注册到PKMS里面。这样PKMS就有了所有的组件信息,这样后续就能提供组件的在线查询服务*/int N = pkg.providers.size();StringBuilder r = null;int i;// 处理该Package中的Provider信息,将其注册到PKMS的mProvider中for (i=0; i<N; i++) {PackageParser.Provider p = pkg.providers.get(i);// 设置进程名称。如果在AndroidManifest里面配置了进程名称,就以配置为准,如果没有配置,就是默认包名p.info.processName = fixProcessName(pkg.applicationInfo.processName,p.info.processName, pkg.applicationInfo.uid);mProviders.addProvider(p);p.syncable = p.info.isSyncable;if (p.info.authority != null) {String names[] = p.info.authority.split(";");p.info.authority = null;for (int j = 0; j < names.length; j++) {if (j == 1 && p.syncable) {// We only want the first authority for a provider to possibly be// syncable, so if we already added this provider using a different// authority clear the syncable flag. We copy the provider before// changing it because the mProviders object contains a reference// to a provider that we don't want to change.// Only do this for the second authority since the resulting provider// object can be the same for all future authorities for this provider.p = new PackageParser.Provider(p);p.syncable = false;}if (!mProvidersByAuthority.containsKey(names[j])) {mProvidersByAuthority.put(names[j], p);if (p.info.authority == null) {p.info.authority = names[j];} else {p.info.authority = p.info.authority + ";" + names[j];}if (DEBUG_PACKAGE_SCANNING) {if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)Log.d(TAG, "Registered content provider: " + names[j]+ ", className = " + p.info.name + ", isSyncable = "+ p.info.isSyncable);}} else {PackageParser.Provider other = mProvidersByAuthority.get(names[j]);Slog.w(TAG, "Skipping provider name " + names[j] +" (in package " + pkg.applicationInfo.packageName +"): name already used by "+ ((other != null && other.getComponentName() != null)? other.getComponentName().getPackageName() : "?"));}}}if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(p.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Providers: " + r);}N = pkg.services.size();r = null;// 处理该Package中的Service信息,将其注册到PKMS的mServices中for (i=0; i<N; i++) {PackageParser.Service s = pkg.services.get(i);s.info.processName = fixProcessName(pkg.applicationInfo.processName,s.info.processName, pkg.applicationInfo.uid);mServices.addService(s);if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(s.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Services: " + r);}N = pkg.receivers.size();r = null;// 处理该Package中的receive信息,然后注册到PKMS的mReceivers中for (i=0; i<N; i++) {PackageParser.Activity a = pkg.receivers.get(i);a.info.processName = fixProcessName(pkg.applicationInfo.processName,a.info.processName, pkg.applicationInfo.uid);mReceivers.addActivity(a, "receiver");if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(a.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Receivers: " + r);}N = pkg.activities.size();r = null;// 处理该Package中的activity信息,然后注册到PKMS的mActivities中for (i=0; i<N; i++) {PackageParser.Activity a = pkg.activities.get(i);a.info.processName = fixProcessName(pkg.applicationInfo.processName,a.info.processName, pkg.applicationInfo.uid);mActivities.addActivity(a, "activity");if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(a.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Activities: " + r);}// 处理该Package中的PermissionGroups信息,注册到PKMSS.mPermissionGroups 中N = pkg.permissionGroups.size();r = null;for (i=0; i<N; i++) {PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);final String curPackageName = cur == null ? null : cur.info.packageName;final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);// 如果isPackageUpdate为 true,说明要更新权限组的信息!// 如果cur为 null,说明是新添加的权限组信息!if (cur == null || isPackageUpdate) {mPermissionGroups.put(pg.info.name, pg); 添加到PKMS的mPermissionGroups中!if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}if (isPackageUpdate) {r.append("UPD:");}r.append(pg.info.name);}} else {Slog.w(TAG, "Permission group " + pg.info.name + " from package "+ pg.info.packageName + " ignored: original from "+ cur.info.packageName);if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append("DUP:");r.append(pg.info.name);}}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permission Groups: " + r);}N = pkg.permissions.size();r = null;//处理该Package中的定义Permission和Permission-tree信息for (i=0; i<N; i++) {PackageParser.Permission p = pkg.permissions.get(i);// Assume by default that we did not install this permission into the system.// 默认取消掉PermissionInfo.FLAG_INSTALLED标志位p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;// Now that permission groups have a special meaning, we ignore permission// groups for legacy apps to prevent unexpected behavior. In particular,// permissions for one app being granted to someone just becase they happen// to be in a group defined by another app (before this had no implications).// 设置权限所属的group,前提是只有Android5.1以后才支持Permission Groups!if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {p.group = mPermissionGroups.get(p.info.group);// Warn for a permission in an unknown group.if (p.info.group != null && p.group == null) {Slog.w(TAG, "Permission " + p.info.name + " from package "+ p.info.packageName + " in an unknown group " + p.info.group);}}// 从Settings中获得权限管理集合,如果该权限是一个Permission-tree,// 那就返回mSettings.mPermissionTrees;否则,返回mSettings.mPermissions!ArrayMap<String, BasePermission> permissionMap =p.tree ? mSettings.mPermissionTrees: mSettings.mPermissions;// 尝试获得上次安装时该权限对应的BasePermission对象!BasePermission bp = permissionMap.get(p.info.name);// Allow system apps to redefine non-system permissions// 允许系统应用来重新定义非系统权限!// 如果上次安装时的BasePermission不为 null,但是当前解析的package不是上次安装时该权限的定义者!if (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) {// 判断上一次安装时,定义该权限的package是否是系统应用!// 如果是currentOwnerIsSystem为true!final boolean currentOwnerIsSystem = (bp.perm != null&& isSystemApp(bp.perm.owner));if (isSystemApp(p.owner)) {// 如果当前解析的定义了该权限的package是系统app,那么进入这里// 如果上次安装时,该权限是一个BasePermission.TYPE_BUILTIN系统权限,且bp.perm为null,// 即:拥有者未知,那么这里我们将这个system package分配给这个权限!if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {// It's a built-in permission and no owner, take ownership nowbp.packageSetting = pkgSetting;bp.perm = p;bp.uid = pkg.applicationInfo.uid;bp.sourcePackage = p.info.packageName;p.info.flags |= PermissionInfo.FLAG_INSTALLED;} else if (!currentOwnerIsSystem) {// 判断上一次安装时,定义该权限的package不是系统应用,而定义相同权限的// 本次解析的package是系统应用,那么该权限会被系统应用重新定义!String msg = "New decl " + p.owner + " of permission  "+ p.info.name + " is system; overriding " + bp.sourcePackage;reportSettingsProblem(Log.WARN, msg);bp = null;}}}// 因为bp为null,所谓我们会使用系统应用重新定义权限,如果是permission-tree,会被添加到 // mSettings.mPermissionTrees 中!if (bp == null) {bp = new BasePermission(p.info.name, p.info.packageName,BasePermission.TYPE_NORMAL);permissionMap.put(p.info.name, bp);}// 重新分配拥有者为该系统应用!if (bp.perm == null) {if (bp.sourcePackage == null|| bp.sourcePackage.equals(p.info.packageName)) {BasePermission tree = findPermissionTreeLP(p.info.name);if (tree == null|| tree.sourcePackage.equals(p.info.packageName)) {bp.packageSetting = pkgSetting;bp.perm = p;bp.uid = pkg.applicationInfo.uid;bp.sourcePackage = p.info.packageName;p.info.flags |= PermissionInfo.FLAG_INSTALLED;if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(p.info.name);}} else {Slog.w(TAG, "Permission " + p.info.name + " from package "+ p.info.packageName + " ignored: base tree "+ tree.name + " is from package "+ tree.sourcePackage);}} else {Slog.w(TAG, "Permission " + p.info.name + " from package "+ p.info.packageName + " ignored: original from "+ bp.sourcePackage);}} else if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append("DUP:");r.append(p.info.name);}if (bp.perm == p) {// 如果上次安装时的权限的定义者,就是本次解析的package,设置protectionLevel!bp.protectionLevel = p.info.protectionLevel;}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permissions: " + r);}N = pkg.instrumentation.size();r = null;//  注册pkg里面的instrumentation到PackageManagerService的mInstrumentation中// Instrumentation用来跟踪本应用内的application及activity的生命周期for (i=0; i<N; i++) {PackageParser.Instrumentation a = pkg.instrumentation.get(i);a.info.packageName = pkg.applicationInfo.packageName;a.info.sourceDir = pkg.applicationInfo.sourceDir;a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;a.info.dataDir = pkg.applicationInfo.dataDir;a.info.deviceProtectedDataDir = pkg.applicationInfo.deviceProtectedDataDir;a.info.credentialProtectedDataDir = pkg.applicationInfo.credentialProtectedDataDir;a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;a.info.secondaryNativeLibraryDir = pkg.applicationInfo.secondaryNativeLibraryDir;mInstrumentation.put(a.getComponentName(), a);if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(a.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Instrumentation: " + r);}if (pkg.protectedBroadcasts != null) {// 处理该Package中的protectedBroadcasts信息N = pkg.protectedBroadcasts.size();for (i=0; i<N; i++) {mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));}}// 更新 PackageSettings中的时间戳pkgSetting.setTimeStamp(scanFileTime);// Create idmap files for pairs of (packages, overlay packages).// Note: "android", ie framework-res.apk, is handled by native layers.// 处理overlay设置if (pkg.mOverlayTarget != null) {// This is an overlay package.if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) {if (!mOverlays.containsKey(pkg.mOverlayTarget)) {mOverlays.put(pkg.mOverlayTarget,new ArrayMap<String, PackageParser.Package>());}ArrayMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget);map.put(pkg.packageName, pkg);PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget);if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) {createIdmapFailed = true;}}} else if (mOverlays.containsKey(pkg.packageName) &&!pkg.packageName.equals("android")) {// This is a regular package, with one or more known overlay packages.createIdmapsForPackageLI(pkg);}}Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);if (createIdmapFailed) {throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"scanPackageLI failed to createIdmap");}return pkg;}

上述源码内容何其多,细节零碎一大推,整得我抓耳又挠腮!让我来理一理啊,细枝末节一边扔,我们只抓重点啊。上述逻辑走主干啊,可以归为如下步骤啊:

  • 第一步:检查被扫描的安装包的代码路径或者资源路径是否存在,两者如果都不存在则抛出异常

  • 第二步:根据policyFlags设置package以及其中applicationInfo等成员信息(policyFlags的值来源于前面的parseFlags)

  • 第三步:如果这个安装包的包名存在已经安装的列表中,说明该APP已经安装了,则不能重复安装,则抛出异常

  • 第四步:扫描data分区时,会判断mExpectingBetter中是否包含该包名。如果mExpectingBetter中有该应用的包名,说明该应用是覆盖system apk,这种情况我们后续会选择versionCode更高的显示出来

  • 第五步:初始化包的安装目录(代码目录与资源目录)

  • 第六步:获得当前扫描的这个package对应的packageSetting对象,如果已经存在就直接返回,不存在就创建

  • 第七步:处理非系统应用共享库情况

  • 第八步:处理被扫描的安装包keySet更新和签名校验的情况

  • 第九步:检查安装包中的provider是不是和现在系统中已经存在包的provider冲突

    只有安装应用时才会进入此分支

  • 第十步:确定当前安装包的进程名

  • 第十一步:设置应用安装包native so库相关情况

  • 第十二步:处理特权APK子包,以及处理系统APK更新时,链接库的改变

    扫描/data/app时不会进入此分支-

  • 第十三步:将扫描得到的Package信息填充到对应PackageSetting中,并且将其加入到PKMS的mPackages中进行管理

  • 第十四步:将上述的的安装包的内容从pkg里面映射到PKMS里面,主要就是向PKMS注册具体的相关组件,从而可以供第三方应用进行查询,其中的核心组件信息如下:

    • 获取pkg对应的provider,并注册到PKMS的变量mProviders里面
    • 获取pkg对应的service,并注册到PKMS的变量mServices里面
    • 获取pkg对应的receiver,并注册到PKMS的变量mReceivers里面
    • 获取pkg对应的activity,并注册到PKMS的变量mActivities里面
    • 获取pkg对应的grouppermission,并注册到PKMS的变量mPermissionGroups里面
    • 获取pkg对应的instrumentation,并注册到PKMS的变量mInstrumentation里面

至此我们基本完成了非系统应用安装目录的扫描工作,并且完成了非系统应用安装目录应用相关组件的注册工作。

这里我的工作到此结束了,至于读者你的吗,那就看读者想要掌握到什么情况了。

总之个人墙裂建议读者在分析PKMS的相关源码逻辑的时候,一定不要一股脑的将拆包,子包等相关逻辑掺和进去,抓住主干,细枝末节的东东等你的吸功大法练好之后再行学习也不迟!

1.9.1 Settings.getPackageLPw(PackageParser.Package pkg …)

可以看到在该方法中它会调用重载的另外一个getPackageLPw方法。

// 【 Settings.java 】PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,String legacyNativeLibraryPathString, String primaryCpuAbi, String secondaryCpuAbi,int pkgFlags, int pkgPrivateFlags, UserHandle user, boolean add) {final String name = pkg.packageName;final String parentPackageName = (pkg.parentPackage != null)? pkg.parentPackage.packageName : null;List<String> childPackageNames = null;if (pkg.childPackages != null) {final int childCount = pkg.childPackages.size();childPackageNames = new ArrayList<>(childCount);for (int i = 0; i < childCount; i++) {String childPackageName = pkg.childPackages.get(i).packageName;childPackageNames.add(childPackageName);}}PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,resourcePath, legacyNativeLibraryPathString, primaryCpuAbi, secondaryCpuAbi,pkg.mVersionCode, pkgFlags, pkgPrivateFlags, user, add, true /* allowInstall */,parentPackageName, childPackageNames);return p;}

此时我么要注意getPackageLPw方法,特别注意如下几个传入的参数的取值:

  • boolean add:传入的值为 false
  • boolean allowInstall:传入的值为 true

该方法传入的参数的是本次扫描解析获得的APK的数据!

// 【 Settings.java】
private PackageSetting getPackageLPw(String name, PackageSetting origPackage,String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,String legacyNativeLibraryPathString, String primaryCpuAbiString,String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags,UserHandle installUser, boolean add, boolean allowInstall, String parentPackage,List<String> childPackageNames) {// 尝试获得之前的安装数据!// 如果是非系统apk,这个安装就是他自身的;如果是覆盖安装的系统app,那么这个数据是data分区下的;PackageSetting p = mPackages.get(name);UserManagerService userManager = UserManagerService.getInstance();// 如果p不为null,说明这个data分区的apk之前就存在,它可能是一个非系统apk;// 也可能是一个系统的apk 覆盖安装到了data 分区!// 下面就要比较codePath!if (p != null) {p.primaryCpuAbiString = primaryCpuAbiString;p.secondaryCpuAbiString = secondaryCpuAbiString;if (childPackageNames != null) {p.childPackageNames = new ArrayList<>(childPackageNames);}// 对于扫描data分区的情况,如果codePath不匹配,会在【 1.9 】 第四步的位置抛出异常,也就是说会忽略掉这个包!// 这里是不会进入的!!if (!p.codePath.equals(codePath)) {if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {...}}if (p.sharedUser != sharedUser) { // 如果共享uid不匹配,就需要创建新的PackageSetting替换以前的!PackageManagerService.reportSettingsProblem(Log.WARN,"Package " + name + " shared user changed from "+ (p.sharedUser != null ? p.sharedUser.name : "<nothing>")+ " to "+ (sharedUser != null ? sharedUser.name : "<nothing>")+ "; replacing with new");p = null;} else {// 如果不是共享uid,或者共享uid匹配,会进入这里:// 这里要注意:pkgFlags 是没有 FLAG_SYSTEM 标志位的,所以 pkgFlags & ApplicationInfo.FLAG_SYSTEM // 会置空该标志,但是由于 | 的特性,并没有影响上一次安装信息的pkgFlags位!// 就是说如果是覆盖更新的system app,即使扫描data分区,也不会取消FLAG_SYSTEM位!p.pkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;p.pkgPrivateFlags |= pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;}}// 如果p == null,// 说明这个data分区的apk是新增的,比如一些OTA版本会在data分区内置一些应用!// 或者是之前的数据和最新的扫描数据比较,共享uid不匹配,那就需要创建新的PackageSetting!// 或者是system的app移动到了data分区!if (p == null) {if (origPackage != null) {//源包不为null的情况,进入这里,使用本次扫描的信息创建新的PackageSetting!p = new PackageSetting(origPackage.name, name, codePath, resourcePath,legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags,parentPackage, childPackageNames);if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "+ name + " is adopting original package " + origPackage.name);PackageSignatures s = p.signatures;p.copyFrom(origPackage);p.signatures = s;p.sharedUser = origPackage.sharedUser;p.appId = origPackage.appId;p.origPackage = origPackage;p.getPermissionsState().copyFrom(origPackage.getPermissionsState());// 重命名为源包的名字后,将新旧名字加入mRenamedPackages集合!mRenamedPackages.put(name, origPackage.name);name = origPackage.name;p.setTimeStamp(codePath.lastModified());} else {// 非系统app会进入这里,使用本次扫描的信息创建PackageSetting!p = new PackageSetting(name, realName, codePath, resourcePath,legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags,parentPackage, childPackageNames);p.setTimeStamp(codePath.lastModified());p.sharedUser = sharedUser;// 系统apk不会进入这个分支!if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {if (DEBUG_STOPPED) {RuntimeException e = new RuntimeException("here");e.fillInStackTrace();Slog.i(PackageManagerService.TAG, "Stopping package " + name, e);}List<UserInfo> users = getAllUsers();final int installUserId = installUser != null ? installUser.getIdentifier() : 0;if (users != null && allowInstall) {for (UserInfo user : users) {final boolean installed = installUser == null|| (installUserId == UserHandle.USER_ALL&& !isAdbInstallDisallowed(userManager, user.id))|| installUserId == user.id;p.setUserState(user.id, 0, COMPONENT_ENABLED_STATE_DEFAULT,installed,true, // stopped,true, // notLaunchedfalse, // hiddenfalse, // suspendednull, null, null,false, // blockUninstallINTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);writePackageRestrictionsLPr(user.id);}}}// 设置系统package的appid// 如果是共享用户id,那appId就是共享用户id;// 如果不是共享用户id,就看系统app是否被覆盖更新过,如果有,就克隆更新前的旧数据,初始化appId,权限等等// 如果系统app 没有被覆盖更新过,就创建新的uid!if (sharedUser != null) {p.appId = sharedUser.userId;} else {PackageSetting dis = mDisabledSysPackages.get(name);if (dis != null) {if (dis.signatures.mSignatures != null) {p.signatures.mSignatures = dis.signatures.mSignatures.clone();}p.appId = dis.appId;p.getPermissionsState().copyFrom(dis.getPermissionsState());List<UserInfo> users = getAllUsers();if (users != null) {for (UserInfo user : users) {int userId = user.id;p.setDisabledComponentsCopy(dis.getDisabledComponents(userId), userId);p.setEnabledComponentsCopy(dis.getEnabledComponents(userId), userId);}}// 将这个新的PackageSetting根据uid添加到mUserIds或者mOtherUserIds中!addUserIdLPw(p.appId, p, name);} else {// 分配一个新的uid,并将映射关系保存到mUserIds中!p.appId = newUserIdLPw(p);}}}if (p.appId < 0) {PackageManagerService.reportSettingsProblem(Log.WARN,"Package " + name + " could not be assigned a valid uid");return null;}if (add) { // add为false,这里不添加,添加的操作在PKMS.scanPackageDirtyLI方法中!addPackageSettingLPw(p, name, sharedUser);}} else {if (installUser != null && allowInstall) {List<UserInfo> users = getAllUsers();if (users != null) {for (UserInfo user : users) {if ((installUser.getIdentifier() == UserHandle.USER_ALL&& !isAdbInstallDisallowed(userManager, user.id))|| installUser.getIdentifier() == user.id) {boolean installed = p.getInstalled(user.id);if (!installed) {p.setInstalled(true, user.id);writePackageRestrictionsLPr(user.id);}}}}}}// 返回查到或者是创建的新的系统 package 对应的 PackageSetting 对象!return p;
}

可以看到上述方法的处理逻辑就是获得当前扫描的这个package对应的packageSetting对象,如果已经存在就直接返回,不存在就创建!如果origPackage不为null,创建新的需要重命名为源包的名字!生成PackageSetting对象,对应的数据结构将序列化在/data/system/packages.xml文中。

1.9.2 PKMS.shouldCheckUpgradeKeySetLP( …)

这里需要注意的是,只有当被扫描的应用以前被有安装过的历史记录时候,才会有可能进入此分支,否则此时她对应的PackageSettings里面的keySetData为空。这点需要特别注意。

这个方法主要检查被扫描应用对应的PackageSettings的密钥集合是否和Settings大管家中的一致,如果不一致则返回false,如果一致则返回true。

// 【 PackageManagerService.java 】private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) {// 判断是否可以进行升级验证的条件if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.sharedUser != null|| !oldPs.keySetData.isUsingUpgradeKeySets()) {return false;}// 获取ksmsKeySetManagerService ksms = mSettings.mKeySetManagerService;// 获取keySet数组long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets();// 遍历keySet数组,检查是否有对应的密钥集for (int i = 0; i < upgradeKeySets.length; i++) {// 如果部分的密钥集合没有对上,说明签名密钥有问题,则返回falseif (!ksms.isIdValidKeySetId(upgradeKeySets[i])) {Slog.wtf(TAG, "Package "+ (oldPs.name != null ? oldPs.name : "<null>")+ " contains upgrade-key-set reference to unknown key-set: "+ upgradeKeySets[i]+ " reverting to signatures check.");return false;}}// 如果所有的密钥都能对上,说明密钥没有问题,则返回truereturn true;}
1.9.3 PKMS.checkUpgradeKeySetLP( …)

在前面的方法中我们判断了是否需要检查应用安装包keyset的情况,在这个方法中就会对前面扫描到的应用公钥是否有原来已经安装的PackageSetting公钥匹配,如果匹配则返回true,否则返回false。

// 【 PackageManagerService.java 】private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {// Upgrade keysets are being used.  Determine if new package has a superset of the// required keys.// 如果升级KeySet,确保新的安装包是否有超集的keys// 获取旧版本的KeySet数组long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();KeySetManagerService ksms = mSettings.mKeySetManagerService;// 遍历KeySet数组for (int i = 0; i < upgradeKeySets.length; i++) {// 根据密钥获取公钥Set<PublicKey> upgradeSet = ksms.getPublicKeysFromKeySetLPr(upgradeKeySets[i]);if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) {// 如果对应上 则返回true,return true;}}// 遍历都没有符合的,则返回falsereturn false;}

1.10 data分区应用安装目录扫描小结

至此data应用安装目录扫描阶段就基本宣告结束了,虽然我的分析已经结束了,但是对于这块的逻辑如果说要达到掌握或者精通是远远不够的,剩下的就只能交给读者自行去打磨其中的各种小细节了。在这里还是非常的有必要对于系统应用安装目录扫描来个整体的总结:

1.10.1 扫描过程总结

该过程牵涉到许多调用,所以这里有必要通过图示直观的归纳总结一下我们前面分析的整理流程如下:

在这里插入图片描述

可以看到上述过程最多的就是通过包解析器解析应用安装包的逻辑了,这也是为什么前面我们花了三篇博客专门来分析它的原因了。

1.10.2 扫描生成数据关系总结

其实PKMS耗费这么多流程来扫描data分区应用安装目录,就是为了将各种安装应用转换成为对应内存中的数据结构,然后管理各种安装应用并且对外提供相关的查询服务。总之经过上述一顿咔咔操作之后,我们可以得到如下的数据结构关系图(其中最最核心的就是Package类图了):

在这里插入图片描述

上述图,示意了一个包最终在内存中的数据结构Package,它包含很多属性,部分属性还是包解析器中的子数据结构。我们可以从设计的角度来理解这个类图:

  • 一个包中有很多组件,为此设计了一个高层的基类Component,所有具体的组件都是Component的子类。什么是组件呢?AndroidManifest.xml文件中所定义的的一些标签,就是组件,譬如<activity>,<service>,<provider>,<permission>等,这些标签分别对应到包解析器中的一个数据结构,它们各自有自身的属性。

  • 诸如<activity>,<service>标签,都可以配置<intent-filter>,来过滤其可以接收的Intent,这些信息也需要在包解析器中体现出来,为此组件Component依赖于IntentInfo这个数据结构。每一个具体的组件所依赖的IntentInfo不同,所以ComponentIntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式,通过泛型的手段实现。

  • 各种组件最终聚合到Package这个数据结构中,形成了最终包解析器的输出。当然,在解析的过程中,还有利用了一些数据结构来优化设计,PackageLiteApkLite就是一些很简单的数据封装。

至于这些数据结构之间更加详尽的关系是怎么关联的,请回头参见前面博客!




二.data分区应用安装目录扫描收尾

  通过前面我们一顿猛虎般的操作,data分区应用安装目录扫描完成,该建立的数据结构已经建立,该注册的相关的组件已经注册OK,该保存的数据结构也已经保存在mPackages中去了。难道我们此时要马放南山,人去嗨吗!当然不是的,虽然我们此时整体流程处理结束了,但是一些相关的小细节还是需要我们特别处理,主要是一些存在升级和可能不存在的系统安装包的处理。我们先来整体看下,然后再细说:

// 【 PackageManagerService.java 】/*** Remove disable package settings for any updated system* apps that were removed via an OTA. If they're not a* previously-updated app, remove them completely.* Otherwise, just revoke their system-level permissions.*//* 进行最后的data分区扫描的收尾工作放在possiblyDeletedUpdatedSystemApps中的应用是在packge.xml中被标记成了待升级的系统应用但是文件却不存在了,因此这里检查用户目录下升级文件是否还存在,然后进行处理*/for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {PackageParser.Package deletedPkg = mPackages.get(deletedAppName);// 从mSettings.mDisabledSysPackages变量中移除去此应用mSettings.removeDisabledSystemPackageLPw(deletedAppName);String msg;if (deletedPkg == null) {// 用户目录中也没有升级包,则肯定是残留的应用信息,则把它的数据目录删除掉// 此时无任何扫描结果,表明这个系统中已经没有改apk了,那就删掉它msg = "Updated system package " + deletedAppName+ " no longer exists; it's data will be wiped";// Actual deletion of code and data will be handled by later// reconciliation step} else {msg = "Updated system app + " + deletedAppName+ " no longer present; removing system privileges for "+ deletedAppName;deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;}//报告系统发生了不一致的情况logCriticalInfo(Log.WARN, msg);}/*** Make sure all system apps that we expected to appear on* the userdata partition actually showed up. If they never* appeared, crawl back and revive the system version.*//**确保所有在用户data分区的应用都显示出来了,如果data分区的无法显示,就显示system分区的现在来处理mExpectingBetter列表,这个列表的应用是带有升级包的系统的应用,前面把他们从mPackages列表中清除了并放到mExpectingBetter列表最后也对它们进行扫描处理*/for (int i = 0; i < mExpectingBetter.size(); i++) {final String packageName = mExpectingBetter.keyAt(i);/* 如果PMS仍然没有扫描到mExpectingBetter列表中的apk,说明data分区的apk无法显示出现这种情况的原因,可能是由于OTA或者异常导致data分区的覆盖安装的应用已经丢失了那就要显示原来system分区的apk!*/if (!mPackages.containsKey(packageName)) {final File scanFile = mExpectingBetter.valueAt(i);logCriticalInfo(Log.WARN, "Expected better " + packageName+ " but never showed up; reverting to system");int reparseFlags = mDefParseFlags;//确保应用位于下面几个系统应用目录,如果不在,则不需要处理if (FileUtils.contains(privilegedAppDir, scanFile)) {reparseFlags = PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR| PackageParser.PARSE_IS_PRIVILEGED;} else if (FileUtils.contains(systemAppDir, scanFile)) {reparseFlags = PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR;} else if (FileUtils.contains(vendorAppDir, scanFile)) {reparseFlags = PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR;} else if (FileUtils.contains(oemAppDir, scanFile)) {reparseFlags = PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR;} else {Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);continue;}//现在把这个apk标示为系统应用,从mSettings.mDisabledSysPackages中删除,//因为在scanDirLI->scanPackageLI中会执行mSettings.disableSystemPackageLPw//所以此时包名的标签是只有<update-package>,执行到这步之后变成<package>标签,//在下面的scanPackageLI中又会添加一个<update-package>标签的mSettings.enableSystemPackageLPw(packageName);try {// 重新扫描一下这个文件,会添加一个<update-package>标签scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to parse original system package: "+ e.getMessage());}}}}//清空目录mExpectingBetter.clear();// Resolve the storage manager.// 获得存储管理对象!mStorageManagerPackage = getStorageManagerPackageName();// Resolve protected action filters. Only the setup wizard is allowed to// have a high priority filter for these actions.// 获得开机向导应用mSetupWizardPackage = getSetupWizardPackageName();if (mProtectedFilters.size() > 0) {if (DEBUG_FILTERS && mSetupWizardPackage == null) {Slog.i(TAG, "No setup wizard;"+ " All protected intents capped to priority 0");}for (ActivityIntentInfo filter : mProtectedFilters) {if (filter.activity.info.packageName.equals(mSetupWizardPackage)) {if (DEBUG_FILTERS) {Slog.i(TAG, "Found setup wizard;"+ " allow priority " + filter.getPriority() + ";"+ " package: " + filter.activity.info.packageName+ " activity: " + filter.activity.className+ " priority: " + filter.getPriority());}// skip setup wizard; allow it to keep the high priority filtercontinue;}Slog.w(TAG, "Protected action; cap priority to 0;"+ " package: " + filter.activity.info.packageName+ " activity: " + filter.activity.className+ " origPrio: " + filter.getPriority());filter.setPriority(0);}}mDeferProtectedFilters = false;mProtectedFilters.clear();// Now that we know all of the shared libraries, update all clients to have// the correct library paths.// 更新所有应用的动态库路径,保证他们有正确的共享库路径updateAllSharedLibrariesLPw();// 调整所有共享uid 的package的指令集!for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {// NOTE: We ignore potential failures here during a system scan (like// the rest of the commands above) because there's precious little we// can do about it. A settings error is reported, though.adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,false /* boot complete */);}// Now that we know all the packages we are keeping,// read and update their last usage times.// 更新所有package的最新使用时间!mPackageUsage.read(mPackages);mCompilerStats.read();

这里我们可以把上述的扫描data分区应用安装收尾的工作整体上归纳为如下两部分:

  • 处理放在possiblyDeletedUpdatedSystemApps中特殊标记的应用,possiblyDeletedUpdatedSystemApps存放的的应用是在packge.xml中被标记成了待升级的系统应用,但是经过前面的文件却不存在了,因此这里检查data分区目录下升覆盖升级的应用文件是否还存在,然后进行相关处理。

    possiblyDeletedUpdatedSystemApps:用来存储那些不存在的被更新过的系统APP!这里我举个简单的栗子:

    譬如原来在/system/app/xxx/xxx.apk的应用被覆盖升级到/data/app/xxx.apk,但是由于OTA升级或者一些其它的操作将/system/app/xxx/xxx.apk给remove掉了,就会进入此时的处理逻辑。

  • 处理mExpectingBetter中特殊标记的应用,这个列表的应用是带有升级包的系统的应用,前面把他们从mPackages列表中清除了并放到mExpectingBetter列表,然后此时对它们进行相关处理,如果此时data分区没有升级的应用了则恢复系统分区的,如果有则使用data分区的。

    mExpectingBetter:用来存放那些在data分区可能有更高版本的系统APP!

    这里为啥是可能呢,因为data分区覆盖安装的应用可能因为一些情况没有了,所以必须使用一个特殊的列表来保存它,当data分区的覆盖升级应用丢失以后,还有从系统分区恢复老版本的补救机会

  • 继续其它的一些剩余操作,比喻更新所有应用的动态库路径,保证他们有正确的共享库路径,调整所有共享uid的package的指令集等等

好了,至此扫描data分区应用安装目录的相关分析就基本结束了!




三.PKMS启动data分区目录扫描阶段总结

  至此PKMS启动data分区目录扫描阶段就分析完成了,读者是感到意犹未尽呢,还是感觉到分析得想吐了呢。木有办法,我们还是得总结一下该流程的主要过程:

  • 通过扫描data分区应用安装目录,得到目录下相关的应用对应的Packages信息

  • 将安装应用信息添加到PKMS中进行l管理,同时将其对应的相关组件注册到PKMS中,以供后续第三方查询和使用

当然实际远远不止如此,上述只是进行了最最简单和i最最核心的归纳而已。其中的各种艰辛和不平凡只能待读者自行去探秘解锁了,总之我个人感觉能分析到此处的都是好手,值得掌声和鲜花!

好了,到这里PackageManagerService启动详解(八)之扫描data分区应用安装目录阶段流程分析就告一段落了,各位青山不改绿水长流,各位江湖见!当然各位读者的点赞和关注是我写作路上前进的最大动力了,如果有啥不对或者不爽的也可以踩一踩也无妨!你们的鼓励和批评是博主前进路上最大的动力。

这篇关于PackageManagerService启动详解(八)之扫描data分区应用安装目录阶段流程分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/189333

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Zookeeper安装和配置说明

一、Zookeeper的搭建方式 Zookeeper安装方式有三种,单机模式和集群模式以及伪集群模式。 ■ 单机模式:Zookeeper只运行在一台服务器上,适合测试环境; ■ 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例; ■ 集群模式:Zookeeper运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble) Zookeeper通过复制来实现

CentOS7安装配置mysql5.7 tar免安装版

一、CentOS7.4系统自带mariadb # 查看系统自带的Mariadb[root@localhost~]# rpm -qa|grep mariadbmariadb-libs-5.5.44-2.el7.centos.x86_64# 卸载系统自带的Mariadb[root@localhost ~]# rpm -e --nodeps mariadb-libs-5.5.44-2.el7

Centos7安装Mongodb4

1、下载源码包 curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.1.tgz 2、解压 放到 /usr/local/ 目录下 tar -zxvf mongodb-linux-x86_64-rhel70-4.2.1.tgzmv mongodb-linux-x86_64-rhel70-4.2.1/

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个