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

一. 概述


        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()这个方法为入口来分析:

扫描二维码关注公众号,回复: 14722431 查看本文章
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
        try {
            scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

接着调用到PKMS的scanDirLI方法

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

private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
        final File[] files = scanDir.listFiles();
        if (ArrayUtils.isEmpty(files)) {
            Log.d(TAG, "No files in app dir " + scanDir);
            return;
        }

        if (DEBUG_PACKAGE_SCANNING) {
            Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
                    + " flags=0x" + Integer.toHexString(parseFlags));
        }
//parallelPackageParser是一个队列,收集系统 apk 文件, 
//然后从这个队列里面一个个取出 apk 调用PackageParser 解析
        try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                mParallelPackageParserCallback)) {
            // Submit files for parsing in parallel
            int fileCount = 0;
            for (File file : files) {
                //是Apk文件,或者是目录
                final boolean isPackage = (isApkFile(file) || file.isDirectory())
                        && !PackageInstallerService.isStageName(file.getName());
                //过滤掉非apk文件,如果不是则跳过继续扫描
                if (!isPackage) {
                    // Ignore entries which are not packages
                    continue;
                }
//把APK信息存入parallelPackageParser中的对象 mQueue,PackageParser()函数赋给了队列中的pkg成员
                parallelPackageParser.submit(file, parseFlags);
                fileCount++;
            }

            // Process results one by one
            //从parallelPackageParser中取出队列apk的信息
            for (; fileCount > 0; fileCount--) {
                ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
                Throwable throwable = parseResult.throwable;
                int errorCode = PackageManager.INSTALL_SUCCEEDED;

                if (throwable == null) {
                    // TODO(toddke): move lower in the scan chain
                    // Static shared libraries have synthetic package names
                    if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
                        renameStaticSharedLibraryPackage(parseResult.pkg);
                    }
//调用 scanPackageChildLI 方法扫描一个特定的 apk 文件 
//该类的实例代表一个APK文件,所以它就是和apk文件对应的数据结构。
                    try {
                        scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
                                currentTime, null);
                    } catch (PackageManagerException e) {
                        errorCode = e.error;
                        Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
                    }
                } else if (throwable instanceof PackageParser.PackageParserException) {
                    PackageParser.PackageParserException e = (PackageParser.PackageParserException)
                            throwable;
                    errorCode = e.error;
                    Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
                } else {
                    throw new IllegalStateException("Unexpected exception occurred while parsing "
                            + parseResult.scanFile, throwable);
                }

                // Delete invalid userdata apps
//如果是非系统 apk 并且解析失败
                if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
                        errorCode != PackageManager.INSTALL_SUCCEEDED) {
                    logCriticalInfo(Log.WARN,
                            "Deleting invalid package at " + parseResult.scanFile);
// 非系统 Package 扫描失败,删除文件
                    removeCodePathLI(parseResult.scanFile);
                }
            }
        }
    }

先看一下时序图

2.1 parallelPackageParser.submit方法

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

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

 
public void submit(File scanFile, int parseFlags) {
    mService.submit(() -> {
        ParseResult pr = new ParseResult();
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
        try {
            PackageParser pp = new PackageParser();
            pp.setSeparateProcesses(mSeparateProcesses);
            pp.setOnlyCoreApps(mOnlyCore);
            pp.setDisplayMetrics(mMetrics);
            pp.setCacheDir(mCacheDir);
            pp.setCallback(mPackageParserCallback);
            pr.scanFile = scanFile;
            //这个方法获取 PackageParser.Package 对象
            pr.pkg = parsePackage(pp, 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();
            // Propagate result to callers of take().
            // This is helpful to prevent main thread from getting stuck waiting on
            // ParallelPackageParser to finish in case of interruption
            mInterruptedInThread = Thread.currentThread().getName();
        }
    });
}

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

 public Package parsePackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {
....
        if (packageFile.isDirectory()) {
            parsed = parseClusterPackage(packageFile, flags);
        } else {
            parsed = parseMonolithicPackage(packageFile, flags);
        }
....
}

先来看看parseClusterPackage()方法:

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

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

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

 
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
    //获取应用目录的PackageLite对象,这个对象分开保存了目录下的核心应用以及非核心应用的名称
    final PackageLite lite = parseClusterPackageLite(packageDir, 0);
    //如果lite中没有核心应用,退出
    if (mOnlyCoreApps && !lite.coreApp) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                "Not a coreApp: " + packageDir);
    }
 
    // Build the split dependency tree.
    //构建分割的依赖项树
    SparseArray<int[]> splitDependencies = null;
    final SplitAssetLoader assetLoader;
    if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
        try {
            splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
            assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
        } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
        }
    } else {
        assetLoader = new DefaultSplitAssetLoader(lite, flags);
    }
 
    try {
        final AssetManager assets = assetLoader.getBaseAssetManager();
        final File baseApk = new File(lite.baseCodePath);
        //对核心应用解析
        final Package pkg = parseBaseApk(baseApk, assets, flags);
        if (pkg == null) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                    "Failed to parse base APK: " + baseApk);
        }
 
        if (!ArrayUtils.isEmpty(lite.splitNames)) {
            final int num = lite.splitNames.length;
            pkg.splitNames = lite.splitNames;
            pkg.splitCodePaths = lite.splitCodePaths;
            pkg.splitRevisionCodes = lite.splitRevisionCodes;
            pkg.splitFlags = new int[num];
            pkg.splitPrivateFlags = new int[num];
            pkg.applicationInfo.splitNames = pkg.splitNames;
            pkg.applicationInfo.splitDependencies = splitDependencies;
            pkg.applicationInfo.splitClassLoaderNames = new String[num];
 
            for (int i = 0; i < num; i++) {
                final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
                //对非核心应用的处理
                parseSplitApk(pkg, i, splitAssets, flags);
            }
        }
 
        pkg.setCodePath(packageDir.getCanonicalPath());
        pkg.setUse32bitAbi(lite.use32bitAbi);
        return pkg;
    } catch (IOException e) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to get path: " + lite.baseCodePath, e);
    } finally {
        IoUtils.closeQuietly(assetLoader);
    }
}

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

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

private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
            int flags) throws PackageParserException {
        ParseResult<PackageParser.PackageLite> liteResult =
                ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
        if (liteResult.isError()) {
            return input.error(liteResult);
        }
 
        final PackageParser.PackageLite lite = liteResult.getResult();
        if (mOnlyCoreApps && !lite.coreApp) {
            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
                    "Not a coreApp: " + apkFile);
        }
 
        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
        try {
            // 对核心应用解析
            ParseResult<ParsingPackage> result = parseBaseApk(input,
                    apkFile,
                    apkFile.getCanonicalPath(),
                    assetLoader.getBaseAssetManager(), flags);
            if (result.isError()) {
                return input.error(result);
            }
 
            return input.success(result.getResult()
                    .setUse32BitAbi(lite.use32bitAbi));
        } catch (IOException e) {
            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to get path: " + apkFile, e);
        } finally {
            IoUtils.closeQuietly(assetLoader);
        }
    }

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

 
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
        throws PackageParserException {
    final String apkPath = apkFile.getAbsolutePath();
  ...
    XmlResourceParser parser = null;
  ...
        final int cookie = assets.findCookieForPath(apkPath);
        if (cookie == 0) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                    "Failed adding asset path: " + apkPath);
        }
        //获得一个 XML 资源解析对象,该对象解析的是 APK 中的 AndroidManifest.xml 文件。
        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
        final Resources res = new Resources(assets, mMetrics, null);
 
        final String[] outError = new String[1];
    //再调用重载函数parseBaseApk()最终到parseBaseApkCommon(),解析AndroidManifest.xml 后得到一个Package对象
        final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
    ...
        pkg.setVolumeUuid(volumeUuid);
        pkg.setApplicationVolumeUuid(volumeUuid);
        pkg.setBaseCodePath(apkPath);
        pkg.setSigningDetails(SigningDetails.UNKNOWN);
 
        return pkg;
  ...
}

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

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

 
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
        XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
        IOException {
  TypedArray sa = res.obtainAttributes(parser,
            com.android.internal.R.styleable.AndroidManifest);
  //拿到AndroidManifest.xml 中的sharedUserId, 一般情况下有“android.uid.system”等信息
  String str = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
      
  while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    //从AndroidManifest.xml中获取标签名
    String tagName = parser.getName();
    //如果读到AndroidManifest.xml中的tag是"application",执行parseBaseApplication()进行解析
    if (tagName.equals(TAG_APPLICATION)) {
            if (foundApp) {
        ...
            }
            foundApp = true;
      //解析"application"的信息,赋值给pkg
            if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                return null;
            }
      ...
      //如果标签是"permission"
      else if (tagName.equals(TAG_PERMISSION)) {
      //进行"permission"的解析
            if (!parsePermission(pkg, res, parser, outError)) {
                return null;
            }
      ....
        } 
        }
  }
}

上面解析AndroidManifest.xml,

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

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

private boolean parseBaseApplication(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError)
  while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
    //获取"application"子标签的标签内容
    String tagName = parser.getName();
    //如果标签是"activity"
        if (tagName.equals("activity")) {
      //解析Activity的信息,把activity加入Package对象
            Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
                    owner.baseHardwareAccelerated);
            if (a == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }
 
            hasActivityOrder |= (a.order != 0);
            owner.activities.add(a);
 
        } else if (tagName.equals("receiver")) {
      //如果标签是"receiver",获取receiver信息,加入Package对象
            Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
                    true, false);
            if (a == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }
 
            hasReceiverOrder |= (a.order != 0);
            owner.receivers.add(a);
 
        }else if (tagName.equals("service")) {
      //如果标签是"service",获取service信息,加入Package对象
            Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
            if (s == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }
 
            hasServiceOrder |= (s.order != 0);
            owner.services.add(s);
 
        }else if (tagName.equals("provider")) {
      //如果标签是"provider",获取provider信息,加入Package对象
            Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
            if (p == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }
 
            owner.providers.add(p);
        }
    ...
  }
}

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

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

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

2.2  scanPackageChildLI()

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

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

private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
    final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
    @Nullable UserHandle user)
            throws PackageManagerException {
  ...
    // Scan the parent
    PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
            scanFlags, currentTime, user);
 
    // Scan the children
    final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
    for (int i = 0; i < childCount; i++) {
        PackageParser.Package childPackage = pkg.childPackages.get(i);
    //在平台初始化期间向内部数据结构添加新包。
    //在platform初始化时,把Package内容加入到内部数据结构,
        addForInitLI(childPackage, parseFlags, scanFlags,
                currentTime, user);
    }
  
    if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
        return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
    }
}

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

private PackageParser.Package addForInitLI(PackageParser.Package pkg,
        @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
        @Nullable UserHandle user)
                throws PackageManagerException {
  // 判断系统应用是否需要更新
    synchronized (mPackages) {
      // 更新子应用
        if (isSystemPkgUpdated) {
        ...
            }
        if (isSystemPkgBetter) {
             // 更新安装包到 system 分区中
            synchronized (mPackages) {
                // just remove the loaded entries from package lists
                mPackages.remove(pkgSetting.name);
            }
      ...
            // 创建安装参数 InstallArgs
            final InstallArgs args = createInstallArgsForExisting(
                    pkgSetting.codePathString,
                    pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
            args.cleanUpResourcesLI();
            synchronized (mPackages) {
                mSettings.enableSystemPackageLPw(pkgSetting.name);
            }
        }
    // 安装包校验
        collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
    ...
    try (PackageFreezer freezer = freezePackage(pkg.packageName,
                        "scanPackageInternalLI")) {
             // 如果两个 apk 签名不匹配,则调用 deletePackageLIF 方法清除 apk 文件及其数据
            deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
        }
    ...
    // 更新系统 apk 程序
        InstallArgs args = createInstallArgsForExisting(
                pkgSetting.codePathString,
                pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
        synchronized (mInstallLock) {
            args.cleanUpResourcesLI();
        }
  }
  // 如果新安装的系统APP 会被旧的APP 数据覆盖,所以需要隐藏隐藏系统应用程序,并重新扫描 /data/app 目录
    if (shouldHideSystemApp) {
        synchronized (mPackages) {
            mSettings.disableSystemPackageLPw(pkg.packageName, true);
        }
    }
}

三. 总结

回顾一下整个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 扫描失败,删除文件

四. 待更新

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

猜你喜欢

转载自blog.csdn.net/u012514113/article/details/129735458