查看原文
其他

Android 10.0 PackageManagerService(三)APK扫描-[Android取经之路]

IngresGe IngresGe 2021-11-05

阅读本文大约需要花费15分钟。

系列文章:

Android取经之路——启动篇

Android系统架构-[Android取经之路]

Android是怎么启动的-[Android取经之路]

Android系统启动之init进程(一)-「Android取经之路」

Android系统启动之init进程(二)-「Android取经之路」

Android 10.0系统启动之init进程(三)-「Android取经之路」

Android 10.0系统启动之init进程(四)-「Android取经之路」

Android 10.0系统启动之Zygote进程(一)-「Android取经之路」

Android 10.0系统启动之Zygote进程(二)-「Android取经之路」

Android 10.0系统启动之Zygote进程(三)-「Android取经之路」

Android 10.0系统启动之Zygote进程(四)-「Android取经之路」

Android 10.0系统启动之SystemServer进程(一)-「Android取经之路」

Android 10.0系统启动之SystemServer进程(二)-「Android取经之路

Android 10.0系统服务之AMS启动流程-「Android取经之路」

Android10.0系统启动之Launcher(桌面)启动流程-[Android取经之路]

Android10.0应用进程创建过程以及Zygote的fork流程-[Android取经之路]

Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]

Android 10.0 PackageManagerService(二)权限扫描-[Android取经之路]


上一节讲解了PKMS的 权限扫描,扫描/system/etc/permissions中的xml,存入相应的结构体中,供之后权限管理使用。

这一节主要来讲讲APK的扫描。


6 扫描APK目录

PackageManagerService的构造函数中调用了scanDirTracedLI方法来扫描某个目录的apk文件。

Android 10.0中,PKMS主要扫描以下路径的APK信息:

/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()为入口来进行分析:


6.1 [ParallelPackageParser.java] scanDirTracedLI()

从下面的函数可见,scanDirTracedLI的入口很简单,首先加入了一些systtrace的日志追踪,然后调用scanDirLI()进行分析

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); }}


6.2 [ParallelPackageParser.java] 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成员 //参考[6.3] parallelPackageParser.submit(file, parseFlags); fileCount++; }
// Process results one by one for (; fileCount > 0; fileCount--) { //从parallelPackageParser中取出队列apk的信息 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); } try { //调用 scanPackageChildLI 方法扫描一个特定的 apk 文件 // 该类的实例代表一个 APK 文件,所以它就是和 apk 文件对应的数据结构。 //参考[6.4] 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); } } }}


6.3 [ParallelPackageParser.java] submit

把扫描路径中的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; 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()) { //如果传入的packageFile是目录,调用parseClusterPackage()解析 parsed = parseClusterPackage(packageFile, flags); } else { //如果是APK文件,就调用parseMonolithicPackage()解析 parsed = parseMonolithicPackage(packageFile, flags); } ... return parsed;}


我们先来看看parseClusterPackage()

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

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

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()


public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps) { if (!lite.coreApp) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + apkFile); } }
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); try { //对核心应用解析 final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags); pkg.setCodePath(apkFile.getCanonicalPath()); pkg.setUse32bitAbi(lite.use32bitAbi); return pkg; } catch (IOException e) { throw new PackageParserException(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); } ... }}


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

下一步就是将该 Package 加入到系统中。此时调用的函数就是另外一个 scanPackageChildLI


6.4 [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 扫描失败,删除文件


: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存