美文网首页
Android 8 应用安装时 ABI 确认过程

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

作者: jerryyyq | 来源:发表于2019-06-10 12:27 被阅读0次

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 位的。

确定的时机

  • 安装一个 apk 时
    会调用:private void installPackageLI(InstallArgs args, PackageInstalledInfo res), 在这个函数中会调用:
    private static void derivePackageAbi(PackageParser.Package pkg, File scanFile, String cpuAbiOverride, boolean extractLibs, File appLib32InstallDir)

  • 系统启动时
    在 PKMS 的构造函数中,会调用 scanDirLI() 逐个解析系统里的所有 apk 文件,这个函数会调用: scanPackageLI,
    而 scanPackageLI 会调用: scanPackageDirtyLI, 在 scanPackageDirtyLI 函数中会调用: derivePackageAbi 等函数来确定 ABI

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 */);
    }

    ... ...
}
  • ActivityManagerService 启动一个应用时
    启动时,如果没能找到有确认的 primaryCpuAbi,就使用 ro.product.cpu.abilist 这个 property 的值的第一项作为它关联的 ABI

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 等文件中。

相关文章

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

    packages.xml 一个 apk 安装后,会在 /data/system/packages.xml 中添加纪...

  • Android ABI的浅析

    Android ABI的概念 ABI全称:Application binary interface(应用程序二进制...

  • Android ABI

    什么是ABI? Android Binary Interface 简称ABI CPU 和架构 不同 Android...

  • Android中的ABI

    Android中的ABI ABI是Application Binary Interface的缩写。 ABI常表示两...

  • Android之UID and PID

    | 标签: 安装应用失败的25种原因 android安装应用失败 android中安装应用失 android之ui...

  • gradle的abi

    gradle 文件中对abi的配置 官方文档对abi的介绍 Android手机支持的abi: 各个abi的含义 通...

  • Android之64位和32位区分

    .. ABI 是什么 在Android编码时候 ,我们经常会看到ABI 。ABI的意思是 Application ...

  • Android 免安装应用

    什么是免安装应用 Android 免安装应用使原生 Android 应用能够在启动网址时运行,无需安装应用。 当 ...

  • [Andriod] apk安装出现:未针对此用户安装

    ''未针对此用户安装'' Android在安装apk到设备时,出现安装失败:应用未安装,应用列表中会标识:未针对此...

  • Android FileProvider之应用安装

    应用安装(FileProvider) 应用内安装apk时涉及到通过Intent在两个应用间共享资源,Android...

网友评论

      本文标题:Android 8 应用安装时 ABI 确认过程

      本文链接:https://www.haomeiwen.com/subject/xctzxctx.html