am 9f3ddd55: am e3218d35: Merge "Installing splits into ASECs!" into lmp-dev

* commit '9f3ddd551393b4fbc421156b8f700cae7dbd6a3b':
  Installing splits into ASECs!
diff --git a/api/current.txt b/api/current.txt
index 3644e58..5a083d0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8675,10 +8675,12 @@
     method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void uninstall(java.lang.String, android.content.IntentSender);
     field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
+    field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
     field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
     field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
     field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+    field public static final java.lang.String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
     field public static final int STATUS_FAILURE = 1; // 0x1
     field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
     field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 46d8ade..d9b40b1 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1004,6 +1004,10 @@
                 params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
             } else if (opt.equals("-p")) {
                 params.mode = SessionParams.MODE_INHERIT_EXISTING;
+                params.appPackageName = nextOptionData();
+                if (params.appPackageName == null) {
+                    throw new IllegalArgumentException("Missing inherit package name");
+                }
             } else if (opt.equals("-S")) {
                 params.setSize(Long.parseLong(nextOptionData()));
             } else if (opt.equals("--abi")) {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 7419ebc..9afdbf7 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -45,6 +45,7 @@
 import java.io.OutputStream;
 import java.security.MessageDigest;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -86,6 +87,8 @@
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you safeguard
      * against this.
+     * <p>
+     * The session to show details for is defined in {@link #EXTRA_SESSION_ID}.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
@@ -95,22 +98,58 @@
             ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS";
 
     /**
-     * An integer session ID.
+     * An integer session ID that an operation is working with.
      *
-     * @see #ACTION_SESSION_DETAILS
+     * @see Intent#getIntExtra(String, int)
      */
     public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
 
-    public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
-    public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
-
     /**
-     * Package name relevant to a status.
+     * Package name that an operation is working with.
      *
      * @see Intent#getStringExtra(String)
      */
     public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
 
+    /**
+     * Current status of an operation. Will be one of
+     * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS},
+     * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED},
+     * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT},
+     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or
+     * {@link #STATUS_FAILURE_STORAGE}.
+     * <p>
+     * More information about a status may be available through additional
+     * extras; see the individual status documentation for details.
+     *
+     * @see Intent#getIntExtra(String, int)
+     */
+    public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
+
+    /**
+     * Detailed string representation of the status, including raw details that
+     * are useful for debugging.
+     *
+     * @see Intent#getStringExtra(String)
+     */
+    public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+
+    /**
+     * Another package name relevant to a status. This is typically the package
+     * responsible for causing an operation failure.
+     *
+     * @see Intent#getStringExtra(String)
+     */
+    public static final String
+            EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
+
+    /**
+     * Storage path relevant to a status.
+     *
+     * @see Intent#getStringExtra(String)
+     */
+    public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
+
     /** {@hide} */
     @Deprecated
     public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
@@ -153,8 +192,12 @@
      * The operation failed because it was blocked. For example, a device policy
      * may be blocking the operation, a package verifier may have blocked the
      * operation, or the app may be required for core system operation.
+     * <p>
+     * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
+     * specific package blocking the install.
      *
      * @see #EXTRA_STATUS_MESSAGE
+     * @see #EXTRA_OTHER_PACKAGE_NAME
      */
     public static final int STATUS_FAILURE_BLOCKED = 2;
 
@@ -182,10 +225,11 @@
      * permission, incompatible certificates, etc. The user may be able to
      * uninstall another app to fix the issue.
      * <p>
-     * The result may also contain {@link #EXTRA_PACKAGE_NAME} with the
+     * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
      * specific package identified as the cause of the conflict.
      *
      * @see #EXTRA_STATUS_MESSAGE
+     * @see #EXTRA_OTHER_PACKAGE_NAME
      */
     public static final int STATUS_FAILURE_CONFLICT = 5;
 
@@ -193,8 +237,12 @@
      * The operation failed because of storage issues. For example, the device
      * may be running low on space, or external media may be unavailable. The
      * user may be able to help free space or insert different external media.
+     * <p>
+     * The result may also contain {@link #EXTRA_STORAGE_PATH} with the path to
+     * the storage device that caused the failure.
      *
      * @see #EXTRA_STATUS_MESSAGE
+     * @see #EXTRA_STORAGE_PATH
      */
     public static final int STATUS_FAILURE_STORAGE = 6;
 
@@ -281,6 +329,13 @@
      * To succeed, the caller must be the current home app.
      */
     public @NonNull List<SessionInfo> getAllSessions() {
+        final ApplicationInfo info = mContext.getApplicationInfo();
+        if ("com.google.android.googlequicksearchbox".equals(info.packageName)
+                && info.versionCode <= 300400070) {
+            Log.d(TAG, "Ignoring callback request from old prebuilt");
+            return Collections.EMPTY_LIST;
+        }
+
         try {
             return mInstaller.getAllSessions(mUserId);
         } catch (RemoteException e) {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index eb8b762..142206a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -264,7 +264,7 @@
         public final boolean coreApp;
         public final boolean multiArch;
 
-        private PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
+        public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
                 String[] splitCodePaths) {
             this.packageName = baseApk.packageName;
             this.versionCode = baseApk.versionCode;
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 939cda9..d1fadd6 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -321,7 +321,7 @@
              * Mount a secure container with the specified key and owner UID.
              * Returns an int consistent with MountServiceResultCode
              */
-            public int mountSecureContainer(String id, String key, int ownerUid)
+            public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)
                     throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -331,6 +331,7 @@
                     _data.writeString(id);
                     _data.writeString(key);
                     _data.writeInt(ownerUid);
+                    _data.writeInt(readOnly ? 1 : 0);
                     mRemote.transact(Stub.TRANSACTION_mountSecureContainer, _data, _reply, 0);
                     _reply.readException();
                     _result = _reply.readInt();
@@ -834,6 +835,27 @@
                 }
                 return _result;
             }
+
+            @Override
+            public int resizeSecureContainer(String id, int sizeMb, String key)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    _data.writeInt(sizeMb);
+                    _data.writeString(key);
+                    mRemote.transact(Stub.TRANSACTION_resizeSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
         }
 
         private static final String DESCRIPTOR = "IMountService";
@@ -918,6 +940,8 @@
 
         static final int TRANSACTION_getField = IBinder.FIRST_CALL_TRANSACTION + 39;
 
+        static final int TRANSACTION_resizeSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 40;
+
         /**
          * Cast an IBinder object into an IMountService interface, generating a
          * proxy if needed.
@@ -1082,7 +1106,9 @@
                     key = data.readString();
                     int ownerUid;
                     ownerUid = data.readInt();
-                    int resultCode = mountSecureContainer(id, key, ownerUid);
+                    boolean readOnly;
+                    readOnly = data.readInt() != 0;
+                    int resultCode = mountSecureContainer(id, key, ownerUid, readOnly);
                     reply.writeNoException();
                     reply.writeInt(resultCode);
                     return true;
@@ -1308,6 +1334,19 @@
                     reply.writeString(contents);
                     return true;
                 }
+                case TRANSACTION_resizeSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    int sizeMb;
+                    sizeMb = data.readInt();
+                    String key;
+                    key = data.readString();
+                    int resultCode = resizeSecureContainer(id, sizeMb, key);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -1405,7 +1444,8 @@
      * Mount a secure container with the specified key and owner UID. Returns an
      * int consistent with MountServiceResultCode
      */
-    public int mountSecureContainer(String id, String key, int ownerUid) throws RemoteException;
+    public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)
+            throws RemoteException;
 
     /**
      * Mount external storage at given mount point. Returns an int consistent
@@ -1571,4 +1611,6 @@
      * @return contents of field
      */
     public String getField(String field) throws RemoteException;
+
+    public int resizeSecureContainer(String id, int sizeMb, String key) throws RemoteException;
 }
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 179d5e8..02f675c 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -25,6 +25,7 @@
 import static android.system.OsConstants.S_IXGRP;
 import static android.system.OsConstants.S_IXOTH;
 
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.Package;
@@ -51,9 +52,11 @@
  */
 public class NativeLibraryHelper {
     private static final String TAG = "NativeHelper";
-
     private static final boolean DEBUG_NATIVE = false;
 
+    public static final String LIB_DIR_NAME = "lib";
+    public static final String LIB64_DIR_NAME = "lib64";
+
     // Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate
     // that the cpuAbiOverride must be clear.
     public static final String CLEAR_ABI_OVERRIDE = "-";
@@ -70,6 +73,7 @@
         private volatile boolean mClosed;
 
         final long[] apkHandles;
+        final boolean multiArch;
 
         public static Handle create(File packageFile) throws IOException {
             try {
@@ -81,14 +85,15 @@
         }
 
         public static Handle create(Package pkg) throws IOException {
-            return create(pkg.getAllCodePaths());
+            return create(pkg.getAllCodePaths(),
+                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0);
         }
 
         public static Handle create(PackageLite lite) throws IOException {
-            return create(lite.getAllCodePaths());
+            return create(lite.getAllCodePaths(), lite.multiArch);
         }
 
-        private static Handle create(List<String> codePaths) throws IOException {
+        private static Handle create(List<String> codePaths, boolean multiArch) throws IOException {
             final int size = codePaths.size();
             final long[] apkHandles = new long[size];
             for (int i = 0; i < size; i++) {
@@ -103,11 +108,12 @@
                 }
             }
 
-            return new Handle(apkHandles);
+            return new Handle(apkHandles, multiArch);
         }
 
-        Handle(long[] apkHandles) {
+        Handle(long[] apkHandles, boolean multiArch) {
             this.apkHandles = apkHandles;
+            this.multiArch = multiArch;
             mGuard.open("close");
         }
 
@@ -159,8 +165,7 @@
      * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
      *         error code from that class if not
      */
-    public static int copyNativeBinariesIfNeededLI(Handle handle, File sharedLibraryDir,
-            String abi) {
+    public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
         for (long apkHandle : handle.apkHandles) {
             int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi);
             if (res != INSTALL_SUCCEEDED) {
@@ -267,7 +272,7 @@
         }
     }
 
-    private static long sumNativeBinaries(Handle handle, String[] abiList) {
+    private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
         int abi = findSupportedAbi(handle, abiList);
         if (abi >= 0) {
             return sumNativeBinaries(handle, abiList[abi]);
@@ -276,7 +281,7 @@
         }
     }
 
-    public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot,
+    public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
             String[] abiList, boolean useIsaSubdir) throws IOException {
         createNativeLibrarySubdir(libraryRoot);
 
@@ -300,7 +305,7 @@
                 subDir = libraryRoot;
             }
 
-            int copyRet = copyNativeBinariesIfNeededLI(handle, subDir, abiList[abi]);
+            int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
             if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
                 return copyRet;
             }
@@ -309,10 +314,10 @@
         return abi;
     }
 
-    public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot,
-            String abiOverride, boolean multiArch) {
+    public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
+            String abiOverride) {
         try {
-            if (multiArch) {
+            if (handle.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.
@@ -322,7 +327,7 @@
 
                 int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
                 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
-                    copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot,
+                    copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
                             Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
@@ -332,7 +337,7 @@
                 }
 
                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
-                    copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot,
+                    copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
                             Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
@@ -355,7 +360,7 @@
                     abiList = Build.SUPPORTED_32_BIT_ABIS;
                 }
 
-                int copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot, abiList,
+                int copyRet = copyNativeBinariesForSupportedAbi(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 + "]");
@@ -370,10 +375,10 @@
         }
     }
 
-    public static long sumNativeBinaries(Handle handle, String abiOverride, boolean multiArch)
+    public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
             throws IOException {
         long sum = 0;
-        if (multiArch) {
+        if (handle.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.
@@ -382,11 +387,11 @@
             }
 
             if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
-                sum += sumNativeBinaries(handle, Build.SUPPORTED_32_BIT_ABIS);
+                sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
             }
 
             if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
-                sum += sumNativeBinaries(handle, Build.SUPPORTED_64_BIT_ABIS);
+                sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
             }
         } else {
             String cpuAbiOverride = null;
@@ -403,7 +408,7 @@
                 abiList = Build.SUPPORTED_32_BIT_ABIS;
             }
 
-            sum += sumNativeBinaries(handle, abiList);
+            sum += sumNativeBinariesForSupportedAbi(handle, abiList);
         }
         return sum;
     }
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index a529cc3..c17f4ee 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.content;
 
+import static android.net.TrafficStats.MB_IN_BYTES;
+
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageParser.PackageLite;
 import android.os.Environment;
 import android.os.Environment.UserEnvironment;
 import android.os.FileUtils;
@@ -77,9 +80,10 @@
         }
     }
 
-    public static String createSdDir(int sizeMb, String cid, String sdEncKey, int uid,
+    public static String createSdDir(long sizeBytes, String cid, String sdEncKey, int uid,
             boolean isExternal) {
-        // Create mount point via MountService
+        // Round up to nearest MB, plus another MB for filesystem overhead
+        final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
         try {
             IMountService mountService = getMountService();
 
@@ -102,19 +106,39 @@
         return null;
     }
 
-   public static String mountSdDir(String cid, String key, int ownerUid) {
-    try {
-        int rc = getMountService().mountSecureContainer(cid, key, ownerUid);
-        if (rc != StorageResultCode.OperationSucceeded) {
-            Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc);
-            return null;
+    public static boolean resizeSdDir(long sizeBytes, String cid, String sdEncKey) {
+        // Round up to nearest MB, plus another MB for filesystem overhead
+        final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
+        try {
+            IMountService mountService = getMountService();
+            int rc = mountService.resizeSecureContainer(cid, sizeMb, sdEncKey);
+            if (rc == StorageResultCode.OperationSucceeded) {
+                return true;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "MountService running?");
         }
-        return getMountService().getSecureContainerPath(cid);
-    } catch (RemoteException e) {
-        Log.e(TAG, "MountService running?");
+        Log.e(TAG, "Failed to create secure container " + cid);
+        return false;
     }
-    return null;
-   }
+
+    public static String mountSdDir(String cid, String key, int ownerUid) {
+        return mountSdDir(cid, key, ownerUid, true);
+    }
+
+    public static String mountSdDir(String cid, String key, int ownerUid, boolean readOnly) {
+        try {
+            int rc = getMountService().mountSecureContainer(cid, key, ownerUid, readOnly);
+            if (rc != StorageResultCode.OperationSucceeded) {
+                Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc);
+                return null;
+            }
+            return getMountService().getSecureContainerPath(cid);
+        } catch (RemoteException e) {
+            Log.e(TAG, "MountService running?");
+        }
+        return null;
+    }
 
    public static boolean unMountSdDir(String cid) {
     try {
@@ -400,6 +424,37 @@
         }
     }
 
+    public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,
+            String abiOverride) throws IOException {
+        NativeLibraryHelper.Handle handle = null;
+        try {
+            handle = NativeLibraryHelper.Handle.create(pkg);
+            return calculateInstalledSize(pkg, handle, isForwardLocked, abiOverride);
+        } finally {
+            IoUtils.closeQuietly(handle);
+        }
+    }
+
+    public static long calculateInstalledSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
+            boolean isForwardLocked, String abiOverride) throws IOException {
+        long sizeBytes = 0;
+
+        // Include raw APKs, and possibly unpacked resources
+        for (String codePath : pkg.getAllCodePaths()) {
+            final File codeFile = new File(codePath);
+            sizeBytes += codeFile.length();
+
+            if (isForwardLocked) {
+                sizeBytes += PackageHelper.extractPublicFiles(codeFile, null);
+            }
+        }
+
+        // Include all relevant native code
+        sizeBytes += NativeLibraryHelper.sumNativeBinariesWithOverride(handle, abiOverride);
+
+        return sizeBytes;
+    }
+
     public static String replaceEnd(String str, String before, String after) {
         if (!str.endsWith(before)) {
             throw new IllegalArgumentException(
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
index 7ad35d0..06c495e 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
@@ -16,7 +16,7 @@
 
 package android.content.pm;
 
-import com.android.internal.content.PackageHelper;
+import static android.net.TrafficStats.MB_IN_BYTES;
 
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -25,6 +25,8 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import com.android.internal.content.PackageHelper;
+
 public class PackageHelperTests extends AndroidTestCase {
     private static final boolean localLOGV = true;
     public static final String TAG = "PackageHelperTests";
@@ -81,8 +83,8 @@
     public void testMountAndPullSdCard() {
         try {
             fullId = PREFIX;
-            fullId2 = PackageHelper.createSdDir(1024, fullId, "none", android.os.Process.myUid(),
-                    true);
+            fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none",
+                    android.os.Process.myUid(), true);
 
             Log.d(TAG,PackageHelper.getSdDir(fullId));
             PackageHelper.unMountSdDir(fullId);
diff --git a/core/tests/coretests/src/android/os/storage/AsecTests.java b/core/tests/coretests/src/android/os/storage/AsecTests.java
index abb8eae..4f724fe 100644
--- a/core/tests/coretests/src/android/os/storage/AsecTests.java
+++ b/core/tests/coretests/src/android/os/storage/AsecTests.java
@@ -90,7 +90,7 @@
         String fullId = SECURE_CONTAINER_PREFIX + localId;
 
         IMountService ms = getMs();
-        return ms.mountSecureContainer(fullId, key, android.os.Process.myUid());
+        return ms.mountSecureContainer(fullId, key, android.os.Process.myUid(), true);
     }
 
     private int renameContainer(String localId1, String localId2) throws Exception {
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 5701678..621de18 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -295,6 +295,8 @@
         data.writeString16(id);
         data.writeString16(key);
         data.writeInt32(ownerUid);
+        // Assume read-only
+        data.writeInt32(1);
         if (remote()->transact(TRANSACTION_mountSecureContainer, data, &reply) != NO_ERROR) {
             ALOGD("mountSecureContainer couldn't call remote");
             return -1;
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index fae30e5..1f28324 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -16,7 +16,7 @@
 
 package com.android.defcontainer;
 
-import static android.net.TrafficStats.MB_IN_BYTES;
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
 
 import android.app.IntentService;
 import android.content.Context;
@@ -67,8 +67,6 @@
 public class DefaultContainerService extends IntentService {
     private static final String TAG = "DefContainer";
 
-    private static final String LIB_DIR_NAME = "lib";
-
     // TODO: migrate native code unpacking to always be a derivative work
 
     private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
@@ -168,7 +166,7 @@
             final long sizeBytes;
             try {
                 pkg = PackageParser.parsePackageLite(packageFile, 0);
-                sizeBytes = calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
+                sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
             } catch (PackageParserException | IOException e) {
                 Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
 
@@ -253,7 +251,7 @@
             final PackageParser.PackageLite pkg;
             try {
                 pkg = PackageParser.parsePackageLite(packageFile, 0);
-                return calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
+                return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
             } catch (PackageParserException | IOException e) {
                 Slog.w(TAG, "Failed to calculate installed size: " + e);
                 return Long.MAX_VALUE;
@@ -315,13 +313,12 @@
 
         // Calculate container size, rounding up to nearest MB and adding an
         // extra MB for filesystem overhead
-        final long sizeBytes = calculateInstalledSizeInner(pkg, handle, isForwardLocked,
-                abiOverride);
-        final int sizeMb = ((int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES)) + 1;
+        final long sizeBytes = PackageHelper.calculateInstalledSize(pkg, handle,
+                isForwardLocked, abiOverride);
 
         // Create new container
-        final String newMountPath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid(),
-                isExternal);
+        final String newMountPath = PackageHelper.createSdDir(sizeBytes, newCid, key,
+                Process.myUid(), isExternal);
         if (newMountPath == null) {
             throw new IOException("Failed to create container " + newCid);
         }
@@ -339,8 +336,8 @@
 
             // Extract native code
             final File libraryRoot = new File(targetDir, LIB_DIR_NAME);
-            final int res = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libraryRoot,
-                    abiOverride, pkg.multiArch);
+            final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+                    abiOverride);
             if (res != PackageManager.INSTALL_SUCCEEDED) {
                 throw new IOException("Failed to extract native code, res=" + res);
             }
@@ -415,35 +412,4 @@
             Os.chmod(targetFile.getAbsolutePath(), 0644);
         }
     }
-
-    private long calculateInstalledSizeInner(PackageLite pkg, boolean isForwardLocked,
-            String abiOverride) throws IOException {
-        NativeLibraryHelper.Handle handle = null;
-        try {
-            handle = NativeLibraryHelper.Handle.create(pkg);
-            return calculateInstalledSizeInner(pkg, handle, isForwardLocked, abiOverride);
-        } finally {
-            IoUtils.closeQuietly(handle);
-        }
-    }
-
-    private long calculateInstalledSizeInner(PackageLite pkg, NativeLibraryHelper.Handle handle,
-            boolean isForwardLocked, String abiOverride) throws IOException {
-        long sizeBytes = 0;
-
-        // Include raw APKs, and possibly unpacked resources
-        for (String codePath : pkg.getAllCodePaths()) {
-            final File codeFile = new File(codePath);
-            sizeBytes += codeFile.length();
-
-            if (isForwardLocked) {
-                sizeBytes += PackageHelper.extractPublicFiles(codeFile, null);
-            }
-        }
-
-        // Include all relevant native code
-        sizeBytes += NativeLibraryHelper.sumNativeBinaries(handle, abiOverride, pkg.multiArch);
-
-        return sizeBytes;
-    }
 }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 50f2ae9..ea24d7c 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -33,7 +33,6 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.hardware.usb.UsbManager;
-import android.app.admin.DevicePolicyManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
@@ -1761,6 +1760,21 @@
         return rc;
     }
 
+    @Override
+    public int resizeSecureContainer(String id, int sizeMb, String key) {
+        validatePermission(android.Manifest.permission.ASEC_CREATE);
+        waitForReady();
+        warnOnNotMounted();
+
+        int rc = StorageResultCode.OperationSucceeded;
+        try {
+            mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
+        } catch (NativeDaemonConnectorException e) {
+            rc = StorageResultCode.OperationFailedInternalError;
+        }
+        return rc;
+    }
+
     public int finalizeSecureContainer(String id) {
         validatePermission(android.Manifest.permission.ASEC_CREATE);
         warnOnNotMounted();
@@ -1835,7 +1849,7 @@
         return rc;
     }
 
-    public int mountSecureContainer(String id, String key, int ownerUid) {
+    public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
         validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
         waitForReady();
         warnOnNotMounted();
@@ -1848,7 +1862,8 @@
 
         int rc = StorageResultCode.OperationSucceeded;
         try {
-            mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid);
+            mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
+                    readOnly ? "ro" : "rw");
         } catch (NativeDaemonConnectorException e) {
             int code = e.getCode();
             if (code != VoldResponseCode.OpFailedStorageBusy) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 5e802de..6f60d24 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -19,7 +19,6 @@
 import static android.content.pm.PackageManager.INSTALL_ALL_USERS;
 import static android.content.pm.PackageManager.INSTALL_FROM_ADB;
 import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING;
-import static android.net.TrafficStats.MB_IN_BYTES;
 import static com.android.internal.util.XmlUtils.readBitmapAttribute;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -48,8 +47,11 @@
 import android.content.pm.IPackageInstallerCallback;
 import android.content.pm.IPackageInstallerSession;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageParser;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageParser.PackageLite;
+import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.net.Uri;
@@ -208,7 +210,7 @@
             // Ignore stages claimed by active sessions
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
-                unclaimed.remove(session.internalStageDir);
+                unclaimed.remove(session.stageDir);
             }
 
             // Clean up orphaned staging directories
@@ -234,7 +236,7 @@
             // Ignore stages claimed by active sessions
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
-                final String cid = session.externalStageCid;
+                final String cid = session.stageCid;
 
                 if (unclaimed.remove(cid)) {
                     // Claimed by active session, mount it
@@ -304,10 +306,10 @@
                             Slog.w(TAG, "Abandoning old session first created at "
                                     + session.createdMillis);
                             valid = false;
-                        } else if (session.internalStageDir != null
-                                && !session.internalStageDir.exists()) {
+                        } else if (session.stageDir != null
+                                && !session.stageDir.exists()) {
                             Slog.w(TAG, "Abandoning internal session with missing stage "
-                                    + session.internalStageDir);
+                                    + session.stageDir);
                             valid = false;
                         } else {
                             valid = true;
@@ -401,12 +403,12 @@
         writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
                 session.installerPackageName);
         writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
-        if (session.internalStageDir != null) {
+        if (session.stageDir != null) {
             writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
-                    session.internalStageDir.getAbsolutePath());
+                    session.stageDir.getAbsolutePath());
         }
-        if (session.externalStageCid != null) {
-            writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.externalStageCid);
+        if (session.stageCid != null) {
+            writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
         }
         writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
 
@@ -479,6 +481,8 @@
             }
         }
 
+        // TODO: treat INHERIT_EXISTING as install for user
+
         // Figure out where we're going to be staging session data
         final boolean stageInternal;
 
@@ -502,22 +506,36 @@
                 Binder.restoreCallingIdentity(ident);
             }
         } else if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
-            // We always stage inheriting sessions on internal storage first,
-            // since we don't want to grow containers until we're sure that
-            // everything looks legit.
-            stageInternal = true;
-            checkInternalStorage(params.sizeBytes);
-
-            // If we have a good hunch we'll end up on external storage, verify
-            // free space there too.
-            final ApplicationInfo info = mPm.getApplicationInfo(params.appPackageName, 0,
+            // Inheriting existing install, so stay on the same storage medium.
+            final ApplicationInfo existingApp = mPm.getApplicationInfo(params.appPackageName, 0,
                     userId);
-            if (info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-                checkExternalStorage(params.sizeBytes);
-
-                throw new UnsupportedOperationException("TODO: finish fleshing out ASEC support");
+            if (existingApp == null) {
+                throw new IllegalStateException(
+                        "Missing existing app " + params.appPackageName);
             }
 
+            final long existingSize;
+            try {
+                final PackageLite existingPkg = PackageParser.parsePackageLite(
+                        new File(existingApp.getCodePath()), 0);
+                existingSize = PackageHelper.calculateInstalledSize(existingPkg, false,
+                        params.abiOverride);
+            } catch (PackageParserException e) {
+                throw new IllegalStateException(
+                        "Failed to calculate size of " + params.appPackageName);
+            }
+
+            if ((existingApp.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0) {
+                // Internal we can link existing install into place, so we only
+                // need enough space for the new data.
+                checkInternalStorage(params.sizeBytes);
+                stageInternal = true;
+            } else {
+                // External we're going to copy existing install into our
+                // container, so we need footprint of both.
+                checkExternalStorage(params.sizeBytes + existingSize);
+                stageInternal = false;
+            }
         } else {
             throw new IllegalArgumentException("Invalid install mode: " + params.mode);
         }
@@ -641,11 +659,7 @@
         }
 
         final String cid = "smdl" + sessionId + ".tmp";
-
-        // Round up to nearest MB, plus another MB for filesystem overhead
-        final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
-
-        if (PackageHelper.createSdDir(sizeMb, cid, PackageManagerService.getEncryptKey(),
+        if (PackageHelper.createSdDir(sizeBytes, cid, PackageManagerService.getEncryptKey(),
                 Process.SYSTEM_UID, true) == null) {
             throw new IOException("Failed to create ASEC");
         }
@@ -857,7 +871,7 @@
                 final String existing = extras.getString(
                         PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
                 if (!TextUtils.isEmpty(existing)) {
-                    fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, existing);
+                    fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
                 }
             }
             try {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 38a2016..5264fc4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
@@ -38,6 +39,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ApkLite;
+import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.Signature;
 import android.os.Bundle;
@@ -47,6 +49,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.system.ErrnoException;
@@ -59,18 +62,21 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
 
+import libcore.io.IoUtils;
 import libcore.io.Libcore;
 
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@@ -95,18 +101,9 @@
     final SessionParams params;
     final long createdMillis;
 
-    /** Internal location where staged data is written. */
-    final File internalStageDir;
-    /** External container where staged data is written. */
-    final String externalStageCid;
-
-    /**
-     * When a {@link SessionParams#MODE_INHERIT_EXISTING} session is installed
-     * into an ASEC, this is the container where the stage is combined with the
-     * existing install.
-     */
-    // TODO: persist this cid once we start splicing
-    String combinedCid;
+    /** Staging location where client data is written. */
+    final File stageDir;
+    final String stageCid;
 
     /** Note that UID is not persisted; it's always derived at runtime. */
     final int installerUid;
@@ -133,20 +130,6 @@
     private String mFinalMessage;
 
     @GuardedBy("mLock")
-    private File mResolvedStageDir;
-
-    /**
-     * Path to the resolved base APK for this session, which may point at an APK
-     * inside the session (when the session defines the base), or it may point
-     * at the existing base APK (when adding splits to an existing app).
-     * <p>
-     * This is used when confirming permissions, since we can't fully stage the
-     * session inside an ASEC before confirming with user.
-     */
-    @GuardedBy("mLock")
-    private String mResolvedBaseCodePath;
-
-    @GuardedBy("mLock")
     private ArrayList<FileBridge> mBridges = new ArrayList<>();
 
     @GuardedBy("mLock")
@@ -157,6 +140,25 @@
     private int mVersionCode;
     private Signature[] mSignatures;
 
+    /**
+     * Path to the validated base APK for this session, which may point at an
+     * APK inside the session (when the session defines the base), or it may
+     * point at the existing base APK (when adding splits to an existing app).
+     * <p>
+     * This is used when confirming permissions, since we can't fully stage the
+     * session inside an ASEC before confirming with user.
+     */
+    @GuardedBy("mLock")
+    private File mResolvedBaseFile;
+
+    @GuardedBy("mLock")
+    private File mResolvedStageDir;
+
+    @GuardedBy("mLock")
+    private final List<File> mResolvedStagedFiles = new ArrayList<>();
+    @GuardedBy("mLock")
+    private final List<File> mResolvedInheritedFiles = new ArrayList<>();
+
     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message msg) {
@@ -168,9 +170,10 @@
                 try {
                     commitLocked();
                 } catch (PackageManagerException e) {
-                    Slog.e(TAG, "Install failed: " + e);
+                    final String completeMsg = ExceptionUtils.getCompleteMessage(e);
+                    Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
                     destroyInternal();
-                    dispatchSessionFinished(e.error, e.getMessage(), null);
+                    dispatchSessionFinished(e.error, completeMsg, null);
                 }
 
                 return true;
@@ -181,7 +184,7 @@
     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
             Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
             String installerPackageName, SessionParams params, long createdMillis,
-            File internalStageDir, String externalStageCid, boolean sealed) {
+            File stageDir, String stageCid, boolean sealed) {
         mCallback = callback;
         mContext = context;
         mPm = pm;
@@ -192,12 +195,12 @@
         this.installerPackageName = installerPackageName;
         this.params = params;
         this.createdMillis = createdMillis;
-        this.internalStageDir = internalStageDir;
-        this.externalStageCid = externalStageCid;
+        this.stageDir = stageDir;
+        this.stageCid = stageCid;
 
-        if ((internalStageDir == null) == (externalStageCid == null)) {
+        if ((stageDir == null) == (stageCid == null)) {
             throw new IllegalArgumentException(
-                    "Exactly one of internal or external stage must be set");
+                    "Exactly one of stageDir or stageCid stage must be set");
         }
 
         mSealed = sealed;
@@ -220,7 +223,8 @@
         synchronized (mLock) {
             info.sessionId = sessionId;
             info.installerPackageName = installerPackageName;
-            info.resolvedBaseCodePath = mResolvedBaseCodePath;
+            info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
+                    mResolvedBaseFile.getAbsolutePath() : null;
             info.progress = mProgress;
             info.sealed = mSealed;
             info.open = mOpenCount.get() > 0;
@@ -253,18 +257,17 @@
      * might point at an ASEC mount point, which is why we delay path resolution
      * until someone actively works with the session.
      */
-    private File getStageDir() throws IOException {
+    private File resolveStageDir() throws IOException {
         synchronized (mLock) {
             if (mResolvedStageDir == null) {
-                if (internalStageDir != null) {
-                    mResolvedStageDir = internalStageDir;
+                if (stageDir != null) {
+                    mResolvedStageDir = stageDir;
                 } else {
-                    final String path = PackageHelper.getSdDir(externalStageCid);
+                    final String path = PackageHelper.getSdDir(stageCid);
                     if (path != null) {
                         mResolvedStageDir = new File(path);
                     } else {
-                        throw new IOException(
-                                "Failed to resolve container path for " + externalStageCid);
+                        throw new IOException("Failed to resolve path to container " + stageCid);
                     }
                 }
             }
@@ -306,7 +309,7 @@
     public String[] getNames() {
         assertNotSealed("getNames");
         try {
-            return getStageDir().list();
+            return resolveStageDir().list();
         } catch (IOException e) {
             throw ExceptionUtils.wrap(e);
         }
@@ -339,8 +342,10 @@
             if (!FileUtils.isValidExtFilename(name)) {
                 throw new IllegalArgumentException("Invalid name: " + name);
             }
-            final File target = new File(getStageDir(), name);
+            final File target = new File(resolveStageDir(), name);
 
+            // TODO: this should delegate to DCS so the system process avoids
+            // holding open FDs into containers.
             final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
                     O_CREAT | O_WRONLY, 0644);
             Os.chmod(target.getAbsolutePath(), 0644);
@@ -350,7 +355,8 @@
             if (lengthBytes > 0) {
                 final StructStat stat = Libcore.os.fstat(targetFd);
                 final long deltaBytes = lengthBytes - stat.st_size;
-                if (deltaBytes > 0) {
+                // Only need to free up space when writing to internal stage
+                if (stageDir != null && deltaBytes > 0) {
                     mPm.freeStorage(deltaBytes);
                 }
                 Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
@@ -385,7 +391,7 @@
             if (!FileUtils.isValidExtFilename(name)) {
                 throw new IllegalArgumentException("Invalid name: " + name);
             }
-            final File target = new File(getStageDir(), name);
+            final File target = new File(resolveStageDir(), name);
 
             final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
             return new ParcelFileDescriptor(targetFd);
@@ -424,22 +430,21 @@
             mCallback.onSessionSealed(this);
         }
 
-        final File stageDir;
         try {
-            stageDir = getStageDir();
+            resolveStageDir();
         } catch (IOException e) {
             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
-                    "Failed to resolve stage dir", e);
+                    "Failed to resolve stage location", e);
         }
 
         // Verify that stage looks sane with respect to existing application.
         // This currently only ensures packageName, versionCode, and certificate
         // consistency.
-        validateInstallLocked(stageDir);
+        validateInstallLocked();
 
         Preconditions.checkNotNull(mPackageName);
         Preconditions.checkNotNull(mSignatures);
-        Preconditions.checkNotNull(mResolvedBaseCodePath);
+        Preconditions.checkNotNull(mResolvedBaseFile);
 
         if (!mPermissionsAccepted) {
             // User needs to accept permissions; give installer an intent they
@@ -454,17 +459,41 @@
             return;
         }
 
+        if (stageCid != null) {
+            // Figure out the final installed size and resize the container once
+            // and for all. Internally the parser handles straddling between two
+            // locations when inheriting.
+            final long finalSize = calculateInstalledSize();
+            resizeContainer(stageCid, finalSize);
+        }
+
         // Inherit any packages and native libraries from existing install that
         // haven't been overridden.
         if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
-            // TODO: implement splicing into existing ASEC
-            spliceExistingFilesIntoStage(stageDir);
+            try {
+                if (stageCid != null) {
+                    // TODO: this should delegate to DCS so the system process
+                    // avoids holding open FDs into containers.
+                    copyFiles(mResolvedInheritedFiles, resolveStageDir());
+                } else {
+                    linkFiles(mResolvedInheritedFiles, resolveStageDir());
+                }
+            } catch (IOException e) {
+                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+                        "Failed to inherit existing install", e);
+            }
         }
 
         // TODO: surface more granular state from dexopt
         mCallback.onSessionProgressChanged(this, 0.9f);
 
-        // TODO: for ASEC based applications, grow and stream in packages
+        // Unpack native libraries
+        extractNativeLibraries(mResolvedStageDir, params.abiOverride);
+
+        // Container is ready to go, let's seal it up!
+        if (stageCid != null) {
+            finalizeAndFixContainer(stageCid);
+        }
 
         // We've reached point of no return; call into PMS to install the stage.
         // Regardless of success or failure we always destroy session.
@@ -482,7 +511,7 @@
             }
         };
 
-        mPm.installStage(mPackageName, this.internalStageDir, this.externalStageCid, localObserver,
+        mPm.installStage(mPackageName, stageDir, stageCid, localObserver,
                 params, installerPackageName, installerUid, new UserHandle(userId));
     }
 
@@ -490,81 +519,88 @@
      * Validate install by confirming that all application packages are have
      * consistent package name, version code, and signing certificates.
      * <p>
+     * Clears and populates {@link #mResolvedBaseFile},
+     * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
+     * <p>
      * Renames package files in stage to match split names defined inside.
      * <p>
      * Note that upgrade compatibility is still performed by
      * {@link PackageManagerService}.
      */
-    private void validateInstallLocked(File stageDir) throws PackageManagerException {
+    private void validateInstallLocked() throws PackageManagerException {
         mPackageName = null;
         mVersionCode = -1;
         mSignatures = null;
-        mResolvedBaseCodePath = null;
 
-        final File[] files = stageDir.listFiles();
+        mResolvedBaseFile = null;
+        mResolvedStagedFiles.clear();
+        mResolvedInheritedFiles.clear();
+
+        final File[] files = mResolvedStageDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
         }
 
         // Verify that all staged packages are internally consistent
-        final ArraySet<String> seenSplits = new ArraySet<>();
+        final ArraySet<String> stagedSplits = new ArraySet<>();
         for (File file : files) {
 
             // Installers can't stage directories, so it's fine to ignore
             // entries like "lost+found".
             if (file.isDirectory()) continue;
 
-            final ApkLite info;
+            final ApkLite apk;
             try {
-                info = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
+                apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
             } catch (PackageParserException e) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                         "Failed to parse " + file + ": " + e);
             }
 
-            if (!seenSplits.add(info.splitName)) {
+            if (!stagedSplits.add(apk.splitName)) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                        "Split " + info.splitName + " was defined multiple times");
+                        "Split " + apk.splitName + " was defined multiple times");
             }
 
             // Use first package to define unknown values
             if (mPackageName == null) {
-                mPackageName = info.packageName;
-                mVersionCode = info.versionCode;
+                mPackageName = apk.packageName;
+                mVersionCode = apk.versionCode;
             }
             if (mSignatures == null) {
-                mSignatures = info.signatures;
+                mSignatures = apk.signatures;
             }
 
-            assertPackageConsistent(String.valueOf(file), info.packageName, info.versionCode,
-                    info.signatures);
+            assertApkConsistent(String.valueOf(file), apk);
 
             // Take this opportunity to enforce uniform naming
             final String targetName;
-            if (info.splitName == null) {
+            if (apk.splitName == null) {
                 targetName = "base.apk";
             } else {
-                targetName = "split_" + info.splitName + ".apk";
+                targetName = "split_" + apk.splitName + ".apk";
             }
             if (!FileUtils.isValidExtFilename(targetName)) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                         "Invalid filename: " + targetName);
             }
 
-            final File targetFile = new File(stageDir, targetName);
+            final File targetFile = new File(mResolvedStageDir, targetName);
             if (!file.equals(targetFile)) {
                 file.renameTo(targetFile);
             }
 
             // Base is coming from session
-            if (info.splitName == null) {
-                mResolvedBaseCodePath = targetFile.getAbsolutePath();
+            if (apk.splitName == null) {
+                mResolvedBaseFile = targetFile;
             }
+
+            mResolvedStagedFiles.add(targetFile);
         }
 
         if (params.mode == SessionParams.MODE_FULL_INSTALL) {
             // Full installs must include a base package
-            if (!seenSplits.contains(null)) {
+            if (!stagedSplits.contains(null)) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                         "Full install must include a base package");
             }
@@ -577,67 +613,204 @@
                         "Missing existing base package for " + mPackageName);
             }
 
-            // Base might be inherited from existing install
-            if (mResolvedBaseCodePath == null) {
-                mResolvedBaseCodePath = app.getBaseCodePath();
-            }
-
-            final ApkLite info;
+            final PackageLite existing;
+            final ApkLite existingBase;
             try {
-                info = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
+                existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0);
+                existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
                         PackageParser.PARSE_COLLECT_CERTIFICATES);
             } catch (PackageParserException e) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                        "Failed to parse existing base " + app.getBaseCodePath() + ": " + e);
+                        "Failed to parse existing package " + app.getCodePath() + ": " + e);
             }
 
-            assertPackageConsistent("Existing base", info.packageName, info.versionCode,
-                    info.signatures);
+            assertApkConsistent("Existing base", existingBase);
+
+            // Inherit base if not overridden
+            if (mResolvedBaseFile == null) {
+                mResolvedBaseFile = new File(app.getBaseCodePath());
+                mResolvedInheritedFiles.add(mResolvedBaseFile);
+            }
+
+            // Inherit splits if not overridden
+            if (!ArrayUtils.isEmpty(existing.splitNames)) {
+                for (int i = 0; i < existing.splitNames.length; i++) {
+                    final String splitName = existing.splitNames[i];
+                    final File splitFile = new File(existing.splitCodePaths[i]);
+
+                    if (!stagedSplits.contains(splitName)) {
+                        mResolvedInheritedFiles.add(splitFile);
+                    }
+                }
+            }
         }
     }
 
-    private void assertPackageConsistent(String tag, String packageName, int versionCode,
-            Signature[] signatures) throws PackageManagerException {
-        if (!mPackageName.equals(packageName)) {
+    private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
+        if (!mPackageName.equals(apk.packageName)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
-                    + packageName + " inconsistent with " + mPackageName);
+                    + apk.packageName + " inconsistent with " + mPackageName);
         }
-        if (mVersionCode != versionCode) {
+        if (mVersionCode != apk.versionCode) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
-                    + " version code " + versionCode + " inconsistent with "
+                    + " version code " + apk.versionCode + " inconsistent with "
                     + mVersionCode);
         }
-        if (!Signature.areExactMatch(mSignatures, signatures)) {
+        if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     tag + " signatures are inconsistent");
         }
     }
 
     /**
-     * Application is already installed; splice existing files that haven't been
-     * overridden into our stage.
+     * Calculate the final install footprint size, combining both staged and
+     * existing APKs together and including unpacked native code from both.
      */
-    private void spliceExistingFilesIntoStage(File stageDir) throws PackageManagerException {
-        final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
+    private long calculateInstalledSize() throws PackageManagerException {
+        Preconditions.checkNotNull(mResolvedBaseFile);
 
-        int n = 0;
-        final File[] oldFiles = new File(app.getCodePath()).listFiles();
-        if (!ArrayUtils.isEmpty(oldFiles)) {
-            for (File oldFile : oldFiles) {
-                if (!PackageParser.isApkFile(oldFile)) continue;
+        final ApkLite baseApk;
+        try {
+            baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0);
+        } catch (PackageParserException e) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Failed to parse base package " + mResolvedBaseFile + ": " + e);
+        }
 
-                final File newFile = new File(stageDir, oldFile.getName());
-                try {
-                    Os.link(oldFile.getAbsolutePath(), newFile.getAbsolutePath());
-                    n++;
-                } catch (ErrnoException e) {
-                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                            "Failed to splice into stage", e);
-                }
+        final List<String> splitPaths = new ArrayList<>();
+        for (File file : mResolvedStagedFiles) {
+            if (mResolvedBaseFile.equals(file)) continue;
+            splitPaths.add(file.getAbsolutePath());
+        }
+        for (File file : mResolvedInheritedFiles) {
+            if (mResolvedBaseFile.equals(file)) continue;
+            splitPaths.add(file.getAbsolutePath());
+        }
+
+        // This is kind of hacky; we're creating a half-parsed package that is
+        // straddled between the inherited and staged APKs.
+        final PackageLite pkg = new PackageLite(null, baseApk, null,
+                splitPaths.toArray(new String[splitPaths.size()]));
+        final boolean isForwardLocked =
+                (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
+
+        try {
+            return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride);
+        } catch (IOException e) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Failed to calculate install size", e);
+        }
+    }
+
+    private static void linkFiles(List<File> fromFiles, File toDir) throws IOException {
+        for (File fromFile : fromFiles) {
+            final File toFile = new File(toDir, fromFile.getName());
+            try {
+                if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
+                Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
+            } catch (ErrnoException e) {
+                throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
+            }
+        }
+        Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
+    }
+
+    private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
+        // Remove any partial files from previous attempt
+        for (File file : toDir.listFiles()) {
+            if (file.getName().endsWith(".tmp")) {
+                file.delete();
             }
         }
 
-        if (LOGD) Slog.d(TAG, "Spliced " + n + " existing APKs into stage");
+        for (File fromFile : fromFiles) {
+            final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
+            if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
+            if (!FileUtils.copyFile(fromFile, tmpFile)) {
+                throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
+            }
+
+            final File toFile = new File(toDir, fromFile.getName());
+            if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
+            if (!tmpFile.renameTo(toFile)) {
+                throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
+            }
+        }
+        Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
+    }
+
+    private static void extractNativeLibraries(File packageDir, String abiOverride)
+            throws PackageManagerException {
+        if (LOGD) Slog.v(TAG, "extractNativeLibraries()");
+
+        // Always start from a clean slate
+        final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
+        NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
+
+        NativeLibraryHelper.Handle handle = null;
+        try {
+            handle = NativeLibraryHelper.Handle.create(packageDir);
+            final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
+                    abiOverride);
+            if (res != PackageManager.INSTALL_SUCCEEDED) {
+                throw new PackageManagerException(res,
+                        "Failed to extract native libraries, res=" + res);
+            }
+        } catch (IOException e) {
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Failed to extract native libraries", e);
+        } finally {
+            IoUtils.closeQuietly(handle);
+        }
+    }
+
+    private static void resizeContainer(String cid, long targetSize)
+            throws PackageManagerException {
+        String path = PackageHelper.getSdDir(cid);
+        if (path == null) {
+            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+                    "Failed to find mounted " + cid);
+        }
+
+        final long currentSize = new File(path).getTotalSpace();
+        if (currentSize > targetSize) {
+            Slog.w(TAG, "Current size " + currentSize + " is larger than target size "
+                    + targetSize + "; skipping resize");
+            return;
+        }
+
+        if (!PackageHelper.unMountSdDir(cid)) {
+            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+                    "Failed to unmount " + cid + " before resize");
+        }
+
+        if (!PackageHelper.resizeSdDir(targetSize, cid,
+                PackageManagerService.getEncryptKey())) {
+            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+                    "Failed to resize " + cid + " to " + targetSize + " bytes");
+        }
+
+        path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
+                Process.SYSTEM_UID, false);
+        if (path == null) {
+            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+                    "Failed to mount " + cid + " after resize");
+        }
+    }
+
+    private void finalizeAndFixContainer(String cid) throws PackageManagerException {
+        if (!PackageHelper.finalizeSdDir(cid)) {
+            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+                    "Failed to finalize container " + cid);
+        }
+
+        final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
+                UserHandle.USER_OWNER);
+        final int gid = UserHandle.getSharedAppGid(uid);
+        if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
+            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+                    "Failed to fix permissions on container " + cid);
+        }
     }
 
     void setPermissionsResult(boolean accepted) {
@@ -694,12 +867,12 @@
             mSealed = true;
             mDestroyed = true;
         }
-        if (internalStageDir != null) {
-            FileUtils.deleteContents(internalStageDir);
-            internalStageDir.delete();
+        if (stageDir != null) {
+            FileUtils.deleteContents(stageDir);
+            stageDir.delete();
         }
-        if (externalStageCid != null) {
-            PackageHelper.destroySdDir(externalStageCid);
+        if (stageCid != null) {
+            PackageHelper.destroySdDir(stageCid);
         }
     }
 
@@ -717,8 +890,8 @@
         pw.printPair("installerPackageName", installerPackageName);
         pw.printPair("installerUid", installerUid);
         pw.printPair("createdMillis", createdMillis);
-        pw.printPair("internalStageDir", internalStageDir);
-        pw.printPair("externalStageCid", externalStageCid);
+        pw.printPair("stageDir", stageDir);
+        pw.printPair("stageCid", stageCid);
         pw.println();
 
         params.dump(pw);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index aa49b27..f06992a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -50,6 +50,8 @@
 import static android.system.OsConstants.O_RDWR;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER;
+import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
 import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.internal.util.ArrayUtils.removeInt;
 
@@ -298,9 +300,6 @@
 
     private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
 
-    private static final String LIB_DIR_NAME = "lib";
-    private static final String LIB64_DIR_NAME = "lib64";
-
     private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
 
     private static String sPreferredInstructionSet;
@@ -1121,7 +1120,7 @@
 
                     if ((state != null) && !state.timeoutExtended()) {
                         final InstallArgs args = state.getInstallArgs();
-                        final Uri originUri = Uri.fromFile(args.originFile);
+                        final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
 
                         Slog.i(TAG, "Verification timed out for " + originUri);
                         mPendingVerification.remove(verificationId);
@@ -1168,7 +1167,7 @@
                         mPendingVerification.remove(verificationId);
 
                         final InstallArgs args = state.getInstallArgs();
-                        final Uri originUri = Uri.fromFile(args.originFile);
+                        final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
 
                         int ret;
                         if (state.isInstallAllowed()) {
@@ -4271,7 +4270,7 @@
 
                     InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                             ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
-                            getAppDexInstructionSets(ps), isMultiArch(ps));
+                            getAppDexInstructionSets(ps));
                     synchronized (mInstallLock) {
                         args.cleanUpResourcesLI();
                     }
@@ -4334,7 +4333,7 @@
                             + " better than installed " + ps.versionCode);
                     InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                             ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
-                            getAppDexInstructionSets(ps), isMultiArch(ps));
+                            getAppDexInstructionSets(ps));
                     synchronized (mInstallLock) {
                         args.cleanUpResourcesLI();
                     }
@@ -5527,8 +5526,9 @@
                         if (isAsec) {
                             abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
                         } else {
-                            abi32 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
-                                    nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, useIsaSpecificSubdirs);
+                            abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+                                    nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+                                    useIsaSpecificSubdirs);
                         }
                     }
 
@@ -5539,8 +5539,9 @@
                         if (isAsec) {
                             abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
                         } else {
-                            abi64 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
-                                    nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, useIsaSpecificSubdirs);
+                            abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+                                    nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+                                    useIsaSpecificSubdirs);
                         }
                     }
 
@@ -5578,7 +5579,7 @@
                     if (isAsec) {
                         copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
                     } else {
-                        copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+                        copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                                 nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
                     }
 
@@ -7782,7 +7783,8 @@
         verificationParams.setInstallerUid(uid);
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
-        msg.obj = new InstallParams(originFile, null, false, observer, filteredFlags,
+        final OriginInfo origin = new OriginInfo(originFile, null, false);
+        msg.obj = new InstallParams(origin, observer, filteredFlags,
                 installerPackageName, verificationParams, user, packageAbiOverride);
         mHandler.sendMessage(msg);
     }
@@ -7794,7 +7796,8 @@
                 params.referrerUri, installerUid, null);
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
-        msg.obj = new InstallParams(stagedDir, stagedCid, true, observer, params.installFlags,
+        final OriginInfo origin = new OriginInfo(stagedDir, stagedCid, true);
+        msg.obj = new InstallParams(origin, observer, params.installFlags,
                 installerPackageName, verifParams, user, params.abiOverride);
         mHandler.sendMessage(msg);
     }
@@ -8487,22 +8490,45 @@
         }
     }
 
-    class InstallParams extends HandlerParams {
+    static class OriginInfo {
         /**
          * Location where install is coming from, before it has been
          * copied/renamed into place. This could be a single monolithic APK
          * file, or a cluster directory. This location may be untrusted.
          */
-        final File originFile;
-        final String originCid;
+        final File file;
+        final String cid;
 
         /**
-         * Flag indicating that {@link #originFile} or {@link #originCid} has
-         * already been staged, meaning downstream users don't need to
-         * defensively copy the contents.
+         * Flag indicating that {@link #file} or {@link #cid} has already been
+         * staged, meaning downstream users don't need to defensively copy the
+         * contents.
          */
-        boolean originStaged;
+        final boolean staged;
 
+        final String resolvedPath;
+        final File resolvedFile;
+
+        public OriginInfo(File file, String cid, boolean staged) {
+            this.file = file;
+            this.cid = cid;
+            this.staged = staged;
+
+            if (cid != null) {
+                resolvedPath = PackageHelper.getSdDir(cid);
+                resolvedFile = new File(resolvedPath);
+            } else if (file != null) {
+                resolvedPath = file.getAbsolutePath();
+                resolvedFile = file;
+            } else {
+                resolvedPath = null;
+                resolvedFile = null;
+            }
+        }
+    }
+
+    class InstallParams extends HandlerParams {
+        final OriginInfo origin;
         final IPackageInstallObserver2 observer;
         int flags;
         final String installerPackageName;
@@ -8510,15 +8536,12 @@
         private InstallArgs mArgs;
         private int mRet;
         final String packageAbiOverride;
-        boolean multiArch;
 
-        InstallParams(File originFile, String originCid, boolean originStaged,
-                IPackageInstallObserver2 observer, int flags, String installerPackageName,
-                VerificationParams verificationParams, UserHandle user, String packageAbiOverride) {
+        InstallParams(OriginInfo origin, IPackageInstallObserver2 observer, int flags,
+                String installerPackageName, VerificationParams verificationParams, UserHandle user,
+                String packageAbiOverride) {
             super(user);
-            this.originFile = originFile;
-            this.originCid = originCid;
-            this.originStaged = originStaged;
+            this.origin = origin;
             this.observer = observer;
             this.flags = flags;
             this.installerPackageName = installerPackageName;
@@ -8529,7 +8552,7 @@
         @Override
         public String toString() {
             return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
-                    + " file=" + originFile + " cid=" + originCid + "}";
+                    + " file=" + origin.file + " cid=" + origin.cid + "}";
         }
 
         public ManifestDigest getManifestDigest() {
@@ -8608,11 +8631,11 @@
             int ret = PackageManager.INSTALL_SUCCEEDED;
 
             // If we're already staged, we've firmly committed to an install location
-            if (originStaged) {
-                if (originFile != null) {
+            if (origin.staged) {
+                if (origin.file != null) {
                     flags |= PackageManager.INSTALL_INTERNAL;
                     flags &= ~PackageManager.INSTALL_EXTERNAL;
-                } else if (originCid != null) {
+                } else if (origin.cid != null) {
                     flags |= PackageManager.INSTALL_EXTERNAL;
                     flags &= ~PackageManager.INSTALL_INTERNAL;
                 } else {
@@ -8622,6 +8645,7 @@
 
             final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
             final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;
+
             PackageInfoLite pkgLite = null;
 
             if (onInt && onSd) {
@@ -8629,21 +8653,14 @@
                 Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
                 ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
             } else {
-                // Remote call to find out default install location
-                final String originPath = originFile.getAbsolutePath();
-                pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
+                pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, flags,
                         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
                  * before giving up.
                  */
-                if (!originStaged && pkgLite.recommendedInstallLocation
+                if (!origin.staged && pkgLite.recommendedInstallLocation
                         == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                     // TODO: focus freeing disk space on the target device
                     final StorageManager storage = StorageManager.from(mContext);
@@ -8651,11 +8668,11 @@
                             Environment.getDataDirectory());
 
                     final long sizeBytes = mContainerService.calculateInstalledSize(
-                            originPath, isForwardLocked(), packageAbiOverride);
+                            origin.resolvedPath, isForwardLocked(), packageAbiOverride);
 
                     if (mInstaller.freeCache(sizeBytes + lowThreshold) >= 0) {
-                        pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
-                                packageAbiOverride);
+                        pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
+                                flags, packageAbiOverride);
                     }
 
                     /*
@@ -8729,10 +8746,10 @@
                 final int requiredUid = mRequiredVerifierPackage == null ? -1
                         : getPackageUid(mRequiredVerifierPackage, userIdentifier);
                 if (requiredUid != -1 && isVerificationEnabled(userIdentifier, flags)) {
-                    // TODO: send verifier the install session instead of uri
                     final Intent verification = new Intent(
                             Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
-                    verification.setDataAndType(Uri.fromFile(originFile), PACKAGE_MIME_TYPE);
+                    verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
+                            PACKAGE_MIME_TYPE);
                     verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
                     final List<ResolveInfo> receivers = queryIntentReceivers(verification,
@@ -8890,8 +8907,7 @@
         int mRet;
 
         MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags,
-                String packageName, String[] instructionSets, int uid, UserHandle user,
-                boolean isMultiArch) {
+                String packageName, String[] instructionSets, int uid, UserHandle user) {
             super(user);
             this.srcArgs = srcArgs;
             this.observer = observer;
@@ -8901,7 +8917,7 @@
             if (srcArgs != null) {
                 final String codePath = srcArgs.getCodePath();
                 targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName,
-                        instructionSets, isMultiArch);
+                        instructionSets);
             } else {
                 targetArgs = null;
             }
@@ -9002,8 +9018,6 @@
     }
 
     private InstallArgs createInstallArgs(InstallParams params) {
-        // TODO: extend to support incoming zero-copy locations
-
         if (installOnSd(params.flags) || params.isForwardLocked()) {
             return new AsecInstallArgs(params);
         } else {
@@ -9016,8 +9030,7 @@
      * when cleaning up old installs, or used as a move source.
      */
     private InstallArgs createInstallArgsForExisting(int flags, String codePath,
-            String resourcePath, String nativeLibraryRoot, String[] instructionSets,
-            boolean isMultiArch) {
+            String resourcePath, String nativeLibraryRoot, String[] instructionSets) {
         final boolean isInAsec;
         if (installOnSd(flags)) {
             /* Apps on SD card are always in ASEC containers. */
@@ -9035,33 +9048,29 @@
 
         if (isInAsec) {
             return new AsecInstallArgs(codePath, instructionSets,
-                    installOnSd(flags), installForwardLocked(flags), isMultiArch);
+                    installOnSd(flags), installForwardLocked(flags));
         } else {
             return new FileInstallArgs(codePath, resourcePath, nativeLibraryRoot,
-                    instructionSets, isMultiArch);
+                    instructionSets);
         }
     }
 
     private InstallArgs createInstallArgsForMoveTarget(String codePath, int flags, String pkgName,
-            String[] instructionSets, boolean isMultiArch) {
+            String[] instructionSets) {
         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, instructionSets, installOnSd(flags),
-                    installForwardLocked(flags), isMultiArch);
+                    installForwardLocked(flags));
         } else {
-            return new FileInstallArgs(codeFile, instructionSets, isMultiArch);
+            return new FileInstallArgs(codeFile, instructionSets);
         }
     }
 
     static abstract class InstallArgs {
-        /** @see InstallParams#originFile */
-        final File originFile;
-        /** @see InstallParams#originStaged */
-        final boolean originStaged;
-
-        // TODO: define inherit location
+        /** @see InstallParams#origin */
+        final OriginInfo origin;
 
         final IPackageInstallObserver2 observer;
         // Always refers to PackageManager flags only
@@ -9070,19 +9079,16 @@
         final ManifestDigest manifestDigest;
         final UserHandle user;
         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[] instructionSets,
-                    String abiOverride, boolean multiArch) {
-            this.originFile = originFile;
-            this.originStaged = originStaged;
+        InstallArgs(OriginInfo origin, IPackageInstallObserver2 observer, int flags,
+                String installerPackageName, ManifestDigest manifestDigest, UserHandle user,
+                String[] instructionSets, String abiOverride) {
+            this.origin = origin;
             this.flags = flags;
             this.observer = observer;
             this.installerPackageName = installerPackageName;
@@ -9090,7 +9096,6 @@
             this.user = user;
             this.instructionSets = instructionSets;
             this.abiOverride = abiOverride;
-            this.multiArch = multiArch;
         }
 
         abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
@@ -9161,10 +9166,9 @@
 
         /** New install */
         FileInstallArgs(InstallParams params) {
-            super(params.originFile, params.originStaged, params.observer, params.flags,
+            super(params.origin, params.observer, params.flags,
                     params.installerPackageName, params.getManifestDigest(), params.getUser(),
-                    null /* instruction sets */, params.packageAbiOverride,
-                    params.multiArch);
+                    null /* instruction sets */, params.packageAbiOverride);
             if (isFwdLocked()) {
                 throw new IllegalArgumentException("Forward locking only supported in ASEC");
             }
@@ -9172,8 +9176,8 @@
 
         /** Existing install */
         FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryPath,
-                String[] instructionSets, boolean isMultiArch) {
-            super(null, false, null, 0, null, null, null, instructionSets, null, isMultiArch);
+                String[] instructionSets) {
+            super(new OriginInfo(null, null, false), null, 0, null, null, null, instructionSets, null);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
             this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
             this.legacyNativeLibraryPath = (legacyNativeLibraryPath != null) ?
@@ -9181,13 +9185,12 @@
         }
 
         /** New install from existing */
-        FileInstallArgs(File originFile, String[] instructionSets, boolean isMultiArch) {
-            super(originFile, false, null, 0, null, null, null, instructionSets, null,
-                    isMultiArch);
+        FileInstallArgs(File originFile, String[] instructionSets) {
+            super(new OriginInfo(originFile, null, false), null, 0, null, null, null, instructionSets, null);
         }
 
         boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
-            final long sizeBytes = imcs.calculateInstalledSize(originFile.getAbsolutePath(),
+            final long sizeBytes = imcs.calculateInstalledSize(origin.file.getAbsolutePath(),
                     isFwdLocked(), abiOverride);
 
             final StorageManager storage = StorageManager.from(mContext);
@@ -9195,53 +9198,53 @@
         }
 
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
-            int ret = PackageManager.INSTALL_SUCCEEDED;
+            if (origin.staged) {
+                Slog.d(TAG, origin.file + " already staged; skipping copy");
+                codeFile = origin.file;
+                resourceFile = origin.file;
+                return PackageManager.INSTALL_SUCCEEDED;
+            }
 
-            if (originStaged) {
-                Slog.d(TAG, originFile + " already staged; skipping copy");
-                codeFile = originFile;
-                resourceFile = originFile;
-            } else {
-                try {
-                    final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
-                    codeFile = tempDir;
-                    resourceFile = tempDir;
-                } catch (IOException e) {
-                    Slog.w(TAG, "Failed to create copy file: " + e);
-                    return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                }
+            try {
+                final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
+                codeFile = tempDir;
+                resourceFile = tempDir;
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed to create copy file: " + e);
+                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            }
 
-                final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
-                    @Override
-                    public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
-                        if (!FileUtils.isValidExtFilename(name)) {
-                            throw new IllegalArgumentException("Invalid filename: " + name);
-                        }
-                        try {
-                            final File file = new File(codeFile, name);
-                            final FileDescriptor fd = Os.open(file.getAbsolutePath(),
-                                    O_RDWR | O_CREAT, 0644);
-                            Os.chmod(file.getAbsolutePath(), 0644);
-                            return new ParcelFileDescriptor(fd);
-                        } catch (ErrnoException e) {
-                            throw new RemoteException("Failed to open: " + e.getMessage());
-                        }
+            final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
+                @Override
+                public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
+                    if (!FileUtils.isValidExtFilename(name)) {
+                        throw new IllegalArgumentException("Invalid filename: " + name);
                     }
-                };
-
-                ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
-                if (ret != PackageManager.INSTALL_SUCCEEDED) {
-                    Slog.e(TAG, "Failed to copy package");
-                    return ret;
+                    try {
+                        final File file = new File(codeFile, name);
+                        final FileDescriptor fd = Os.open(file.getAbsolutePath(),
+                                O_RDWR | O_CREAT, 0644);
+                        Os.chmod(file.getAbsolutePath(), 0644);
+                        return new ParcelFileDescriptor(fd);
+                    } catch (ErrnoException e) {
+                        throw new RemoteException("Failed to open: " + e.getMessage());
+                    }
                 }
+            };
+
+            int ret = PackageManager.INSTALL_SUCCEEDED;
+            ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
+            if (ret != PackageManager.INSTALL_SUCCEEDED) {
+                Slog.e(TAG, "Failed to copy package");
+                return ret;
             }
 
             final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
             NativeLibraryHelper.Handle handle = null;
             try {
                 handle = NativeLibraryHelper.Handle.create(codeFile);
-                ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libraryRoot,
-                        abiOverride, multiArch);
+                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+                        abiOverride);
             } catch (IOException e) {
                 Slog.e(TAG, "Copying native libraries failed", e);
                 ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
@@ -9429,18 +9432,18 @@
 
         /** New install */
         AsecInstallArgs(InstallParams params) {
-            super(params.originFile, params.originStaged, params.observer, params.flags,
+            super(params.origin, params.observer, params.flags,
                     params.installerPackageName, params.getManifestDigest(),
                     params.getUser(), null /* instruction sets */,
-                    params.packageAbiOverride, params.multiArch);
+                    params.packageAbiOverride);
         }
 
         /** Existing install */
         AsecInstallArgs(String fullCodePath, String[] instructionSets,
-                        boolean isExternal, boolean isForwardLocked, boolean isMultiArch) {
-            super(null, false, null, (isExternal ? INSTALL_EXTERNAL : 0)
+                        boolean isExternal, boolean isForwardLocked) {
+            super(new OriginInfo(null, null, false), null, (isExternal ? INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
-                    instructionSets, null, isMultiArch);
+                    instructionSets, null);
             // Hackily pretend we're still looking at a full code path
             if (!fullCodePath.endsWith(RES_FILE_NAME)) {
                 fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath();
@@ -9454,21 +9457,20 @@
             setMountPath(subStr1);
         }
 
-        AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked,
-                        boolean isMultiArch) {
-            super(null, false, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
+        AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
+            super(new OriginInfo(null, null, false), null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
-                    instructionSets, null, isMultiArch);
+                    instructionSets, null);
             this.cid = cid;
             setMountPath(PackageHelper.getSdDir(cid));
         }
 
         /** New install from existing */
         AsecInstallArgs(File originPackageFile, String cid, String[] instructionSets,
-                boolean isExternal, boolean isForwardLocked, boolean isMultiArch) {
-            super(originPackageFile, false, null, (isExternal ? INSTALL_EXTERNAL : 0)
+                boolean isExternal, boolean isForwardLocked) {
+            super(new OriginInfo(originPackageFile, null, false), null, (isExternal ? INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
-                    instructionSets, null, isMultiArch);
+                    instructionSets, null);
             this.cid = cid;
         }
 
@@ -9496,7 +9498,13 @@
         }
 
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
-            // TODO: if already staged, we only need to extract native code
+            if (origin.staged) {
+                Slog.d(TAG, origin.cid + " already staged; skipping copy");
+                cid = origin.cid;
+                setMountPath(PackageHelper.getSdDir(cid));
+                return PackageManager.INSTALL_SUCCEEDED;
+            }
+
             if (temp) {
                 createCopyFile();
             } else {
@@ -9508,7 +9516,7 @@
             }
 
             final String newMountPath = imcs.copyPackageToContainer(
-                    originFile.getAbsolutePath(), cid, getEncryptKey(), isExternal(),
+                    origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternal(),
                     isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */));
 
             if (newMountPath != null) {
@@ -10133,8 +10141,7 @@
                         deletedPackage.applicationInfo.getCodePath(),
                         deletedPackage.applicationInfo.getResourcePath(),
                         deletedPackage.applicationInfo.nativeLibraryRootDir,
-                        getAppDexInstructionSets(deletedPackage.applicationInfo),
-                        isMultiArch(deletedPackage.applicationInfo));
+                        getAppDexInstructionSets(deletedPackage.applicationInfo));
             } else {
                 res.removedInfo.args = null;
             }
@@ -10920,7 +10927,7 @@
         if (deleteCodeAndResources && (outInfo != null)) {
             outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                     ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
-                    getAppDexInstructionSets(ps), isMultiArch(ps));
+                    getAppDexInstructionSets(ps));
             if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
         }
         return true;
@@ -12725,7 +12732,7 @@
                     }
 
                     final AsecInstallArgs args = new AsecInstallArgs(cid,
-                            getAppDexInstructionSets(ps), isForwardLocked(ps), isMultiArch(ps));
+                            getAppDexInstructionSets(ps), isForwardLocked(ps));
                     // The package status is changed only if the code path
                     // matches between settings and the container id.
                     if (ps.codePathString != null
@@ -13019,7 +13026,7 @@
              * anyway.
              */
             if (returnCode != PackageManager.MOVE_SUCCEEDED) {
-                processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1, user, false),
+                processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1, user),
                         returnCode);
             } else {
                 Message msg = mHandler.obtainMessage(INIT_COPY);
@@ -13027,9 +13034,9 @@
                 final boolean multiArch = isMultiArch(pkg.applicationInfo);
                 InstallArgs srcArgs = createInstallArgsForExisting(currFlags,
                         pkg.applicationInfo.getCodePath(), pkg.applicationInfo.getResourcePath(),
-                        pkg.applicationInfo.nativeLibraryRootDir, instructionSets, multiArch);
+                        pkg.applicationInfo.nativeLibraryRootDir, instructionSets);
                 MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
-                        instructionSets, pkg.applicationInfo.uid, user, multiArch);
+                        instructionSets, pkg.applicationInfo.uid, user);
                 msg.obj = mp;
                 mHandler.sendMessage(msg);
             }
@@ -13107,8 +13114,8 @@
                                             final int abi = NativeLibraryHelper.findSupportedAbi(
                                                     handle, Build.SUPPORTED_ABIS);
                                             if (abi >= 0) {
-                                                NativeLibraryHelper.copyNativeBinariesIfNeededLI(
-                                                        handle, newNativeDir, Build.SUPPORTED_ABIS[abi]);
+                                                NativeLibraryHelper.copyNativeBinaries(handle,
+                                                        newNativeDir, Build.SUPPORTED_ABIS[abi]);
                                             }
                                         } catch (IOException ioe) {
                                             Slog.w(TAG, "Unable to extract native libs for package :"