浅谈Android PMS解析APP信息流程

前言

前面我们了解了Zygote的启动流程,知道AMSPMS都是由SystemServer进程启动的,我们都知道PMS主要负责App管理工作,这里我们简单从源码角度分析下PMS是如何解析APP解析的;

源码分析(API 30为例)

我们还是从PackageManagerService.main方法出发;

...
  PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);//这里会构建一个PackageManagerService对象
  ...

我们看下PackageManagerService的构造方法

    private static final File sAppInstallDir =
            new File(Environment.getDataDirectory(), "app");
if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
                        packageParser, executorService);

            }

这里会调用scanDirTracedLI扫描/data/app目录下的文件;

scanDirTracedLI又会调用scanDirLI方法;

    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
            PackageParser2 packageParser, ExecutorService executorService) {
        final File[] files = scanDir.listFiles();
        if (ArrayUtils.isEmpty(files)) {  //1.首先会判断文件夹下是否有文件;
            Log.d(TAG, "No files in app dir " + scanDir);
            return;
        }
		//2.构造一个ParallelPackageParser对象;
        ParallelPackageParser parallelPackageParser =
                new ParallelPackageParser(packageParser, executorService);

        int fileCount = 0;
        for (File file : files) {
			//3.判断文件是否是apk文件 && 不是临时文件
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                continue;
            }
            //4.内部实际上是通过线程池提交任务的方式解析file
            parallelPackageParser.submit(file, parseFlags);
            fileCount++;
        }

        for (; fileCount > 0; fileCount--) {
        	//5.获取结果
            ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
          	...
            }
        }
    }

可以看到scanDirLI方法主要做了以下几件事:

  1. 过滤出apk文件;
  2. 构建parallelPackageParser对象,并向其提交file文件;
  3. 获取结果做一些其他工作;

那我们接下来就重点关注ParallelPackageParser.submit方法;

   public void submit(File scanFile, int parseFlags) {
        mExecutorService.submit(() -> {
            ParseResult pr = new ParseResult();
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
            try {
                pr.scanFile = scanFile;
                pr.parsedPackage = parsePackage(scanFile, parseFlags);
            } catch (Throwable e) {
                pr.throwable = e;
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            try {
                mQueue.put(pr);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
           
                mInterruptedInThread = Thread.currentThread().getName();
            }
        });
    }

可以看到只是往线程池中丢入之前扫描到的apk文件,具体解析工作由parsePackage(scanFile, parseFlags);执行;

我们继续跟进

    protected ParsedPackage parsePackage(File scanFile, int parseFlags)
            throws PackageParser.PackageParserException {
        return mPackageParser.parsePackage(scanFile, parseFlags, true);
    }

又交给了mPackageParser.parsePackage方法,这里mPackageParser在API30上是PackageParser2类;

我们在跟踪PackageParser2.parsePackage方法

    public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {
     	...
        ParseInput input = mSharedResult.get().reset();
        ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
        ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
        ...
        return parsed;
    }

可以看到最终解析任务又交给了ParsingPackageUtils这个类;

    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
          int flags)
          throws PackageParserException {
      if (packageFile.isDirectory()) {
          return parseClusterPackage(input, packageFile, flags);
      } else {
          return parseMonolithicPackage(input, packageFile, flags);
      }
  }

任务进一步交由parseClusterPackage方法完成;

 private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
            int flags) {
            
            ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
                    lite.codePath, assets, flags);
    }

任务进一步交给parseBaseApk方法执行;

 private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
            String codePath, AssetManager assets, int flags) {
        final String apkPath = apkFile.getAbsolutePath();
      	...
        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
                PackageParser.ANDROID_MANIFEST_FILENAME)) {
            final Resources res = new Resources(assets, mDisplayMetrics, null);
            ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,parser, flags);
            final ParsingPackage pkg = result.getResult();
 			....
            return input.success(pkg);
        } catch (Exception e) {
            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to read manifest from " + apkPath, e);
        }
    }

可以看到parseBaseApk五个参数的方法,会去解析AndroidManifest.xml文件得到XmlResourceParser对象,传给六参方法

parseBaseApk(ParseInput input, String apkPath, String codePath, Resources res, XmlResourceParser parser, int flags)方法

    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
            String codePath, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException, PackageParserException {
   			...
            final ParseResult<ParsingPackage> result =
                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
            if (result.isError()) {
                return result;
            }
            return input.success(pkg);
        } finally {
            manifestArray.recycle();
        }
    }

我们重点分析下parseBaseApkTags方法;

   private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
            TypedArray sa, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        //解析得到SharedUserId和SharedUserLabel
        ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
     	...
        boolean foundApp = false;
        final int depth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
            String tagName = parser.getName();
            final ParseResult result;
            if (PackageParser.TAG_APPLICATION.equals(tagName)) {
                if (foundApp) {
                    if (PackageParser.RIGID_PARSER) {
                        result = input.error("<manifest> has more than one <application>");
                    } else {
                        Slog.w(TAG, "<manifest> has more than one <application>");
                        result = input.success(null);
                    }
                } else {
                    foundApp = true;
                    //解析Application标签
                    result = parseBaseApplication(input, pkg, res, parser, flags);
                }
            } else {
                //解析uses-permission、queries等,即非application标签
                result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
            }

            if (result.isError()) {
                return input.error(result);
            }
        }
		...
        return input.success(pkg);
    }

这里,我们可以看到parseBaseApkTags方法,便是做AndroidManifest.xml具体解析的方法;

  1. 解析得到SharedUserId和SharedUserLabel;
  2. 解析Application标签内容;不用想肯定包含四大组件解析;
  3. 解析uses-permission、queries等,即非application标签;

那我们就重点看parseBaseApplication方法是怎么解析四大组件信息的;

    private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        final String pkgName = pkg.getPackageName();
        int targetSdk = pkg.getTargetSdkVersion();

        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
        try {
     		...
            //解析application标签内的一些配置属性,如allowBackup、LargeHeap等
            parseBaseAppBasicFlags(pkg, sa);
			...
            //解析Android任务栈信息
            ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
                    pkgName, pkgName, taskAffinity, input);
            if (taskAffinityResult.isError()) {
                return input.error(taskAffinityResult);
            }

   			...
   			//看着像是处理开进程的情况
            ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
                    pkgName, null, pname, flags, mSeparateProcesses, input);
            if (processNameResult.isError()) {
                return input.error(processNameResult);
            }

            String processName = processNameResult.getResult();
            pkg.setProcessName(processName);

       	...
       	//下面表示解析四大组件
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final ParseResult result;
            String tagName = parser.getName();
            boolean isActivity = false;
            switch (tagName) {
                case "activity":
                    isActivity = true;
                    // fall-through
                case "receiver":
                    ParseResult<ParsedActivity> activityResult =
                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                                    res, parser, flags, PackageParser.sUseRoundIcon, input);

                    if (activityResult.isSuccess()) {
                        ParsedActivity activity = activityResult.getResult();
                        if (isActivity) {
                            hasActivityOrder |= (activity.getOrder() != 0);
                            pkg.addActivity(activity);
                        } else {
                            hasReceiverOrder |= (activity.getOrder() != 0);
                            pkg.addReceiver(activity);
                        }
                    }

                    result = activityResult;
                    break;
                case "service":
                    ParseResult<ParsedService> serviceResult =
                            ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
                                    flags, PackageParser.sUseRoundIcon, input);
                    if (serviceResult.isSuccess()) {
                        ParsedService service = serviceResult.getResult();
                        hasServiceOrder |= (service.getOrder() != 0);
                        pkg.addService(service);
                    }

                    result = serviceResult;
                    break;
                case "provider":
                    ParseResult<ParsedProvider> providerResult =
                            ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
                                    flags, PackageParser.sUseRoundIcon, input);
                    if (providerResult.isSuccess()) {
                        pkg.addProvider(providerResult.getResult());
                    }

                    result = providerResult;
                    break;
                case "activity-alias":
                    activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
                            parser, PackageParser.sUseRoundIcon, input);
                    if (activityResult.isSuccess()) {
                        ParsedActivity activity = activityResult.getResult();
                        hasActivityOrder |= (activity.getOrder() != 0);
                        pkg.addActivity(activity);
                    }

                    result = activityResult;
                    break;
                default:
                    result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
                    break;
            }

            if (result.isError()) {
                return input.error(result);
            }
        }
        ...
        return input.success(pkg);
    }

至此,完成apk的解析,同时将解析到的信息缓存到ParsingPackage
中,其对应实现类为ParsingPackageImpl,我们简单看下定义的一些关键属性;

public class ParsingPackageImpl implements ParsingPackage, Parcelable {
	//缓存解析到的activity数据
 	 @NonNull
    protected List<ParsedActivity> activities = emptyList();
	//缓存解析到的receivers数据
    @NonNull
    protected List<ParsedActivity> receivers = emptyList();
	//缓存解析到的services数据
    @NonNull
    protected List<ParsedService> services = emptyList();
	//缓存解析到的providers数据
    @NonNull
    protected List<ParsedProvider> providers = emptyList();
	//缓存解析到的permissions数据
    @NonNull
    protected List<ParsedPermission> permissions = emptyList();

}

整体流程图

解析APP信息流程图

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值