diff options
author | Narayan Kamath <narayan@google.com> | 2014-07-04 18:30:45 +0100 |
---|---|---|
committer | Narayan Kamath <narayan@google.com> | 2014-07-10 17:16:14 +0100 |
commit | ff110bd61a69f7ed8602ae14b27f7befec76b2e7 (patch) | |
tree | 952fc963ae155edfc6f781611ee3dd1b68d3a071 | |
parent | 3e9090524a9a2ba686d2c115ee8c65a8d379be12 (diff) | |
download | frameworks_base-ff110bd61a69f7ed8602ae14b27f7befec76b2e7.zip frameworks_base-ff110bd61a69f7ed8602ae14b27f7befec76b2e7.tar.gz frameworks_base-ff110bd61a69f7ed8602ae14b27f7befec76b2e7.tar.bz2 |
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
12 files changed, 910 insertions, 564 deletions
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 class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 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 cpuAbi; + public String apkRoot; /** * The kernel user-ID that has been assigned to this application; @@ -641,7 +683,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 class PackageInfoLite implements Parcelable { 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 @@ public class PackageParser { /** 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 @@ public class PackageParser { this.codePath = codePath; this.baseCodePath = baseApk.codePath; this.splitCodePaths = splitCodePaths; + this.multiArch = baseApk.multiArch; } public List<String> getAllCodePaths() { @@ -282,9 +285,11 @@ public class PackageParser { 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 @@ public class PackageParser { this.installLocation = installLocation; this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]); this.signatures = signatures; + this.multiArch = multiArch; } } @@ -1114,6 +1120,7 @@ public class PackageParser { 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 @@ public class PackageParser { } 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 @@ public class PackageParser { } 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 @@ public class NativeLibraryHelper { * * @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 @@ public class NativeLibraryHelper { 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. diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 67ed97c..20a621b 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -50,6 +50,7 @@ import com.android.internal.content.PackageHelper; import com.android.internal.os.IParcelFileDescriptorFactory; import com.android.internal.util.ArrayUtils; +import dalvik.system.VMRuntime; import libcore.io.IoUtils; import libcore.io.Streams; @@ -186,6 +187,7 @@ public class DefaultContainerService extends IntentService { ret.verifiers = pkg.verifiers; ret.recommendedInstallLocation = recommendAppInstallLocation(pkg, flags, threshold, abiOverride); + ret.multiArch = pkg.multiArch; return ret; } @@ -363,31 +365,26 @@ public class DefaultContainerService extends IntentService { final String resFileName = "pkg.apk"; final String publicResFileName = "res.zip"; - // The .apk file - String codePath = pkg.baseCodePath; - File codeFile = new File(codePath); - - String[] abiList = Build.SUPPORTED_ABIS; - if (abiOverride != null) { - abiList = new String[] { abiOverride }; - } else { - try { - if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && - NativeLibraryHelper.hasRenderscriptBitcode(handle)) { - abiList = Build.SUPPORTED_32_BIT_ABIS; - } - } catch (IOException ioe) { - Slog.w(TAG, "Problem determining ABI for: " + codeFile.getPath()); - return null; - } + if (pkg.multiArch) { + // TODO: Support multiArch installs on ASEC. + throw new IllegalArgumentException("multiArch not supported on ASEC installs."); } - final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle, abiList); + // The .apk file + final String codePath = pkg.baseCodePath; + final File codeFile = new File(codePath); + final String[] abis; + try { + abis = calculateAbiList(handle, abiOverride, pkg.multiArch); + } catch (IOException ioe) { + Slog.w(TAG, "Problem determining app ABIS: " + ioe); + return null; + } // Calculate size of container needed to hold base APK. final int sizeMb; try { - sizeMb = calculateContainerSize(pkg, handle, isForwardLocked, abiIndex); + sizeMb = calculateContainerSize(pkg, handle, isForwardLocked, abis); } catch (IOException e) { Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath()); return null; @@ -451,17 +448,18 @@ public class DefaultContainerService extends IntentService { final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME); if (sharedLibraryDir.mkdir()) { int ret = PackageManager.INSTALL_SUCCEEDED; - if (abiIndex >= 0) { + if (abis != null) { + // TODO(multiArch): Support multi-arch installs on asecs. Note that we are NOT + // using an ISA specific subdir here for now. + final String abi = abis[0]; ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, - sharedLibraryDir, abiList[abiIndex]); - } else if (abiIndex != PackageManager.NO_NATIVE_LIBRARIES) { - ret = abiIndex; - } + sharedLibraryDir, abi); - if (ret != PackageManager.INSTALL_SUCCEEDED) { - Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath()); - PackageHelper.destroySdDir(newCid); - return null; + if (ret != PackageManager.INSTALL_SUCCEEDED) { + Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath()); + PackageHelper.destroySdDir(newCid); + return null; + } } } else { Slog.e(TAG, "Could not create native lib directory: " + sharedLibraryDir.getPath()); @@ -687,14 +685,53 @@ public class DefaultContainerService extends IntentService { NativeLibraryHelper.Handle handle = null; try { handle = NativeLibraryHelper.Handle.create(pkg); - final int abi = NativeLibraryHelper.findSupportedAbi(handle, - (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS); - return calculateContainerSize(pkg, handle, isForwardLocked, abi); + return calculateContainerSize(pkg, handle, isForwardLocked, + calculateAbiList(handle, abiOverride, pkg.multiArch)); } finally { IoUtils.closeQuietly(handle); } } + private String[] calculateAbiList(NativeLibraryHelper.Handle handle, String abiOverride, + boolean isMultiArch) throws IOException { + if (isMultiArch) { + final int abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS); + final int abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS); + + if (abi32 >= 0 && abi64 >= 0) { + return new String[] { Build.SUPPORTED_64_BIT_ABIS[abi64], Build.SUPPORTED_32_BIT_ABIS[abi32] }; + } else if (abi64 >= 0) { + return new String[] { Build.SUPPORTED_64_BIT_ABIS[abi64] }; + } else if (abi32 >= 0) { + return new String[] { Build.SUPPORTED_32_BIT_ABIS[abi32] }; + } + + if (abi64 != PackageManager.NO_NATIVE_LIBRARIES || abi32 != PackageManager.NO_NATIVE_LIBRARIES) { + throw new IOException("Error determining ABI list: errorCode=[" + abi32 + "," + abi64 + "]"); + } + + } else { + String[] abiList = Build.SUPPORTED_ABIS; + if (abiOverride != null) { + abiList = new String[] { abiOverride }; + } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && + NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + } + + final int abi = NativeLibraryHelper.findSupportedAbi(handle,abiList); + if (abi >= 0) { + return new String[]{Build.SUPPORTED_ABIS[abi]}; + } + + if (abi != PackageManager.NO_NATIVE_LIBRARIES) { + throw new IOException("Error determining ABI list: errorCode=" + abi); + } + } + + return null; + } + /** * Calculate the container size for a package. * @@ -702,7 +739,7 @@ public class DefaultContainerService extends IntentService { * @throws IOException when there is a problem reading the file */ private int calculateContainerSize(PackageLite pkg, NativeLibraryHelper.Handle handle, - boolean isForwardLocked, int abiIndex) throws IOException { + boolean isForwardLocked, String[] abis) throws IOException { // Calculate size of container needed to hold APKs. long sizeBytes = 0; for (String codePath : pkg.getAllCodePaths()) { @@ -715,9 +752,8 @@ public class DefaultContainerService extends IntentService { // Check all the native files that need to be copied and add that to the // container size. - if (abiIndex >= 0) { - sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle, - Build.SUPPORTED_ABIS[abiIndex]); + if (abis != null) { + sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle, abis); } int sizeMb = (int) (sizeBytes >> 20); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 053fb5a..c7eabe8 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2985,7 +2985,7 @@ public final class ActivityManagerService extends ActivityManagerNative debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; } - String requiredAbi = (abiOverride != null) ? abiOverride : app.info.cpuAbi; + String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi; if (requiredAbi == null) { requiredAbi = Build.SUPPORTED_ABIS[0]; } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 6ad2e60..1193968 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -365,7 +365,7 @@ public final class Installer extends SystemService { } public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath, - String fwdLockApkPath, String asecPath, String instructionSet, PackageStats pStats) { + String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); @@ -374,13 +374,17 @@ public final class Installer extends SystemService { builder.append(' '); builder.append(apkPath); builder.append(' '); + // TODO: Extend getSizeInfo to look at the full subdirectory tree, + // not just the first level. builder.append(libDirPath != null ? libDirPath : "!"); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); builder.append(' '); builder.append(asecPath != null ? asecPath : "!"); builder.append(' '); - builder.append(instructionSet); + // TODO: Extend getSizeInfo to look at *all* instrution sets, not + // just the primary. + builder.append(instructionSets[0]); String s = transaction(builder.toString()); String res[] = s.split(" "); @@ -404,18 +408,17 @@ public final class Installer extends SystemService { } /** - * Links the native library directory in an application's directory to its - * real location. + * Links the 32 bit native library directory in an application's data directory to the + * real location for backward compatibility. Note that no such symlink is created for + * 64 bit shared libraries. * - * @param dataPath data directory where the application is - * @param nativeLibPath target native library path * @return -1 on error */ - public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath, int userId) { + public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) { if (dataPath == null) { Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null"); return -1; - } else if (nativeLibPath == null) { + } else if (nativeLibPath32 == null) { Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null"); return -1; } @@ -423,7 +426,7 @@ public final class Installer extends SystemService { StringBuilder builder = new StringBuilder("linklib "); builder.append(dataPath); builder.append(' '); - builder.append(nativeLibPath); + builder.append(nativeLibPath32); builder.append(' '); builder.append(userId); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index fe9ce8f..5c05f45 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18,7 +18,6 @@ package com.android.server.pm; import static android.Manifest.permission.GRANT_REVOKE_PERMISSIONS; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; -import static android.Manifest.permission.INSTALL_PACKAGES; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; @@ -34,7 +33,6 @@ import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.EEXIST; import static android.system.OsConstants.O_EXCL; import static android.system.OsConstants.O_RDWR; -import static android.system.OsConstants.O_WRONLY; import static android.system.OsConstants.S_IRGRP; import static android.system.OsConstants.S_IROTH; import static android.system.OsConstants.S_IRWXU; @@ -57,7 +55,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; -import com.android.internal.util.XmlUtils; import com.android.server.EventLogTags; import com.android.server.IntentResolver; import com.android.server.LocalServices; @@ -67,14 +64,11 @@ import com.android.server.Watchdog; import com.android.server.pm.Settings.DatabaseVersion; import com.android.server.storage.DeviceStorageMonitorInternal; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IActivityManager; -import android.app.PackageInstallObserver; import android.app.admin.IDevicePolicyManager; import android.app.backup.IBackupManager; import android.content.BroadcastReceiver; @@ -149,7 +143,6 @@ import android.security.KeyStore; import android.security.SystemKeyStore; import android.system.ErrnoException; import android.system.Os; -import android.system.OsConstants; import android.system.StructStat; import android.text.TextUtils; import android.util.ArraySet; @@ -162,7 +155,6 @@ import android.util.PrintStreamPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.util.Xml; import android.view.Display; import java.io.BufferedInputStream; @@ -172,7 +164,6 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; @@ -204,7 +195,6 @@ import dalvik.system.StaleDexCacheError; import dalvik.system.VMRuntime; import libcore.io.IoUtils; -import libcore.io.Libcore; /** * Keep track of all those .apks everywhere. @@ -233,6 +223,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_APP_DIR_OBSERVER = false; private static final boolean DEBUG_VERIFY = false; private static final boolean DEBUG_DEXOPT = false; + private static final boolean DEBUG_ABI_SELECTION = false; private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; @@ -368,10 +359,10 @@ public class PackageManagerService extends IPackageManager.Stub { final File mAppInstallDir; /** - * Directory to which applications installed internally have native - * libraries copied. + * Directory to which applications installed internally have their + * 32 bit native libraries copied. */ - private File mAppLibInstallDir; + private File mAppLib32InstallDir; // Directory containing the private parts (e.g. code and non-resource assets) of forward-locked // apps. @@ -1354,7 +1345,7 @@ public class PackageManagerService extends IPackageManager.Stub { File dataDir = Environment.getDataDirectory(); mAppDataDir = new File(dataDir, "data"); mAppInstallDir = new File(dataDir, "app"); - mAppLibInstallDir = new File(dataDir, "app-lib"); + mAppLib32InstallDir = new File(dataDir, "app-lib"); mAsecInternalPath = new File(dataDir, "app-asec").getPath(); mUserAppDataDir = new File(dataDir, "user"); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); @@ -2089,7 +2080,8 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY; pkg.applicationInfo.dataDir = getDataPathForPackage(packageName, 0).getPath(); - pkg.applicationInfo.cpuAbi = ps.cpuAbiString; + pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString; + pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString; } return generatePackageInfo(pkg, flags, userId); } @@ -4269,8 +4261,8 @@ public class PackageManagerService extends IPackageManager.Stub { + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, - getAppInstructionSetFromSettings(ps)); + ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, + getAppDexInstructionSets(ps), isMultiArch(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -4334,8 +4326,8 @@ public class PackageManagerService extends IPackageManager.Stub { + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, - getAppInstructionSetFromSettings(ps)); + ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, + getAppDexInstructionSets(ps), isMultiArch(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -4585,7 +4577,7 @@ public class PackageManagerService extends IPackageManager.Stub { mPackageUsage.write(true); } - private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet, + private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets, boolean forceDex, boolean defer, HashSet<String> done) { for (int i=0; i<libs.size(); i++) { PackageParser.Package libPkg; @@ -4600,7 +4592,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } if (libPkg != null && !done.contains(libName)) { - performDexOptLI(libPkg, instructionSet, forceDex, defer, done); + performDexOptLI(libPkg, instructionSets, forceDex, defer, done); } } } @@ -4610,89 +4602,98 @@ public class PackageManagerService extends IPackageManager.Stub { static final int DEX_OPT_DEFERRED = 2; static final int DEX_OPT_FAILED = -1; - private int performDexOptLI(PackageParser.Package pkg, String instructionSetOverride, + private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets, boolean forceDex, boolean defer, HashSet<String> done) { - final String instructionSet = instructionSetOverride != null ? - instructionSetOverride : getAppInstructionSet(pkg.applicationInfo); + final String[] instructionSets = targetInstructionSets != null ? + targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo); if (done != null) { done.add(pkg.packageName); if (pkg.usesLibraries != null) { - performDexOptLibsLI(pkg.usesLibraries, instructionSet, forceDex, defer, done); + performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer, done); } if (pkg.usesOptionalLibraries != null) { - performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSet, forceDex, defer, done); + performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer, done); } } if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { final Collection<String> paths = pkg.getAllCodePaths(); for (String path : paths) { - try { - boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path, - pkg.packageName, instructionSet, defer); - // There are three basic cases here: - // 1.) we need to dexopt, either because we are forced or it is needed - // 2.) we are defering a needed dexopt - // 3.) we are skipping an unneeded dexopt - if (forceDex || (!defer && isDexOptNeededInternal)) { - Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); - final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); - int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), - pkg.packageName, instructionSet); - // Note that we ran dexopt, since rerunning will - // probably just result in an error again. - pkg.mDexOptNeeded = false; - if (ret < 0) { - return DEX_OPT_FAILED; + for (String instructionSet : instructionSets) { + try { + boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path, + pkg.packageName, instructionSet, defer); + // There are three basic cases here: + // 1.) we need to dexopt, either because we are forced or it is needed + // 2.) we are defering a needed dexopt + // 3.) we are skipping an unneeded dexopt + if (forceDex || (!defer && isDexOptNeededInternal)) { + Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); + final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), + pkg.packageName, instructionSet); + // Note that we ran dexopt, since rerunning will + // probably just result in an error again. + pkg.mDexOptNeeded = false; + if (ret < 0) { + return DEX_OPT_FAILED; + } + return DEX_OPT_PERFORMED; } - return DEX_OPT_PERFORMED; - } - if (defer && isDexOptNeededInternal) { - if (mDeferredDexOpt == null) { - mDeferredDexOpt = new HashSet<PackageParser.Package>(); + if (defer && isDexOptNeededInternal) { + if (mDeferredDexOpt == null) { + mDeferredDexOpt = new HashSet<PackageParser.Package>(); + } + mDeferredDexOpt.add(pkg); + return DEX_OPT_DEFERRED; } - mDeferredDexOpt.add(pkg); - return DEX_OPT_DEFERRED; + pkg.mDexOptNeeded = false; + return DEX_OPT_SKIPPED; + } catch (FileNotFoundException e) { + Slog.w(TAG, "Apk not found for dexopt: " + path); + return DEX_OPT_FAILED; + } catch (IOException e) { + Slog.w(TAG, "IOException reading apk: " + path, e); + return DEX_OPT_FAILED; + } catch (StaleDexCacheError e) { + Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e); + return DEX_OPT_FAILED; + } catch (Exception e) { + Slog.w(TAG, "Exception when doing dexopt : ", e); + return DEX_OPT_FAILED; } - pkg.mDexOptNeeded = false; - return DEX_OPT_SKIPPED; - } catch (FileNotFoundException e) { - Slog.w(TAG, "Apk not found for dexopt: " + path); - return DEX_OPT_FAILED; - } catch (IOException e) { - Slog.w(TAG, "IOException reading apk: " + path, e); - return DEX_OPT_FAILED; - } catch (StaleDexCacheError e) { - Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e); - return DEX_OPT_FAILED; - } catch (Exception e) { - Slog.w(TAG, "Exception when doing dexopt : ", e); - return DEX_OPT_FAILED; } } } return DEX_OPT_SKIPPED; } - private String getAppInstructionSet(ApplicationInfo info) { - String instructionSet = getPreferredInstructionSet(); - - if (info.cpuAbi != null) { - instructionSet = VMRuntime.getInstructionSet(info.cpuAbi); + private static String[] getAppDexInstructionSets(ApplicationInfo info) { + if (info.primaryCpuAbi != null) { + if (info.secondaryCpuAbi != null) { + return new String[] { + VMRuntime.getInstructionSet(info.primaryCpuAbi), + VMRuntime.getInstructionSet(info.secondaryCpuAbi) }; + } else { + return new String[] { + VMRuntime.getInstructionSet(info.primaryCpuAbi) }; + } } - return instructionSet; + return new String[] { getPreferredInstructionSet() }; } - private String getAppInstructionSetFromSettings(PackageSetting ps) { - String instructionSet = getPreferredInstructionSet(); - - if (ps.cpuAbiString != null) { - instructionSet = VMRuntime.getInstructionSet(ps.cpuAbiString); + private static String[] getAppDexInstructionSets(PackageSetting ps) { + if (ps.primaryCpuAbiString != null) { + if (ps.secondaryCpuAbiString != null) { + return new String[] { ps.primaryCpuAbiString, ps.secondaryCpuAbiString }; + } else { + return new String[] { ps.primaryCpuAbiString }; + } } - return instructionSet; + return new String[] { getPreferredInstructionSet() }; } private static String getPreferredInstructionSet() { @@ -4726,7 +4727,7 @@ public class PackageManagerService extends IPackageManager.Stub { } else { done = null; } - return performDexOptLI(pkg, null /* instruction set override */, forceDex, defer, done); + return performDexOptLI(pkg, null /* target instruction sets */, forceDex, defer, done); } private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { @@ -4790,12 +4791,6 @@ public class PackageManagerService extends IPackageManager.Stub { } } - final File nativeLibraryFile = new File(mAppLibInstallDir, packageName); - NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile); - if (!nativeLibraryFile.delete()) { - Slog.w(TAG, "Couldn't delete native library directory " + nativeLibraryFile.getPath()); - } - return res; } @@ -4827,7 +4822,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Fix that up here. if (isSystemApp(pkg)) { PackageSetting ps = mSettings.mPackages.get(pkg.applicationInfo.packageName); - setInternalAppNativeLibraryPath(pkg, ps); + setBundledAppAbisAndRoots(pkg, ps); } if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) { @@ -5064,8 +5059,9 @@ public class PackageManagerService extends IPackageManager.Stub { // Just create the setting, don't add it yet. For already existing packages // the PkgSetting exists already and doesn't have to be created. pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, - destResourceFile, pkg.applicationInfo.nativeLibraryDir, - pkg.applicationInfo.cpuAbi, + destResourceFile, pkg.applicationInfo.legacyNativeLibraryDir, + pkg.applicationInfo.primaryCpuAbi, + pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags, user, false); if (pkgSetting == null) { Slog.w(TAG, "Creating application package " + pkg.packageName + " failed"); @@ -5289,6 +5285,7 @@ public class PackageManagerService extends IPackageManager.Stub { + pkg.applicationInfo.uid + "/fs_" + currentUid; pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir; + pkg.applicationInfo.legacyNativeLibraryDir = pkg.applicationInfo.dataDir; String msg = "Package " + pkg.packageName + " has mismatched uid: " + currentUid + " on disk, " @@ -5320,6 +5317,7 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.seinfo); if (ret < 0) { // Error from installer + Slog.w(TAG, "Unable to create data dirs [errorCode=" + ret + "]"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } @@ -5332,158 +5330,185 @@ public class PackageManagerService extends IPackageManager.Stub { } } - /* - * Set the data dir to the default "/data/data/<package name>/lib" - * if we got here without anyone telling us different (e.g., apps - * stored on SD card have their native libraries stored in the ASEC - * container with the APK). - * - * This happens during an upgrade from a package settings file that - * doesn't have a native library path attribute at all. - */ - if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) { - if (pkgSetting.nativeLibraryPathString == null) { - setInternalAppNativeLibraryPath(pkg, pkgSetting); - } else { - pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString; - } - } pkgSetting.uidError = uidError; } final String path = scanFile.getPath(); - /* Note: We don't want to unpack the native binaries for - * system applications, unless they have been updated - * (the binaries are already under /system/lib). - * Also, don't unpack libs for apps on the external card - * since they should have their libraries in the ASEC - * container already. - * - * In other words, we're going to unpack the binaries - * only for non-system apps and system app upgrades. - */ - if (pkg.applicationInfo.nativeLibraryDir != null) { + final String codePath = pkg.applicationInfo.getCodePath(); + if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { + // For the case where we had previously uninstalled an update, get rid + // of any native binaries we might have unpackaged. Note that this assumes + // that system app updates were not installed via ASEC. + // + // TODO(multiArch): Is this cleanup really necessary ? + NativeLibraryHelper.removeNativeBinariesFromDirLI( + new File(codePath, LIB_DIR_NAME), false /* delete dirs */); + setBundledAppAbisAndRoots(pkg, pkgSetting); + } else { + // TODO: We can probably be smarter about this stuff. For installed apps, + // we can calculate this information at install time once and for all. For + // system apps, we can probably assume that this information doesn't change + // after the first boot scan. As things stand, we do lots of unnecessary work. + + final boolean isAsec = isForwardLocked(pkg) || isExternal(pkg); + final String nativeLibraryRootStr; + final boolean useIsaSpecificSubdirs; + if (pkg.applicationInfo.legacyNativeLibraryDir != null) { + nativeLibraryRootStr = pkg.applicationInfo.legacyNativeLibraryDir; + useIsaSpecificSubdirs = false; + } else { + nativeLibraryRootStr = new File(pkg.codePath, LIB_DIR_NAME).getAbsolutePath(); + useIsaSpecificSubdirs = true; + } + NativeLibraryHelper.Handle handle = null; try { handle = NativeLibraryHelper.Handle.create(scanFile); - // 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. - String[] abiList = Build.SUPPORTED_ABIS; - boolean hasLegacyRenderscriptBitcode = false; - if (abiOverride != null) { - abiList = new String[] { abiOverride }; - } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && - NativeLibraryHelper.hasRenderscriptBitcode(handle)) { - abiList = Build.SUPPORTED_32_BIT_ABIS; - hasLegacyRenderscriptBitcode = true; - } - - File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); - final String dataPathString = dataPath.getCanonicalPath(); - - if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { - /* - * Upgrading from a previous version of the OS sometimes - * leaves native libraries in the /data/data/<app>/lib - * directory for system apps even when they shouldn't be. - * Recent changes in the JNI library search path - * necessitates we remove those to match previous behavior. - */ - if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) { - Log.i(TAG, "removed obsolete native libraries for system package " - + path); + // TODO(multiArch): This can be null for apps that didn't go through the + // usual installation process. We can calculate it again, like we + // do during install time. + // + // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally + // unnecessary. + final File nativeLibraryRoot = new File(nativeLibraryRootStr); + + // Null out the abis so that they can be recalculated. + pkg.applicationInfo.primaryCpuAbi = null; + pkg.applicationInfo.secondaryCpuAbi = null; + if (isMultiArch(pkg.applicationInfo)) { + // 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 (abiOverride != null) { + Slog.w(TAG, "Ignoring abiOverride for multi arch application."); + } + + int abi32 = PackageManager.NO_NATIVE_LIBRARIES; + int abi64 = PackageManager.NO_NATIVE_LIBRARIES; + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + if (isAsec) { + abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS); + } else { + abi32 = copyNativeLibrariesForInternalApp(handle, + nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, useIsaSpecificSubdirs); + } } - if (abiOverride != null || hasLegacyRenderscriptBitcode) { - pkg.applicationInfo.cpuAbi = abiList[0]; - pkgSetting.cpuAbiString = abiList[0]; - } else { - setInternalAppAbi(pkg, pkgSetting); + + if (abi32 < 0 && abi32 != PackageManager.NO_NATIVE_LIBRARIES) { + Slog.w(TAG, "Error unpackaging 32 bit native libs for multiarch app, errorCode=" + abi32); + mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + return null; } - } else { - if (!isForwardLocked(pkg) && !isExternal(pkg)) { - /* - * Update native library dir if it starts with - * /data/data - */ - if (nativeLibraryDir.getPath().startsWith(dataPathString)) { - setInternalAppNativeLibraryPath(pkg, pkgSetting); - nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); + + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + if (isAsec) { + abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS); + } else { + abi64 = copyNativeLibrariesForInternalApp(handle, + nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, useIsaSpecificSubdirs); } + } - try { - int copyRet = copyNativeLibrariesForInternalApp(handle, - nativeLibraryDir, abiList); - if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { - Slog.e(TAG, "Unable to copy native libraries"); - mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - return null; - } + if (abi64 < 0 && abi64 != PackageManager.NO_NATIVE_LIBRARIES) { + Slog.w(TAG, "Error unpackaging 64 bit native libs for multiarch app, errorCode=" + abi32); + mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + return null; + } - // We've successfully copied native libraries across, so we make a - // note of what ABI we're using - if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) { - pkg.applicationInfo.cpuAbi = abiList[copyRet]; - } else if (abiOverride != null || hasLegacyRenderscriptBitcode) { - pkg.applicationInfo.cpuAbi = abiList[0]; - } else { - pkg.applicationInfo.cpuAbi = null; - } - } catch (IOException e) { - Slog.e(TAG, "Unable to copy native libraries", e); - mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - return null; - } - } else { - // We don't have to copy the shared libraries if we're in the ASEC container - // but we still need to scan the file to figure out what ABI the app needs. - // - // TODO: This duplicates work done in the default container service. It's possible - // to clean this up but we'll need to change the interface between this service - // and IMediaContainerService (but doing so will spread this logic out, rather - // than centralizing it). - final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList); - if (abi >= 0) { - pkg.applicationInfo.cpuAbi = abiList[abi]; - } else if (abi == PackageManager.NO_NATIVE_LIBRARIES) { - // Note that (non upgraded) system apps will not have any native - // libraries bundled in their APK, but we're guaranteed not to be - // such an app at this point. - if (abiOverride != null || hasLegacyRenderscriptBitcode) { - pkg.applicationInfo.cpuAbi = abiList[0]; - } else { - pkg.applicationInfo.cpuAbi = null; - } + + if (abi64 >= 0) { + pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64]; + } + + if (abi32 >= 0) { + final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32]; + if (abi64 >= 0) { + pkg.applicationInfo.secondaryCpuAbi = abi; } else { - mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - return null; + pkg.applicationInfo.primaryCpuAbi = abi; } } + } else { + String[] abiList = (abiOverride != null) ? + new String[] { abiOverride } : 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. + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && abiOverride == null && + NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + } - if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path); - final int[] userIds = sUserManager.getUserIds(); - synchronized (mInstallLock) { - for (int userId : userIds) { - if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, - pkg.applicationInfo.nativeLibraryDir, userId) < 0) { - Slog.w(TAG, "Failed linking native library dir (user=" + userId - + ")"); - mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - return null; - } - } + final int copyRet; + if (isAsec) { + copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList); + } else { + copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryRoot, abiList, + useIsaSpecificSubdirs); + } + + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { + Slog.w(TAG, "Error unpackaging native libs for app, errorCode=" + copyRet); + mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + return null; } - } - pkgSetting.cpuAbiString = pkg.applicationInfo.cpuAbi; + if (copyRet >= 0) { + pkg.applicationInfo.primaryCpuAbi = abiList[copyRet]; + } + } } catch (IOException ioe) { Slog.e(TAG, "Unable to get canonical file " + ioe.toString()); } finally { IoUtils.closeQuietly(handle); } + + if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path); + final int[] userIds = sUserManager.getUserIds(); + synchronized (mInstallLock) { + // Create a native library symlink only if we have native libraries + // and if the native libraries are 32 bit libraries. We do not provide + // this symlink for 64 bit libraries. + if (pkg.applicationInfo.primaryCpuAbi != null && + !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) { + final String nativeLibPath; + if (pkg.applicationInfo.legacyNativeLibraryDir != null) { + nativeLibPath = pkg.applicationInfo.legacyNativeLibraryDir; + } else { + nativeLibPath = new File(nativeLibraryRootStr, + VMRuntime.getInstructionSet(pkg.applicationInfo.primaryCpuAbi)).getAbsolutePath(); + } + + for (int userId : userIds) { + if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) < 0) { + Slog.w(TAG, "Failed linking native library dir (user=" + userId + + ")"); + mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + return null; + } + } + } + } + + pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi; + pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi; + } + + if (DEBUG_ABI_SELECTION) { + Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" + + " primary=" + pkg.applicationInfo.primaryCpuAbi + + " secondary=" + pkg.applicationInfo.secondaryCpuAbi); } + // Check if we have a legacy native library path, use it if we do. + pkg.applicationInfo.legacyNativeLibraryDir = pkgSetting.legacyNativeLibraryPathString; + + // Now that we've calculated the ABIs and determined if it's an internal app, + // we will go ahead and populate the nativeLibraryPath. + populateDefaultNativeLibraryPath(pkg, pkg.applicationInfo); + if ((scanMode&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) { // We don't do this here during boot because we can do it all // at once after scanning all existing packages. @@ -5929,6 +5954,9 @@ public class PackageManagerService extends IPackageManager.Stub { a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs; a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs; a.info.dataDir = pkg.applicationInfo.dataDir; + + // TODO: Update instrumentation.nativeLibraryDir as well ? Does it + // need other information about the application, like the ABI and what not ? a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir; mInstrumentation.put(a.getComponentName(), a); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { @@ -5989,13 +6017,16 @@ public class PackageManagerService extends IPackageManager.Stub { * 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. */ private boolean adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt) { String requiredInstructionSet = null; - if (scannedPackage != null && scannedPackage.applicationInfo.cpuAbi != null) { + if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) { requiredInstructionSet = VMRuntime.getInstructionSet( - scannedPackage.applicationInfo.cpuAbi); + scannedPackage.applicationInfo.primaryCpuAbi); } PackageSetting requirer = null; @@ -6005,11 +6036,11 @@ public class PackageManagerService extends IPackageManager.Stub { // 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.cpuAbiString == null) { + if (ps.primaryCpuAbiString == null) { continue; } - final String instructionSet = VMRuntime.getInstructionSet(ps.cpuAbiString); + final String instructionSet = VMRuntime.getInstructionSet(ps.primaryCpuAbiString); if (requiredInstructionSet != null) { if (!instructionSet.equals(requiredInstructionSet)) { // We have a mismatch between instruction sets (say arm vs arm64). @@ -6037,30 +6068,30 @@ public class PackageManagerService extends IPackageManager.Stub { // 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.cpuAbiString; + adjustedAbi = requirer.primaryCpuAbiString; if (scannedPackage != null) { - scannedPackage.applicationInfo.cpuAbi = adjustedAbi; + scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi; } } else { // requirer == null implies that we're updating all ABIs in the set to // match scannedPackage. - adjustedAbi = scannedPackage.applicationInfo.cpuAbi; + adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi; } for (PackageSetting ps : packagesForUser) { if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { - if (ps.cpuAbiString != null) { + if (ps.primaryCpuAbiString != null) { continue; } - ps.cpuAbiString = adjustedAbi; + ps.primaryCpuAbiString = adjustedAbi; if (ps.pkg != null && ps.pkg.applicationInfo != null) { - ps.pkg.applicationInfo.cpuAbi = adjustedAbi; + ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi; Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + adjustedAbi); if (performDexOptLI(ps.pkg, forceDexOpt, deferDexOpt, true) == DEX_OPT_FAILED) { - ps.cpuAbiString = null; - ps.pkg.applicationInfo.cpuAbi = null; + ps.primaryCpuAbiString = null; + ps.pkg.applicationInfo.primaryCpuAbi = null; return false; } else { mInstaller.rmdex(ps.codePathString, getPreferredInstructionSet()); @@ -6097,7 +6128,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private String calculateApkRoot(final String codePathString) { + private static String calculateApkRoot(final String codePathString) { final File codePath = new File(codePathString); final File codeRoot; if (FileUtils.contains(Environment.getRootDirectory(), codePath)) { @@ -6121,7 +6152,7 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Unrecognized code path " + codePath + " - using " + codeRoot); } catch (IOException e) { - // Can't canonicalize the lib path -- shenanigans? + // Can't canonicalize the code path -- shenanigans? Slog.w(TAG, "Can't canonicalize code path " + codePath); return Environment.getRootDirectory().getPath(); } @@ -6129,104 +6160,154 @@ public class PackageManagerService extends IPackageManager.Stub { return codeRoot.getPath(); } - // This is the initial scan-time determination of how to handle a given - // package for purposes of native library location. - private void setInternalAppNativeLibraryPath(PackageParser.Package pkg, - PackageSetting pkgSetting) { - // "bundled" here means system-installed with no overriding update - final boolean bundledApk = isSystemApp(pkg) && !isUpdatedSystemApp(pkg); - final File codeFile = new File(pkg.applicationInfo.getCodePath()); - final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath()); - - String nativeLibraryPath = null; - if (bundledApk) { - // If "/system/lib64/apkname" exists, assume that is the per-package - // native library directory to use; otherwise use "/system/lib/apkname". - String apkRoot = calculateApkRoot(pkg.applicationInfo.getCodePath()); - File lib64 = new File(apkRoot, LIB64_DIR_NAME); - File packLib64 = new File(lib64, apkName); - File libDir = (packLib64.exists()) ? lib64 : new File(apkRoot, LIB_DIR_NAME); - nativeLibraryPath = (new File(libDir, apkName)).getAbsolutePath(); - } else if (isApkFile(codeFile)) { - // Monolithic install - nativeLibraryPath = (new File(mAppLibInstallDir, apkName)).getAbsolutePath(); - } else { - // Cluster install - // TODO: pipe through abiOverride - String[] abiList = Build.SUPPORTED_ABIS; - NativeLibraryHelper.Handle handle = null; - try { - handle = NativeLibraryHelper.Handle.create(codeFile); - if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && - NativeLibraryHelper.hasRenderscriptBitcode(handle)) { - abiList = Build.SUPPORTED_32_BIT_ABIS; + private void populateDefaultNativeLibraryPath(PackageParser.Package pkg, + ApplicationInfo info) { + if (info.legacyNativeLibraryDir != null) { + // Not a cluster install. + if (DEBUG_ABI_SELECTION) { + Log.i(TAG, "Set nativeLibraryDir [non_cluster] for: " + pkg.packageName + + " to " + info.legacyNativeLibraryDir); + } + info.nativeLibraryDir = info.legacyNativeLibraryDir; + } else if (info.primaryCpuAbi != null) { + final boolean is64Bit = VMRuntime.is64BitAbi(info.primaryCpuAbi); + if (info.apkRoot != null) { + // 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(pkg.applicationInfo.getCodePath()); + final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME; + info.nativeLibraryDir = (new File(info.apkRoot, new File(libDir, apkName).getAbsolutePath())) + .getAbsolutePath(); + + if (DEBUG_ABI_SELECTION) { + Log.i(TAG, "Set nativeLibraryDir [system] for: " + pkg.packageName + + " to " + info.nativeLibraryDir); } + } else { + // Cluster install. legacyNativeLibraryDir == null && primaryCpuAbi = null + // implies this must be a cluster package. + final String codePath = pkg.codePath; + final File libPath = new File(new File(codePath, LIB_DIR_NAME), + VMRuntime.getInstructionSet(info.primaryCpuAbi)); + info.nativeLibraryDir = libPath.getAbsolutePath(); - final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle, abiList); - if (abiIndex >= 0) { - final File baseLibFile = new File(codeFile, LIB_DIR_NAME); - final String abi = Build.SUPPORTED_ABIS[abiIndex]; - final String instructionSet = VMRuntime.getInstructionSet(abi); - nativeLibraryPath = new File(baseLibFile, instructionSet).getAbsolutePath(); + if (DEBUG_ABI_SELECTION) { + Log.i(TAG, "Set nativeLibraryDir [cluster] for: " + pkg.packageName + + " to " + info.nativeLibraryDir); } - } catch (IOException e) { - Slog.e(TAG, "Failed to detect native libraries", e); - } finally { - IoUtils.closeQuietly(handle); } + } else { + if (DEBUG_ABI_SELECTION) { + Log.i(TAG, "Setting nativeLibraryDir to null for: " + pkg.packageName); + } + + info.nativeLibraryDir = null; } - pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath; + } + + /** + * 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. + */ + private 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 = calculateApkRoot(pkg.applicationInfo.sourceDir); + pkg.applicationInfo.apkRoot = apkRoot; + 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.nativeLibraryPathString = nativeLibraryPath; + pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi; + pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi; } } - // Deduces the required ABI of an upgraded system app. - private void setInternalAppAbi(PackageParser.Package pkg, PackageSetting pkgSetting) { - final String apkRoot = calculateApkRoot(pkg.applicationInfo.getCodePath()); - final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath()); - + /** + * Deduces the ABI of a bundled app and sets the relevant fields on the + * parsed pkg object. + * + * @param apkRoot the root of the installed apk, something like {@code /system} or {@code /oem} + * under which system libraries are installed. + * @param apkName the name of the installed package. + */ + private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) { // This is of the form "/system/lib64/<packagename>", "/vendor/lib64/<packagename>" // or similar. - final File lib64 = new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath()); - final File lib = new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath()); - - // Assume that the bundled native libraries always correspond to the - // most preferred 32 or 64 bit ABI. - if (lib64.exists()) { - pkg.applicationInfo.cpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; - pkgSetting.cpuAbiString = Build.SUPPORTED_64_BIT_ABIS[0]; - } else if (lib.exists()) { - pkg.applicationInfo.cpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; - pkgSetting.cpuAbiString = Build.SUPPORTED_32_BIT_ABIS[0]; + final boolean has64BitLibs = (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists(); + final boolean has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists(); + + if (has64BitLibs && !has32BitLibs) { + // The package has 64 bit libs, but not 32 bit libs. Its primary + // ABI should be 64 bit. We can safely assume here that the bundled + // native libraries correspond to the most preferred ABI in the list. + + pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + pkg.applicationInfo.secondaryCpuAbi = null; + } else if (has32BitLibs && !has64BitLibs) { + // The package has 32 bit libs but not 64 bit libs. Its primary + // ABI should be 32 bit. + + pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + pkg.applicationInfo.secondaryCpuAbi = null; + } else if (has32BitLibs && has64BitLibs) { + // The application has both 64 and 32 bit bundled libraries. We check + // here that the app declares multiArch support, and warn if it doesn't. + // + // We will be lenient here and record both ABIs. The primary will be the + // ABI that's higher on the list, i.e, a device that's configured to prefer + // 64 bit apps will see a 64 bit primary ABI, + + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) { + Slog.e(TAG, "Package: " + pkg + " has multiple bundled libs, but is not multiarch."); + } + + if (VMRuntime.is64BitAbi(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 { - // This is the case where the app has no native code. - pkg.applicationInfo.cpuAbi = null; - pkgSetting.cpuAbiString = null; + pkg.applicationInfo.primaryCpuAbi = null; + pkg.applicationInfo.secondaryCpuAbi = null; } } - private static int copyNativeLibrariesForInternalApp(NativeLibraryHelper.Handle handle, - final File nativeLibraryDir, String[] abiList) throws IOException { - if (!nativeLibraryDir.isDirectory()) { - nativeLibraryDir.delete(); + private static void createNativeLibrarySubdir(File path) throws IOException { + if (!path.isDirectory()) { + path.delete(); - if (!nativeLibraryDir.mkdir()) { - throw new IOException("Cannot create " + nativeLibraryDir.getPath()); + if (!path.mkdir()) { + throw new IOException("Cannot create " + path.getPath()); } try { - Os.chmod(nativeLibraryDir.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); } catch (ErrnoException e) { throw new IOException("Cannot chmod native library directory " - + nativeLibraryDir.getPath(), e); + + path.getPath(), e); } - } else if (!SELinux.restorecon(nativeLibraryDir)) { - throw new IOException("Cannot set SELinux context for " + nativeLibraryDir.getPath()); + } else if (!SELinux.restorecon(path)) { + throw new IOException("Cannot set SELinux context for " + path.getPath()); } + } + + private static int copyNativeLibrariesForInternalApp(NativeLibraryHelper.Handle handle, + final File nativeLibraryRoot, String[] abiList, boolean useIsaSubdir) throws IOException { + createNativeLibrarySubdir(nativeLibraryRoot); /* * If this is an internal application or our nativeLibraryPath points to @@ -6234,8 +6315,22 @@ public class PackageManagerService extends IPackageManager.Stub { */ int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList); if (abi >= 0) { + /* + * If we have a matching instruction set, construct a subdir under the native + * library root that corresponds to this instruction set. + */ + final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]); + final File subDir; + if (useIsaSubdir) { + final File isaSubdir = new File(nativeLibraryRoot, instructionSet); + createNativeLibrarySubdir(isaSubdir); + subDir = isaSubdir; + } else { + subDir = nativeLibraryRoot; + } + int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, - nativeLibraryDir, Build.SUPPORTED_ABIS[abi]); + subDir, Build.SUPPORTED_ABIS[abi]); if (copyRet != PackageManager.INSTALL_SUCCEEDED) { return copyRet; } @@ -8374,7 +8469,7 @@ public class PackageManagerService extends IPackageManager.Stub { private InstallArgs mArgs; private int mRet; final String packageAbiOverride; - final String packageInstructionSetOverride; + boolean multiArch; InstallParams(File originFile, boolean originStaged, IPackageInstallObserver2 observer, int flags, String installerPackageName, VerificationParams verificationParams, @@ -8387,8 +8482,6 @@ public class PackageManagerService extends IPackageManager.Stub { this.installerPackageName = installerPackageName; this.verificationParams = verificationParams; this.packageAbiOverride = packageAbiOverride; - this.packageInstructionSetOverride = (packageAbiOverride == null) ? - packageAbiOverride : VMRuntime.getInstructionSet(packageAbiOverride); } @Override @@ -8499,6 +8592,11 @@ public class PackageManagerService extends IPackageManager.Stub { final String originPath = originFile.getAbsolutePath(); pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags, lowThreshold, packageAbiOverride); + // Keep track of whether this package is a multiArch package until + // we perform a full scan of it. We need to do this because we might + // end up extracting the package shared libraries before we perform + // a full scan. + multiArch = pkgLite.multiArch; /* * If we have too little free space, try to free cache @@ -8744,7 +8842,8 @@ public class PackageManagerService extends IPackageManager.Stub { int mRet; MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags, - String packageName, String instructionSet, int uid, UserHandle user) { + String packageName, String[] instructionSets, int uid, UserHandle user, + boolean isMultiArch) { super(user); this.srcArgs = srcArgs; this.observer = observer; @@ -8754,7 +8853,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (srcArgs != null) { final String codePath = srcArgs.getCodePath(); targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName, - instructionSet); + instructionSets, isMultiArch); } else { targetArgs = null; } @@ -8869,7 +8968,8 @@ public class PackageManagerService extends IPackageManager.Stub { * when cleaning up old installs, or used as a move source. */ private InstallArgs createInstallArgsForExisting(int flags, String codePath, - String resourcePath, String nativeLibraryPath, String instructionSet) { + String resourcePath, String nativeLibraryRoot, String[] instructionSets, + boolean isMultiArch) { final boolean isInAsec; if (installOnSd(flags)) { /* Apps on SD card are always in ASEC containers. */ @@ -8886,23 +8986,24 @@ public class PackageManagerService extends IPackageManager.Stub { } if (isInAsec) { - return new AsecInstallArgs(codePath, resourcePath, nativeLibraryPath, - instructionSet, installOnSd(flags), installForwardLocked(flags)); + return new AsecInstallArgs(codePath, instructionSets, + installOnSd(flags), installForwardLocked(flags), isMultiArch); } else { - return new FileInstallArgs(codePath, resourcePath, nativeLibraryPath, instructionSet); + return new FileInstallArgs(codePath, resourcePath, nativeLibraryRoot, + instructionSets, isMultiArch); } } private InstallArgs createInstallArgsForMoveTarget(String codePath, int flags, String pkgName, - String instructionSet) { + String[] instructionSets, boolean isMultiArch) { final File codeFile = new File(codePath); if (installOnSd(flags) || installForwardLocked(flags)) { String cid = getNextCodePath(codePath, pkgName, "/" + AsecInstallArgs.RES_FILE_NAME); - return new AsecInstallArgs(codeFile, cid, instructionSet, installOnSd(flags), - installForwardLocked(flags)); + return new AsecInstallArgs(codeFile, cid, instructionSets, installOnSd(flags), + installForwardLocked(flags), isMultiArch); } else { - return new FileInstallArgs(codeFile, instructionSet); + return new FileInstallArgs(codeFile, instructionSets, isMultiArch); } } @@ -8920,12 +9021,18 @@ public class PackageManagerService extends IPackageManager.Stub { final String installerPackageName; final ManifestDigest manifestDigest; final UserHandle user; - final String instructionSet; final String abiOverride; + final boolean multiArch; + + // The list of instruction sets supported by this app. This is currently + // only used during the rmdex() phase to clean up resources. We can get rid of this + // if we move dex files under the common app path. + /* nullable */ String[] instructionSets; InstallArgs(File originFile, boolean originStaged, IPackageInstallObserver2 observer, - int flags, String installerPackageName, ManifestDigest manifestDigest, - UserHandle user, String instructionSet, String abiOverride) { + int flags, String installerPackageName, ManifestDigest manifestDigest, + UserHandle user, String[] instructionSets, + String abiOverride, boolean multiArch) { this.originFile = originFile; this.originStaged = originStaged; this.flags = flags; @@ -8933,8 +9040,9 @@ public class PackageManagerService extends IPackageManager.Stub { this.installerPackageName = installerPackageName; this.manifestDigest = manifestDigest; this.user = user; - this.instructionSet = instructionSet; + this.instructionSets = instructionSets; this.abiOverride = abiOverride; + this.multiArch = multiArch; } abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException; @@ -8951,8 +9059,7 @@ public class PackageManagerService extends IPackageManager.Stub { abstract String getCodePath(); /** @see PackageSettingBase#resourcePathString */ abstract String getResourcePath(); - /** @see PackageSettingBase#nativeLibraryPathString */ - abstract String getNativeLibraryPath(); + abstract String getLegacyNativeLibraryPath(); // Need installer lock especially for dex file removal. abstract void cleanUpResourcesLI(); @@ -8995,7 +9102,7 @@ public class PackageManagerService extends IPackageManager.Stub { class FileInstallArgs extends InstallArgs { private File codeFile; private File resourceFile; - private File nativeLibraryFile; + private File legacyNativeLibraryPath; // Example topology: // /data/app/com.example/base.apk @@ -9008,24 +9115,27 @@ public class PackageManagerService extends IPackageManager.Stub { FileInstallArgs(InstallParams params) { super(params.originFile, params.originStaged, params.observer, params.flags, params.installerPackageName, params.getManifestDigest(), params.getUser(), - params.packageInstructionSetOverride, params.packageAbiOverride); + null /* instruction sets */, params.packageAbiOverride, + params.multiArch); if (isFwdLocked()) { throw new IllegalArgumentException("Forward locking only supported in ASEC"); } } /** Existing install */ - FileInstallArgs(String codePath, String resourcePath, String nativeLibraryPath, - String instructionSet) { - super(null, false, null, 0, null, null, null, instructionSet, null); + FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryRoot, + String[] instructionSets, boolean isMultiArch) { + super(null, false, null, 0, null, null, null, instructionSets, null, isMultiArch); this.codeFile = (codePath != null) ? new File(codePath) : null; this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; - this.nativeLibraryFile = (nativeLibraryPath != null) ? new File(nativeLibraryPath) : null; + this.legacyNativeLibraryPath = (legacyNativeLibraryRoot != null) ? + new File(legacyNativeLibraryRoot) : null; } /** New install from existing */ - FileInstallArgs(File originFile, String instructionSet) { - super(originFile, false, null, 0, null, null, null, instructionSet, null); + FileInstallArgs(File originFile, String[] instructionSets, boolean isMultiArch) { + super(originFile, false, null, 0, null, null, null, instructionSets, null, + isMultiArch); } boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { @@ -9091,35 +9201,67 @@ public class PackageManagerService extends IPackageManager.Stub { } } - String[] abiList = (abiOverride != null) ? - new String[] { abiOverride } : Build.SUPPORTED_ABIS; + final File libraryRoot = new File(codeFile, LIB_DIR_NAME); NativeLibraryHelper.Handle handle = null; try { handle = NativeLibraryHelper.Handle.create(codeFile); - if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && - abiOverride == null && - NativeLibraryHelper.hasRenderscriptBitcode(handle)) { - abiList = Build.SUPPORTED_32_BIT_ABIS; - } - - // TODO: refactor to avoid double findSupportedAbi() - final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle, abiList); - if (abiIndex < 0 && abiIndex != PackageManager.NO_NATIVE_LIBRARIES) { - return abiIndex; - } else if (abiIndex >= 0) { - final File baseLibFile = new File(codeFile, LIB_DIR_NAME); - baseLibFile.mkdir(); - Os.chmod(baseLibFile.getAbsolutePath(), 0755); - - final String abi = Build.SUPPORTED_ABIS[abiIndex]; - final String instructionSet = VMRuntime.getInstructionSet(abi); - nativeLibraryFile = new File(baseLibFile, instructionSet); - nativeLibraryFile.mkdir(); - Os.chmod(nativeLibraryFile.getAbsolutePath(), 0755); - - copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList); - } - } catch (IOException | ErrnoException e) { + if (multiArch) { + // 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 (abiOverride != null) { + Slog.w(TAG, "Ignoring abiOverride for multi arch application."); + } + + int copyRet = PackageManager.NO_NATIVE_LIBRARIES; + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot, + Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */); + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { + Slog.w(TAG, "Failure copying 32 bit native libraries [errorCode=" + copyRet + "]"); + return copyRet; + } + } + + if (DEBUG_ABI_SELECTION && copyRet >= 0) { + Log.d(TAG, "Installed 32 bit libraries under: " + codeFile + " abi=" + + Build.SUPPORTED_32_BIT_ABIS[copyRet]); + } + + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot, + Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */); + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { + Slog.w(TAG, "Failure copying 64 bit native libraries [errorCode=" + copyRet + "]"); + return copyRet; + } + } + + if (DEBUG_ABI_SELECTION && copyRet >= 0) { + Log.d(TAG, "Installed 64 bit libraries under: " + codeFile + " abi=" + + Build.SUPPORTED_64_BIT_ABIS[copyRet]); + } + } else { + String[] abiList = (abiOverride != null) ? + new String[] { abiOverride } : Build.SUPPORTED_ABIS; + + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && abiOverride == null && + NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + } + + int copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot, abiList, + true /* use isa specific subdirs */); + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { + Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]"); + return copyRet; + } + + if (DEBUG_ABI_SELECTION && copyRet >= 0) { + Log.d(TAG, "Installed libraries under: " + codeFile + " abi=" + abiList[copyRet]); + } + } + } catch (IOException e) { Slog.e(TAG, "Copying native libraries failed", e); ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } finally { @@ -9156,8 +9298,6 @@ public class PackageManagerService extends IPackageManager.Stub { // Reflect the rename internally codeFile = afterCodeFile; resourceFile = afterCodeFile; - nativeLibraryFile = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, - nativeLibraryFile); // Reflect the rename in scanned details pkg.codePath = afterCodeFile.getAbsolutePath(); @@ -9173,7 +9313,8 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.setResourcePath(pkg.codePath); pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath); pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths); - pkg.applicationInfo.nativeLibraryDir = getNativeLibraryPath(); + // Null out the legacy native dir so we stop using it. + pkg.applicationInfo.legacyNativeLibraryDir = null; return true; } @@ -9197,8 +9338,8 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - String getNativeLibraryPath() { - return (nativeLibraryFile != null) ? nativeLibraryFile.getAbsolutePath() : null; + String getLegacyNativeLibraryPath() { + return (legacyNativeLibraryPath != null) ? legacyNativeLibraryPath.getAbsolutePath() : null; } private boolean cleanUp() { @@ -9215,9 +9356,11 @@ public class PackageManagerService extends IPackageManager.Stub { resourceFile.delete(); } - if (nativeLibraryFile != null && !FileUtils.contains(codeFile, nativeLibraryFile)) { - FileUtils.deleteContents(nativeLibraryFile); - nativeLibraryFile.delete(); + if (legacyNativeLibraryPath != null && !FileUtils.contains(codeFile, legacyNativeLibraryPath)) { + if (!FileUtils.deleteContents(legacyNativeLibraryPath)) { + Slog.w(TAG, "Couldn't delete native library directory " + legacyNativeLibraryPath); + } + legacyNativeLibraryPath.delete(); } return true; @@ -9238,16 +9381,18 @@ public class PackageManagerService extends IPackageManager.Stub { cleanUp(); if (!allCodePaths.isEmpty()) { - if (instructionSet == null) { + if (instructionSets == null) { throw new IllegalStateException("instructionSet == null"); } for (String codePath : allCodePaths) { - int retCode = mInstaller.rmdex(codePath, instructionSet); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove dex file for package: " - + " at location " + codePath + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion + for (String instructionSet : instructionSets) { + int retCode = mInstaller.rmdex(codePath, instructionSet); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove dex file for package: " + + " at location " + codePath + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion + } } } } @@ -9289,21 +9434,22 @@ public class PackageManagerService extends IPackageManager.Stub { String cid; String packagePath; String resourcePath; - String libraryPath; + String legacyNativeLibraryDir; /** New install */ AsecInstallArgs(InstallParams params) { super(params.originFile, params.originStaged, params.observer, params.flags, - params.installerPackageName, params.getManifestDigest(), params.getUser(), - params.packageInstructionSetOverride, params.packageAbiOverride); + params.installerPackageName, params.getManifestDigest(), + params.getUser(), null /* instruction sets */, + params.packageAbiOverride, params.multiArch); } /** Existing install */ - AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, - String instructionSet, boolean isExternal, boolean isForwardLocked) { + AsecInstallArgs(String fullCodePath, String[] instructionSets, + boolean isExternal, boolean isForwardLocked, boolean isMultiArch) { super(null, false, null, (isExternal ? INSTALL_EXTERNAL : 0) | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, - instructionSet, null); + instructionSets, null, isMultiArch); // Extract cid from fullCodePath int eidx = fullCodePath.lastIndexOf("/"); String subStr1 = fullCodePath.substring(0, eidx); @@ -9312,20 +9458,21 @@ public class PackageManagerService extends IPackageManager.Stub { setCachePath(subStr1); } - AsecInstallArgs(String cid, String instructionSet, boolean isForwardLocked) { + AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked, + boolean isMultiArch) { super(null, false, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0) | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, - instructionSet, null); + instructionSets, null, isMultiArch); this.cid = cid; setCachePath(PackageHelper.getSdDir(cid)); } /** New install from existing */ - AsecInstallArgs(File originPackageFile, String cid, String instructionSet, - boolean isExternal, boolean isForwardLocked) { + AsecInstallArgs(File originPackageFile, String cid, String[] instructionSets, + boolean isExternal, boolean isForwardLocked, boolean isMultiArch) { super(originPackageFile, false, null, (isExternal ? INSTALL_EXTERNAL : 0) | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, - instructionSet, null); + instructionSets, null, isMultiArch); this.cid = cid; } @@ -9376,8 +9523,8 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - String getNativeLibraryPath() { - return libraryPath; + String getLegacyNativeLibraryPath() { + return legacyNativeLibraryDir; } int doPreInstall(int status) { @@ -9452,14 +9599,16 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.setResourcePath(getResourcePath()); pkg.applicationInfo.setBaseResourcePath(getResourcePath()); pkg.applicationInfo.setSplitResourcePaths(null); - pkg.applicationInfo.nativeLibraryDir = getNativeLibraryPath(); + // ASEC installs are considered "legacy" because we don't support + // multiarch on them yet, and use the old style paths on them. + pkg.applicationInfo.legacyNativeLibraryDir = legacyNativeLibraryDir; return true; } private void setCachePath(String newCachePath) { File cachePath = new File(newCachePath); - libraryPath = new File(cachePath, LIB_DIR_NAME).getPath(); + legacyNativeLibraryDir = new File(cachePath, LIB_DIR_NAME).getPath(); packagePath = new File(cachePath, RES_FILE_NAME).getPath(); if (isFwdLocked()) { @@ -9508,15 +9657,17 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanUpResourcesLI() { String sourceFile = getCodePath(); // Remove dex file - if (instructionSet == null) { + if (instructionSets == null) { throw new IllegalStateException("instructionSet == null"); } - int retCode = mInstaller.rmdex(sourceFile, instructionSet); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove dex file for package: " - + " at location " - + sourceFile.toString() + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion + for (String instructionSet : instructionSets) { + int retCode = mInstaller.rmdex(sourceFile, instructionSet); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove dex file for package: " + + " at location " + + sourceFile.toString() + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion + } } cleanUp(); } @@ -9922,8 +10073,9 @@ public class PackageManagerService extends IPackageManager.Stub { res.removedInfo.args = createInstallArgsForExisting(0, deletedPackage.applicationInfo.getCodePath(), deletedPackage.applicationInfo.getResourcePath(), - deletedPackage.applicationInfo.nativeLibraryDir, - getAppInstructionSet(deletedPackage.applicationInfo)); + deletedPackage.applicationInfo.legacyNativeLibraryDir, + getAppDexInstructionSets(deletedPackage.applicationInfo), + isMultiArch(deletedPackage.applicationInfo)); } else { res.removedInfo.args = null; } @@ -9983,10 +10135,11 @@ public class PackageManagerService extends IPackageManager.Stub { private int moveDexFilesLI(String oldCodePath, PackageParser.Package newPackage) { // TODO: extend to move split APK dex files if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - final String instructionSet = getAppInstructionSet(newPackage.applicationInfo); - int retCode = mInstaller.movedex(oldCodePath, newPackage.baseCodePath, - instructionSet); - if (retCode != 0) { + final String[] instructionSets = getAppDexInstructionSets(newPackage.applicationInfo); + for (String instructionSet : instructionSets) { + int retCode = mInstaller.movedex(oldCodePath, newPackage.baseCodePath, + instructionSet); + if (retCode != 0) { /* * Programs may be lazily run through dexopt, so the * source may not exist. However, something seems to @@ -9995,9 +10148,10 @@ public class PackageManagerService extends IPackageManager.Stub { * remove the target to make sure there isn't a stale * file from a previous version of the package. */ - newPackage.mDexOptNeeded = true; - mInstaller.rmdex(oldCodePath, instructionSet); - mInstaller.rmdex(newPackage.baseCodePath, instructionSet); + newPackage.mDexOptNeeded = true; + mInstaller.rmdex(oldCodePath, instructionSet); + mInstaller.rmdex(newPackage.baseCodePath, instructionSet); + } } } return PackageManager.INSTALL_SUCCEEDED; @@ -10234,6 +10388,14 @@ public class PackageManagerService extends IPackageManager.Stub { return (ps.pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0; } + private static boolean isMultiArch(PackageSetting ps) { + return (ps.pkgFlags & ApplicationInfo.FLAG_MULTIARCH) != 0; + } + + private static boolean isMultiArch(ApplicationInfo info) { + return (info.flags & ApplicationInfo.FLAG_MULTIARCH) != 0; + } + private static boolean isExternal(PackageParser.Package pkg) { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; } @@ -10591,7 +10753,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Reinstate the old system package mSettings.enableSystemPackageLPw(newPs.name); // Remove any native libraries from the upgraded package. - NativeLibraryHelper.removeNativeBinariesLI(newPs.nativeLibraryPathString); + NativeLibraryHelper.removeNativeBinariesLI(newPs.legacyNativeLibraryPathString); } // Install the system package if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); @@ -10610,7 +10772,7 @@ public class PackageManagerService extends IPackageManager.Stub { // writer synchronized (mPackages) { PackageSetting ps = mSettings.mPackages.get(newPkg.packageName); - setInternalAppNativeLibraryPath(newPkg, ps); + setBundledAppAbisAndRoots(newPkg, ps); updatePermissionsLPw(newPkg.packageName, newPkg, UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG); if (applyUserRestrictions) { @@ -10650,8 +10812,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Delete application code and resources if (deleteCodeAndResources && (outInfo != null)) { outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, - getAppInstructionSetFromSettings(ps)); + ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, + getAppDexInstructionSets(ps), isMultiArch(ps)); } return true; } @@ -11056,7 +11218,7 @@ public class PackageManagerService extends IPackageManager.Stub { } PackageParser.Package p; boolean dataOnly = false; - String libDirPath = null; + String libDirRoot = null; String asecPath = null; PackageSetting ps = null; synchronized (mPackages) { @@ -11071,7 +11233,7 @@ public class PackageManagerService extends IPackageManager.Stub { p = ps.pkg; } if (ps != null) { - libDirPath = ps.nativeLibraryPathString; + libDirRoot = ps.legacyNativeLibraryPathString; } if (p != null && (isExternal(p) || isForwardLocked(p))) { String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath()); @@ -11092,8 +11254,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } // TODO: extend to measure size of split APKs - int res = mInstaller.getSizeInfo(packageName, userHandle, p.baseCodePath, libDirPath, - publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps), + // TODO(multiArch): Extend getSizeInfo to look at the full subdirectory tree, + // not just the first level. + // TODO(multiArch): Extend getSizeInfo to look at *all* instruction sets, not + // just the primary. + int res = mInstaller.getSizeInfo(packageName, userHandle, p.baseCodePath, libDirRoot, + publicSrcDir, asecPath, getAppDexInstructionSets(ps), pStats); if (res < 0) { return false; @@ -12357,8 +12523,7 @@ public class PackageManagerService extends IPackageManager.Stub { } final AsecInstallArgs args = new AsecInstallArgs(cid, - getAppInstructionSetFromSettings(ps), - isForwardLocked(ps)); + getAppDexInstructionSets(ps), isForwardLocked(ps), isMultiArch(ps)); // The package status is changed only if the code path // matches between settings and the container id. if (ps.codePathString != null && ps.codePathString.equals(args.getCodePath())) { @@ -12676,16 +12841,17 @@ public class PackageManagerService extends IPackageManager.Stub { * anyway. */ if (returnCode != PackageManager.MOVE_SUCCEEDED) { - processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1, user), + processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1, user, false), returnCode); } else { Message msg = mHandler.obtainMessage(INIT_COPY); - final String instructionSet = getAppInstructionSet(pkg.applicationInfo); + final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo); + final boolean multiArch = isMultiArch(pkg.applicationInfo); InstallArgs srcArgs = createInstallArgsForExisting(currFlags, pkg.applicationInfo.getCodePath(), pkg.applicationInfo.getResourcePath(), - pkg.applicationInfo.nativeLibraryDir, instructionSet); + pkg.applicationInfo.legacyNativeLibraryDir, instructionSets, multiArch); MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, - instructionSet, pkg.applicationInfo.uid, user); + instructionSets, pkg.applicationInfo.uid, user, multiArch); msg.obj = mp; mHandler.sendMessage(msg); } @@ -12747,12 +12913,15 @@ public class PackageManagerService extends IPackageManager.Stub { final String oldCodePath = pkg.codePath; final String newCodePath = mp.targetArgs.getCodePath(); final String newResPath = mp.targetArgs.getResourcePath(); - final String newNativePath = mp.targetArgs - .getNativeLibraryPath(); - - final File newNativeDir = new File(newNativePath); + // TODO: This assumes the new style of installation. + // should we look at legacyNativeLibraryPath ? + final String newNativeRoot = new File(pkg.codePath, LIB_DIR_NAME).getAbsolutePath(); + final File newNativeDir = new File(newNativeRoot); if (!isForwardLocked(pkg) && !isExternal(pkg)) { + // TODO(multiArch): Fix this so that it looks at the existing + // recorded CPU abis from the package. There's no need for a separate + // round of ABI scanning here. NativeLibraryHelper.Handle handle = null; try { handle = NativeLibraryHelper.Handle.create( @@ -12771,11 +12940,17 @@ public class PackageManagerService extends IPackageManager.Stub { IoUtils.closeQuietly(handle); } } + final int[] users = sUserManager.getUserIds(); if (returnCode == PackageManager.MOVE_SUCCEEDED) { for (int user : users) { + // TODO(multiArch): Fix this so that it links to the + // correct directory. We're currently pointing to root. but we + // must point to the arch specific subdirectory (if applicable). + // + // TODO(multiArch): Bogus reference to nativeLibraryDir. if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, - newNativePath, user) < 0) { + newNativeRoot, user) < 0) { returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; } } @@ -12801,15 +12976,21 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.setResourcePath(newResPath); pkg.applicationInfo.setBaseResourcePath(newResPath); pkg.applicationInfo.setSplitResourcePaths(null); - pkg.applicationInfo.nativeLibraryDir = newNativePath; + // Null out the legacy nativeLibraryDir so that we stop using it and + // always derive the codepath. + pkg.applicationInfo.legacyNativeLibraryDir = null; PackageSetting ps = (PackageSetting) pkg.mExtras; ps.codePath = new File(pkg.applicationInfo.getCodePath()); ps.codePathString = ps.codePath.getPath(); - ps.resourcePath = new File( - pkg.applicationInfo.getResourcePath()); + ps.resourcePath = new File(pkg.applicationInfo.getResourcePath()); ps.resourcePathString = ps.resourcePath.getPath(); - ps.nativeLibraryPathString = newNativePath; + + // Note that we don't have to recalculate the primary and secondary + // CPU ABIs because they must already have been calculated during the + // initial install of the app. + ps.legacyNativeLibraryPathString = null; + // Set the application info flag // correctly. if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) { diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 284da99..a6571cf 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -30,9 +30,10 @@ final class PackageSetting extends PackageSettingBase { SharedUserSetting sharedUser; PackageSetting(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, String cpuAbiString, int pVersionCode, int pkgFlags) { - super(name, realName, codePath, resourcePath, nativeLibraryPathString, cpuAbiString, pVersionCode, - pkgFlags); + String legacyNativeLibraryPathString, String primaryCpuAbiString, + String secondaryCpuAbiString, int pVersionCode, int pkgFlags) { + super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString, + primaryCpuAbiString, secondaryCpuAbiString, pVersionCode, pkgFlags); } /** diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index f263e07..3390efe 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -55,8 +55,16 @@ class PackageSettingBase extends GrantedPermissions { String codePathString; File resourcePath; String resourcePathString; - String nativeLibraryPathString; - String cpuAbiString; + + /** + * The path under which native libraries for legacy apps are unpacked. + * Will be set to {@code null} for newer installs, where the path can be + * derived from {@link #codePath} unambiguously. + */ + String legacyNativeLibraryPathString; + + String primaryCpuAbiString; + String secondaryCpuAbiString; long timeStamp; long firstInstallTime; long lastUpdateTime; @@ -84,11 +92,13 @@ class PackageSettingBase extends GrantedPermissions { /* package name of the app that installed this package */ String installerPackageName; PackageSettingBase(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, String cpuAbiString, int pVersionCode, int pkgFlags) { + String legacyNativeLibraryPathString, String primaryCpuAbiString, + String secondaryCpuAbiString, int pVersionCode, int pkgFlags) { super(pkgFlags); this.name = name; this.realName = realName; - init(codePath, resourcePath, nativeLibraryPathString, cpuAbiString, pVersionCode); + init(codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString, + secondaryCpuAbiString, pVersionCode); } /** @@ -104,8 +114,9 @@ class PackageSettingBase extends GrantedPermissions { codePathString = base.codePathString; resourcePath = base.resourcePath; resourcePathString = base.resourcePathString; - nativeLibraryPathString = base.nativeLibraryPathString; - cpuAbiString = base.cpuAbiString; + legacyNativeLibraryPathString = base.legacyNativeLibraryPathString; + primaryCpuAbiString = base.primaryCpuAbiString; + secondaryCpuAbiString = base.secondaryCpuAbiString; timeStamp = base.timeStamp; firstInstallTime = base.firstInstallTime; lastUpdateTime = base.lastUpdateTime; @@ -132,14 +143,15 @@ class PackageSettingBase extends GrantedPermissions { } - void init(File codePath, File resourcePath, String nativeLibraryPathString, - String requiredCpuAbiString, int pVersionCode) { + void init(File codePath, File resourcePath, String legacyNativeLibraryPathString, + String primaryCpuAbiString, String secondaryCpuAbiString, int pVersionCode) { this.codePath = codePath; this.codePathString = codePath.toString(); this.resourcePath = resourcePath; this.resourcePathString = resourcePath.toString(); - this.nativeLibraryPathString = nativeLibraryPathString; - this.cpuAbiString = requiredCpuAbiString; + this.legacyNativeLibraryPathString = legacyNativeLibraryPathString; + this.primaryCpuAbiString = primaryCpuAbiString; + this.secondaryCpuAbiString = secondaryCpuAbiString; this.versionCode = pVersionCode; } @@ -170,7 +182,8 @@ class PackageSettingBase extends GrantedPermissions { grantedPermissions = base.grantedPermissions; gids = base.gids; - cpuAbiString = base.cpuAbiString; + primaryCpuAbiString = base.primaryCpuAbiString; + secondaryCpuAbiString = base.secondaryCpuAbiString; timeStamp = base.timeStamp; firstInstallTime = base.firstInstallTime; lastUpdateTime = base.lastUpdateTime; diff --git a/services/core/java/com/android/server/pm/PendingPackage.java b/services/core/java/com/android/server/pm/PendingPackage.java index 36c3a34..85be651 100644 --- a/services/core/java/com/android/server/pm/PendingPackage.java +++ b/services/core/java/com/android/server/pm/PendingPackage.java @@ -22,9 +22,10 @@ final class PendingPackage extends PackageSettingBase { final int sharedId; PendingPackage(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, String requiredCpuAbiString, int sharedId, int pVersionCode, int pkgFlags) { - super(name, realName, codePath, resourcePath, nativeLibraryPathString, requiredCpuAbiString, pVersionCode, - pkgFlags); + String nativeLibrary32PathString, String nativeLibrary64PathString, + String requiredCpuAbiString, int sharedId, int pVersionCode, int pkgFlags) { + super(name, realName, codePath, resourcePath, nativeLibrary32PathString, nativeLibrary64PathString, + requiredCpuAbiString, pVersionCode, pkgFlags); this.sharedId = sharedId; } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 81ea72c..71b8974 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -29,7 +29,13 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ResolveInfo; import android.net.Uri; +import android.os.Binder; +import android.os.Environment; +import android.os.FileUtils; import android.os.PatternMatcher; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; import android.util.LogPrinter; import com.android.internal.util.FastXmlSerializer; @@ -56,12 +62,6 @@ import android.content.pm.Signature; import android.content.pm.UserInfo; import android.content.pm.PackageUserState; import android.content.pm.VerifierDeviceIdentity; -import android.os.Binder; -import android.os.Environment; -import android.os.FileUtils; -import android.os.Process; -import android.os.UserHandle; -import android.os.UserManager; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -317,11 +317,12 @@ final class Settings { PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, String cpuAbiString, int pkgFlags, UserHandle user, boolean add) { + String nativeLibraryRoot, String primaryCpuAbi, String secondaryCpuAbi, int pkgFlags, + UserHandle user, boolean add) { final String name = pkg.packageName; PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath, - resourcePath, nativeLibraryPathString, cpuAbiString, pkg.mVersionCode, pkgFlags, - user, add, true /* allowInstall */); + resourcePath, nativeLibraryRoot, primaryCpuAbi, secondaryCpuAbi, + pkg.mVersionCode, pkgFlags, user, add, true /* allowInstall */); return p; } @@ -407,7 +408,8 @@ final class Settings { p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath, - p.nativeLibraryPathString, p.cpuAbiString, p.appId, p.versionCode, p.pkgFlags); + p.legacyNativeLibraryPathString, p.primaryCpuAbiString, + p.secondaryCpuAbiString, p.appId, p.versionCode, p.pkgFlags); mDisabledSysPackages.remove(name); return ret; } @@ -421,7 +423,8 @@ final class Settings { } PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, String cpuAbiString, int uid, int vc, int pkgFlags) { + String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, + int uid, int vc, int pkgFlags) { PackageSetting p = mPackages.get(name); if (p != null) { if (p.appId == uid) { @@ -431,8 +434,8 @@ final class Settings { "Adding duplicate package, keeping first: " + name); return null; } - p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString, cpuAbiString, - vc, pkgFlags); + p = new PackageSetting(name, realName, codePath, resourcePath, + legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, vc, pkgFlags); p.appId = uid; if (addUserIdLPw(uid, p, name)) { mPackages.put(name, p); @@ -500,11 +503,12 @@ final class Settings { private PackageSetting getPackageLPw(String name, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, String cpuAbiString, int vc, int pkgFlags, - UserHandle installUser, boolean add, boolean allowInstall) { + String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, + int vc, int pkgFlags, UserHandle installUser, boolean add, boolean allowInstall) { PackageSetting p = mPackages.get(name); if (p != null) { - p.cpuAbiString = cpuAbiString; + p.primaryCpuAbiString = primaryCpuAbiString; + p.secondaryCpuAbiString = secondaryCpuAbiString; if (!p.codePath.equals(codePath)) { // Check to see if its a disabled system app if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { @@ -524,7 +528,7 @@ final class Settings { * package settings since we might have moved from * internal to external storage or vice versa. */ - p.nativeLibraryPathString = nativeLibraryPathString; + p.legacyNativeLibraryPathString = legacyNativeLibraryPathString; } } if (p.sharedUser != sharedUser) { @@ -548,7 +552,7 @@ final class Settings { if (origPackage != null) { // We are consuming the data from an existing package. p = new PackageSetting(origPackage.name, name, codePath, resourcePath, - nativeLibraryPathString, cpuAbiString, vc, pkgFlags); + legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, vc, pkgFlags); if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + name + " is adopting original package " + origPackage.name); // Note that we will retain the new package's signature so @@ -565,7 +569,7 @@ final class Settings { p.setTimeStamp(codePath.lastModified()); } else { p = new PackageSetting(name, realName, codePath, resourcePath, - nativeLibraryPathString, cpuAbiString, vc, pkgFlags); + legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, vc, pkgFlags); p.setTimeStamp(codePath.lastModified()); p.sharedUser = sharedUser; // If this is not a system app, it starts out stopped. @@ -699,14 +703,15 @@ final class Settings { p.resourcePath = new File(resourcePath); p.resourcePathString = resourcePath; } - // Update the native library path if needed - final String nativeLibraryPath = pkg.applicationInfo.nativeLibraryDir; - if (nativeLibraryPath != null - && !nativeLibraryPath.equalsIgnoreCase(p.nativeLibraryPathString)) { - p.nativeLibraryPathString = nativeLibraryPath; + // Update the native library paths if needed + final String nativeLibraryRoot = pkg.applicationInfo.legacyNativeLibraryDir; + if (nativeLibraryRoot != null && !nativeLibraryRoot.equalsIgnoreCase(p.legacyNativeLibraryPathString)) { + p.legacyNativeLibraryPathString = nativeLibraryRoot; } + // Update the required Cpu Abi - p.cpuAbiString = pkg.applicationInfo.cpuAbi; + p.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi; + p.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi; // Update version code if needed if (pkg.mVersionCode != p.versionCode) { p.versionCode = pkg.mVersionCode; @@ -1861,12 +1866,16 @@ final class Settings { if (!pkg.resourcePathString.equals(pkg.codePathString)) { serializer.attribute(null, "resourcePath", pkg.resourcePathString); } - if (pkg.nativeLibraryPathString != null) { - serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); + if (pkg.legacyNativeLibraryPathString != null) { + serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString); } - if (pkg.cpuAbiString != null) { - serializer.attribute(null, "requiredCpuAbi", pkg.cpuAbiString); + if (pkg.primaryCpuAbiString != null) { + serializer.attribute(null, "primaryCpuAbi", pkg.primaryCpuAbiString); } + if (pkg.secondaryCpuAbiString != null) { + serializer.attribute(null, "secondaryCpuAbi", pkg.secondaryCpuAbiString); + } + if (pkg.sharedUser == null) { serializer.attribute(null, "userId", Integer.toString(pkg.appId)); } else { @@ -1906,12 +1915,17 @@ final class Settings { if (!pkg.resourcePathString.equals(pkg.codePathString)) { serializer.attribute(null, "resourcePath", pkg.resourcePathString); } - if (pkg.nativeLibraryPathString != null) { - serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); + + if (pkg.legacyNativeLibraryPathString != null) { + serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString); + } + if (pkg.primaryCpuAbiString != null) { + serializer.attribute(null, "primaryCpuAbi", pkg.primaryCpuAbiString); } - if (pkg.cpuAbiString != null) { - serializer.attribute(null, "requiredCpuAbi", pkg.cpuAbiString); + if (pkg.secondaryCpuAbiString != null) { + serializer.attribute(null, "secondaryCpuAbi", pkg.secondaryCpuAbiString); } + serializer.attribute(null, "flags", Integer.toString(pkg.pkgFlags)); serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime)); @@ -2219,7 +2233,8 @@ final class Settings { if (idObj != null && idObj instanceof SharedUserSetting) { PackageSetting p = getPackageLPw(pp.name, null, pp.realName, (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, - pp.nativeLibraryPathString, pp.cpuAbiString, pp.versionCode, pp.pkgFlags, + pp.legacyNativeLibraryPathString, pp.primaryCpuAbiString, + pp.secondaryCpuAbiString, pp.versionCode, pp.pkgFlags, null, true /* add */, false /* allowInstall */); if (p == null) { PackageManagerService.reportSettingsProblem(Log.WARN, @@ -2638,8 +2653,16 @@ final class Settings { String realName = parser.getAttributeValue(null, "realName"); String codePathStr = parser.getAttributeValue(null, "codePath"); String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); - String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - String cpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); + + String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi"); + String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); + + String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi"); + String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi"); + + if (primaryCpuAbiStr == null && legacyCpuAbiStr != null) { + primaryCpuAbiStr = legacyCpuAbiStr; + } if (resourcePathStr == null) { resourcePathStr = codePathStr; @@ -2660,7 +2683,8 @@ final class Settings { pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED; } PackageSetting ps = new PackageSetting(name, realName, codePathFile, - new File(resourcePathStr), nativeLibraryPathStr, cpuAbiString, versionCode, pkgFlags); + new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr, + secondaryCpuAbiStr, versionCode, pkgFlags); String timeStampStr = parser.getAttributeValue(null, "ft"); if (timeStampStr != null) { try { @@ -2726,8 +2750,10 @@ final class Settings { String sharedIdStr = null; String codePathStr = null; String resourcePathStr = null; - String nativeLibraryPathStr = null; - String cpuAbiString = null; + String legacyCpuAbiString = null; + String legacyNativeLibraryPathStr = null; + String primaryCpuAbiString = null; + String secondaryCpuAbiString = null; String systemStr = null; String installerPackageName = null; String uidError = null; @@ -2746,9 +2772,17 @@ final class Settings { sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); codePathStr = parser.getAttributeValue(null, "codePath"); resourcePathStr = parser.getAttributeValue(null, "resourcePath"); - nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - cpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); + legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); + + legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); + primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi"); + secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi"); + + if (primaryCpuAbiString == null && legacyCpuAbiString != null) { + primaryCpuAbiString = legacyCpuAbiString; + } +; version = parser.getAttributeValue(null, "version"); if (version != null) { try { @@ -2825,8 +2859,8 @@ final class Settings { + parser.getPositionDescription()); } else if (userId > 0) { packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), - new File(resourcePathStr), nativeLibraryPathStr, cpuAbiString, userId, versionCode, - pkgFlags); + new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString, + secondaryCpuAbiString, userId, versionCode, pkgFlags); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId=" + userId + " pkg=" + packageSetting); @@ -2843,8 +2877,8 @@ final class Settings { userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; if (userId > 0) { packageSetting = new PendingPackage(name.intern(), realName, new File( - codePathStr), new File(resourcePathStr), nativeLibraryPathStr, cpuAbiString, userId, - versionCode, pkgFlags); + codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr, + primaryCpuAbiString, legacyCpuAbiString, userId, versionCode, pkgFlags); packageSetting.setTimeStamp(timeStamp); packageSetting.firstInstallTime = firstInstallTime; packageSetting.lastUpdateTime = lastUpdateTime; @@ -2871,8 +2905,9 @@ final class Settings { if (packageSetting != null) { packageSetting.uidError = "true".equals(uidError); packageSetting.installerPackageName = installerPackageName; - packageSetting.nativeLibraryPathString = nativeLibraryPathStr; - packageSetting.cpuAbiString = cpuAbiString; + packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr; + packageSetting.primaryCpuAbiString = primaryCpuAbiString; + packageSetting.secondaryCpuAbiString = secondaryCpuAbiString; // Handle legacy string here for single-user mode final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); if (enabledStr != null) { @@ -3417,8 +3452,9 @@ final class Settings { pw.print(prefix); pw.print(" pkg="); pw.println(ps.pkg); pw.print(prefix); pw.print(" codePath="); pw.println(ps.codePathString); pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.resourcePathString); - pw.print(prefix); pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString); - pw.print(prefix); pw.print(" requiredCpuAbi="); pw.println(ps.cpuAbiString); + pw.print(prefix); pw.print(" legacyNativeLibraryDir="); pw.println(ps.legacyNativeLibraryPathString); + pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.primaryCpuAbiString); + pw.print(prefix); pw.print(" secondaryCpuAbi="); pw.println(ps.secondaryCpuAbiString); pw.print(prefix); pw.print(" versionCode="); pw.print(ps.versionCode); if (ps.pkg != null) { pw.print(" targetSdk="); pw.print(ps.pkg.applicationInfo.targetSdkVersion); |