Android 8 应用安装时 ABI 确认过程

2019-06-10  本文已影响0人  jerryyyq

packages.xml

一个 apk 安装后,会在 /data/system/packages.xml 中添加纪录,例如:
<package name="com.qqgame.happymj" codePath="/data/app/com.qqgame.happymj-zOiH1EwGGCNwlG6u5BbgLg==" nativeLibraryPath="/data/app/com.qqgame.happymj-zOiH1EwGGCNwlG6u5BbgLg==/lib" primaryCpuAbi="armeabi-v7a" publicFlags="973618756" privateFlags="0" ft="16b12582130" it="16b12584a42" ut="16b12584a42" version="71630" userId="10086">

其中的 primaryCpuAbi 决定了该应用是 32 位的还是 64 位的。
这个值由 PackageManagerService 在做 scanPackageLI 的时候决定,具体这个值的得出有一个公式化的过程,主要就是判断这个 apk 有没有使用 native 的库,如果使用了,那就看使用了的是 32 位的还是 64 位的,另外还要看系统支持的是 32 位还是 64 位的。

确定的时机

public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    ... ...

    scanDirLI();

    ... ...

    // 当所有的 apk 文件解析完之后,对使用了相同 UID 的 apk, 调用 adjustCpuAbisForSharedUserLPw
    for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
        // setting.packages 是所有使用相同 UID 的 apk 的集合
        adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
                false /* force dexopt */, false /* defer dexopt */);
    }

    ... ...
}

ABI 确定过程

scanPackageDirtyLI

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
            final int policyFlags, final int scanFlags, long currentTime, @Nullable UserHandle user),在这个函数中会调用:

    ......

            // 如果不是第一次开机或者升级,就从 mSettings 中读取 PackageSetting 类型的值,再将其中存储的关于 ABI 的值保存到临时变量中 

            if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) == 0) {
                PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);
                if (foundPs != null) {
                    primaryCpuAbiFromSettings = foundPs.primaryCpuAbiString;
                    secondaryCpuAbiFromSettings = foundPs.secondaryCpuAbiString;
                }
            }


    ......

        if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
            if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {

                // 是第一次开机或者升级,调用 derivePackageAbi 方法解析abi路径等相关信息存到 pkg 中

                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
                final boolean extractNativeLibs = !pkg.isLibrary();
                derivePackageAbi(pkg, scanFile, cpuAbiOverride, extractNativeLibs, mAppLib32InstallDir);
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

                // 如果是 system app, 并且通过 derivePackageAbi() 方法没有确定 primaryCpuAbi 的值,那么尝试用下面的方法来确定
                // 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) {

                    // 如果是 system app,并且这个 app 没有通过上面的函数找到 primaryCpuAbi 的值
                    setBundledAppAbisAndRoots(pkg, pkgSetting);

                    // setNativeLibraryPaths 方法会根据 CpuAbi 的值确定 apk 使用的 so 库的安装路径
                    setNativeLibraryPaths(pkg, mAppLib32InstallDir);
                }
            } else {

                // 如果不是第一次开机或者升级,将上面第一次判断得到的临时变量的值复制到 pkg 中

                // 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);
                }
            }
        } else {
            ......
        }

        // 当前解析的 apk 是 framework-res.apk, 对这个特殊的apk, 让它的 ABI 的值与系统相同。在我这里,它就是 arm64-v8a
        // This is a special case for the "system" package, where the ABI is
        // dictated by the zygote configuration (and init.rc). We should keep track
        // of this ABI so that we can deal with "normal" applications that run under
        // the same UID correctly.
        if (mPlatformPackage == pkg) {
            pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?
                    Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
        }

derivePackageAbi

private static void derivePackageAbi(PackageParser.Package pkg, File scanFile, String cpuAbiOverride, boolean extractLibs, File appLib32InstallDir)
            throws PackageManagerException {

        // 这里会先设置一个默认的 so 库安装路径
        // Give ourselves some initial paths; we'll come back for another
        // pass once we've determined ABI below.
        setNativeLibraryPaths(pkg, appLib32InstallDir);

        // 如果是系统级别的 APP 则不用每次都提取
        // We would never need to extract libs for forward-locked and external packages,
        // since the container service will do it for us. We shouldn't attempt to
        // extract libs from system app when it was not updated.
        if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
                (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
            extractLibs = false;
        }

        // 本地库目录
        final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
        // 是否有设置过 nativeLibraryRootRequiresIsa
        final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;

        NativeLibraryHelper.Handle handle = null;
        try {
            handle = NativeLibraryHelper.Handle.create(pkg);
            // TODO(multiArch): This can be null for apps that didn't go through the
            // usual installation process. We can calculate it again, like we
            // do during install time.
            //
            // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
            // unnecessary.
            // 获取本地库的 File
            final File nativeLibraryRoot = new File(nativeLibraryRootStr);

            // Null out the abis so that they can be recalculated.
            pkg.applicationInfo.primaryCpuAbi = null;
            pkg.applicationInfo.secondaryCpuAbi = null;
            if (isMultiArch(pkg.applicationInfo)) {

                // 这里处理的是支持两种 abi 的 apk, 这种 apk 的 AndroidManifest.xml 里会设置 android:multiarch 为 true
                // Warn if we've set an abiOverride for multi-lib packages..
                // By definition, we need to copy both 32 and 64 bit libraries for
                // such packages.
                if (pkg.cpuAbiOverride != null
                        && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
                    Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
                }

                int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
                int abi64 = PackageManager.NO_NATIVE_LIBRARIES;

                // 如果有 设备支持的 32 位 abi
                if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
                    if (extractLibs) {
                        // 如果需要导出
                        // 调用 NativeLibraryHelper 的 copyNativeBinariesForSupportedAbi 方法进行 so 库拷贝
                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
                        abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                                nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
                                useIsaSpecificSubdirs);
                    } else {
                        // 调用 NativeLibraryHelper 的 findSupportedAbi 获取该应用的 so 库,在当前系统所能支持的最小 ABI 索引值
                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
                        abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
                    }
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }

                // Shared library native code should be in the APK zip aligned
                if (abi32 >= 0 && pkg.isLibrary() && extractLibs) {
                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                            "Shared library native lib extraction not supported");
                }

                // 检查是否有异常
                maybeThrowExceptionForMultiArchCopy(
                        "Error unpackaging 32 bit native libs for multiarch app.", abi32);

                if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
                    if (extractLibs) {
                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
                        abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                                nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
                                useIsaSpecificSubdirs);
                    } else {
                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
                        abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
                    }
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }

                maybeThrowExceptionForMultiArchCopy(
                        "Error unpackaging 64 bit native libs for multiarch app.", abi64);

                if (abi64 >= 0) {
                    // 如果 abi64 有值,则说明有支持的 64 位库
                    // Shared library native libs should be in the APK zip aligned
                    if (extractLibs && pkg.isLibrary()) {
                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                                "Shared library native lib extraction not supported");
                    }

                    // 设置第一顺位的 abi 即 primaryCpuAbi 为支持的 64 位 ABI
                    pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
                }

                if (abi32 >= 0) {
                    final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                    if (abi64 >= 0) {
                        // 如果同时还支持 64 位
                        if (pkg.use32bitAbi) {
                            pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
                            pkg.applicationInfo.primaryCpuAbi = abi;
                        } else {
                            pkg.applicationInfo.secondaryCpuAbi = abi;
                        }
                    } else {
                        pkg.applicationInfo.primaryCpuAbi = abi;
                    }
                }
            } else {
                // 不支持多平台
                // 获取设备中支持的 CPU 架构
                String[] abiList = (cpuAbiOverride != null) ?
                        new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;

                // Enable gross and lame hacks for apps that are built with old
                // SDK tools. We must scan their APKs for renderscript bitcode and
                // not launch them if it's present. Don't bother checking on devices
                // that don't have 64 bit support.
                // 是否需要 RenderScript 重写,RenderScript 是 Android 平台的一种类 C 脚本语言
                boolean needsRenderScriptOverride = false;
                if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
                        NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
                    abiList = Build.SUPPORTED_32_BIT_ABIS;
                    needsRenderScriptOverride = true;
                }

                final int copyRet;

            if (extractLibs) {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");

                    // 这是一个 JNI 函数,作用就是根据 apk 包里的 lib/ 目录下的 .so 的 ABI 确定返回值,并且把 apk 的 lib 库文件 copy 到创建的 lib 目录
                    copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                            nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
                } else {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");

                    // 这是一个 JNI 函数,作用就是根据 apk 包里的 lib/ 目录下的 .so 的 ABI 确定返回值
                    copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
                }
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

                // 判断是否出现异常
                if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                            "Error unpackaging native libs for app, errorCode=" + copyRet);
                }

                // 根据 copyRet 的值,确定当前 app 的 primaryCpuAbi 值
                if (copyRet >= 0) {
                    // Shared libraries that have native libs must be multi-architecture
                    if (pkg.isLibrary()) {
                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                                "Shared library with native libs must be multiarch");
                    }
                    pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
                } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
                    // 没有本地库
                    pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
                } else if (needsRenderScriptOverride) {
                    pkg.applicationInfo.primaryCpuAbi = abiList[0];
                }

                // 到此处,如果是 arm64-v8a, 并且 copyRet == 1,那么 pkg.applicationInfo.primaryCpuAbi 为 “armeabi-v7a”
            }
        } catch (IOException ioe) {
            Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
        } finally {
            IoUtils.closeQuietly(handle);
        }

        // 到这里有一些 app 已经确定了 primaryCpuAbi 的值,所以再调一次这个函数,更新它使用的 .so 库的安装位置
        // Now that we've calculated the ABIs and determined if it's an internal app,
        // we will go ahead and populate the nativeLibraryPath.
        setNativeLibraryPaths(pkg, appLib32InstallDir);
    }

其中 NativeLibraryHelper.findSupportedAbi 实现在:frameworks/base/core/java/com/android/internal/content/NativeLibraryHelper.java,
这个函数会调用: nativeFindSupportedAbi, 该函数实现在:frameworks/base/core/jni/com_android_internal_content_NativeLibraryHelper.cpp 中,被高通修改过,最终实现函数为:
static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray, jboolean debuggable)
这个的作用是如果 apk 的文件夹下有 so 库,系统会根据 so 库的 ABI 去决定 app 的 ABI

通过这段代码会可以看出:一些 apk 包里 lib 目录下有 .so 文件的,可以通过 .so 文件的 ABI 来确定 app 的 primaryCpuAbi 的值。
对于那些 lib 下没有 .so 文件的 apk, 比如不使用 so 库的或者是系统 app,运行完这个方法之后,primaryCpuAbi 的值仍然是空。

接下来看下 系统app 是如何通过 setBundledAppAbisAndRoots() 方法来确定 primaryCpuAbi 的值的

setBundledAppAbisAndRoots

    private static void setBundledAppAbisAndRoots(PackageParser.Package pkg,
                                           PackageSetting pkgSetting) {
        final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());

        // If "/system/lib64/apkname" exists, assume that is the per-package
        // native library directory to use; otherwise use "/system/lib/apkname".
        final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);

        // 使用 setBundledAppAbi() 方法确定 primaryCpuAbi 值
        setBundledAppAbi(pkg, apkRoot, apkName);
        // pkgSetting might be null during rescan following uninstall of updates
        // to a bundled app, so accommodate that possibility.  The settings in
        // that case will be established later from the parsed package.
        //
        // If the settings aren't null, sync them up with what we've just derived.
        // note that apkRoot isn't stored in the package settings.
        if (pkgSetting != null) {
            pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
            pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
        }
    }

    /**
     * Deduces the ABI of a bundled app and sets the relevant fields on the
     * parsed pkg object.
     *
     * @param apkRoot the root of the installed apk, something like {@code /system} or {@code /oem}
     *        under which system libraries are installed.
     * @param apkName the name of the installed package.
     */
    private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {
        final File codeFile = new File(pkg.codePath);

        final boolean has64BitLibs;
        final boolean has32BitLibs;
        if (isApkFile(codeFile)) {
            // 只有 framework-res.apk 这个包会进这个 if 分支,has64BitLibs 和 has32BitLibs 的值都是 false
            // 在前面 scanPackageDirtyLI 里有说过,这个 app 的 primaryCpuAbi 的值是 arm64-v8a
            // Monolithic install
            has64BitLibs = (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists();
            has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists();
        } else {
            // 对于其它的 app, codeFile 是 apk 所在的路径

            // Cluster install
            final File rootDir = new File(codeFile, LIB_DIR_NAME);
            if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS)
                    && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) {

                // 通过判断 /system/app/${APP_NAME}/lib64 这个文件夹是否存在决定 has64BitLibs 的值
                final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]);
                has64BitLibs = (new File(rootDir, isa)).exists();
            } else {
                has64BitLibs = false;
            }

            if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS)
                    && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) {
                
                // 通过判断 /system/app/${APP_NAME}/lib 这个文件夹是否存在决定 has32BitLibs 的值
                final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]);
                has32BitLibs = (new File(rootDir, isa)).exists();
            } else {
                has32BitLibs = false;
            }
        }

        // 下面这一段会根据 has64BitLibs 和 has32BitLibs 的值来确定 app 的 primaryCpuAbi 的值
        if (has64BitLibs && !has32BitLibs) {
            // The package has 64 bit libs, but not 32 bit libs. Its primary
            // ABI should be 64 bit. We can safely assume here that the bundled
            // native libraries correspond to the most preferred ABI in the list.

            pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
            pkg.applicationInfo.secondaryCpuAbi = null;
        } else if (has32BitLibs && !has64BitLibs) {
            // The package has 32 bit libs but not 64 bit libs. Its primary
            // ABI should be 32 bit.

            pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
            pkg.applicationInfo.secondaryCpuAbi = null;
        } else if (has32BitLibs && has64BitLibs) {
            // The application has both 64 and 32 bit bundled libraries. We check
            // here that the app declares multiArch support, and warn if it doesn't.
            //
            // We will be lenient here and record both ABIs. The primary will be the
            // ABI that's higher on the list, i.e, a device that's configured to prefer
            // 64 bit apps will see a 64 bit primary ABI,

            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {
                Slog.e(TAG, "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
            }

            if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
                pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
                pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
            } else {
                pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
                pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
            }
        } else {
            pkg.applicationInfo.primaryCpuAbi = null;
            pkg.applicationInfo.secondaryCpuAbi = null;
        }
    }

    ......

    public static String getPreferredInstructionSet() {
        return PREFERRED_INSTRUCTION_SET;
    }

    private static final String PREFERRED_INSTRUCTION_SET =
            VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);;

dalvik.system.VMRuntime 实现在:libcore/libart/src/main/java/dalvik/system/VMRuntime.java

public final class VMRuntime {


    private static final Map<String, String> ABI_TO_INSTRUCTION_SET_MAP = new HashMap<String, String>(16);
    static {
        ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm");
        ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm");
        ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips");
        ABI_TO_INSTRUCTION_SET_MAP.put("mips64", "mips64");
        ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86");
        ABI_TO_INSTRUCTION_SET_MAP.put("x86_64", "x86_64");
        ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64");
    }

    public static String getInstructionSet(String abi) {
        final String instructionSet = ABI_TO_INSTRUCTION_SET_MAP.get(abi);
        if (instructionSet == null) {
            throw new IllegalArgumentException("Unsupported ABI: " + abi);
        }

        return instructionSet;
    }

    public static boolean is64BitInstructionSet(String instructionSet) {
        return "arm64".equals(instructionSet) ||
                "x86_64".equals(instructionSet) ||
                "mips64".equals(instructionSet);
    }

    public static boolean is64BitAbi(String abi) {
        return is64BitInstructionSet(getInstructionSet(abi));
    }

根据上面的代码,可以知道:

对 系统app 而言,根据 /system/app/${APP_NAME}/lib/system/app/${APP_NAME}/lib64 这两个文件夹是否存在,来确定它的 primaryCpuAbi 的值。
当然,如果 系统app 不存在上述两个文件夹,那它的 primaryCpuAbi 的值仍然为空,所以在经过 scanPackageDirtyLI() 方法之后,会存在以下四种情况:

  1. 无论是 系统app 还是 第三方app, 如果 apk 包里 lib 目录存在 .so 文件,会根据 .so 文件来确定 primaryCpuAbi 的值
  2. 如果是 系统app, apk 包里又不存在 .so 文件,就会进一步根据 /system/app/${APP_NAME}/lib/system/app/${APP_NAME}/lib64 这两个文件夹是否存在,来确定它的 primaryCpuAbi 的值
  3. 对于 framework-res.apk 为个特殊的apk文件,它的 primaryCpuAbi 的值由虚拟机是什么架构来决定,在我这里,它是 arm64-v8a
  4. 对于其余的 apk, 它们的 primaryCpuAbi 的值仍然为空

adjustCpuAbisForSharedUserLPw

先来看下 adjustCpuAbisForSharedUserLPw 的调用位置,在 PKMS 的构造函数里:

public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    ... ...

    scanDirLI();

    ... ...

    // 当所有的 apk 文件解析完之后,对使用了相同 UID 的 apk, 调用 adjustCpuAbisForSharedUserLPw
    for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
        // setting.packages 是所有使用相同 UID 的 apk 的集合
        adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
                false /* force dexopt */, false /* defer dexopt */);
    }

    ... ...
}

adjustCpuAbisForSharedUserLPw 的作用就是调整使用相同 UID 的package 的 primaryCpuAbi 的值,将那些还没有确定 primaryCpuAbi 的 package 用已经确定了的 Abi 的值代替。
这里将是那些没有确定 primaryCpuAbi 的 apk 再次确定 abi 值的最后一次机会,如果在这里还无法确定,那就在启动进程时,使用系统默认值。

private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
        PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt) {
    String requiredInstructionSet = null;
    ... ...

    PackageSetting requirer = null;
    for (PackageSetting ps : packagesForUser) {
        if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
            if (ps.primaryCpuAbiString == null) {
                continue;
            }
            // 这个 for 循环的作用就是遍历所有使用相同 UID 的 package,把遍历过程中遇到的第一个确定 primaryCpuAbi
            // 的那个 package 取出来,保存到 requirer 中
            final String instructionSet = VMRuntime.getInstructionSet(ps.primaryCpuAbiString);
            if (requiredInstructionSet == null) {
                // 只取第一个被遍历到的
                requiredInstructionSet = instructionSet;
                requirer = ps;
            }
        }
    }

    if (requiredInstructionSet != null) {
        String adjustedAbi;
        if (requirer != null) {
            // 证明在这个集合中找到了已经确定 primaryCpuAbi 的那个 package
            adjustedAbi = requirer.primaryCpuAbiString;
        } else {
            // scannedPackage == null 时,这种情况不存在,所以不考虑这里
        }

        for (PackageSetting ps : packagesForUser) {
            if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
                if (ps.primaryCpuAbiString != null) {
                    continue;
                }
                // 将 adjustedAbi 的值给那些使用同一个 UID 并且 primaryCpuAbi 是空的 package
                ps.primaryCpuAbiString = adjustedAbi;
                if (ps.pkg != null && ps.pkg.applicationInfo != null) {
                    ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;

                    ... ...
                }
            }
        }
    }
}

总结

最后来总结一下 Android 系统确定 app 进程关联哪种 ABI 的流程:

  1. 如果 apk 包中 lib 文件夹下有 .so 库,就根据这个 .so 库的架构模式,确定 app 的 primaryCpuAbi 的值
  2. 对于 system app, 如果没法通过第一步确定 primaryCpuAbi 的值,PKMS 会根据 /system/app/${APP_NAME}/lib/system/app/${APP_NAME}/lib64 这两个文件夹是否存在,来确定它的 primaryCpuAbi 的值
  3. 对于还没有确定的 app, 在最后还会将自己的 primaryCpuAbi 值与和他使用相同 UID 的 package 的值设成一样
  4. 对于到这里还没有确认 primaryCpuAbi 的 app,就会在启动进程时使用 ro.product.cpu.abilist 这个 property 的值的第一项作为它关联的 ABI

保存到 package.xml

在 scanPackageDirtyLI 函数的最后部分,会调用 commitPackageSettings 保存获取到的 pkginfo,在 commitPackageSettings 方法中会调用:mSettings.insertPackageSettingLPw(pkgSetting, pkg)

[Settings.java]
void insertPackageSettingLPw(PackageSetting p, PackageParser.Package pkg) {

    addPackageSettingLPw(p, p.sharedUser);
}

private void addPackageSettingLPw(PackageSetting p, SharedUserSetting sharedUser) {
    //这里会加入到mPackages中
    mPackages.put(p.name, p);
}

PKMS 的构造函数最后会执行 mSettings.writeLPr() ,writeLPr 函数会把 mPackages 相关信息写入到 package.xml 等文件中。

上一篇下一篇

猜你喜欢

热点阅读