Multi-arch application installs.
Each application now has two ABIs, the primary
and the secondary. The app is always launched with
the primary, but the secondary might be used by other apps
that load the given applications code. This implies we
must:
- dex2oat the app both ways.
- extract shared libraries for both abis.
The former is relatively straightforward but the latter
requires us to change the layout for shared libs that we
unpack from applications. The bulk of this change deals
with the latter.
This change continues to fill in nativeLibraryPath during
scans for backwards compatibility. This will be removed in
a future patch.
Change-Id: Ia943dd11ef815c5cbfc60f17929eaa2a652a385a
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1e93d92..4939fb6 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -491,13 +491,55 @@
public String nativeLibraryDir;
/**
- * The ABI that this application requires, This is inferred from the ABIs
+ * The path under the apps data directory we store unpacked libraries. For
+ * new installs, we create subdirectories under legacyNativeLibraryDir that are
+ * architecture specific. For legacy installs, the shared libraries are
+ * placed directly under this path.
+ *
+ * For "legacy" installs {@code nativeLibraryDir} will be equal to this path.
+ * For newer installs, it will be derived based on the codePath and the primary
+ * cpu abi.
+ *
+ * @hide.
+ */
+ public String legacyNativeLibraryDir;
+
+ /**
+ * The primary ABI that this application requires, This is inferred from the ABIs
* of the native JNI libraries the application bundles. Will be {@code null}
* if this application does not require any particular ABI.
*
+ * If non-null, the application will always be launched with this ABI.
+ *
* {@hide}
*/
- public String cpuAbi;
+ public String primaryCpuAbi;
+
+ /**
+ * The secondary ABI for this application. Might be non-null for multi-arch
+ * installs. The application itself never uses this ABI, but other applications that
+ * use its code might.
+ *
+ * {@hide}
+ */
+ public String secondaryCpuAbi;
+
+ /**
+ * The derived APK "root" for the given package. Will be non-null for bundled and
+ * updated system apps. This will be a top level path under which apks and libraries
+ * are installed, for eg. {@code /system}, {@code /oem} or {@code /vendor}. This is
+ * used to calculate the location of native code for a given package, for e.g
+ * {@code /vendor/lib} or {@code /vendor/lib64}.
+ *
+ * For app updates or fresh app installs, this will be {@code null} and we will use
+ * {@code legacyNativeLibraryDir}
+ *
+ * NOTE: This can be removed if we have a unified layout for bundled and installed
+ * apps.
+ *
+ * {@hide}
+ */
+ public String apkRoot;
/**
* The kernel user-ID that has been assigned to this application;
@@ -641,7 +683,10 @@
splitSourceDirs = orig.splitSourceDirs;
splitPublicSourceDirs = orig.splitPublicSourceDirs;
nativeLibraryDir = orig.nativeLibraryDir;
- cpuAbi = orig.cpuAbi;
+ legacyNativeLibraryDir = orig.legacyNativeLibraryDir;
+ primaryCpuAbi = orig.primaryCpuAbi;
+ secondaryCpuAbi = orig.secondaryCpuAbi;
+ apkRoot = orig.apkRoot;
resourceDirs = orig.resourceDirs;
seinfo = orig.seinfo;
sharedLibraryFiles = orig.sharedLibraryFiles;
@@ -685,7 +730,10 @@
dest.writeStringArray(splitSourceDirs);
dest.writeStringArray(splitPublicSourceDirs);
dest.writeString(nativeLibraryDir);
- dest.writeString(cpuAbi);
+ dest.writeString(legacyNativeLibraryDir);
+ dest.writeString(primaryCpuAbi);
+ dest.writeString(secondaryCpuAbi);
+ dest.writeString(apkRoot);
dest.writeStringArray(resourceDirs);
dest.writeString(seinfo);
dest.writeStringArray(sharedLibraryFiles);
@@ -728,7 +776,10 @@
splitSourceDirs = source.readStringArray();
splitPublicSourceDirs = source.readStringArray();
nativeLibraryDir = source.readString();
- cpuAbi = source.readString();
+ legacyNativeLibraryDir = source.readString();
+ primaryCpuAbi = source.readString();
+ secondaryCpuAbi = source.readString();
+ apkRoot = source.readString();
resourceDirs = source.readStringArray();
seinfo = source.readString();
sharedLibraryFiles = source.readStringArray();
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index a1566da..50a0483 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -37,6 +37,13 @@
public int versionCode;
/**
+ * The android:multiArch flag from the package manifest. If set,
+ * we will extract all native libraries for the given app, not just those
+ * from the preferred ABI.
+ */
+ public boolean multiArch;
+
+ /**
* Specifies the recommended install location. Can be one of
* {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage
* {@link #PackageHelper.RECOMMEND_INSTALL_EXTERNAL} to install on external media
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ab0bf25..6e0ca50 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -249,6 +249,8 @@
/** Paths of any split APKs, ordered by parsed splitName */
public final String[] splitCodePaths;
+ public final boolean multiArch;
+
private PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
String[] splitCodePaths) {
this.packageName = baseApk.packageName;
@@ -259,6 +261,7 @@
this.codePath = codePath;
this.baseCodePath = baseApk.codePath;
this.splitCodePaths = splitCodePaths;
+ this.multiArch = baseApk.multiArch;
}
public List<String> getAllCodePaths() {
@@ -282,9 +285,11 @@
public final int installLocation;
public final VerifierInfo[] verifiers;
public final Signature[] signatures;
+ public final boolean multiArch;
public ApkLite(String codePath, String packageName, String splitName, int versionCode,
- int installLocation, List<VerifierInfo> verifiers, Signature[] signatures) {
+ int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
+ boolean multiArch) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -292,6 +297,7 @@
this.installLocation = installLocation;
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
this.signatures = signatures;
+ this.multiArch = multiArch;
}
}
@@ -1114,6 +1120,7 @@
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
int numFound = 0;
+ boolean multiArch = false;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
String attr = attrs.getAttributeName(i);
if (attr.equals("installLocation")) {
@@ -1123,8 +1130,11 @@
} else if (attr.equals("versionCode")) {
versionCode = attrs.getAttributeIntValue(i, 0);
numFound++;
+ } else if (attr.equals("multiArch")) {
+ multiArch = attrs.getAttributeBooleanValue(i, false);
+ numFound++;
}
- if (numFound >= 2) {
+ if (numFound >= 3) {
break;
}
}
@@ -1149,7 +1159,7 @@
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
- installLocation, verifiers, signatures);
+ installLocation, verifiers, signatures, multiArch);
}
/**
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index d66a7bb..d76d0d4 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -131,10 +131,15 @@
*
* @return size of all native binary files in bytes
*/
- public static long sumNativeBinariesLI(Handle handle, String abi) {
+ public static long sumNativeBinariesLI(Handle handle, String[] abis) {
long sum = 0;
for (long apkHandle : handle.apkHandles) {
- sum += nativeSumNativeBinaries(apkHandle, abi);
+ // NOTE: For a given APK handle, we parse the central directory precisely
+ // once, but prefix matching of entries requires a CD traversal, which can
+ // take a while (even if it needs no additional I/O).
+ for (String abi : abis) {
+ sum += nativeSumNativeBinaries(apkHandle, abi);
+ }
}
return sum;
}
@@ -196,45 +201,47 @@
private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis);
// Convenience method to call removeNativeBinariesFromDirLI(File)
- public static boolean removeNativeBinariesLI(String nativeLibraryPath) {
- if (nativeLibraryPath == null) return false;
- return removeNativeBinariesFromDirLI(new File(nativeLibraryPath));
+ public static void removeNativeBinariesLI(String nativeLibraryPath) {
+ if (nativeLibraryPath == null) return;
+ removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */);
}
- // Remove the native binaries of a given package. This simply
- // gets rid of the files in the 'lib' sub-directory.
- public static boolean removeNativeBinariesFromDirLI(File nativeLibraryDir) {
+ /**
+ * Remove the native binaries of a given package. This deletes the files
+ */
+ public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir) {
if (DEBUG_NATIVE) {
- Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryDir.getPath());
+ Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
}
- boolean deletedFiles = false;
-
/*
* Just remove any file in the directory. Since the directory is owned
* by the 'system' UID, the application is not supposed to have written
* anything there.
*/
- if (nativeLibraryDir.exists()) {
- final File[] binaries = nativeLibraryDir.listFiles();
- if (binaries != null) {
- for (int nn = 0; nn < binaries.length; nn++) {
+ if (nativeLibraryRoot.exists()) {
+ final File[] files = nativeLibraryRoot.listFiles();
+ if (files != null) {
+ for (int nn = 0; nn < files.length; nn++) {
if (DEBUG_NATIVE) {
- Slog.d(TAG, " Deleting " + binaries[nn].getName());
+ Slog.d(TAG, " Deleting " + files[nn].getName());
}
- if (!binaries[nn].delete()) {
- Slog.w(TAG, "Could not delete native binary: " + binaries[nn].getPath());
- } else {
- deletedFiles = true;
+ if (files[nn].isDirectory()) {
+ removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */);
+ } else if (!files[nn].delete()) {
+ Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath());
}
}
}
- // Do not delete 'lib' directory itself, or this will prevent
- // installation of future updates.
+ // Do not delete 'lib' directory itself, unless we're specifically
+ // asked to or this will prevent installation of future updates.
+ if (deleteRootDir) {
+ if (!nativeLibraryRoot.delete()) {
+ Slog.w(TAG, "Could not delete native binary directory: " + nativeLibraryRoot.getPath());
+ }
+ }
}
-
- return deletedFiles;
}
// We don't care about the other return values for now.