美文网首页
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 确认过程

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