diff --git a/services/core/java/com/android/server/pm/InstructionSets.java b/services/core/java/com/android/server/pm/InstructionSets.java index f326f1d20c46f..ec48713b08744 100644 --- a/services/core/java/com/android/server/pm/InstructionSets.java +++ b/services/core/java/com/android/server/pm/InstructionSets.java @@ -22,11 +22,11 @@ import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArraySet; +import dalvik.system.VMRuntime; + import java.util.ArrayList; import java.util.List; -import dalvik.system.VMRuntime; - /** * Provides various methods for obtaining and converting of instruction sets. * @@ -113,12 +113,15 @@ public class InstructionSets { return allInstructionSets; } - public static String getPrimaryInstructionSet(ApplicationInfo info) { - if (info.primaryCpuAbi == null) { + /** + * Calculates the primary instruction set based on the computed Abis of a given package. + */ + public static String getPrimaryInstructionSet(PackageAbiHelper.Abis abis) { + if (abis.primary == null) { return getPreferredInstructionSet(); } - return VMRuntime.getInstructionSet(info.primaryCpuAbi); + return VMRuntime.getInstructionSet(abis.primary); } } diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java index da4558277b95d..6f46564068d91 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelper.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java @@ -18,56 +18,44 @@ package com.android.server.pm; import android.annotation.Nullable; import android.content.pm.PackageParser; +import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import java.io.File; -import java.util.List; import java.util.Set; @VisibleForTesting interface PackageAbiHelper { /** - * Derive and set the location of native libraries for the given package, + * Derive and get the location of native libraries for the given package, * which varies depending on where and how the package was installed. - * - * WARNING: This API enables modifying of the package. - * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary */ - void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir); + NativeLibraryPaths getNativeLibraryPaths( + PackageParser.Package pkg, File appLib32InstallDir); /** - * Calculate the abis and roots for a bundled app. These can uniquely - * be determined from the contents of the system partition, i.e whether - * it contains 64 or 32 bit shared libraries etc. We do not validate any - * of this information, and instead assume that the system was built - * sensibly. - * - * WARNING: This API enables modifying of the package. - * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary + * Calculate the abis for a bundled app. These can uniquely be determined from the contents of + * the system partition, i.e whether it contains 64 or 32 bit shared libraries etc. We do not + * validate any of this information, and instead assume that the system was built sensibly. */ - void setBundledAppAbisAndRoots(PackageParser.Package pkg, - PackageSetting pkgSetting); + Abis getBundledAppAbis(PackageParser.Package pkg); /** - * 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}. + * Derive the ABI of a non-system package located at {@code pkg}. This information + * is derived purely on the basis of the contents of {@code pkg} and {@code cpuAbiOverride}. * * If {@code extractLibs} is true, native libraries are extracted from the app if required. - * - * WARNING: This API enables modifying of the package. - * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary */ - void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride, - boolean extractLibs) + Pair derivePackageAbi( + PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs) throws PackageManagerException; /** - * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. - * i.e, so that all packages can be run inside a single process if required. + * Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all + * match. i.e, so that all packages can be run inside a single process if required. * - * Optionally, callers can pass in a parsed package via {@code newPackage} in which case + * Optionally, callers can pass in a parsed package via {@code scannedPackage} in which case * this function will either try and make the ABI for all packages in * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This @@ -76,10 +64,72 @@ interface PackageAbiHelper { * NOTE: We currently only match for the primary CPU abi string. Matching the secondary * adds unnecessary complexity. * - * WARNING: This API enables modifying of the package. - * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary + * @return the calculated primary abi that should be set for all non-specified packages + * belonging to the shared user. */ @Nullable - List adjustCpuAbisForSharedUser( + String getAdjustedAbiForSharedUser( Set packagesForUser, PackageParser.Package scannedPackage); + + /** + * The native library paths and related properties that should be set on a + * {@link android.content.pm.PackageParser.Package}. + */ + final class NativeLibraryPaths { + public final String nativeLibraryRootDir; + public final boolean nativeLibraryRootRequiresIsa; + public final String nativeLibraryDir; + public final String secondaryNativeLibraryDir; + + @VisibleForTesting + NativeLibraryPaths(String nativeLibraryRootDir, + boolean nativeLibraryRootRequiresIsa, String nativeLibraryDir, + String secondaryNativeLibraryDir) { + this.nativeLibraryRootDir = nativeLibraryRootDir; + this.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa; + this.nativeLibraryDir = nativeLibraryDir; + this.secondaryNativeLibraryDir = secondaryNativeLibraryDir; + } + + public void applyTo(PackageParser.Package pkg) { + pkg.applicationInfo.nativeLibraryRootDir = nativeLibraryRootDir; + pkg.applicationInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa; + pkg.applicationInfo.nativeLibraryDir = nativeLibraryDir; + pkg.applicationInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir; + } + } + + /** + * The primary and secondary ABIs that should be set on a package and its package setting. + */ + final class Abis { + public final String primary; + public final String secondary; + + @VisibleForTesting + Abis(String primary, String secondary) { + this.primary = primary; + this.secondary = secondary; + } + + Abis(PackageParser.Package pkg) { + this(pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi); + } + + public void applyTo(PackageParser.Package pkg) { + pkg.applicationInfo.primaryCpuAbi = primary; + pkg.applicationInfo.secondaryCpuAbi = secondary; + } + public void applyTo(PackageSetting pkgSetting) { + // 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 derived. + if (pkgSetting != null) { + pkgSetting.primaryCpuAbiString = primary; + pkgSetting.secondaryCpuAbiString = secondary; + } + } + } } diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java index 4ecc888cb33c6..1d3d24c270412 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java @@ -34,6 +34,7 @@ import android.os.Environment; import android.os.FileUtils; import android.os.Trace; import android.text.TextUtils; +import android.util.Pair; import android.util.Slog; import com.android.internal.content.NativeLibraryHelper; @@ -45,430 +46,10 @@ import libcore.io.IoUtils; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.Set; final class PackageAbiHelperImpl implements PackageAbiHelper { - @Override - public 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 - info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath(); - info.nativeLibraryRootRequiresIsa = true; - - info.nativeLibraryDir = new File(info.nativeLibraryRootDir, - getPrimaryInstructionSet(info)).getAbsolutePath(); - - if (info.secondaryCpuAbi != null) { - info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir, - VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath(); - } - } - } - - @Override - public 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(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 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)) { - // 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 { - // 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])) { - 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])) { - final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]); - has32BitLibs = (new File(rootDir, isa)).exists(); - } else { - has32BitLibs = false; - } - } - - 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(PackageManagerService.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; - } - } - - @Override - public 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, PackageManagerService.sAppLib32InstallDir); - - // We shouldn't attempt to extract libs from system app when it was not updated. - if (PackageManagerService.isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { - extractLibs = false; - } - - final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir; - 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. - 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 ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0) { - // 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(PackageManagerService.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) { - if (extractLibs) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); - abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, - useIsaSpecificSubdirs); - } else { - 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) { - // 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 { - 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) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); - copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, abiList, useIsaSpecificSubdirs); - } else { - 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); - } - - 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(PackageManagerService.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. - setNativeLibraryPaths(pkg, PackageManagerService.sAppLib32InstallDir); - } - - /** - * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. - * i.e, so that all packages can be run inside a single process if required. - * - * Optionally, callers can pass in a parsed package via {@code newPackage} in which case - * this function will either try and make the ABI for all packages in - * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of - * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This - * variant is used when installing or updating a package that belongs to a shared user. - * - * NOTE: We currently only match for the primary CPU abi string. Matching the secondary - * adds unnecessary complexity. - */ - @Override - @Nullable - public List adjustCpuAbisForSharedUser( - Set packagesForUser, PackageParser.Package scannedPackage) { - List changedAbiCodePath = null; - String requiredInstructionSet = null; - if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) { - requiredInstructionSet = VMRuntime.getInstructionSet( - scannedPackage.applicationInfo.primaryCpuAbi); - } - - PackageSetting requirer = null; - for (PackageSetting ps : packagesForUser) { - // If packagesForUser contains scannedPackage, we skip it. This will happen - // when scannedPackage is an update of an existing package. Without this check, - // we will never be able to change the ABI of any package belonging to a shared - // user, even if it's compatible with other packages. - if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { - if (ps.primaryCpuAbiString == null) { - continue; - } - - final String instructionSet = - VMRuntime.getInstructionSet(ps.primaryCpuAbiString); - if (requiredInstructionSet != null - && !instructionSet.equals(requiredInstructionSet)) { - // We have a mismatch between instruction sets (say arm vs arm64) warn about - // this but there's not much we can do. - String errorMessage = "Instruction set mismatch, " - + ((requirer == null) ? "[caller]" : requirer) - + " requires " + requiredInstructionSet + " whereas " + ps - + " requires " + instructionSet; - Slog.w(PackageManagerService.TAG, errorMessage); - } - - if (requiredInstructionSet == null) { - requiredInstructionSet = instructionSet; - requirer = ps; - } - } - } - - if (requiredInstructionSet != null) { - String adjustedAbi; - if (requirer != null) { - // requirer != null implies that either scannedPackage was null or that - // scannedPackage did not require an ABI, in which case we have to adjust - // scannedPackage to match the ABI of the set (which is the same as - // requirer's ABI) - adjustedAbi = requirer.primaryCpuAbiString; - if (scannedPackage != null) { - scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi; - } - } else { - // requirer == null implies that we're updating all ABIs in the set to - // match scannedPackage. - adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi; - } - - for (PackageSetting ps : packagesForUser) { - if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { - if (ps.primaryCpuAbiString != null) { - continue; - } - - ps.primaryCpuAbiString = adjustedAbi; - if (ps.pkg != null && ps.pkg.applicationInfo != null - && !TextUtils.equals( - adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) { - ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi; - if (PackageManagerService.DEBUG_ABI_SELECTION) { - Slog.i(PackageManagerService.TAG, - "Adjusting ABI for " + ps.name + " to " + adjustedAbi - + " (requirer=" - + (requirer != null ? requirer.pkg : "null") - + ", scannedPackage=" - + (scannedPackage != null ? scannedPackage : "null") - + ")"); - } - if (changedAbiCodePath == null) { - changedAbiCodePath = new ArrayList<>(); - } - changedAbiCodePath.add(ps.codePathString); - } - } - } - } - return changedAbiCodePath; - } - private static String calculateBundledApkRoot(final String codePathString) { final File codePath = new File(codePathString); final File codeRoot; @@ -539,4 +120,409 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { } } + @Override + public NativeLibraryPaths getNativeLibraryPaths( + PackageParser.Package pkg, File appLib32InstallDir) { + return getNativeLibraryPaths(new Abis(pkg), appLib32InstallDir, pkg.codePath, + pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(), + pkg.applicationInfo.isUpdatedSystemApp()); + } + + private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis, + final File appLib32InstallDir, final String codePath, final String sourceDir, + final boolean isSystemApp, final boolean isUpdatedSystemApp) { + final File codeFile = new File(codePath); + final boolean bundledApp = isSystemApp && !isUpdatedSystemApp; + + final String nativeLibraryRootDir; + final boolean nativeLibraryRootRequiresIsa; + final String nativeLibraryDir; + final String secondaryNativeLibraryDir; + + 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(sourceDir); + final boolean is64Bit = VMRuntime.is64BitInstructionSet( + getPrimaryInstructionSet(abis)); + + // 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; + nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir, + apkName).getAbsolutePath(); + + if (abis.secondary != null) { + final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME; + secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot), + secondaryLibDir, apkName).getAbsolutePath(); + } else { + secondaryNativeLibraryDir = null; + } + } else { + final String apkName = deriveCodePathName(codePath); + nativeLibraryRootDir = new File(appLib32InstallDir, apkName) + .getAbsolutePath(); + secondaryNativeLibraryDir = null; + } + + nativeLibraryRootRequiresIsa = false; + nativeLibraryDir = nativeLibraryRootDir; + } else { + // Cluster install + nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath(); + nativeLibraryRootRequiresIsa = true; + + nativeLibraryDir = new File(nativeLibraryRootDir, + getPrimaryInstructionSet(abis)).getAbsolutePath(); + + if (abis.secondary != null) { + secondaryNativeLibraryDir = new File(nativeLibraryRootDir, + VMRuntime.getInstructionSet(abis.secondary)).getAbsolutePath(); + } else { + secondaryNativeLibraryDir = null; + } + } + return new NativeLibraryPaths(nativeLibraryRootDir, nativeLibraryRootRequiresIsa, + nativeLibraryDir, secondaryNativeLibraryDir); + } + + @Override + public Abis getBundledAppAbis(PackageParser.Package pkg) { + 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); + final Abis abis = getBundledAppAbi(pkg, apkRoot, apkName); + return abis; + } + + /** + * 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 Abis getBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) { + final File codeFile = new File(pkg.codePath); + + final boolean has64BitLibs; + final boolean has32BitLibs; + + final String primaryCpuAbi; + final String secondaryCpuAbi; + if (isApkFile(codeFile)) { + // 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 { + // 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])) { + 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])) { + final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]); + has32BitLibs = (new File(rootDir, isa)).exists(); + } else { + has32BitLibs = false; + } + } + + 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. + + primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + secondaryCpuAbi = null; + } else if (has32BitLibs && !has64BitLibs) { + // The package has 32 bit libs but not 64 bit libs. Its primary + // ABI should be 32 bit. + + primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + 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(PackageManagerService.TAG, + "Package " + pkg + " has multiple bundled libs, but is not multiarch."); + } + + if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) { + primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + } else { + primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + } + } else { + primaryCpuAbi = null; + secondaryCpuAbi = null; + } + return new Abis(primaryCpuAbi, secondaryCpuAbi); + } + + @Override + public Pair 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. + final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(new Abis(pkg), + PackageManagerService.sAppLib32InstallDir, pkg.codePath, + pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(), + pkg.applicationInfo.isUpdatedSystemApp()); + + // We shouldn't attempt to extract libs from system app when it was not updated. + if (PackageManagerService.isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { + extractLibs = false; + } + + final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir; + final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa; + + String primaryCpuAbi = null; + String secondaryCpuAbi = null; + + 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. + final File nativeLibraryRoot = new File(nativeLibraryRootStr); + + // Null out the abis so that they can be recalculated. + primaryCpuAbi = null; + secondaryCpuAbi = null; + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0) { + // 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(PackageManagerService.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) { + if (extractLibs) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); + abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, + useIsaSpecificSubdirs); + } else { + 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) { + // 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"); + } + 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) { + secondaryCpuAbi = primaryCpuAbi; + primaryCpuAbi = abi; + } else { + secondaryCpuAbi = abi; + } + } else { + primaryCpuAbi = abi; + } + } + } else { + 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) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); + copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, abiList, useIsaSpecificSubdirs); + } else { + 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); + } + + 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"); + } + primaryCpuAbi = abiList[copyRet]; + } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES + && cpuAbiOverride != null) { + primaryCpuAbi = cpuAbiOverride; + } else if (needsRenderScriptOverride) { + primaryCpuAbi = abiList[0]; + } + } + } catch (IOException ioe) { + Slog.e(PackageManagerService.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. + + final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi); + return new Pair<>(abis, + getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir, + pkg.codePath, pkg.applicationInfo.sourceDir, + pkg.applicationInfo.isSystemApp(), + pkg.applicationInfo.isUpdatedSystemApp())); + } + + /** + * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. + * i.e, so that all packages can be run inside a single process if required. + * + * Optionally, callers can pass in a parsed package via {@code newPackage} in which case + * this function will either try and make the ABI for all packages in + * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of + * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This + * variant is used when installing or updating a package that belongs to a shared user. + * + * NOTE: We currently only match for the primary CPU abi string. Matching the secondary + * adds unnecessary complexity. + */ + @Override + @Nullable + public String getAdjustedAbiForSharedUser( + Set packagesForUser, PackageParser.Package scannedPackage) { + String requiredInstructionSet = null; + if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) { + requiredInstructionSet = VMRuntime.getInstructionSet( + scannedPackage.applicationInfo.primaryCpuAbi); + } + + PackageSetting requirer = null; + for (PackageSetting ps : packagesForUser) { + // If packagesForUser contains scannedPackage, we skip it. This will happen + // when scannedPackage is an update of an existing package. Without this check, + // we will never be able to change the ABI of any package belonging to a shared + // user, even if it's compatible with other packages. + if (scannedPackage != null && scannedPackage.packageName.equals(ps.name)) { + continue; + } + if (ps.primaryCpuAbiString == null) { + continue; + } + + final String instructionSet = + VMRuntime.getInstructionSet(ps.primaryCpuAbiString); + if (requiredInstructionSet != null && !requiredInstructionSet.equals(instructionSet)) { + // We have a mismatch between instruction sets (say arm vs arm64) warn about + // this but there's not much we can do. + String errorMessage = "Instruction set mismatch, " + + ((requirer == null) ? "[caller]" : requirer) + + " requires " + requiredInstructionSet + " whereas " + ps + + " requires " + instructionSet; + Slog.w(PackageManagerService.TAG, errorMessage); + } + + if (requiredInstructionSet == null) { + requiredInstructionSet = instructionSet; + requirer = ps; + } + } + + if (requiredInstructionSet == null) { + return null; + } + final String adjustedAbi; + if (requirer != null) { + // requirer != null implies that either scannedPackage was null or that + // scannedPackage did not require an ABI, in which case we have to adjust + // scannedPackage to match the ABI of the set (which is the same as + // requirer's ABI) + adjustedAbi = requirer.primaryCpuAbiString; + } else { + // requirer == null implies that we're updating all ABIs in the set to + // match scannedPackage. + adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi; + } + return adjustedAbi; + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index bf6f51510389c..56d2258b8c917 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3156,8 +3156,9 @@ public class PackageManagerService extends IPackageManager.Stub // the rest of the commands above) because there's precious little we // can do about it. A settings error is reported, though. final List changedAbiCodePath = - mInjector.getAbiHelper().adjustCpuAbisForSharedUser( - setting.packages, null /*scannedPackage*/); + applyAdjustedAbiToSharedUser(setting, null /*scannedPackage*/, + mInjector.getAbiHelper().getAdjustedAbiForSharedUser( + setting.packages, null /*scannedPackage*/)); if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { final String codePathString = changedAbiCodePath.get(i); @@ -11247,6 +11248,50 @@ public class PackageManagerService extends IPackageManager.Stub pkg.setPackageName(renamedPackageName); } + /** + * Applies the adjusted ABI calculated by + * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, PackageParser.Package)} to all + * relevant packages and settings. + * @param sharedUserSetting The {@code SharedUserSetting} to adjust + * @param scannedPackage the package being scanned or null + * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper} + * @return the list of code paths that belong to packages that had their ABIs adjusted. + */ + private static List applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting, + PackageParser.Package scannedPackage, String adjustedAbi) { + if (scannedPackage != null) { + scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi; + } + List changedAbiCodePath = null; + for (PackageSetting ps : sharedUserSetting.packages) { + if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { + if (ps.primaryCpuAbiString != null) { + continue; + } + + ps.primaryCpuAbiString = adjustedAbi; + if (ps.pkg != null && ps.pkg.applicationInfo != null + && !TextUtils.equals( + adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) { + ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi; + if (DEBUG_ABI_SELECTION) { + Slog.i(TAG, + "Adjusting ABI for " + ps.name + " to " + adjustedAbi + + " (scannedPackage=" + + (scannedPackage != null ? scannedPackage : "null") + + ")"); + } + if (changedAbiCodePath == null) { + changedAbiCodePath = new ArrayList<>(); + } + changedAbiCodePath.add(ps.codePathString); + } + } + } + return changedAbiCodePath; + } + + /** * Just scans the package without any side effects. *

Not entirely true at the moment. There is still one side effect -- this @@ -11419,7 +11464,10 @@ public class PackageManagerService extends IPackageManager.Stub if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); final boolean extractNativeLibs = !pkg.isLibrary(); - packageAbiHelper.derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); + final Pair derivedAbi = + packageAbiHelper.derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); + derivedAbi.first.applyTo(pkg); + derivedAbi.second.applyTo(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Some system apps still use directory structure for native libraries @@ -11427,8 +11475,13 @@ public class PackageManagerService extends IPackageManager.Stub // structure. Try to detect abi based on directory structure. if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() && pkg.applicationInfo.primaryCpuAbi == null) { - packageAbiHelper.setBundledAppAbisAndRoots(pkg, pkgSetting); - packageAbiHelper.setNativeLibraryPaths(pkg, sAppLib32InstallDir); + final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis( + pkg); + abis.applyTo(pkg); + abis.applyTo(pkgSetting); + final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = + packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir); + nativeLibraryPaths.applyTo(pkg); } } else { // This is not a first boot or an upgrade, don't bother deriving the @@ -11437,7 +11490,9 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings; pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings; - packageAbiHelper.setNativeLibraryPaths(pkg, sAppLib32InstallDir); + final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = + packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir); + nativeLibraryPaths.applyTo(pkg); if (DEBUG_ABI_SELECTION) { Slog.i(TAG, "Using ABIS and native lib paths from settings : " + @@ -11458,7 +11513,9 @@ public class PackageManagerService extends IPackageManager.Stub // ABIs we've determined above. For non-moves, the path will be updated based on the // ABIs we determined during compilation, but the path will depend on the final // package path (after the rename away from the stage path). - packageAbiHelper.setNativeLibraryPaths(pkg, sAppLib32InstallDir); + final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = + packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir); + nativeLibraryPaths.applyTo(pkg); } // This is a special case for the "system" package, where the ABI is @@ -11512,8 +11569,9 @@ public class PackageManagerService extends IPackageManager.Stub // We also do this *before* we perform dexopt on this package, so that // we can avoid redundant dexopts, and also to make sure we've got the // code and package path correct. - changedAbiCodePath = packageAbiHelper.adjustCpuAbisForSharedUser( - pkgSetting.sharedUser.packages, pkg); + changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, pkg, + packageAbiHelper.getAdjustedAbiForSharedUser( + pkgSetting.sharedUser.packages, pkg)); } if (isUnderFactoryTest && pkg.requestedPermissions.contains( @@ -17190,7 +17248,11 @@ public class PackageManagerService extends IPackageManager.Stub String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ? args.abiOverride : pkg.cpuAbiOverride); final boolean extractNativeLibs = !pkg.isLibrary(); - mInjector.getAbiHelper().derivePackageAbi(pkg, abiOverride, extractNativeLibs); + final Pair + derivedAbi = mInjector.getAbiHelper().derivePackageAbi( + pkg, abiOverride, extractNativeLibs); + derivedAbi.first.applyTo(pkg); + derivedAbi.second.applyTo(pkg); } catch (PackageManagerException pme) { Slog.e(TAG, "Error deriving application ABI", pme); throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index cc70ef89c75fe..dd3d8b9297931 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -34,9 +34,7 @@ import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertNotSame; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.when; import android.Manifest; @@ -48,6 +46,7 @@ import android.os.Environment; import android.os.UserHandle; import android.os.UserManagerInternal; import android.platform.test.annotations.Presubmit; +import android.util.Pair; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; @@ -62,6 +61,7 @@ import java.io.File; @RunWith(MockitoJUnitRunner.class) @Presubmit +// TODO: shared user tests public class ScanTests { private static final String DUMMY_PACKAGE_NAME = "some.app.to.test"; @@ -76,6 +76,24 @@ public class ScanTests { when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); } + @Before + public void setupDefaultAbiBehavior() throws Exception { + when(mMockPackageAbiHelper.derivePackageAbi( + any(PackageParser.Package.class), nullable(String.class), anyBoolean())) + .thenReturn(new Pair<>( + new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"), + new PackageAbiHelper.NativeLibraryPaths( + "derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2"))); + when(mMockPackageAbiHelper.getNativeLibraryPaths( + any(PackageParser.Package.class), any(File.class))) + .thenReturn(new PackageAbiHelper.NativeLibraryPaths( + "getRootDir", true, "getNativeDir", "getNativeDir2" + )); + when(mMockPackageAbiHelper.getBundledAppAbis( + any(PackageParser.Package.class))) + .thenReturn(new PackageAbiHelper.Abis("bundledPrimary", "bundledSecondary")); + } + @Test public void newInstallSimpleAllNominal() throws Exception { final PackageManagerService.ScanRequest scanRequest = @@ -84,15 +102,11 @@ public class ScanTests { .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP) .build(); - final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/); assertThat(scanResult.existingSettingCopied, is(false)); - verify(mMockPackageAbiHelper, never()).derivePackageAbi(any(PackageParser.Package.class), - anyString() /*abioverride*/, anyBoolean() /*extractNativeLibs*/); - verify(mMockPackageAbiHelper).setNativeLibraryPaths( - scanResult.pkgSetting.pkg, PackageManagerService.sAppLib32InstallDir); + assertPathsNotDerived(scanResult); } @Test @@ -162,10 +176,7 @@ public class ScanTests { assertThat(scanResult.pkgSetting.secondaryCpuAbiString, is("secondaryCpuAbi")); assertThat(scanResult.pkgSetting.cpuAbiOverrideString, nullValue()); - verify(mMockPackageAbiHelper, never()).derivePackageAbi(any(PackageParser.Package.class), - anyString() /*abioverride*/, anyBoolean() /*extractNativeLibs*/); - verify(mMockPackageAbiHelper).setNativeLibraryPaths( - scanResult.pkgSetting.pkg, PackageManagerService.sAppLib32InstallDir); + assertPathsNotDerived(scanResult); } @Test @@ -293,12 +304,13 @@ public class ScanTests { .build(); - executeScan(new ScanRequestBuilder(basicPackage) + final PackageManagerService.ScanResult scanResult = executeScan(new ScanRequestBuilder( + basicPackage) .setPkgSetting(pkgSetting) .addScanFlag(SCAN_FIRST_BOOT_OR_UPGRADE) .build()); - verify(mMockPackageAbiHelper).derivePackageAbi(basicPackage, "testOverride", true); + assertAbiAndPathssDerived(scanResult); } @Test @@ -484,7 +496,7 @@ public class ScanTests { assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting); final ApplicationInfo applicationInfo = pkgSetting.pkg.applicationInfo; - verifyBasicApplicationInfo(scanResult, applicationInfo); + assertBasicApplicationInfo(scanResult, applicationInfo); } @@ -497,14 +509,12 @@ public class ScanTests { assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L})); assertThat(pkgSetting.pkg, is(scanResult.request.pkg)); assertThat(pkgSetting.pkg.mExtras, is(pkgSetting)); - assertThat(pkgSetting.legacyNativeLibraryPathString, - is("/data/tmp/randompath/base.apk:/lib")); assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName)))); assertThat(pkgSetting.resourcePath, is(new File(createResourcePath(packageName)))); assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345))); } - private static void verifyBasicApplicationInfo(PackageManagerService.ScanResult scanResult, + private static void assertBasicApplicationInfo(PackageManagerService.ScanResult scanResult, ApplicationInfo applicationInfo) { assertThat(applicationInfo.processName, is(scanResult.request.pkg.packageName)); @@ -517,4 +527,25 @@ public class ScanTests { assertThat(applicationInfo.credentialProtectedDataDir, is(calculatedCredentialId)); assertThat(applicationInfo.dataDir, is(applicationInfo.credentialProtectedDataDir)); } + + private static void assertAbiAndPathssDerived(PackageManagerService.ScanResult scanResult) { + final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo; + assertThat(applicationInfo.primaryCpuAbi, is("derivedPrimary")); + assertThat(applicationInfo.secondaryCpuAbi, is("derivedSecondary")); + + assertThat(applicationInfo.nativeLibraryRootDir, is("derivedRootDir")); + assertThat(scanResult.pkgSetting.legacyNativeLibraryPathString, is("derivedRootDir")); + assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true)); + assertThat(applicationInfo.nativeLibraryDir, is("derivedNativeDir")); + assertThat(applicationInfo.secondaryNativeLibraryDir, is("derivedNativeDir2")); + } + + private static void assertPathsNotDerived(PackageManagerService.ScanResult scanResult) { + final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo; + assertThat(applicationInfo.nativeLibraryRootDir, is("getRootDir")); + assertThat(scanResult.pkgSetting.legacyNativeLibraryPathString, is("getRootDir")); + assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true)); + assertThat(applicationInfo.nativeLibraryDir, is("getNativeDir")); + assertThat(applicationInfo.secondaryNativeLibraryDir, is("getNativeDir2")); + } }