【Android Framework系列】第4章 PMS原理

1 PMS简介

PMS(PackageManagerService)是Android提供的包管理系统服务,它用来管理所有的包信息,包括应用安装卸载更新以及解析AndroidManifest.xml。通过解析每个安装应用的AndroidManifest.xml,将xml中的数据全部都保存起来,后续提供给AMS所需要的数据,它是具有保存应用数据的缓存。

我们都知道AndroidManifest.xml定义了apk中所有的四大组件权限等等信息,它是一个定义文件。PMS对apk的解析最主要的就是去扫描/data/app和/system/app目录下的apk文件,找到apk包中的AndroidManifest.xml,然后解析AndroidManifest.xml的信息保存到系统内存中,这样AMS在需要应用数据时,就能找到PMS快速的从内存中拿到相关信息。

我们知道Android设备安装的应用越多,开机启动的速度就越慢。原因是安装的应用多了,自然PMS的解析耗时就会增加,在开机启动的耗时中70%的都是在PMS的解析上,如果说优化开机启动速度,不妨从PMS入手。

本文基于Android10(Q)的源码做分析

2 PMS启动

在Android系统所有的核心服务都会经过SystemServer启动,PMS也不例外,SystemServer会在手机开机时启动运行。关于SystemServer是如何启动的可以查看文章【Android Framework系列】第3章 Zygote进程相关和【Android车载系列】第10章 系统服务-SystemServer源码分析(API28)

我们知道PMS是在SystemServer进程中被启动,下面我们来看看具体是怎么启动的PMS:

/frameworks/base/services/java/com/android/server/SystemServer.java

348      public static void main(String[] args) {
349          new SystemServer().run();
350      }
......
370      private void run() {
......
507          // Start services.
508          try {
509              traceBeginAndSlog("StartServices");
510              startBootstrapServices();
511              startCoreServices();
512              startOtherServices();
513              SystemServerInitThreadPool.shutdown();
514          } catch (Throwable ex) {
515              Slog.e("System", "******************************************");
516              Slog.e("System", "************ Failure starting system services", ex);
517              throw ex;
518          } finally {
519              traceEnd();
520          }
......
543      }

......
623      private void startBootstrapServices() {
......
734          try {
735              Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
736              mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
737                      mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
738          } finally {
739              Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
740          }
741          mFirstBoot = mPackageManagerService.isFirstBoot();
742          mPackageManager = mSystemContext.getPackageManager();
......
818      }

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

2283      public static PackageManagerService main(Context context, Installer installer,
2284              boolean factoryTest, boolean onlyCore) {
2285          // Self-check for initial settings.
2286          PackageManagerServiceCompilerMapping.checkProperties();
2287  
2288          PackageManagerService m = new PackageManagerService(context, installer,
2289                  factoryTest, onlyCore);
2290          m.enableSystemUserPackages();
2291          ServiceManager.addService("package", m);
2292          final PackageManagerNative pmn = m.new PackageManagerNative();
2293          ServiceManager.addService("package_native", pmn);
2294          return m;
2295      }

Zygote进程调用SystemServer.java类中main()方法,在run()方法中的startBootstrapServices()方法对PMS等核心服务进行初始化,调用其main()方法进行创建对应的服务,并将PMS服务添加到ServiceManager中(AMS也是一样的操作)进行服务的管理。

ServiceManager只提供了addService()getService()方法,当app进程需要获取到对应的系统服务,都会通过ServiceManager拿到相应服务的Binder代理,使用Binder通信获取数据。

/frameworks/base/core/java/android/app/ActivityThread.java

2131      @UnsupportedAppUsage
2132      public static IPackageManager getPackageManager() {
2133          if (sPackageManager != null) {
2134              //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
2135              return sPackageManager;
2136          }
2137          IBinder b = ServiceManager.getService("package");
2138          //Slog.v("PackageManager", "default service binder = " + b);
2139          sPackageManager = IPackageManager.Stub.asInterface(b);
2140          //Slog.v("PackageManager", "default service = " + sPackageManager);
2141          return sPackageManager;
2142      }

3 PMS解析

PMS解析主要做了三件事:

1. 遍历/data/app和/system/app文件夹,找到apk文件
2. 解压apk文件
3. dom解析AndroidManifest.xml文件,将xml信息存储起来提供给AMS使用

3.1 遍历/data/app的文件夹

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

......
		 // /data/app目录
663      private static final File sAppInstallDir =
664              new File(Environment.getDataDirectory(), "app");
......
2380      public PackageManagerService(Context context, Installer installer,
2381              boolean factoryTest, boolean onlyCore) {
......
				  // /system/app 目录
2667              final File systemAppDir = new File(Environment.getRootDirectory(), "app");
				  // 扫描/system/app 目录下的apk文件
2668              scanDirTracedLI(systemAppDir,
2669                      mDefParseFlags
2670                      | PackageParser.PARSE_IS_SYSTEM_DIR,
2671                      scanFlags
2672                      | SCAN_AS_SYSTEM,
2673                      0);
......
				      // 扫描/data/app 目录下的apk文件
2914                  scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
......
3372      }
......
9003      private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
9004          Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
9005          try {
9006              scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
9007          } finally {
9008              Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
9009          }
9010      }
9011  
9012      private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
9013          final File[] files = scanDir.listFiles();
......
9023          try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
9024                  mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
9025                  mParallelPackageParserCallback)) {
9026              // Submit files for parsing in parallel
9027              int fileCount = 0;
9028              for (File file : files) {
9029                  final boolean isPackage = (isApkFile(file) || file.isDirectory())
9030                          && !PackageInstallerService.isStageName(file.getName());
9031                  if (!isPackage) {
9032                      // Ignore entries which are not packages
9033                      continue;
9034                  }
9035                  parallelPackageParser.submit(file, parseFlags);
9036                  fileCount++;
9037              }
......
9076          }
9077      }

我们看到这里遍历/data/app/system/app文件夹,找到apk文件,然后通过submit()方法进行了apk的解析。我们继续往下看submit()方法

3.2 解压apk文件

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

100      /**
101       * Submits the file for parsing
102       * @param scanFile file to scan
103       * @param parseFlags parse falgs
104       */
105      public void submit(File scanFile, int parseFlags) {
106          mService.submit(() -> {
107              ParseResult pr = new ParseResult();
108              Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
109              try {
110                  PackageParser pp = new PackageParser();
111                  pp.setSeparateProcesses(mSeparateProcesses);
112                  pp.setOnlyCoreApps(mOnlyCore);
113                  pp.setDisplayMetrics(mMetrics);
114                  pp.setCacheDir(mCacheDir);
115                  pp.setCallback(mPackageParserCallback);
					 // 需要解析的apk文件路径
116                  pr.scanFile = scanFile;
					 // 通过PackageParser对apk进行解析
117                  pr.pkg = parsePackage(pp, scanFile, parseFlags);
118              } catch (Throwable e) {
119                  pr.throwable = e;
120              } finally {
121                  Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
122              }
123              try {
124                  mQueue.put(pr);
125              } catch (InterruptedException e) {
126                  Thread.currentThread().interrupt();
127                  // Propagate result to callers of take().
128                  // This is helpful to prevent main thread from getting stuck waiting on
129                  // ParallelPackageParser to finish in case of interruption
130                  mInterruptedInThread = Thread.currentThread().getName();
131              }
132          });
133      }
134  
135      @VisibleForTesting
136      protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
137              int parseFlags) throws PackageParser.PackageParserException {
138          return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
139      }

将上面找到的apk文件路径传入PackageParser对象的parsePackage()进行apk的解析。这里要注意:在不同的系统源码版本解析的方式也不相同,在6.0、7.0、8.0版本启动解析的方式还是直接解析的,但在10.0版本开始使用线程池放到子线程去解析,加快了手机启动速度

3.3 dom解析AndroidManifest.xml文件

/frameworks/base/core/java/android/content/pm/PackageParser.java

1011      @UnsupportedAppUsage
1012      public Package parsePackage(File packageFile, int flags, boolean useCaches)
1013              throws PackageParserException {
1014          Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
1015          if (parsed != null) {
				  // 直接返回缓存
1016              return parsed;
1017          }
1018  
1019          long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
			  // apk文件非目录,执行parseMonolithicPackage()
1020          if (packageFile.isDirectory()) {
1021              parsed = parseClusterPackage(packageFile, flags);
1022          } else {
1023              parsed = parseMonolithicPackage(packageFile, flags);
1024          }
......
1036          return parsed;
1037      }
......
1289      @Deprecated
1290      @UnsupportedAppUsage
1291      public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
......
1300          final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
1301          try {
				  // apk解析方法parseBaseApk()
1302              final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
1303              pkg.setCodePath(apkFile.getCanonicalPath());
1304              pkg.setUse32bitAbi(lite.use32bitAbi);
1305              return pkg;
1306          } catch (IOException e) {
1307              throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
1308                      "Failed to get path: " + apkFile, e);
1309          } finally {
1310              IoUtils.closeQuietly(assetLoader);
1311          }
1312      }
1313  
1314      private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
1315              throws PackageParserException {
1316          final String apkPath = apkFile.getAbsolutePath();
......
1328  		  // 开始 dom 解析 AndroidManifest.xml
1329          XmlResourceParser parser = null;
1330          try {
1331              final int cookie = assets.findCookieForPath(apkPath);
1332              if (cookie == 0) {
1333                  throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
1334                          "Failed adding asset path: " + apkPath);
1335              }
1336              parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
......
1340              final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
......
1351              return pkg;
1352  
1353          } catch (PackageParserException e) {
1354              throw e;
1355          } catch (Exception e) {
1356              throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
1357                      "Failed to read manifest from " + apkPath, e);
1358          } finally {
1359              IoUtils.closeQuietly(parser);
1360          }
1361      }
......

1913      @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
1914      private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
1915              String[] outError) throws XmlPullParserException, IOException {
1916          final String splitName;
1917          final String pkgName;
1918  
1919          try {
1920              Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
1921              pkgName = packageSplit.first; // 包名
1922              splitName = packageSplit.second;
......
1932          }
......
			  // 将解析的信息(四大组件、权限等)存储到Package
1943          final Package pkg = new Package(pkgName);
.....
1975          return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
1976      }
......
6403      /**
6404       * Representation of a full package parsed from APK files on disk. A package
6405       * consists of a single base APK, and zero or more split APKs.
6406       */
6407      public final static class Package implements Parcelable {
......
			  // 包名
6409          @UnsupportedAppUsage
6410          public String packageName; 
			  // 应用信息
6453          @UnsupportedAppUsage
6454          public ApplicationInfo applicationInfo = new ApplicationInfo();
6455  
			  // 权限相关信息
6456          @UnsupportedAppUsage
6457          public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
6458          @UnsupportedAppUsage
6459          public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
			  // 四大组件相关信息
6460          @UnsupportedAppUsage
6461          public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
6462          @UnsupportedAppUsage
6463          public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
6464          @UnsupportedAppUsage
6465          public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
6466          @UnsupportedAppUsage
6467          public final ArrayList<Service> services = new ArrayList<Service>(0);
......
7499      }

到这里解析三步流程已经完成:

  1. 通过遍历/data/app/system/app文件夹找到apk文件路径;
  2. 把找到的路径传入PackageParser对象的parsePackage()方法对apk的AndroidManifest.xml进行dom解析;
  3. 然后根据不同标签解析信息存储到Package类的对应字段并缓存到内存中,如:四大组件、权限等信息。方便后续AMS直接从PMS的Package缓存中获取使用。
    【Android Framework系列】第4章 PMS原理_第1张图片

4 总结

我们再来简单总结下PMS:
PMS是包管理系统服务,用来管理所有的包信息,包括应用安装卸载更新以及解析AndroidManifest.xml。手机开机后,它会遍历设备上/data/app//system/app/目录下的所有apk文件,通过解析所有安装应用的AndroidManifest.xml,将xml中的数据(应用信息权限四大组件等)信息都缓存到内存中,后续提供给AMS等服务使用。

PMS的整体流程:

  1. 手机开机,内核进程启动init进程init进程启动SeriviceManager进程启动Zygote进程Zygote进程启动SystemServerSystemServer进程启动AMSPMS,并注册到ServiceManager
  2. PMSSystemServer初始化后,开始扫描/data/app//system/app/目录下的所有apk文件,获取每个apk文件AndroidManifest.xml文件,并进行dom解析
  3. 解析AndroidManifest.xml应用信息权限四大组件等数据信息转换为Java Bean缓存到内存中
  4. AMS需要获取apk数据信息时,通过ServiceManager获取到PMS的Binder代理通过Binder通信获取。
    【Android Framework系列】第4章 PMS原理_第2张图片

5 面试题

1 PMS 是干什么的,你是怎么理解PMS

包管理,包解析,结果缓存,提供查询接口。
1. 遍历/data/app和/system/app文件夹,找到apk文件
2. 解压apk文件
3. dom解析AndroidManifest.xml文件,将xml信息存储起来提供给AMS使用

2 熟悉PMS源码有什么用处

1. 帮助了解Android包管理系统原理
2. 配合AMS通过Hook技术,实现热更新、插件化等功能。

比如,我们可以通过反射获取到PackageParser对象,再反射调用它的 parsePackage() 传入 apk 路径完成解析获取到 Package 对象,再反射 PMS 的 activities、providers、receivers、services 变量,将我们解析的数据添加进去,这样就实现了动态加载(不需要AndroidManifest.xml文件中添加信息)。

你可能感兴趣的:(Android,Framework,Java,android,Framework,PMS)