Merge "Re-implement native library search and copies."
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c97c2b8..8ce7e97 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -675,6 +675,25 @@
     public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
 
     /**
+     * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
+     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
+     * if the system failed to install the package because its packaged native code did not
+     * match any of the ABIs supported by the system.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_NO_MATCHING_ABIS = -112;
+
+    /**
+     * Internal return code for NativeLibraryHelper methods to indicate that the package
+     * being processed did not contain any native code. This is placed here only so that
+     * it can belong to the same value space as the other install failure codes.
+     *
+     * @hide
+     */
+    public static final int NO_NATIVE_LIBRARIES = -113;
+
+    /**
      * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the
      * package's data directory.
      *
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index bc51a60..c3313c5 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -74,7 +74,14 @@
 
     /** A hardware serial number, if available.  Alphanumeric only, case-insensitive. */ 
     public static final String SERIAL = getString("ro.serialno");
-  
+
+    /**
+     * A list of ABIs (in priority) order supported by this device.
+     *
+     * @hide
+     */
+    public static final String[] SUPPORTED_ABIS = getString("ro.product.cpu.abilist").split(",");
+
     /** Various version strings. */
     public static class VERSION {
         /**
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 6d65782..ba419f9 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.content;
 
-import android.os.Build;
+import android.content.pm.PackageManager;
 import android.util.Slog;
 
 import java.io.File;
@@ -31,38 +31,76 @@
 
     private static final boolean DEBUG_NATIVE = false;
 
-    private static native long nativeSumNativeBinaries(String file, String cpuAbi, String cpuAbi2);
-
     /**
-     * Sums the size of native binaries in an APK.
+     * A handle to an opened APK. Used as input to the various NativeLibraryHelper
+     * methods. Allows us to scan and parse the APK exactly once instead of doing
+     * it multiple times.
      *
-     * @param apkFile APK file to scan for native libraries
-     * @return size of all native binary files in bytes
+     * @hide
      */
-    public static long sumNativeBinariesLI(File apkFile) {
-        final String cpuAbi = Build.CPU_ABI;
-        final String cpuAbi2 = Build.CPU_ABI2;
-        return nativeSumNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2);
+    public static class ApkHandle {
+        final String apkPath;
+        final long apkHandle;
+
+        public ApkHandle(String path) {
+            apkPath = path;
+            apkHandle = nativeOpenApk(apkPath);
+        }
+
+        public ApkHandle(File apkFile) {
+            apkPath = apkFile.getPath();
+            apkHandle = nativeOpenApk(apkPath);
+        }
+
+        public void close() {
+            nativeClose(apkHandle);
+        }
     }
 
-    private native static int nativeCopyNativeBinaries(String filePath, String sharedLibraryPath,
-            String cpuAbi, String cpuAbi2);
+
+    private static native long nativeOpenApk(String path);
+    private static native void nativeClose(long handle);
+
+    private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
+
+    /**
+     * Sums the size of native binaries in an APK for a given ABI.
+     *
+     * @return size of all native binary files in bytes
+     */
+    public static long sumNativeBinariesLI(ApkHandle handle, String abi) {
+        return nativeSumNativeBinaries(handle.apkHandle, abi);
+    }
+
+    private native static int nativeCopyNativeBinaries(long handle,
+            String sharedLibraryPath, String abiToCopy);
 
     /**
      * Copies native binaries to a shared library directory.
      *
-     * @param apkFile APK file to scan for native libraries
+     * @param handle APK file to scan for native libraries
      * @param sharedLibraryDir directory for libraries to be copied to
      * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
      *         error code from that class if not
      */
-    public static int copyNativeBinariesIfNeededLI(File apkFile, File sharedLibraryDir) {
-        final String cpuAbi = Build.CPU_ABI;
-        final String cpuAbi2 = Build.CPU_ABI2;
-        return nativeCopyNativeBinaries(apkFile.getPath(), sharedLibraryDir.getPath(), cpuAbi,
-                cpuAbi2);
+    public static int copyNativeBinariesIfNeededLI(ApkHandle handle, File sharedLibraryDir,
+            String abi) {
+        return nativeCopyNativeBinaries(handle.apkHandle, sharedLibraryDir.getPath(), abi);
     }
 
+    /**
+     * Checks if a given APK contains native code for any of the provided
+     * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
+     * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
+     * APK doesn't contain any native code, and
+     * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
+     */
+    public static int findSupportedAbi(ApkHandle handle, String[] supportedAbis) {
+        return nativeFindSupportedAbi(handle.apkHandle, supportedAbis);
+    }
+
+    private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis);
+
     // Convenience method to call removeNativeBinariesFromDirLI(File)
     public static boolean removeNativeBinariesLI(String nativeLibraryPath) {
         return removeNativeBinariesFromDirLI(new File(nativeLibraryPath));
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index a860918..2004576 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -19,11 +19,12 @@
 
 #include <android_runtime/AndroidRuntime.h>
 
-#include <utils/Log.h>
-#include <androidfw/ZipFileRO.h>
-#include <androidfw/ZipUtils.h>
 #include <ScopedUtfChars.h>
 #include <UniquePtr.h>
+#include <androidfw/ZipFileRO.h>
+#include <androidfw/ZipUtils.h>
+#include <utils/Log.h>
+#include <utils/Vector.h>
 
 #include <zlib.h>
 
@@ -54,17 +55,19 @@
 namespace android {
 
 // These match PackageManager.java install codes
-typedef enum {
+enum install_status_t {
     INSTALL_SUCCEEDED = 1,
     INSTALL_FAILED_INVALID_APK = -2,
     INSTALL_FAILED_INSUFFICIENT_STORAGE = -4,
     INSTALL_FAILED_CONTAINER_ERROR = -18,
     INSTALL_FAILED_INTERNAL_ERROR = -110,
-} install_status_t;
+    INSTALL_FAILED_NO_MATCHING_ABIS = -112,
+    NO_NATIVE_LIBRARIES = -113
+};
 
 typedef install_status_t (*iterFunc)(JNIEnv*, void*, ZipFileRO*, ZipEntryRO, const char*);
 
-// Equivalent to isFilenameSafe
+// Equivalent to android.os.FileUtils.isFilenameSafe
 static bool
 isFilenameSafe(const char* filename)
 {
@@ -268,126 +271,252 @@
     return INSTALL_SUCCEEDED;
 }
 
-static install_status_t
-iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2,
-        iterFunc callFunc, void* callArg) {
-    ScopedUtfChars filePath(env, javaFilePath);
-    ScopedUtfChars cpuAbi(env, javaCpuAbi);
-    ScopedUtfChars cpuAbi2(env, javaCpuAbi2);
-
-    UniquePtr<ZipFileRO> zipFile(ZipFileRO::open(filePath.c_str()));
-    if (zipFile.get() == NULL) {
-        ALOGI("Couldn't open APK %s\n", filePath.c_str());
-        return INSTALL_FAILED_INVALID_APK;
+/*
+ * An iterator over all shared libraries in a zip file. An entry is
+ * considered to be a shared library if all of the conditions below are
+ * satisfied :
+ *
+ * - The entry is under the lib/ directory.
+ * - The entry name ends with ".so" and the entry name starts with "lib",
+ *   an exception is made for entries whose name is "gdbserver".
+ * - The entry filename is "safe" (as determined by isFilenameSafe).
+ *
+ */
+class NativeLibrariesIterator {
+private:
+    NativeLibrariesIterator(ZipFileRO* zipFile, void* cookie)
+        : mZipFile(zipFile), mCookie(cookie), mLastSlash(NULL) {
+        fileName[0] = '\0';
     }
 
+public:
+    static NativeLibrariesIterator* create(ZipFileRO* zipFile) {
+        void* cookie = NULL;
+        if (!zipFile->startIteration(&cookie)) {
+            return NULL;
+        }
+
+        return new NativeLibrariesIterator(zipFile, cookie);
+    }
+
+    ZipEntryRO next() {
+        ZipEntryRO next = NULL;
+        while ((next = mZipFile->nextEntry(mCookie)) != NULL) {
+            // Make sure this entry has a filename.
+            if (mZipFile->getEntryFileName(next, fileName, sizeof(fileName))) {
+                continue;
+            }
+
+            // Make sure we're in the lib directory of the ZIP.
+            if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) {
+                continue;
+            }
+
+            // Make sure the filename is at least to the minimum library name size.
+            const size_t fileNameLen = strlen(fileName);
+            static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
+            if (fileNameLen < minLength) {
+                continue;
+            }
+
+            const char* lastSlash = strrchr(fileName, '/');
+            ALOG_ASSERT(lastSlash != NULL, "last slash was null somehow for %s\n", fileName);
+
+            // Exception: If we find the gdbserver binary, return it.
+            if (!strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) {
+                break;
+            }
+
+            // Make sure the filename starts with lib and ends with ".so".
+            if (strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN)
+                || strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)) {
+                continue;
+            }
+
+            // Make sure the filename is safe.
+            if (!isFilenameSafe(lastSlash + 1)) {
+                continue;
+            }
+
+            mLastSlash = lastSlash;
+            break;
+        }
+
+        return next;
+    }
+
+    inline const char* currentEntry() const {
+        return fileName;
+    }
+
+    inline const char* lastSlash() const {
+        return mLastSlash;
+    }
+
+    virtual ~NativeLibrariesIterator() {
+        mZipFile->endIteration(mCookie);
+    }
+private:
+
     char fileName[PATH_MAX];
-    bool hasPrimaryAbi = false;
+    ZipFileRO* const mZipFile;
+    void* mCookie;
+    const char* mLastSlash;
+};
 
-    void* cookie = NULL;
-    if (!zipFile->startIteration(&cookie)) {
-        ALOGI("Couldn't iterate over APK%s\n", filePath.c_str());
+static install_status_t
+iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi,
+                       iterFunc callFunc, void* callArg) {
+    ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
+    if (zipFile == NULL) {
         return INSTALL_FAILED_INVALID_APK;
     }
 
+    UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+    if (it.get() == NULL) {
+        return INSTALL_FAILED_INVALID_APK;
+    }
+
+    const ScopedUtfChars cpuAbi(env, javaCpuAbi);
+    if (cpuAbi.c_str() == NULL) {
+        // This would've thrown, so this return code isn't observable by
+        // Java.
+        return INSTALL_FAILED_INVALID_APK;
+    }
     ZipEntryRO entry = NULL;
-    while ((entry = zipFile->nextEntry(cookie)) != NULL) {
-        // Make sure this entry has a filename.
-        if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
-            continue;
-        }
-
-        // Make sure we're in the lib directory of the ZIP.
-        if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) {
-            continue;
-        }
-
-        // Make sure the filename is at least to the minimum library name size.
-        const size_t fileNameLen = strlen(fileName);
-        static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
-        if (fileNameLen < minLength) {
-            continue;
-        }
-
-        const char* lastSlash = strrchr(fileName, '/');
-        ALOG_ASSERT(lastSlash != NULL, "last slash was null somehow for %s\n", fileName);
+    while ((entry = it->next()) != NULL) {
+        const char* fileName = it->currentEntry();
+        const char* lastSlash = it->lastSlash();
 
         // Check to make sure the CPU ABI of this file is one we support.
         const char* cpuAbiOffset = fileName + APK_LIB_LEN;
         const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset;
 
-        ALOGV("Comparing ABIs %s and %s versus %s\n", cpuAbi.c_str(), cpuAbi2.c_str(), cpuAbiOffset);
-        if (cpuAbi.size() == cpuAbiRegionSize
-                && *(cpuAbiOffset + cpuAbi.size()) == '/'
-                && !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) {
-            ALOGV("Using primary ABI %s\n", cpuAbi.c_str());
-            hasPrimaryAbi = true;
-        } else if (cpuAbi2.size() == cpuAbiRegionSize
-                && *(cpuAbiOffset + cpuAbi2.size()) == '/'
-                && !strncmp(cpuAbiOffset, cpuAbi2.c_str(), cpuAbiRegionSize)) {
-
-            /*
-             * If this library matches both the primary and secondary ABIs,
-             * only use the primary ABI.
-             */
-            if (hasPrimaryAbi) {
-                ALOGV("Already saw primary ABI, skipping secondary ABI %s\n", cpuAbi2.c_str());
-                continue;
-            } else {
-                ALOGV("Using secondary ABI %s\n", cpuAbi2.c_str());
-            }
-        } else {
-            ALOGV("abi didn't match anything: %s (end at %zd)\n", cpuAbiOffset, cpuAbiRegionSize);
-            continue;
-        }
-
-        // If this is a .so file, check to see if we need to copy it.
-        if ((!strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN)
-                    && !strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)
-                    && isFilenameSafe(lastSlash + 1))
-                || !strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) {
-
-            install_status_t ret = callFunc(env, callArg, zipFile.get(), entry, lastSlash + 1);
+        if (cpuAbi.size() == cpuAbiRegionSize && !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) {
+            install_status_t ret = callFunc(env, callArg, zipFile, entry, lastSlash + 1);
 
             if (ret != INSTALL_SUCCEEDED) {
                 ALOGV("Failure for entry %s", lastSlash + 1);
-                zipFile->endIteration(cookie);
                 return ret;
             }
         }
     }
 
-    zipFile->endIteration(cookie);
-
     return INSTALL_SUCCEEDED;
 }
 
+
+static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray) {
+    const int numAbis = env->GetArrayLength(supportedAbisArray);
+    Vector<ScopedUtfChars*> supportedAbis;
+
+    for (int i = 0; i < numAbis; ++i) {
+        supportedAbis.add(new ScopedUtfChars(env,
+            (jstring) env->GetObjectArrayElement(supportedAbisArray, i)));
+    }
+
+    ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
+    if (zipFile == NULL) {
+        return INSTALL_FAILED_INVALID_APK;
+    }
+
+    UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+    if (it.get() == NULL) {
+        return INSTALL_FAILED_INVALID_APK;
+    }
+
+    ZipEntryRO entry = NULL;
+    char fileName[PATH_MAX];
+    int status = NO_NATIVE_LIBRARIES;
+    while ((entry = it->next()) != NULL) {
+        // We're currently in the lib/ directory of the APK, so it does have some native
+        // code. We should return INSTALL_FAILED_NO_MATCHING_ABIS if none of the
+        // libraries match.
+        if (status == NO_NATIVE_LIBRARIES) {
+            status = INSTALL_FAILED_NO_MATCHING_ABIS;
+        }
+
+        const char* fileName = it->currentEntry();
+        const char* lastSlash = it->lastSlash();
+
+        // Check to see if this CPU ABI matches what we are looking for.
+        const char* abiOffset = fileName + APK_LIB_LEN;
+        const size_t abiSize = lastSlash - abiOffset;
+        for (int i = 0; i < numAbis; i++) {
+            const ScopedUtfChars* abi = supportedAbis[i];
+            if (abi->size() == abiSize && !strncmp(abiOffset, abi->c_str(), abiSize)) {
+                // The entry that comes in first (i.e. with a lower index) has the higher priority.
+                if (((i < status) && (status >= 0)) || (status < 0) ) {
+                    status = i;
+                }
+            }
+        }
+    }
+
+    for (int i = 0; i < numAbis; ++i) {
+        delete supportedAbis[i];
+    }
+
+    return status;
+}
+
 static jint
 com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
-        jstring javaFilePath, jstring javaNativeLibPath, jstring javaCpuAbi, jstring javaCpuAbi2)
+        jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi)
 {
-    return (jint) iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2,
+    return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi,
             copyFileIfChanged, &javaNativeLibPath);
 }
 
 static jlong
 com_android_internal_content_NativeLibraryHelper_sumNativeBinaries(JNIEnv *env, jclass clazz,
-        jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2)
+        jlong apkHandle, jstring javaCpuAbi)
 {
     size_t totalSize = 0;
 
-    iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2, sumFiles, &totalSize);
+    iterateOverNativeFiles(env, apkHandle, javaCpuAbi, sumFiles, &totalSize);
 
     return totalSize;
 }
 
+static jint
+com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz,
+        jlong apkHandle, jobjectArray javaCpuAbisToSearch)
+{
+    return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch);
+}
+
+static jlong
+com_android_internal_content_NativeLibraryHelper_openApk(JNIEnv *env, jclass, jstring apkPath)
+{
+    ScopedUtfChars filePath(env, apkPath);
+    ZipFileRO* zipFile = ZipFileRO::open(filePath.c_str());
+
+    return reinterpret_cast<jlong>(zipFile);
+}
+
+static void
+com_android_internal_content_NativeLibraryHelper_close(JNIEnv *env, jclass, jlong apkHandle)
+{
+    delete reinterpret_cast<ZipFileRO*>(apkHandle);
+}
+
 static JNINativeMethod gMethods[] = {
+    {"nativeOpenApk",
+            "(Ljava/lang/String;)J",
+            (void *)com_android_internal_content_NativeLibraryHelper_openApk},
+    {"nativeClose",
+            "(J)V",
+            (void *)com_android_internal_content_NativeLibraryHelper_close},
     {"nativeCopyNativeBinaries",
-            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+            "(JLjava/lang/String;Ljava/lang/String;)I",
             (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
     {"nativeSumNativeBinaries",
-            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
+            "(JLjava/lang/String;)J",
             (void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
+    {"nativeFindSupportedAbi",
+            "(J[Ljava/lang/String;)I",
+            (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
 };
 
 
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 6e34bbb..48ef9db 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -30,6 +30,7 @@
 import android.content.res.ObbInfo;
 import android.content.res.ObbScanner;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Environment.UserEnvironment;
 import android.os.FileUtils;
@@ -39,10 +40,8 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StatFs;
-import android.os.SystemClock;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.app.IMediaContainerService;
@@ -343,11 +342,13 @@
         // The .apk file
         String codePath = packageURI.getPath();
         File codeFile = new File(codePath);
+        NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath);
+        final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
 
         // Calculate size of container needed to hold base APK.
         final int sizeMb;
         try {
-            sizeMb = calculateContainerSize(codeFile, isForwardLocked);
+            sizeMb = calculateContainerSize(handle, codeFile, abi, isForwardLocked);
         } catch (IOException e) {
             Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath());
             return null;
@@ -410,7 +411,14 @@
 
         final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);
         if (sharedLibraryDir.mkdir()) {
-            int ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(codeFile, sharedLibraryDir);
+            int ret = PackageManager.INSTALL_SUCCEEDED;
+            if (abi >= 0) {
+                ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+                        sharedLibraryDir, Build.SUPPORTED_ABIS[abi]);
+            } else if (abi != PackageManager.NO_NATIVE_LIBRARIES) {
+                ret = abi;
+            }
+
             if (ret != PackageManager.INSTALL_SUCCEEDED) {
                 Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath());
                 PackageHelper.destroySdDir(newCid);
@@ -824,6 +832,17 @@
         return availSdMb > sizeMb;
     }
 
+    private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException {
+        NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(apkFile);
+        final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+
+        try {
+            return calculateContainerSize(handle, apkFile, abi, forwardLocked);
+        } finally {
+            handle.close();
+        }
+    }
+
     /**
      * Calculate the container size for an APK. Takes into account the
      * 
@@ -831,7 +850,8 @@
      * @return size in megabytes (2^20 bytes)
      * @throws IOException when there is a problem reading the file
      */
-    private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException {
+    private int calculateContainerSize(NativeLibraryHelper.ApkHandle apkHandle,
+            File apkFile, int abiIndex, boolean forwardLocked) throws IOException {
         // Calculate size of container needed to hold base APK.
         long sizeBytes = apkFile.length();
         if (sizeBytes == 0 && !apkFile.exists()) {
@@ -840,7 +860,10 @@
 
         // Check all the native files that need to be copied and add that to the
         // container size.
-        sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkFile);
+        if (abiIndex >= 0) {
+            sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkHandle,
+                    Build.SUPPORTED_ABIS[abiIndex]);
+        }
 
         if (forwardLocked) {
             sizeBytes += PackageHelper.extractPublicFiles(apkFile.getPath(), null);
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index f33f7394..7d13caa 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -59,8 +59,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
-import android.content.ServiceConnection;
 import android.content.IntentSender.SendIntentException;
+import android.content.ServiceConnection;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ContainerEncryptionParams;
@@ -72,14 +72,15 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.ManifestDigest;
 import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageUserState;
 import android.content.pm.PackageParser.ActivityIntentInfo;
 import android.content.pm.PackageStats;
+import android.content.pm.PackageUserState;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
@@ -87,7 +88,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
-import android.content.pm.ManifestDigest;
 import android.content.pm.VerificationParams;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
@@ -97,6 +97,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Environment.UserEnvironment;
 import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -113,7 +114,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.Environment.UserEnvironment;
 import android.os.UserManager;
 import android.security.KeyStore;
 import android.security.SystemKeyStore;
@@ -4700,7 +4700,8 @@
                         }
 
                         try {
-                            if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) {
+                            int copyRet = copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir);
+                            if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
                                 Slog.e(TAG, "Unable to copy native libraries");
                                 mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
                                 return null;
@@ -5251,7 +5252,21 @@
          * If this is an internal application or our nativeLibraryPath points to
          * the app-lib directory, unpack the libraries if necessary.
          */
-        return NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir);
+        final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
+        try {
+            int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+            if (abi >= 0) {
+                int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+                        nativeLibraryDir, Build.SUPPORTED_ABIS[abi]);
+                if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+                    return copyRet;
+                }
+            }
+
+            return abi;
+        } finally {
+            handle.close();
+        }
     }
 
     private void killApplication(String pkgName, int appId, String reason) {
@@ -8137,7 +8152,7 @@
             }
             try {
                 int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile);
-                if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+                if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
                     return copyRet;
                 }
             } catch (IOException e) {
@@ -11512,8 +11527,17 @@
                                     final File newNativeDir = new File(newNativePath);
 
                                     if (!isForwardLocked(pkg) && !isExternal(pkg)) {
-                                        NativeLibraryHelper.copyNativeBinariesIfNeededLI(
-                                                new File(newCodePath), newNativeDir);
+                                        // NOTE: We do not report any errors from the APK scan and library
+                                        // copy at this point.
+                                        NativeLibraryHelper.ApkHandle handle =
+                                                new NativeLibraryHelper.ApkHandle(newCodePath);
+                                        final int abi = NativeLibraryHelper.findSupportedAbi(
+                                                handle, Build.SUPPORTED_ABIS);
+                                        if (abi >= 0) {
+                                            NativeLibraryHelper.copyNativeBinariesIfNeededLI(
+                                                    handle, newNativeDir, Build.SUPPORTED_ABIS[abi]);
+                                        }
+                                        handle.close();
                                     }
                                     final int[] users = sUserManager.getUserIds();
                                     for (int user : users) {