Android PackageManagerService总结(三) APK扫描流程

超、凢脫俗 2024-03-26 20:30 196阅读 0赞

一. 概述

PackageManagerService(简称PKMS),是Android系统中核心服务之一,管理着所有与package相关的工作,常见的比如安装、卸载应用, 信息查询等工作, 主要完成以下核心功能

  1. 解析AndroidManifest.xml清单文件,解析清单文件中的所有节点信息

  2. 扫描本地文件,主要针对apk,主要是系统应用、本地安装应用等。

  3. 管理本地apk,主要包括安装、删除等等

  4. 管理设备上安装的所有应用程序,并在系统启动时加载应用程序

  5. 根据请求的Intent匹配到对应的Activity、Provider、Service,提供包含包名和Component的信息对象

  6. 调用需要权限的系统函数时,检查程序是否具备相应权限从而保证系统安全

  7. 提供应用程序的安装、卸载的接口

本篇文章重点介绍一下apk的扫描过程

二. 扫描流程

在上一篇文章介绍了 PKMS的构造函数中调用了 scanDirTracedLI 方法来扫描某个目录的apk文件, 在Android10.0 扫描的目录有:

/vendor/overlay

/product/overlay

/product_services/overlay

/odm/overlay

/oem/overlay

/system/framework

/system/priv-app

/system/app

/vendor/priv-app

/vendor/app

/odm/priv-app

/odm/app

/oem/app

/oem/priv-app

/product/priv-app

/product/app

/product_services/priv-app

/product_services/app

/product_services/priv-app

我们就 scanDirTracedLI()这个方法为入口来分析:

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

  1. private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
  2. Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
  3. try {
  4. scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
  5. } finally {
  6. Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
  7. }
  8. }

接着调用到PKMS的scanDirLI方法

scanDirLI()中使用了ParallelPackageParser的对象,ParallelPackageParser是一个队列,我们这里手机所有系统的apk,然后从这些队列里面取出apk,再调用PackageParser 解析进行解析

  1. private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
  2. final File[] files = scanDir.listFiles();
  3. if (ArrayUtils.isEmpty(files)) {
  4. Log.d(TAG, "No files in app dir " + scanDir);
  5. return;
  6. }
  7. if (DEBUG_PACKAGE_SCANNING) {
  8. Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
  9. + " flags=0x" + Integer.toHexString(parseFlags));
  10. }
  11. //parallelPackageParser是一个队列,收集系统 apk 文件,
  12. //然后从这个队列里面一个个取出 apk 调用PackageParser 解析
  13. try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
  14. mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
  15. mParallelPackageParserCallback)) {
  16. // Submit files for parsing in parallel
  17. int fileCount = 0;
  18. for (File file : files) {
  19. //是Apk文件,或者是目录
  20. final boolean isPackage = (isApkFile(file) || file.isDirectory())
  21. && !PackageInstallerService.isStageName(file.getName());
  22. //过滤掉非apk文件,如果不是则跳过继续扫描
  23. if (!isPackage) {
  24. // Ignore entries which are not packages
  25. continue;
  26. }
  27. //把APK信息存入parallelPackageParser中的对象 mQueue,PackageParser()函数赋给了队列中的pkg成员
  28. parallelPackageParser.submit(file, parseFlags);
  29. fileCount++;
  30. }
  31. // Process results one by one
  32. //从parallelPackageParser中取出队列apk的信息
  33. for (; fileCount > 0; fileCount--) {
  34. ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
  35. Throwable throwable = parseResult.throwable;
  36. int errorCode = PackageManager.INSTALL_SUCCEEDED;
  37. if (throwable == null) {
  38. // TODO(toddke): move lower in the scan chain
  39. // Static shared libraries have synthetic package names
  40. if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
  41. renameStaticSharedLibraryPackage(parseResult.pkg);
  42. }
  43. //调用 scanPackageChildLI 方法扫描一个特定的 apk 文件
  44. //该类的实例代表一个APK文件,所以它就是和apk文件对应的数据结构。
  45. try {
  46. scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
  47. currentTime, null);
  48. } catch (PackageManagerException e) {
  49. errorCode = e.error;
  50. Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
  51. }
  52. } else if (throwable instanceof PackageParser.PackageParserException) {
  53. PackageParser.PackageParserException e = (PackageParser.PackageParserException)
  54. throwable;
  55. errorCode = e.error;
  56. Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
  57. } else {
  58. throw new IllegalStateException("Unexpected exception occurred while parsing "
  59. + parseResult.scanFile, throwable);
  60. }
  61. // Delete invalid userdata apps
  62. //如果是非系统 apk 并且解析失败
  63. if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
  64. errorCode != PackageManager.INSTALL_SUCCEEDED) {
  65. logCriticalInfo(Log.WARN,
  66. "Deleting invalid package at " + parseResult.scanFile);
  67. // 非系统 Package 扫描失败,删除文件
  68. removeCodePathLI(parseResult.scanFile);
  69. }
  70. }
  71. }
  72. }

先看一下时序图

686118799c3245d58bfc2d6ca1445157.png

2.1 parallelPackageParser.submit方法

我们继续分析parallelPackageParser.submit(file, parseFlags)这个方法

把扫描路径中的APK等内容,放入队列mQueue,并把parsePackage()赋给ParseResult,用于后面的调用

  1. public void submit(File scanFile, int parseFlags) {
  2. mService.submit(() -> {
  3. ParseResult pr = new ParseResult();
  4. Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
  5. try {
  6. PackageParser pp = new PackageParser();
  7. pp.setSeparateProcesses(mSeparateProcesses);
  8. pp.setOnlyCoreApps(mOnlyCore);
  9. pp.setDisplayMetrics(mMetrics);
  10. pp.setCacheDir(mCacheDir);
  11. pp.setCallback(mPackageParserCallback);
  12. pr.scanFile = scanFile;
  13. //这个方法获取 PackageParser.Package 对象
  14. pr.pkg = parsePackage(pp, scanFile, parseFlags);
  15. } catch (Throwable e) {
  16. pr.throwable = e;
  17. } finally {
  18. Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
  19. }
  20. try {
  21. mQueue.put(pr);
  22. } catch (InterruptedException e) {
  23. Thread.currentThread().interrupt();
  24. // Propagate result to callers of take().
  25. // This is helpful to prevent main thread from getting stuck waiting on
  26. // ParallelPackageParser to finish in case of interruption
  27. mInterruptedInThread = Thread.currentThread().getName();
  28. }
  29. });
  30. }

通过parsePackage 进行apk解析,如果传入的packageFile是目录,调用parseClusterPackage()解析,如果传入的是APK文件,就调用parseMonolithicPackage()解析

  1. public Package parsePackage(File packageFile, int flags, boolean useCaches)
  2. throws PackageParserException {
  3. ....
  4. if (packageFile.isDirectory()) {
  5. parsed = parseClusterPackage(packageFile, flags);
  6. } else {
  7. parsed = parseMonolithicPackage(packageFile, flags);
  8. }
  9. ....
  10. }

先来看看parseClusterPackage()方法:

作用:解析给定目录中包含的所有apk,将它们视为单个包。这还可以执行完整性检查,比如需要相同的包名和版本代码、单个基本APK和惟一的拆分名称

首先通过parseClusterPackageLite()对目录下的apk文件进行初步分析,主要区别是核心应用还是非核心应用。核心应用只有一个,非核心应用可以没有,或者多个,非核心应用的作用主要用来保存资源和代码。然后对核心应用调用parseBaseApk分析并生成Package。对非核心应用调用parseSplitApk,分析结果放在前面的Package对象中

frameworks/base/core/java/android/content/pm/PackageParser.java的 parseClusterPackage方法如下:

  1. private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
  2. //获取应用目录的PackageLite对象,这个对象分开保存了目录下的核心应用以及非核心应用的名称
  3. final PackageLite lite = parseClusterPackageLite(packageDir, 0);
  4. //如果lite中没有核心应用,退出
  5. if (mOnlyCoreApps && !lite.coreApp) {
  6. throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
  7. "Not a coreApp: " + packageDir);
  8. }
  9. // Build the split dependency tree.
  10. //构建分割的依赖项树
  11. SparseArray<int[]> splitDependencies = null;
  12. final SplitAssetLoader assetLoader;
  13. if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
  14. try {
  15. splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
  16. assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
  17. } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
  18. throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
  19. }
  20. } else {
  21. assetLoader = new DefaultSplitAssetLoader(lite, flags);
  22. }
  23. try {
  24. final AssetManager assets = assetLoader.getBaseAssetManager();
  25. final File baseApk = new File(lite.baseCodePath);
  26. //对核心应用解析
  27. final Package pkg = parseBaseApk(baseApk, assets, flags);
  28. if (pkg == null) {
  29. throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
  30. "Failed to parse base APK: " + baseApk);
  31. }
  32. if (!ArrayUtils.isEmpty(lite.splitNames)) {
  33. final int num = lite.splitNames.length;
  34. pkg.splitNames = lite.splitNames;
  35. pkg.splitCodePaths = lite.splitCodePaths;
  36. pkg.splitRevisionCodes = lite.splitRevisionCodes;
  37. pkg.splitFlags = new int[num];
  38. pkg.splitPrivateFlags = new int[num];
  39. pkg.applicationInfo.splitNames = pkg.splitNames;
  40. pkg.applicationInfo.splitDependencies = splitDependencies;
  41. pkg.applicationInfo.splitClassLoaderNames = new String[num];
  42. for (int i = 0; i < num; i++) {
  43. final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
  44. //对非核心应用的处理
  45. parseSplitApk(pkg, i, splitAssets, flags);
  46. }
  47. }
  48. pkg.setCodePath(packageDir.getCanonicalPath());
  49. pkg.setUse32bitAbi(lite.use32bitAbi);
  50. return pkg;
  51. } catch (IOException e) {
  52. throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
  53. "Failed to get path: " + lite.baseCodePath, e);
  54. } finally {
  55. IoUtils.closeQuietly(assetLoader);
  56. }
  57. }

再看parseMonolithicPackage(),它的作用是解析给定的APK文件,将其作为单个单块包处理。

最终也是调用parseBaseApk()进行解析,我们接下来看下parseBaseApk()

  1. private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
  2. int flags) throws PackageParserException {
  3. ParseResult<PackageParser.PackageLite> liteResult =
  4. ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
  5. if (liteResult.isError()) {
  6. return input.error(liteResult);
  7. }
  8. final PackageParser.PackageLite lite = liteResult.getResult();
  9. if (mOnlyCoreApps && !lite.coreApp) {
  10. return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
  11. "Not a coreApp: " + apkFile);
  12. }
  13. final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
  14. try {
  15. // 对核心应用解析
  16. ParseResult<ParsingPackage> result = parseBaseApk(input,
  17. apkFile,
  18. apkFile.getCanonicalPath(),
  19. assetLoader.getBaseAssetManager(), flags);
  20. if (result.isError()) {
  21. return input.error(result);
  22. }
  23. return input.success(result.getResult()
  24. .setUse32BitAbi(lite.use32bitAbi));
  25. } catch (IOException e) {
  26. return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
  27. "Failed to get path: " + apkFile, e);
  28. } finally {
  29. IoUtils.closeQuietly(assetLoader);
  30. }
  31. }

parseBaseApk()主要是对AndroidManifest.xml进行解析,解析后所有的信息放在Package对象中

  1. private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
  2. throws PackageParserException {
  3. final String apkPath = apkFile.getAbsolutePath();
  4. ...
  5. XmlResourceParser parser = null;
  6. ...
  7. final int cookie = assets.findCookieForPath(apkPath);
  8. if (cookie == 0) {
  9. throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
  10. "Failed adding asset path: " + apkPath);
  11. }
  12. //获得一个 XML 资源解析对象,该对象解析的是 APK 中的 AndroidManifest.xml 文件。
  13. parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
  14. final Resources res = new Resources(assets, mMetrics, null);
  15. final String[] outError = new String[1];
  16. //再调用重载函数parseBaseApk()最终到parseBaseApkCommon(),解析AndroidManifest.xml 后得到一个Package对象
  17. final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
  18. ...
  19. pkg.setVolumeUuid(volumeUuid);
  20. pkg.setApplicationVolumeUuid(volumeUuid);
  21. pkg.setBaseCodePath(apkPath);
  22. pkg.setSigningDetails(SigningDetails.UNKNOWN);
  23. return pkg;
  24. ...
  25. }

从AndroidManifest.xml中获取标签名,解析标签中的各个item的内容,存入Package对象中

例如获取标签”application”、”permission”

  1. private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
  2. XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
  3. IOException {
  4. TypedArray sa = res.obtainAttributes(parser,
  5. com.android.internal.R.styleable.AndroidManifest);
  6. //拿到AndroidManifest.xml 中的sharedUserId, 一般情况下有“android.uid.system”等信息
  7. String str = sa.getNonConfigurationString(
  8. com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
  9. while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
  10. && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
  11. //从AndroidManifest.xml中获取标签名
  12. String tagName = parser.getName();
  13. //如果读到AndroidManifest.xml中的tag是"application",执行parseBaseApplication()进行解析
  14. if (tagName.equals(TAG_APPLICATION)) {
  15. if (foundApp) {
  16. ...
  17. }
  18. foundApp = true;
  19. //解析"application"的信息,赋值给pkg
  20. if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
  21. return null;
  22. }
  23. ...
  24. //如果标签是"permission"
  25. else if (tagName.equals(TAG_PERMISSION)) {
  26. //进行"permission"的解析
  27. if (!parsePermission(pkg, res, parser, outError)) {
  28. return null;
  29. }
  30. ....
  31. }
  32. }
  33. }
  34. }

上面解析AndroidManifest.xml,

会得到”application”、”overlay”、”permission”、”uses-permission”等信息

我们下面就针对”application”进行展开分析一下,进入parseBaseApplication()函数

  1. private boolean parseBaseApplication(Package owner, Resources res,
  2. XmlResourceParser parser, int flags, String[] outError)
  3. while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
  4. && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
  5. //获取"application"子标签的标签内容
  6. String tagName = parser.getName();
  7. //如果标签是"activity"
  8. if (tagName.equals("activity")) {
  9. //解析Activity的信息,把activity加入Package对象
  10. Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
  11. owner.baseHardwareAccelerated);
  12. if (a == null) {
  13. mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
  14. return false;
  15. }
  16. hasActivityOrder |= (a.order != 0);
  17. owner.activities.add(a);
  18. } else if (tagName.equals("receiver")) {
  19. //如果标签是"receiver",获取receiver信息,加入Package对象
  20. Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
  21. true, false);
  22. if (a == null) {
  23. mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
  24. return false;
  25. }
  26. hasReceiverOrder |= (a.order != 0);
  27. owner.receivers.add(a);
  28. }else if (tagName.equals("service")) {
  29. //如果标签是"service",获取service信息,加入Package对象
  30. Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
  31. if (s == null) {
  32. mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
  33. return false;
  34. }
  35. hasServiceOrder |= (s.order != 0);
  36. owner.services.add(s);
  37. }else if (tagName.equals("provider")) {
  38. //如果标签是"provider",获取provider信息,加入Package对象
  39. Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
  40. if (p == null) {
  41. mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
  42. return false;
  43. }
  44. owner.providers.add(p);
  45. }
  46. ...
  47. }
  48. }

关于如何使用 XmlPullParser来解析xml文件,请查阅笔者之前写文章

Android XML文件结构 和 用XmlPullParser 来解析xml文件

在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完整的 Package 对象.

2.2 scanPackageChildLI()

我们在回到PackageManagerService.java中 scanPackageChildLI()方法

调用addForInitLI()在platform初始化时,把Package内容加入到内部数据结构

  1. private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
  2. final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
  3. @Nullable UserHandle user)
  4. throws PackageManagerException {
  5. ...
  6. // Scan the parent
  7. PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
  8. scanFlags, currentTime, user);
  9. // Scan the children
  10. final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
  11. for (int i = 0; i < childCount; i++) {
  12. PackageParser.Package childPackage = pkg.childPackages.get(i);
  13. //在平台初始化期间向内部数据结构添加新包。
  14. //在platform初始化时,把Package内容加入到内部数据结构,
  15. addForInitLI(childPackage, parseFlags, scanFlags,
  16. currentTime, user);
  17. }
  18. if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
  19. return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
  20. }
  21. }

在addForInitLI()中,进行安装包校验、签名检查、apk更新等操作,把Package加入系统

  1. private PackageParser.Package addForInitLI(PackageParser.Package pkg,
  2. @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
  3. @Nullable UserHandle user)
  4. throws PackageManagerException {
  5. // 判断系统应用是否需要更新
  6. synchronized (mPackages) {
  7. // 更新子应用
  8. if (isSystemPkgUpdated) {
  9. ...
  10. }
  11. if (isSystemPkgBetter) {
  12. // 更新安装包到 system 分区中
  13. synchronized (mPackages) {
  14. // just remove the loaded entries from package lists
  15. mPackages.remove(pkgSetting.name);
  16. }
  17. ...
  18. // 创建安装参数 InstallArgs
  19. final InstallArgs args = createInstallArgsForExisting(
  20. pkgSetting.codePathString,
  21. pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
  22. args.cleanUpResourcesLI();
  23. synchronized (mPackages) {
  24. mSettings.enableSystemPackageLPw(pkgSetting.name);
  25. }
  26. }
  27. // 安装包校验
  28. collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
  29. ...
  30. try (PackageFreezer freezer = freezePackage(pkg.packageName,
  31. "scanPackageInternalLI")) {
  32. // 如果两个 apk 签名不匹配,则调用 deletePackageLIF 方法清除 apk 文件及其数据
  33. deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
  34. }
  35. ...
  36. // 更新系统 apk 程序
  37. InstallArgs args = createInstallArgsForExisting(
  38. pkgSetting.codePathString,
  39. pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
  40. synchronized (mInstallLock) {
  41. args.cleanUpResourcesLI();
  42. }
  43. }
  44. // 如果新安装的系统APP 会被旧的APP 数据覆盖,所以需要隐藏隐藏系统应用程序,并重新扫描 /data/app 目录
  45. if (shouldHideSystemApp) {
  46. synchronized (mPackages) {
  47. mSettings.disableSystemPackageLPw(pkg.packageName, true);
  48. }
  49. }
  50. }

三. 总结

回顾一下整个APK的扫描过程:

  1. 按照core app >system app > other app 优先级扫描APK,解析AndroidManifest.xml文件,得到各个标签内容

  2. 解析XML文件得到的信息由 Package 保存。从该类的成员变量可看出,和 Android 四大组件相关的信息分别由 activites、receivers、providers、services 保存。由于一个 APK 可声明多个组件,因此 activites 和 receivers等均声明为 ArrayList。

  3. 在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完整的 Package 对象,下一步就是将该 Package 加入到系统中

  4. 非系统 Package 扫描失败,删除文件

四. 待更新

再有新的细节理解加入本文中来

发表评论

表情:
评论列表 (有 0 条评论,196人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android WiFi扫描流程

    Android WiFi扫描流程 Android设备提供了方便的API来实现WiFi扫描功能。在本文中,我将详细介绍Android中进行WiFi扫描的流程,并提供相应的源代码

    相关 Android项目构建Apk流程

        好久都没有写博文了,主要是现在的工作重点是前端开发了,最近在学习webpack、weex等前端知识,说起weex,一直都在填坑中,目前还没爬出来了!    好了,回归到