ABI(Application Binary Interface,应用程序二进制接口)是对目标代码的约束。 ABI是和平台密切相关的,这相当于cpu支持的指令集, 在Android适配过程中,支持的api要写在BoardConfig.mk文件中,也就是板级信息,我截取pixel的配置,如下
TARGET_ARCH := arm64 TARGET_ARCH_VARIANT := armv8-a TARGET_CPU_ABI := arm64-v8a TARGET_CPU_ABI2 := #TODO: add kryo support? TARGET_CPU_VARIANT := kryo TARGET_CPU_VARIANT := generic TARGET_2ND_ARCH := arm TARGET_2ND_ARCH_VARIANT := armv7-a-neon TARGET_2ND_CPU_ABI := armeabi-v7a TARGET_2ND_CPU_ABI2 := armeabi TARGET_2ND_CPU_VARIANT := krait这些配置最终会生成system properties,指导Pms来选择安装的abi。 举个例子, pixel
[ro.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi] [ro.product.cpu.abilist32]: [armeabi-v7a,armeabi] [ro.product.cpu.abilist64]: [arm64-v8a]上述配置表示该手机的cpu支持64位abi和32位abi, 支持的32位abi为armeabi-v7a和armeabi, 支持的64位abi为arm64-v8a。 全部支持的abi为arm64-v8a,armeabi-v7a,armeabi。
Pms在安装应用的时候,cpu支持的abi是一手资料, 还要看应用程序提供哪些abi的库,和系统支持的abi是否匹配, 匹配则可以进行安装,否则不可以安装。 一般32位cpu只支持32位的abi, 而64位的cpu一般可以兼容32位abi。所以我们在64位的手机上可以看到ro.product.cpu.abilist64和o.product.cpu.abilist32 都是有值的。 另外其实x86cpu也可以支持arm的abi,这要通过native bridage来实现,我们这里并不关心。
abi的体现在Pms一端其实就是安装哪个abi类型的lib库。 说完平台一方再说应用一方,影响应用使用abi的情况有两种
安装的时候指定override abi, 比如pm install --abi 选项AndroidManifest.xml 中的multiArch 选项,表示安装该应用需要安装32位和64位版本的库(一般作为共享库给其他应用使用)。下面来看下Pms安装应用的时候具体安装什么abi的库的情况。
/** * Derive the ABI of a non-system package located at {@code scanFile}. This information * is derived purely on the basis of the contents of {@code scanFile} and * {@code cpuAbiOverride}. * * If {@code extractLibs} is true, native libraries are extracted from the app if required. */ private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs) throws PackageManagerException { // Give ourselves some initial paths; we'll come back for another // pass once we've determined ABI below. setNativeLibraryPaths(pkg, sAppLib32InstallDir); // 1 主要设置pkg.applicationInfo.nativeLibraryRootDir文件夹,一般为/data/app/\${packageName}/lib // We shouldn't attempt to extract libs from system app when it was not updated. if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { //2 extractLibs 表示使用从apk中解压lib, 由于系统应用在system分区,是只读的,不支持解压。 extractLibs = false; } final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir; final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa; NativeLibraryHelper.Handle handle = null; try { // 3 创建一个NativeLibraryHelper,主要用于abi的选择。 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. 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)) { // 4 支持多平台的情况 // 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. // 4.1 多平台的情况由于要拷贝多份库,所以可以忽略cpuAbiOverride 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; if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { // 4.2 安装32位库 if (extractLibs) { // 4.2.1 需要解压库的情况,会从apk文件中拷贝库文件到/data/app/\${packageName}/lib/arm/ 下面,返回abi在SUPPORTED_32_BIT_ABIS的下标 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, useIsaSpecificSubdirs); } else { // 4.2.2 不需要解压的情况,直接返回abi在SUPPORTED_32_BIT_ABIS的下标 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) { // 4.3 处理64位库 if (extractLibs) { // 4.3.1 需要解压库的情况,会从apk文件中拷贝库文件到/data/app/\${packageName}/lib/arm64/ 下面,返回abi在SUPPORTED_64_BIT_ABIS的下标 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, useIsaSpecificSubdirs); } else { // 4.3.2 不需要解压的情况,直接返回abi在SUPPORTED_64_BIT_ABIS的下标 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); // 4.4 设置primaryCpuAbi 和 secondaryCpuAbi, primaryCpuAbi表示应用启动使用的abi,secondaryCpuAbi为支持的其他abi if (abi64 >= 0) { // 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"); } pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64]; } if (abi32 >= 0) { final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32]; if (abi64 >= 0) { if (pkg.use32bitAbi) { pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi; pkg.applicationInfo.primaryCpuAbi = abi; } else { pkg.applicationInfo.secondaryCpuAbi = abi; } } else { pkg.applicationInfo.primaryCpuAbi = abi; } } } else { // 5 不支持多平台的情况只选择一种abi即可,设置了cpuAbiOverride则使用cpuAbiOverride, 否则使用ro.product.cpu.abilist属性支持的abi 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. 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) { // 4.1.1 解压abi Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, nativeLibraryRoot, abiList, useIsaSpecificSubdirs); } else { // 4.1.2 拷贝abi Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); 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); } // 4.2 设置 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]; } } } catch (IOException ioe) { Slog.e(TAG, "Unable to get canonical file " + ioe.toString()); } finally { IoUtils.closeQuietly(handle); } // Now that we've calculated the ABIs and determined if it's an internal app, // we will go ahead and populate the nativeLibraryPath. // 6 设置ApplicationInfo的 库路径信息,供应用启动时加载。 setNativeLibraryPaths(pkg, sAppLib32InstallDir); }解释以注释形式给出,涉及到的路径以pixel arm64平台为例。
再来详细看下setNativeLibraryPaths
/** * Derive and set the location of native libraries for the given package, * which varies depending on where and how the package was installed. */ private static void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir) { final ApplicationInfo info = pkg.applicationInfo; final String codePath = pkg.codePath; final File codeFile = new File(codePath); final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp(); info.nativeLibraryRootDir = null; info.nativeLibraryRootRequiresIsa = false; info.nativeLibraryDir = null; info.secondaryNativeLibraryDir = null; if (isApkFile(codeFile)) { // Monolithic install if (bundledApp) { // 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(info.sourceDir); final boolean is64Bit = VMRuntime.is64BitInstructionSet( getPrimaryInstructionSet(info)); // This is a bundled system app so choose the path based on the ABI. // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this // is just the default path. final String apkName = deriveCodePathName(codePath); final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME; info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir, apkName).getAbsolutePath(); if (info.secondaryCpuAbi != null) { final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME; info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot), secondaryLibDir, apkName).getAbsolutePath(); } } else { final String apkName = deriveCodePathName(codePath); info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName) .getAbsolutePath(); } info.nativeLibraryRootRequiresIsa = false; info.nativeLibraryDir = info.nativeLibraryRootDir; } else { // Cluster install // 1 info.nativeLibraryRootDir = /data/app/\${packageName}/lib info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath(); info.nativeLibraryRootRequiresIsa = true; // 2 info.nativeLibraryDir = /data/app/\${packageName}/lib/\${primaryCpuAbi_isa} info.nativeLibraryDir = new File(info.nativeLibraryRootDir, getPrimaryInstructionSet(info)).getAbsolutePath(); if (info.secondaryCpuAbi != null) { // 3 info.secondaryNativeLibraryDir = /data/app/\${packageName}/lib/\${secondaryCpuAbi_isa} info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir, VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath(); } } }调用完setNativeLibraryPaths 函数ApplicationInfo的库信息就准备好了。
