[Android Framework Series] Chapter 4 PMS Principle

1 Introduction to PMS

PMS(PackageManagerService)Is provided by Android 包管理系统服务, it is used to manage all 包信息, including 应用安装, 卸载, 更新and解析AndroidManifest.xml . By parsing each installed application AndroidManifest.xml, all the data in the xml is saved, and the data required by AMS is subsequently provided. It is a cache that saves application data.

We all know that AndroidManifest.xmlit defines all the information in the apk 四大组件, 权限etc., it is a definition file. The most important thing for PMS to analyze the apk is to 扫描/data/app和/system/app目录下的apk文件find the apk package AndroidManifest.xml, and then 解析AndroidManifest.xml的信息保存到系统内存中, when AMS needs application data, it can find PMS and quickly get relevant information from the memory.

We know that the more apps installed on an Android device, the slower it will start up. The reason is that if there are more applications installed, PMSthe time-consuming analysis will naturally increase. The time-consuming start-up 70%is all about PMSthe analysis. If you say it 优化开机启动速度, you might as well PMSstart with it.

This article is based on Android10(Q)the source code analysis

2 PMS start

All core services in the Android system will be started SystemServer, and PMS is no exception. SystemServerIt will start and run when the phone is turned on. About how SystemServer is started, you can check the article [Android Framework Series] Chapter 3 Zygote Process Related and [Android Vehicle Series] Chapter 10 System Services - SystemServer Source Code Analysis (API28)

We know that PMS is started in the SystemServer process, let's take a look at how to start 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进程Call the method SystemServer.javain the class main(), the method run()in the method startBootstrapServices()initializes the core services such as PMS, calls its main()method to create the corresponding service, and adds the PMS service to it ServiceManager(AMS is also the same operation) for service management.

ServiceManageraddService()Only the method and method are provided getService(). When app进程you need to obtain the corresponding data 系统服务, you will use the proxy to ServiceManagerobtain the corresponding service and use Binder communication to obtain data.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 Analysis

PMS analysis mainly does three things:

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

3.1 Traversing the /data/app folder

/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      }

We see here traversing /data/appand /system/appfolders, finding the apk file, and then submit()analyzing the apk through the method. Let's continue to look at submit()the method

3.2 Unzip the apk file

/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      }

Pass the apk file path found above into the PackageParser object parsePackage()to parse the apk. It should be noted here: the parsing methods are different in different system source code versions. The way to start parsing in versions 6.0, 7.0, and 8.0 is still direct parsing, but in 10.0版本开始使用线程池放到子线程去解析,加快了手机启动速度.

3.3 dom parsing AndroidManifest.xml file

/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      }

The three-step process of parsing here has been completed:

  1. Find the apk file path by traversing /data/appor /system/appfolder;
  2. The method of passing the found path into the PackageParser object parsePackage()performs dom analysis on the AndroidManifest.xml of the apk;
  3. Then parse the information according to different tags and store it in the corresponding field of the Package class and cache it in the memory, such as: four major components, permissions and other information. It is convenient for subsequent AMS to obtain and use directly from the Package cache of PMS.
    insert image description here

4 Summary

Let's briefly summarize the PMS:
PMS is 包管理系统服务used to manage all 包信息, including 应用安装, 卸载, 更新and 解析AndroidManifest.xml. After the mobile phone is turned on, it will traverse all the devices /data/app/and /system/app/directories apk文件, and by parsing all installed applications , the data ( , , etc.) information AndroidManifest.xmlin the xml will be cached in the memory, and then provided to other services.应用信息权限四大组件AMS

The overall process of PMS:

  1. The phone turns on, 内核进程starts init进程, init进程starts SeriviceManager进程and 启动Zygote进程, Zygote进程starts SystemServer, SystemServerthe process starts AMS, PMS, and registers to ServiceManager.
  2. PMSAfter being SystemServerinitialized, start scanning /data/app/and /system/app/all under the directory apk文件, get each apk文件file AndroidManifest.xml, and proceed dom解析.
  3. Parsing AndroidManifest.xmlconverts data information such as 应用信息, 权限, , etc. into a cache .四大组件Java Bean内存中
  4. When AMSfetch is needed apk数据信息, get by get ServiceManagerto get PMS的Binder代理by Binder通信.
    insert image description here

5 interview questions

1 What does PMS do, how do you understand PMS

Package management, package parsing, result caching, and query interfaces are provided.
1. 遍历/data/app和/system/app文件夹,找到apk文件
2. 解压apk文件
3. dom解析AndroidManifest.xml文件,将xml信息存储起来提供给AMS使用

2 What is the use of being familiar with PMS source code

1. Help understand the principles of the Android package management system
2. Cooperate with AMS through Hook technology to realize hot update, plug-in and other functions.

For example, we can obtain the PackageParser object through reflection, and then call its parsePackage() to pass in the apk path to complete the parsing to obtain the Package object, and then reflect the activities, providers, receivers, and services variables of the PMS to add the data we parsed into it , so that dynamic loading is realized (no need to add information in the AndroidManifest.xml file).

Guess you like

Origin blog.csdn.net/u010687761/article/details/131445261