Android 8 应用安装时 ABI 确认过程
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() 方法之后,会存在以下四种情况:
- 无论是 系统app 还是 第三方app, 如果 apk 包里 lib 目录存在 .so 文件,会根据 .so 文件来确定 primaryCpuAbi 的值
- 如果是 系统app, apk 包里又不存在 .so 文件,就会进一步根据
/system/app/${APP_NAME}/lib
和/system/app/${APP_NAME}/lib64
这两个文件夹是否存在,来确定它的 primaryCpuAbi 的值 - 对于 framework-res.apk 为个特殊的apk文件,它的 primaryCpuAbi 的值由虚拟机是什么架构来决定,在我这里,它是 arm64-v8a
- 对于其余的 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 的流程:
- 如果 apk 包中 lib 文件夹下有 .so 库,就根据这个 .so 库的架构模式,确定 app 的 primaryCpuAbi 的值
- 对于 system app, 如果没法通过第一步确定 primaryCpuAbi 的值,PKMS 会根据
/system/app/${APP_NAME}/lib
和/system/app/${APP_NAME}/lib64
这两个文件夹是否存在,来确定它的 primaryCpuAbi 的值 - 对于还没有确定的 app, 在最后还会将自己的 primaryCpuAbi 值与和他使用相同 UID 的 package 的值设成一样
- 对于到这里还没有确认 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 等文件中。