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