Merge "PackageAbiHepler no longer modifies Package"
This commit is contained in:
committed by
Android (Google) Code Review
commit
c7487391b0
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Abis, NativeLibraryPaths> 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<String> adjustCpuAbisForSharedUser(
|
||||
String getAdjustedAbiForSharedUser(
|
||||
Set<PackageSetting> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> adjustCpuAbisForSharedUser(
|
||||
Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) {
|
||||
List<String> 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<Abis, NativeLibraryPaths> 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<PackageSetting> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3089,8 +3089,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<String> 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);
|
||||
@@ -10663,6 +10664,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<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
|
||||
PackageParser.Package scannedPackage, String adjustedAbi) {
|
||||
if (scannedPackage != null) {
|
||||
scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi;
|
||||
}
|
||||
List<String> 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.
|
||||
* <p>Not entirely true at the moment. There is still one side effect -- this
|
||||
@@ -10835,7 +10880,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<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> 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
|
||||
@@ -10843,8 +10891,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
|
||||
@@ -10853,7 +10906,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 : " +
|
||||
@@ -10874,7 +10929,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
|
||||
@@ -10928,8 +10985,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(
|
||||
@@ -16534,7 +16592,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<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
|
||||
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,
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user