Android O 中 PackageManagerService 扫描系统Apk 的流程及原生问题

本文主要记录工作中所遇到的bug解析过程,不详细讲解PMS(因为本人小菜一枚哈哈哈哈)。

首先,介绍bug复现的场景。系统中的某个预装apk 有三个不同的版本V1,V2,V3且包含so文件,其中V1中so文件在armeabi文件夹下,V2,V3中so文件在armeabi-v7a文件夹下。以下为复现步骤:

  • step1 在Android N 上预装一个版本V1 apk;
  • step2 然后将apk版本升级到V3;
  • step3 接着升级系统到Android O(O中预装的是版本V2);
  • step4 卸载V3版本,回退到预装版本;
  • step5 打开应用出现找不到so文件的crash

通过命令抓取Package相关信息:adb shell dumpsys package com.xxxx > log.txt
对应step1 log1:

	Packages:
	Package [XXXXX):
	.....
	primaryCpuAbi=armeabi
	secondaryCpuAbi=null
	versionName=v1
	.....

对应step 2 log2:

Packages:
.....
primaryCpuAbi=armeabi-v7a
secondaryCpuAbi=null
versionName=v3
......

Hidden system packages:
.....
primaryCpuAbi=armeabi
secondaryCpuAbi=null
versionName=v1

对应step 4 log4:

Packages:
.....
primaryCpuAbi=armeabi-v7a
secondaryCpuAbi=null
versionName=v3
......

Hidden system packages:
.....
primaryCpuAbi=armeabi
secondaryCpuAbi=null
versionName=v2

对应step 5 log5:

Packages:
.....
primaryCpuAbi=armeabi
secondaryCpuAbi=null
versionName=v2
......

注意log1 ,log2 ,log4 ,log5 中 Package的数量,primaryCpuAbi 以及versionName的变化。按理说版本V2 apk中的primaryCpuAbi 应该为armeabi-v7a,而现在的为armeabi,此时打开应用就会出现crash。

接下来分析为什么会出现这样的现象。在系统升级到之后,系统创建PMS会调用构造函数PackageManagerService(),会重新扫描apk,最后通过mSettings.writeLPr()保存修改到package.xml等文件中,package.xm中保存了cpuabi等信息。如果不是第一次启动则直接读取保存的配置文件;这部分不是本文重点,可以参考https://www.2cto.com/kf/201801/711281.html

对应于step 3 :首先在PMS 的scanPackageInternalLI方法中 扫面到的版本号v2小于已安装的版本号v3 (updatedPkg为预装的hidden system Package的配置,此时为v1 的),更改部分配置。

//updatedPkg为对象引用,赋值可以直接更改内存中的值。(从list中根据PackageName获得对象)
updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
........
//此时,mVersionCode为扫描的,versionCode为升级安装的
if (pkg.mVersionCode <= ps.versionCode) {
    // The system package has been updated and the code path does not match
    // Ignore entry. Skip it.
    if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + scanFile
            + " ignored: updated version " + ps.versionCode
            + " better than this " + pkg.mVersionCode);
    if (!updatedPkg.codePath.equals(scanFile)) {
        Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg "
                + ps.name + " changing from " + updatedPkg.codePathString
                + " to " + scanFile);
        //更新path
        updatedPkg.codePath = scanFile;
        updatedPkg.codePathString = scanFile.toString();
        updatedPkg.resourcePath = scanFile;
        updatedPkg.resourcePathString = scanFile.toString();
    }
    updatedPkg.pkg = pkg;
    //更新版本号
    updatedPkg.versionCode = pkg.mVersionCode;
    //更新子包
    // Update the disabled system child packages to point to the package too.
    final int childCount = updatedPkg.childPackageNames != null
            ? updatedPkg.childPackageNames.size() : 0;
    for (int i = 0; i < childCount; i++) {
        String childPackageName = updatedPkg.childPackageNames.get(i);
        PackageSetting updatedChildPkg = mSettings.getDisabledSystemPkgLPr(
                childPackageName);
        if (updatedChildPkg != null) {
            updatedChildPkg.pkg = pkg;
            updatedChildPkg.versionCode = pkg.mVersionCode;
        }
    }
}

通过derivePackageAbi在此处得到v2 primaryCpuAbiString之后,抛出异常,而没有更新updatedPkg 中的primaryCpuAbiString值(即还是v1 的primaryCpuAbiString)。derivePackageAbi方法主要来确定Package中的so文件位置,但并没有赋值给 pkg.applicationInfo.primaryCpuAbi和pkg.applicationInfo.secondaryCpuAbi。

       if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {
            final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, updatedPkg);
            derivePackageAbi(pkg, scanFile, cpuAbiOverride, false, mAppLib32InstallDir);
            /**
            *问题出现的地方, 应该增加对updatedPkg的primaryCpuAbiString和secondaryCpuAbiString的更新
            */
           
        } else {
            pkg.applicationInfo.primaryCpuAbi = updatedPkg.primaryCpuAbiString;
            pkg.applicationInfo.secondaryCpuAbi = updatedPkg.secondaryCpuAbiString;
        }

        throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at "
                + scanFile + " ignored: updated version " + ps.versionCode
                + " better than this " + pkg.mVersionCode);

2 对应step 4 :删除v3后,系统将重新安装 v2 版本 方法调用为scanPackageInternalLI–>scanPackageLI–>scanPackageDirtyLI;

更改主要在scanPackageDirtyLI中,部分代码如下:

   //不是第一次启动or升级系统,那么根据PackageName 得到配置foundPs且不为空;
      if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) == 0) {
            PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);
            if (foundPs != null) {
                primaryCpuAbiFromSettings = foundPs.primaryCpuAbiString;
                secondaryCpuAbiFromSettings = foundPs.secondaryCpuAbiString;
            }
        }

if中条件很多不清楚的,可以看else中的注释就知道了,不是第一次启动or升级系统,则使用保存的配置,所以没有重新扫面v2 apk ,根据上边分析可知primaryCpuAbiString保存的为armeabi而不是 v2的armeabi-v7a.

	//第一次启动或者升级系统,重新扫描abi
        if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0 ||
                mApplicationSettings.isPreinstalled(pkg.packageName) ||
                ConfigHolder.getInstance().inFullBlacklist(pkg.packageName)) {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
            final boolean extractNativeLibs = !pkg.isLibrary();
            derivePackageAbi(pkg, scanFile, cpuAbiOverride, extractNativeLibs,
                    mAppLib32InstallDir);
            
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            // Some system apps still use directory structure for native libraries
            // in which case we might end up not detecting abi solely based on apk
            // structure. Try to detect abi based on directory structure.
            if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
                    pkg.applicationInfo.primaryCpuAbi == null) {
                setBundledAppAbisAndRoots(pkg, pkgSetting);
                setNativeLibraryPaths(pkg, mAppLib32InstallDir);
            }
        } else {//其他情况则直接使用保存的数据
            // This is not a first boot or an upgrade, don't bother deriving the
            // ABI during the scan. Instead, trust the value that was stored in the
            // package setting.

            pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
            pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
            setNativeLibraryPaths(pkg, mAppLib32InstallDir);

            if (DEBUG_ABI_SELECTION) {
                Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
                        pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " +
                        pkg.applicationInfo.secondaryCpuAbi);
            }
        }
    原文作者:lyldding-HFFW
    原文地址: https://blog.csdn.net/lylddingHFFW/article/details/80063883
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞