本文主要记录工作中所遇到的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);
}
}