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);
            }
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章