OSDN Git Service

Slow progress towards APK clusters.
authorJeff Sharkey <jsharkey@android.com>
Thu, 19 Jun 2014 00:46:05 +0000 (17:46 -0700)
committerJeff Sharkey <jsharkey@android.com>
Thu, 3 Jul 2014 02:03:54 +0000 (19:03 -0700)
Differentiate between "split APKs" and "cluster packages".  A cluster
package is a directory containing zero or more APKs (base+splits),
and a monolithic package is a single APK (base).

PackageSetting will use the directory name as its codePath, so track
the baseCodePath separately.  Clarify documentation in several
places.

Require that all installers provide file:// URIs through existing
hidden APIs; PackageInstaller hasn't been able to read content://
URIs for a long time.

Bug: 14975160
Change-Id: I1c6fed1b55205c2474b09871161a98a26669d22e

core/java/android/content/pm/ApplicationInfo.java
core/java/android/content/pm/PackageParser.java
core/java/android/os/FileUtils.java
core/java/android/os/SELinux.java
core/java/com/android/internal/app/IMediaContainerService.aidl
core/java/com/android/internal/util/ArrayUtils.java
packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
services/core/java/com/android/server/pm/PackageInstallerSession.java
services/core/java/com/android/server/pm/PackageKeySetData.java
services/core/java/com/android/server/pm/PackageManagerService.java
services/core/java/com/android/server/pm/PackageSettingBase.java

index be4e864..bb90fd7 100644 (file)
@@ -413,6 +413,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
     /**
      * Full path to the base APK for this application.
      */
+    // TODO: verify that nobody is doing codePath comparisons against this
     public String sourceDir;
 
     /**
index f176dfb..8b6f3d4 100644 (file)
@@ -279,11 +279,11 @@ public class PackageParser {
         mMetrics = metrics;
     }
 
-    private static final boolean isPackageFilename(File file) {
+    public static final boolean isPackageFilename(File file) {
         return isPackageFilename(file.getName());
     }
 
-    private static final boolean isPackageFilename(String name) {
+    public static final boolean isPackageFilename(String name) {
         return name.endsWith(".apk");
     }
 
@@ -552,7 +552,7 @@ public class PackageParser {
      * Note that this <em>does not</em> perform signature verification; that
      * must be done separately in {@link #collectCertificates(Package, int)}.
      */
-    public Package parseSplitPackage(File apkDir, int flags) throws PackageParserException {
+    public Package parseClusterPackage(File apkDir, int flags) throws PackageParserException {
         final File[] files = apkDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
@@ -600,27 +600,23 @@ public class PackageParser {
                     "Missing base APK in " + apkDir);
         }
 
-        // Always apply deterministic ordering based on splitName
-        final int size = apks.size();
-
-        final String[] splitNames = apks.keySet().toArray(new String[size]);
-        Arrays.sort(splitNames, sSplitNameComparator);
-
-        final File[] splitFiles = new File[size];
-        for (int i = 0; i < size; i++) {
-            splitFiles[i] = apks.get(splitNames[i]);
-        }
-
         final Package pkg = parseBaseApk(baseFile, flags);
         if (pkg == null) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                     "Failed to parse base APK: " + baseFile);
         }
 
-        for (File splitFile : splitFiles) {
-            parseSplitApk(pkg, splitFile, flags);
+        // Always apply deterministic ordering based on splitName
+        final int size = apks.size();
+        final String[] splitNames = apks.keySet().toArray(new String[size]);
+        Arrays.sort(splitNames, sSplitNameComparator);
+
+        for (String splitName : splitNames) {
+            final File splitFile = apks.get(splitName);
+            parseSplitApk(pkg, splitFile, splitName, flags);
         }
 
+        pkg.codePath = apkDir.getAbsolutePath();
         return pkg;
     }
 
@@ -632,11 +628,12 @@ public class PackageParser {
      */
     public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
         final Package pkg = parseBaseApk(apkFile, flags);
-        if (pkg != null) {
-            return pkg;
-        } else {
+        if (pkg == null) {
             throw new PackageParserException(mParseError, "Failed to parse " + apkFile);
         }
+
+        pkg.codePath = apkFile.getAbsolutePath();
+        return pkg;
     }
 
     private Package parseBaseApk(File apkFile, int flags) {
@@ -723,19 +720,22 @@ public class PackageParser {
         parser.close();
         assmgr.close();
 
-        pkg.codePath = apkPath;
+        pkg.baseCodePath = apkPath;
         pkg.mSignatures = null;
 
         return pkg;
     }
 
-    private void parseSplitApk(Package pkg, File apkFile, int flags) throws PackageParserException {
-        final String apkPath = apkFile.getAbsolutePath();
+    private void parseSplitApk(Package pkg, File apkFile, String splitName, int flags)
+            throws PackageParserException {
+        final String splitCodePath = apkFile.getAbsolutePath();
         mArchiveSourcePath = apkFile.getAbsolutePath();
 
         // TODO: expand split APK parsing
+        // TODO: extract splitName during parse
+        pkg.splitNames = ArrayUtils.appendElement(String.class, pkg.splitNames, splitName);
         pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths,
-                apkFile.getAbsolutePath());
+                splitCodePath);
     }
 
     /**
@@ -748,7 +748,7 @@ public class PackageParser {
 
         // TODO: extend to gather digest for split APKs
         try {
-            final StrictJarFile jarFile = new StrictJarFile(pkg.codePath);
+            final StrictJarFile jarFile = new StrictJarFile(pkg.baseCodePath);
             try {
                 final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
                 if (je != null) {
@@ -773,7 +773,7 @@ public class PackageParser {
         pkg.mSignatures = null;
         pkg.mSigningKeys = null;
 
-        collectCertificates(pkg, new File(pkg.codePath), flags);
+        collectCertificates(pkg, new File(pkg.baseCodePath), flags);
 
         if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
             for (String splitCodePath : pkg.splitCodePaths) {
@@ -3692,12 +3692,21 @@ public class PackageParser {
     public final static class Package {
 
         public String packageName;
+        /** Names of any split APKs, ordered by parsed splitName */
+        public String[] splitNames;
 
         // TODO: work towards making these paths invariant
 
-        /** Base APK */
+        /**
+         * Path where this package was found on disk. For monolithic packages
+         * this is path to single base APK file; for cluster packages this is
+         * path to the cluster directory.
+         */
         public String codePath;
-        /** Split APKs, ordered by parsed splitName */
+
+        /** Path of base APK */
+        public String baseCodePath;
+        /** Paths of any split APKs, ordered by parsed splitName */
         public String[] splitCodePaths;
 
         // For now we only support one application per package.
@@ -3816,9 +3825,9 @@ public class PackageParser {
             applicationInfo.uid = -1;
         }
 
-        public Collection<String> getAllCodePaths() {
+        public List<String> getAllCodePaths() {
             ArrayList<String> paths = new ArrayList<>();
-            paths.add(codePath);
+            paths.add(baseCodePath);
             if (!ArrayUtils.isEmpty(splitCodePaths)) {
                 Collections.addAll(paths, splitCodePaths);
             }
index 1dba77d..d5d5eb8 100644 (file)
@@ -384,16 +384,18 @@ public class FileUtils {
         return filePath.startsWith(dirPath);
     }
 
-    public static void deleteContents(File dir) {
+    public static boolean deleteContents(File dir) {
         File[] files = dir.listFiles();
+        boolean success = true;
         if (files != null) {
             for (File file : files) {
                 if (file.isDirectory()) {
-                    deleteContents(file);
+                    success &= deleteContents(file);
                 }
-                file.delete();
+                success &= file.delete();
             }
         }
+        return success;
     }
 
     /**
@@ -411,4 +413,23 @@ public class FileUtils {
         }
         return true;
     }
+
+    public static String rewriteAfterRename(File beforeDir, File afterDir, String path) {
+        final File result = rewriteAfterRename(beforeDir, afterDir, new File(path));
+        return (result != null) ? result.getAbsolutePath() : null;
+    }
+
+    /**
+     * Given a path under the "before" directory, rewrite it to live under the
+     * "after" directory. For example, {@code /before/foo/bar.txt} would become
+     * {@code /after/foo/bar.txt}.
+     */
+    public static File rewriteAfterRename(File beforeDir, File afterDir, File file) {
+        if (contains(beforeDir, file)) {
+            final String splice = file.getAbsolutePath().substring(
+                    beforeDir.getAbsolutePath().length());
+            return new File(afterDir, splice);
+        }
+        return null;
+    }
 }
index c9dd5d7..71d12c6 100644 (file)
@@ -171,4 +171,23 @@ public class SELinux {
             return false;
         }
     }
+
+    /**
+     * Recursively restores all files under the given path to their default
+     * SELinux security context. If the system is not compiled with SELinux,
+     * then {@code true} is automatically returned. If SELinux is compiled in,
+     * but disabled, then {@code true} is returned.
+     *
+     * @return a boolean indicating whether the relabeling succeeded.
+     */
+    public static boolean restoreconTree(File dir) {
+        final File[] files = dir.listFiles();
+        boolean success = true;
+        if (files != null) {
+            for (File file : files) {
+                success &= restorecon(file);
+            }
+        }
+        return success;
+    }
 }
index 77f0dec..3cd0417 100644 (file)
 
 package com.android.internal.app;
 
-import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.content.pm.ContainerEncryptionParams;
 import android.content.pm.PackageInfoLite;
 import android.content.res.ObbInfo;
 
 interface IMediaContainerService {
-    String copyResourceToContainer(in Uri packageURI, String containerId, String key,
+    String copyResourceToContainer(String packagePath, String containerId, String key,
             String resFileName, String publicResFileName, boolean isExternal,
-            boolean isForwardLocked, in String abiOverride);
-    int copyResource(in Uri packageURI, in ContainerEncryptionParams encryptionParams,
+            boolean isForwardLocked, String abiOverride);
+    int copyResource(String packagePath, in ContainerEncryptionParams encryptionParams,
             in ParcelFileDescriptor outStream);
-    PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold,
-            in String abiOverride);
-    boolean checkInternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in long threshold);
-    boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in String abiOverride);
-    ObbInfo getObbInfo(in String filename);
-    long calculateDirectorySize(in String directory);
+    PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, long threshold,
+            String abiOverride);
+    boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked, long threshold);
+    boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked, String abiOverride);
+    ObbInfo getObbInfo(String filename);
+    long calculateDirectorySize(String directory);
     /** Return file system stats: [0] is total bytes, [1] is available bytes */
-    long[] getFileSystemStats(in String path);
-    void clearDirectory(in String directory);
-    long calculateInstalledSize(in String packagePath, boolean isForwardLocked,
-            in String abiOverride);
+    long[] getFileSystemStats(String path);
+    void clearDirectory(String directory);
+    long calculateInstalledSize(String packagePath, boolean isForwardLocked, String abiOverride);
 }
index d66ef83..7f6159d 100644 (file)
@@ -138,6 +138,7 @@ public class ArrayUtils
      * not found.
      */
     public static <T> int indexOf(T[] array, T value) {
+        if (array == null) return -1;
         for (int i = 0; i < array.length; i++) {
             if (array[i] == null) {
                 if (value == null) return i;
@@ -161,6 +162,7 @@ public class ArrayUtils
     }
 
     public static boolean contains(int[] array, int value) {
+        if (array == null) return false;
         for (int element : array) {
             if (element == value) {
                 return true;
@@ -170,6 +172,7 @@ public class ArrayUtils
     }
 
     public static boolean contains(long[] array, long value) {
+        if (array == null) return false;
         for (long element : array) {
             if (element == value) {
                 return true;
@@ -325,4 +328,8 @@ public class ArrayUtils
         }
         return cur;
     }
+
+    public static long[] cloneOrNull(long[] array) {
+        return (array != null) ? array.clone() : null;
+    }
 }
index 168fb41..74ff3b1 100644 (file)
@@ -30,7 +30,6 @@ import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.res.ObbInfo;
 import android.content.res.ObbScanner;
-import android.net.Uri;
 import android.os.Build;
 import android.os.Environment;
 import android.os.Environment.UserEnvironment;
@@ -45,7 +44,6 @@ import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructStatVfs;
-import android.util.DisplayMetrics;
 import android.util.Slog;
 
 import com.android.internal.app.IMediaContainerService;
@@ -53,6 +51,9 @@ import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.NativeLibraryHelper.ApkHandle;
 import com.android.internal.content.PackageHelper;
 
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -71,9 +72,6 @@ import javax.crypto.CipherInputStream;
 import javax.crypto.Mac;
 import javax.crypto.NoSuchPaddingException;
 
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
 /*
  * This service copies a downloaded apk to a file passed in as
  * a ParcelFileDescriptor or to a newly created container specified
@@ -101,14 +99,14 @@ public class DefaultContainerService extends IntentService {
          * @return Returns the new cache path where the resource has been copied into
          *
          */
-        public String copyResourceToContainer(final Uri packageURI, final String cid,
+        @Override
+        public String copyResourceToContainer(final String packagePath, final String cid,
                 final String key, final String resFileName, final String publicResFileName,
                 boolean isExternal, boolean isForwardLocked, String abiOverride) {
-            if (packageURI == null || cid == null) {
+            if (packagePath == null || cid == null) {
                 return null;
             }
 
-
             if (isExternal) {
                 // Make sure the sdcard is mounted.
                 String status = Environment.getExternalStorageState();
@@ -120,11 +118,11 @@ public class DefaultContainerService extends IntentService {
 
             ApkHandle handle = null;
             try {
-                handle = ApkHandle.create(packageURI.getPath());
-                return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName,
+                handle = ApkHandle.create(packagePath);
+                return copyResourceInner(packagePath, cid, key, resFileName, publicResFileName,
                         isExternal, isForwardLocked, handle, abiOverride);
             } catch (IOException ioe) {
-                Slog.w(TAG, "Problem opening APK: " + packageURI.getPath());
+                Slog.w(TAG, "Problem opening APK: " + packagePath);
                 return null;
             } finally {
                 IoUtils.closeQuietly(handle);
@@ -142,9 +140,10 @@ public class DefaultContainerService extends IntentService {
          * @return returns status code according to those in
          *         {@link PackageManager}
          */
-        public int copyResource(final Uri packageURI, ContainerEncryptionParams encryptionParams,
-                ParcelFileDescriptor outStream) {
-            if (packageURI == null || outStream == null) {
+        @Override
+        public int copyResource(final String packagePath,
+                ContainerEncryptionParams encryptionParams, ParcelFileDescriptor outStream) {
+            if (packagePath == null || outStream == null) {
                 return PackageManager.INSTALL_FAILED_INVALID_URI;
             }
 
@@ -152,18 +151,18 @@ public class DefaultContainerService extends IntentService {
                     = new ParcelFileDescriptor.AutoCloseOutputStream(outStream);
 
             try {
-                copyFile(packageURI, autoOut, encryptionParams);
+                copyFile(packagePath, autoOut, encryptionParams);
                 return PackageManager.INSTALL_SUCCEEDED;
             } catch (FileNotFoundException e) {
-                Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " FNF: "
+                Slog.e(TAG, "Could not copy URI " + packagePath.toString() + " FNF: "
                         + e.getMessage());
                 return PackageManager.INSTALL_FAILED_INVALID_URI;
             } catch (IOException e) {
-                Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " IO: "
+                Slog.e(TAG, "Could not copy URI " + packagePath.toString() + " IO: "
                         + e.getMessage());
                 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
             } catch (DigestException e) {
-                Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " Security: "
+                Slog.e(TAG, "Could not copy URI " + packagePath.toString() + " Security: "
                                 + e.getMessage());
                 return PackageManager.INSTALL_FAILED_INVALID_APK;
             } finally {
@@ -217,9 +216,9 @@ public class DefaultContainerService extends IntentService {
         }
 
         @Override
-        public boolean checkInternalFreeStorage(Uri packageUri, boolean isForwardLocked,
+        public boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked,
                 long threshold) throws RemoteException {
-            final File apkFile = new File(packageUri.getPath());
+            final File apkFile = new File(packagePath);
             try {
                 return isUnderInternalThreshold(apkFile, isForwardLocked, threshold);
             } catch (IOException e) {
@@ -228,9 +227,9 @@ public class DefaultContainerService extends IntentService {
         }
 
         @Override
-        public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked,
+        public boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked,
                 String abiOverride) throws RemoteException {
-            final File apkFile = new File(packageUri.getPath());
+            final File apkFile = new File(packagePath);
             try {
                 return isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride);
             } catch (IOException e) {
@@ -238,6 +237,7 @@ public class DefaultContainerService extends IntentService {
             }
         }
 
+        @Override
         public ObbInfo getObbInfo(String filename) {
             try {
                 return ObbScanner.getObbInfo(filename);
@@ -347,11 +347,11 @@ public class DefaultContainerService extends IntentService {
         return mBinder;
     }
 
-    private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName,
+    private String copyResourceInner(String packagePath, String newCid, String key, String resFileName,
             String publicResFileName, boolean isExternal, boolean isForwardLocked,
             ApkHandle handle, String abiOverride) {
         // The .apk file
-        String codePath = packageURI.getPath();
+        String codePath = packagePath;
         File codeFile = new File(codePath);
 
         String[] abiList = Build.SUPPORTED_ABIS;
@@ -492,39 +492,13 @@ public class DefaultContainerService extends IntentService {
         }
     }
 
-    private void copyFile(Uri pPackageURI, OutputStream outStream,
+    private void copyFile(String packagePath, OutputStream outStream,
             ContainerEncryptionParams encryptionParams) throws FileNotFoundException, IOException,
             DigestException {
-        String scheme = pPackageURI.getScheme();
         InputStream inStream = null;
         try {
-            if (scheme == null || scheme.equals("file")) {
-                final InputStream is = new FileInputStream(new File(pPackageURI.getPath()));
-                inStream = new BufferedInputStream(is);
-            } else if (scheme.equals("content")) {
-                final ParcelFileDescriptor fd;
-                try {
-                    fd = getContentResolver().openFileDescriptor(pPackageURI, "r");
-                } catch (FileNotFoundException e) {
-                    Slog.e(TAG, "Couldn't open file descriptor from download service. "
-                            + "Failed with exception " + e);
-                    throw e;
-                }
-
-                if (fd == null) {
-                    Slog.e(TAG, "Provider returned no file descriptor for " +
-                            pPackageURI.toString());
-                    throw new FileNotFoundException("provider returned no file descriptor");
-                } else {
-                    if (localLOGV) {
-                        Slog.i(TAG, "Opened file descriptor from download service.");
-                    }
-                    inStream = new ParcelFileDescriptor.AutoCloseInputStream(fd);
-                }
-            } else {
-                Slog.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
-                throw new FileNotFoundException("Package URI is not 'file:' or 'content:'");
-            }
+            final InputStream is = new FileInputStream(new File(packagePath));
+            inStream = new BufferedInputStream(is);
 
             /*
              * If this resource is encrypted, get the decrypted stream version
index 0658eee..11e546f 100644 (file)
@@ -41,6 +41,7 @@ import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SELinux;
+import android.os.UserHandle;
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.system.StructStat;
@@ -272,7 +273,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
             }
         };
 
-        mPm.installStage(mPackageName, this.sessionDir, localObserver, params.installFlags);
+        mPm.installStage(mPackageName, this.sessionDir, localObserver, params, installerPackageName,
+                installerUid, new UserHandle(userId));
     }
 
     /**
index d470807..11ed5d2 100644 (file)
@@ -18,7 +18,6 @@ package com.android.server.pm;
 
 import com.android.internal.util.ArrayUtils;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -35,19 +34,17 @@ public class PackageKeySetData {
 
     private long[] mDefinedKeySets;
 
-    private final Map<String, Long> mKeySetAliases;
+    private final Map<String, Long> mKeySetAliases = new HashMap<String, Long>();
 
     PackageKeySetData() {
         mProperSigningKeySet = KEYSET_UNASSIGNED;
-        mKeySetAliases = new HashMap<String, Long>();
     }
 
     PackageKeySetData(PackageKeySetData original) {
-        mSigningKeySets = original.getSigningKeySets().clone();
-        mUpgradeKeySets = original.getUpgradeKeySets().clone();
-        mDefinedKeySets = original.getDefinedKeySets().clone();
-        mKeySetAliases = new HashMap<String, Long>();
-        mKeySetAliases.putAll(original.getAliases());
+        mSigningKeySets = ArrayUtils.cloneOrNull(original.mSigningKeySets);
+        mUpgradeKeySets = ArrayUtils.cloneOrNull(original.mUpgradeKeySets);
+        mDefinedKeySets = ArrayUtils.cloneOrNull(original.mDefinedKeySets);
+        mKeySetAliases.putAll(original.mKeySetAliases);
     }
 
     protected void setProperSigningKeySet(long ks) {
@@ -149,4 +146,4 @@ public class PackageKeySetData {
         /* should never be the case that mUpgradeKeySets.length == 0 */
         return (mUpgradeKeySets != null && mUpgradeKeySets.length > 0);
     }
-}
\ No newline at end of file
+}
index bc45ae0..2d9176d 100644 (file)
@@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageParser.isPackageFilename;
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
 import static android.system.OsConstants.S_IRGRP;
@@ -46,6 +47,7 @@ import com.android.internal.content.PackageHelper;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.EventLogTags;
 import com.android.server.IntentResolver;
@@ -92,6 +94,7 @@ import android.content.pm.ManifestDigest;
 import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageInstallerParams;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser.ActivityIntentInfo;
 import android.content.pm.PackageParser.PackageParserException;
@@ -349,6 +352,7 @@ public class PackageManagerService extends IPackageManager.Stub {
     // LOCK HELD.  Can be called with mInstallLock held.
     final Installer mInstaller;
 
+    /** Directory where installed third-party apps stored */
     final File mAppInstallDir;
 
     /**
@@ -361,6 +365,7 @@ public class PackageManagerService extends IPackageManager.Stub {
     // apps.
     final File mDrmAppPrivateInstallDir;
 
+    /** Directory where third-party apps are staged before install */
     final File mAppStagingDir;
 
     // ----------------------------------------------------------------
@@ -1143,17 +1148,18 @@ public class PackageManagerService extends IPackageManager.Stub {
 
                     if ((state != null) && !state.timeoutExtended()) {
                         final InstallArgs args = state.getInstallArgs();
-                        Slog.i(TAG, "Verification timed out for " + args.packageURI.toString());
+                        final Uri fromUri = Uri.fromFile(args.fromFile);
+
+                        Slog.i(TAG, "Verification timed out for " + fromUri);
                         mPendingVerification.remove(verificationId);
 
                         int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
 
                         if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
-                            Slog.i(TAG, "Continuing with installation of "
-                                    + args.packageURI.toString());
+                            Slog.i(TAG, "Continuing with installation of " + fromUri);
                             state.setVerifierResponse(Binder.getCallingUid(),
                                     PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
-                            broadcastPackageVerified(verificationId, args.packageURI,
+                            broadcastPackageVerified(verificationId, fromUri,
                                     PackageManager.VERIFICATION_ALLOW,
                                     state.getInstallArgs().getUser());
                             try {
@@ -1162,7 +1168,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                                 Slog.e(TAG, "Could not contact the ContainerService");
                             }
                         } else {
-                            broadcastPackageVerified(verificationId, args.packageURI,
+                            broadcastPackageVerified(verificationId, fromUri,
                                     PackageManager.VERIFICATION_REJECT,
                                     state.getInstallArgs().getUser());
                         }
@@ -1189,11 +1195,12 @@ public class PackageManagerService extends IPackageManager.Stub {
                         mPendingVerification.remove(verificationId);
 
                         final InstallArgs args = state.getInstallArgs();
+                        final Uri fromUri = Uri.fromFile(args.fromFile);
 
                         int ret;
                         if (state.isInstallAllowed()) {
                             ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
-                            broadcastPackageVerified(verificationId, args.packageURI,
+                            broadcastPackageVerified(verificationId, fromUri,
                                     response.code, state.getInstallArgs().getUser());
                             try {
                                 ret = args.copyApk(mContainerService, true);
@@ -1528,14 +1535,14 @@ public class PackageManagerService extends IPackageManager.Stub {
             // overlay packages if they reside in VENDOR_OVERLAY_DIR.
             File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
             mVendorOverlayInstallObserver = new AppDirObserver(
-                vendorOverlayDir.getPath(), OBSERVER_EVENTS, true, false);
+                    vendorOverlayDir.getPath(), OBSERVER_EVENTS, true, false);
             mVendorOverlayInstallObserver.startWatching();
             scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_TRUSTED_OVERLAY, 0);
 
             // Find base frameworks (resource packages without code).
             mFrameworkInstallObserver = new AppDirObserver(
-                frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
+                    frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
             mFrameworkInstallObserver.startWatching();
             scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR
@@ -1547,14 +1554,14 @@ public class PackageManagerService extends IPackageManager.Stub {
             mPrivilegedInstallObserver = new AppDirObserver(
                     privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true);
             mPrivilegedInstallObserver.startWatching();
-                scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
-                        | PackageParser.PARSE_IS_SYSTEM_DIR
-                        | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);
+            scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
+                    | PackageParser.PARSE_IS_SYSTEM_DIR
+                    | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);
 
             // Collect ordinary system packages.
             File systemAppDir = new File(Environment.getRootDirectory(), "app");
             mSystemInstallObserver = new AppDirObserver(
-                systemAppDir.getPath(), OBSERVER_EVENTS, true, false);
+                    systemAppDir.getPath(), OBSERVER_EVENTS, true, false);
             mSystemInstallObserver.startWatching();
             scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
@@ -1567,7 +1574,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                 // failed to look up canonical path, continue with original one
             }
             mVendorInstallObserver = new AppDirObserver(
-                vendorAppDir.getPath(), OBSERVER_EVENTS, true, false);
+                    vendorAppDir.getPath(), OBSERVER_EVENTS, true, false);
             mVendorInstallObserver.startWatching();
             scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
@@ -1825,6 +1832,10 @@ public class PackageManagerService extends IPackageManager.Stub {
     void cleanupInstallFailedPackage(PackageSetting ps) {
         Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
         removeDataDirsLI(ps.name);
+
+        // TODO: try cleaning up codePath directory contents first, since it
+        // might be a cluster
+
         if (ps.codePath != null) {
             if (!ps.codePath.delete()) {
                 Slog.w(TAG, "Unable to remove old code file: " + ps.codePath);
@@ -2071,15 +2082,12 @@ public class PackageManagerService extends IPackageManager.Stub {
                 if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
                     return null;
                 }
-                // App code is gone, so we aren't worried about split paths
+                // Only data remains, so we aren't worried about code paths
                 pkg = new PackageParser.Package(packageName);
                 pkg.applicationInfo.packageName = packageName;
                 pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
-                pkg.applicationInfo.sourceDir = ps.codePathString;
-                pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
                 pkg.applicationInfo.dataDir =
                         getDataPathForPackage(packageName, 0).getPath();
-                pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
                 pkg.applicationInfo.cpuAbi = ps.cpuAbiString;
             }
             return generatePackageInfo(pkg, flags, userId);
@@ -4041,20 +4049,21 @@ public class PackageManagerService extends IPackageManager.Stub {
     private boolean createIdmapForPackagePairLI(PackageParser.Package pkg,
             PackageParser.Package opkg) {
         if (!opkg.mTrustedOverlay) {
-            Slog.w(TAG, "Skipping target and overlay pair " + pkg.codePath + " and " +
-                    opkg.codePath + ": overlay not trusted");
+            Slog.w(TAG, "Skipping target and overlay pair " + pkg.baseCodePath + " and " +
+                    opkg.baseCodePath + ": overlay not trusted");
             return false;
         }
         HashMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
         if (overlaySet == null) {
-            Slog.e(TAG, "was about to create idmap for " + pkg.codePath + " and " +
-                    opkg.codePath + " but target package has no known overlays");
+            Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " +
+                    opkg.baseCodePath + " but target package has no known overlays");
             return false;
         }
         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
         // TODO: generate idmap for split APKs
-        if (mInstaller.idmap(pkg.codePath, opkg.codePath, sharedGid) != 0) {
-            Slog.e(TAG, "Failed to generate idmap for " + pkg.codePath + " and " + opkg.codePath);
+        if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) {
+            Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and "
+                    + opkg.baseCodePath);
             return false;
         }
         PackageParser.Package[] overlayArray =
@@ -4075,8 +4084,8 @@ public class PackageManagerService extends IPackageManager.Stub {
     }
 
     private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
-        String[] files = dir.list();
-        if (files == null) {
+        final File[] files = dir.listFiles();
+        if (ArrayUtils.isEmpty(files)) {
             Log.d(TAG, "No files in app dir " + dir);
             return;
         }
@@ -4086,10 +4095,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                     + " flags=0x" + Integer.toHexString(flags));
         }
 
-        int i;
-        for (i=0; i<files.length; i++) {
-            File file = new File(dir, files[i]);
-            if (!isPackageFilename(files[i])) {
+        for (File file : files) {
+            if (!isPackageFilename(file)) {
                 // Ignore entries which are not apk's
                 continue;
             }
@@ -4258,7 +4265,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                             + ": new version " + pkg.mVersionCode
                             + " better than installed " + ps.versionCode);
 
-                    InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
+                    InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                             ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString,
                             getAppInstructionSetFromSettings(ps));
                     synchronized (mInstallLock) {
@@ -4323,7 +4330,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                     Slog.w(TAG, "Package " + ps.name + " at " + scanFile + "reverting from "
                             + ps.codePathString + ": new version " + pkg.mVersionCode
                             + " better than installed " + ps.versionCode);
-                    InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
+                    InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                             ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString,
                             getAppInstructionSetFromSettings(ps));
                     synchronized (mInstallLock) {
@@ -4343,29 +4350,27 @@ public class PackageManagerService extends IPackageManager.Stub {
             }
         }
 
-        final String codePath = pkg.codePath;
+        final String baseCodePath = pkg.baseCodePath;
         final String[] splitCodePaths = pkg.splitCodePaths;
 
-        String resPath = null;
-        String[] splitResPaths = null;
+        // TODO: extend to support forward-locked splits
+        String baseResPath = null;
         if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
             if (ps != null && ps.resourcePathString != null) {
-                resPath = ps.resourcePathString;
-                splitResPaths = deriveSplitResPaths(pkg.splitCodePaths);
+                baseResPath = ps.resourcePathString;
             } else {
                 // Should not happen at all. Just log an error.
                 Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
             }
         } else {
-            resPath = pkg.codePath;
-            splitResPaths = pkg.splitCodePaths;
+            baseResPath = pkg.baseCodePath;
         }
 
         // Set application objects path explicitly.
-        pkg.applicationInfo.sourceDir = codePath;
-        pkg.applicationInfo.publicSourceDir = resPath;
+        pkg.applicationInfo.sourceDir = baseCodePath;
+        pkg.applicationInfo.publicSourceDir = baseResPath;
         pkg.applicationInfo.splitSourceDirs = splitCodePaths;
-        pkg.applicationInfo.splitPublicSourceDirs = splitResPaths;
+        pkg.applicationInfo.splitPublicSourceDirs = splitCodePaths;
 
         // Note that we invoke the following method only if we are about to unpack an application
         PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
@@ -6419,10 +6424,6 @@ public class PackageManagerService extends IPackageManager.Stub {
         }
     }
 
-    private static final boolean isPackageFilename(String name) {
-        return name != null && name.endsWith(".apk");
-    }
-
     private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) {
         for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
             if (pkgInfo.permissions.get(i).info.name.equals(perm)) {
@@ -7701,8 +7702,13 @@ public class PackageManagerService extends IPackageManager.Stub {
 
         verificationParams.setInstallerUid(uid);
 
+        if (!"file".equals(packageURI.getScheme())) {
+            throw new UnsupportedOperationException("Only file:// URIs are supported");
+        }
+        final File fromFile = new File(packageURI.getPath());
+
         final Message msg = mHandler.obtainMessage(INIT_COPY);
-        msg.obj = new InstallParams(packageURI, observer, observer2, filteredFlags,
+        msg.obj = new InstallParams(fromFile, observer, observer2, filteredFlags,
                 installerPackageName, verificationParams, encryptionParams, user,
                 packageAbiOverride);
         mHandler.sendMessage(msg);
@@ -7820,11 +7826,12 @@ public class PackageManagerService extends IPackageManager.Stub {
         }
     }
 
-    void installStage(String basePackageName, File stageDir, IPackageInstallObserver2 observer,
-            int flags) {
-        // TODO: install stage!
+    void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer2,
+            PackageInstallerParams params, String installerPackageName, int installerUid,
+            UserHandle user) {
+        Slog.e(TAG, "TODO: install stage!");
         try {
-            observer.packageInstalled(basePackageName, null,
+            observer2.packageInstalled(packageName, null,
                     PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
         } catch (RemoteException ignored) {
         }
@@ -8407,30 +8414,38 @@ public class PackageManagerService extends IPackageManager.Stub {
     }
 
     class InstallParams extends HandlerParams {
+        /**
+         * 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.
+         */
+        private final File mFromFile;
+
+        /**
+         * Local copy of {@link #mFromFile}, if generated.
+         */
+        private File mLocalFromFile;
+
         final IPackageInstallObserver observer;
         final IPackageInstallObserver2 observer2;
         int flags;
-
-        private final Uri mPackageURI;
         final String installerPackageName;
         final VerificationParams verificationParams;
         private InstallArgs mArgs;
         private int mRet;
-        private File mTempPackage;
         final ContainerEncryptionParams encryptionParams;
         final String packageAbiOverride;
         final String packageInstructionSetOverride;
 
-        InstallParams(Uri packageURI,
-                IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
-                int flags, String installerPackageName, VerificationParams verificationParams,
-                ContainerEncryptionParams encryptionParams, UserHandle user,
-                String packageAbiOverride) {
+        InstallParams(File fromFile, IPackageInstallObserver observer,
+                IPackageInstallObserver2 observer2, int flags, String installerPackageName,
+                VerificationParams verificationParams, ContainerEncryptionParams encryptionParams,
+                UserHandle user, String packageAbiOverride) {
             super(user);
-            this.mPackageURI = packageURI;
-            this.flags = flags;
+            mFromFile = Preconditions.checkNotNull(fromFile);
             this.observer = observer;
             this.observer2 = observer2;
+            this.flags = flags;
             this.installerPackageName = installerPackageName;
             this.verificationParams = verificationParams;
             this.encryptionParams = encryptionParams;
@@ -8443,7 +8458,7 @@ public class PackageManagerService extends IPackageManager.Stub {
         public String toString() {
             return "InstallParams{"
                 + Integer.toHexString(System.identityHashCode(this))
-                + " " + mPackageURI + "}";
+                + " " + mFromFile + "}";
         }
 
         public ManifestDigest getManifestDigest() {
@@ -8543,76 +8558,55 @@ public class PackageManagerService extends IPackageManager.Stub {
                     Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
                 }
 
-                try {
-                    mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, mPackageURI,
-                            Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
-                    final File packageFile;
-                    if (encryptionParams != null || !"file".equals(mPackageURI.getScheme())) {
-                        mTempPackage = createTempPackageFile(mDrmAppPrivateInstallDir);
-                        if (mTempPackage != null) {
-                            ParcelFileDescriptor out;
-                            try {
-                                out = ParcelFileDescriptor.open(mTempPackage,
-                                        ParcelFileDescriptor.MODE_READ_WRITE);
-                            } catch (FileNotFoundException e) {
-                                out = null;
-                                Slog.e(TAG, "Failed to create temporary file for : " + mPackageURI);
-                            }
-
-                            // Make a temporary file for decryption.
-                            ret = mContainerService
-                                    .copyResource(mPackageURI, encryptionParams, out);
+                if (encryptionParams != null) {
+                    // Make a temporary file for decryption.
+                    mLocalFromFile = createTempPackageFile(mDrmAppPrivateInstallDir);
+                    if (mLocalFromFile != null) {
+                        ParcelFileDescriptor out = null;
+                        try {
+                            out = ParcelFileDescriptor.open(mLocalFromFile,
+                                    ParcelFileDescriptor.MODE_READ_WRITE);
+                            ret = mContainerService.copyResource(mFromFile.getAbsolutePath(),
+                                    encryptionParams, out);
+                        } catch (FileNotFoundException e) {
+                            Slog.e(TAG, "Failed to create temporary file for: " + mFromFile);
+                        } finally {
                             IoUtils.closeQuietly(out);
-
-                            packageFile = mTempPackage;
-
-                            FileUtils.setPermissions(packageFile.getAbsolutePath(),
-                                    FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP
-                                            | FileUtils.S_IROTH,
-                                    -1, -1);
-                        } else {
-                            packageFile = null;
                         }
-                    } else {
-                        packageFile = new File(mPackageURI.getPath());
+
+                        FileUtils.setPermissions(mLocalFromFile, 0644, -1, -1);
                     }
+                }
 
-                    if (packageFile != null) {
-                        // Remote call to find out default install location
-                        final String packageFilePath = packageFile.getAbsolutePath();
-                        pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags,
-                                lowThreshold, packageAbiOverride);
+                // Remote call to find out default install location
+                final String fromPath = getFromFile().getAbsolutePath();
+                pkgLite = mContainerService.getMinimalPackageInfo(fromPath, flags, lowThreshold,
+                        packageAbiOverride);
 
-                        /*
-                         * If we have too little free space, try to free cache
-                         * before giving up.
-                         */
-                        if (pkgLite.recommendedInstallLocation
-                                == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
-                            final long size = mContainerService.calculateInstalledSize(
-                                    packageFilePath, isForwardLocked(), packageAbiOverride);
-                            if (mInstaller.freeCache(size + lowThreshold) >= 0) {
-                                pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath,
-                                        flags, lowThreshold, packageAbiOverride);
-                            }
-                            /*
-                             * The cache free must have deleted the file we
-                             * downloaded to install.
-                             *
-                             * TODO: fix the "freeCache" call to not delete
-                             *       the file we care about.
-                             */
-                            if (pkgLite.recommendedInstallLocation
-                                    == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
-                                pkgLite.recommendedInstallLocation
-                                    = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
-                            }
-                        }
+                /*
+                 * If we have too little free space, try to free cache
+                 * before giving up.
+                 */
+                if (pkgLite.recommendedInstallLocation
+                        == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+                    final long size = mContainerService.calculateInstalledSize(
+                            fromPath, isForwardLocked(), packageAbiOverride);
+                    if (mInstaller.freeCache(size + lowThreshold) >= 0) {
+                        pkgLite = mContainerService.getMinimalPackageInfo(fromPath,
+                                flags, lowThreshold, packageAbiOverride);
+                    }
+                    /*
+                     * The cache free must have deleted the file we
+                     * downloaded to install.
+                     *
+                     * TODO: fix the "freeCache" call to not delete
+                     *       the file we care about.
+                     */
+                    if (pkgLite.recommendedInstallLocation
+                            == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
+                        pkgLite.recommendedInstallLocation
+                            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                     }
-                } finally {
-                    mContext.revokeUriPermission(mPackageURI,
-                            Intent.FLAG_GRANT_READ_URI_PERMISSION);
                 }
             }
 
@@ -8672,9 +8666,10 @@ public class PackageManagerService extends IPackageManager.Stub {
                 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(getPackageUri(), PACKAGE_MIME_TYPE);
+                    verification.setDataAndType(Uri.fromFile(getFromFile()), PACKAGE_MIME_TYPE);
                     verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
                     final List<ResolveInfo> receivers = queryIntentReceivers(verification,
@@ -8802,10 +8797,9 @@ public class PackageManagerService extends IPackageManager.Stub {
             if (mArgs != null) {
                 processPendingInstall(mArgs, mRet);
 
-                if (mTempPackage != null) {
-                    if (!mTempPackage.delete()) {
-                        Slog.w(TAG, "Couldn't delete temporary file: " +
-                                mTempPackage.getAbsolutePath());
+                if (mLocalFromFile != null) {
+                    if (!mLocalFromFile.delete()) {
+                        Slog.w(TAG, "Couldn't delete temporary file: " + mLocalFromFile);
                     }
                 }
             }
@@ -8821,11 +8815,11 @@ public class PackageManagerService extends IPackageManager.Stub {
             return (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
         }
 
-        public Uri getPackageUri() {
-            if (mTempPackage != null) {
-                return Uri.fromFile(mTempPackage);
+        public File getFromFile() {
+            if (mLocalFromFile != null) {
+                return mLocalFromFile;
             } else {
-                return mPackageURI;
+                return mFromFile;
             }
         }
     }
@@ -8856,8 +8850,9 @@ public class PackageManagerService extends IPackageManager.Stub {
             this.packageName = packageName;
             this.uid = uid;
             if (srcArgs != null) {
-                Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath()));
-                targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir, instructionSet);
+                final String codePath = srcArgs.getCodePath();
+                targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName, dataDir,
+                        instructionSet);
             } else {
                 targetArgs = null;
             }
@@ -8958,6 +8953,8 @@ public class PackageManagerService extends IPackageManager.Stub {
     }
 
     private InstallArgs createInstallArgs(InstallParams params) {
+        // TODO: extend to support incoming zero-copy locations
+
         if (installOnSd(params.flags) || params.isForwardLocked()) {
             return new AsecInstallArgs(params);
         } else {
@@ -8965,14 +8962,18 @@ public class PackageManagerService extends IPackageManager.Stub {
         }
     }
 
-    private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath,
-            String nativeLibraryPath, String instructionSet) {
+    /**
+     * Create args that describe an existing installed package. Typically used
+     * when cleaning up old installs, or used as a move source.
+     */
+    private InstallArgs createInstallArgsForExisting(int flags, String codePath,
+            String resourcePath, String nativeLibraryPath, String instructionSet) {
         final boolean isInAsec;
         if (installOnSd(flags)) {
             /* Apps on SD card are always in ASEC containers. */
             isInAsec = true;
         } else if (installForwardLocked(flags)
-                && !fullCodePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) {
+                && !codePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) {
             /*
              * Forward-locked apps are only in ASEC containers if they're the
              * new style
@@ -8983,44 +8984,49 @@ public class PackageManagerService extends IPackageManager.Stub {
         }
 
         if (isInAsec) {
-            return new AsecInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath,
+            return new AsecInstallArgs(codePath, resourcePath, nativeLibraryPath,
                     instructionSet, installOnSd(flags), installForwardLocked(flags));
         } else {
-            return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath,
-                    instructionSet);
+            return new FileInstallArgs(codePath, resourcePath, nativeLibraryPath, instructionSet);
         }
     }
 
-    // Used by package mover
-    private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir,
-            String instructionSet) {
+    private InstallArgs createInstallArgsForMoveTarget(String codePath, int flags, String pkgName,
+            String dataDir, String instructionSet) {
+        final File codeFile = new File(codePath);
         if (installOnSd(flags) || installForwardLocked(flags)) {
-            String cid = getNextCodePath(packageURI.getPath(), pkgName, "/"
+            String cid = getNextCodePath(codePath, pkgName, "/"
                     + AsecInstallArgs.RES_FILE_NAME);
-            return new AsecInstallArgs(packageURI, cid, instructionSet, installOnSd(flags),
+            return new AsecInstallArgs(codeFile, cid, instructionSet, installOnSd(flags),
                     installForwardLocked(flags));
         } else {
-            return new FileInstallArgs(packageURI, pkgName, dataDir, instructionSet);
+            return new FileInstallArgs(codeFile, pkgName, dataDir, instructionSet);
         }
     }
 
     static abstract class InstallArgs {
+        /**
+         * 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 is typically untrusted.
+         */
+        final File fromFile;
+
         final IPackageInstallObserver observer;
         final IPackageInstallObserver2 observer2;
         // Always refers to PackageManager flags only
         final int flags;
-        final Uri packageURI;
         final String installerPackageName;
         final ManifestDigest manifestDigest;
         final UserHandle user;
         final String instructionSet;
         final String abiOverride;
 
-        InstallArgs(Uri packageURI,
-                IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
-                int flags, String installerPackageName, ManifestDigest manifestDigest,
-                UserHandle user, String instructionSet, String abiOverride) {
-            this.packageURI = packageURI;
+        InstallArgs(File fromFile, IPackageInstallObserver observer,
+                IPackageInstallObserver2 observer2, int flags, String installerPackageName,
+                ManifestDigest manifestDigest, UserHandle user, String instructionSet,
+                String abiOverride) {
+            this.fromFile = fromFile;
             this.flags = flags;
             this.observer = observer;
             this.observer2 = observer2;
@@ -9031,24 +9037,23 @@ public class PackageManagerService extends IPackageManager.Stub {
             this.abiOverride = abiOverride;
         }
 
-        abstract void createCopyFile();
         abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
         abstract int doPreInstall(int status);
         abstract boolean doRename(int status, String pkgName, String oldCodePath);
-
         abstract int doPostInstall(int status, int uid);
+
+        /** @see PackageSettingBase#codePathString */
         abstract String getCodePath();
+        /** @see PackageSettingBase#resourcePathString */
         abstract String getResourcePath();
+        /** @see PackageSettingBase#nativeLibraryPathString */
         abstract String getNativeLibraryPath();
+
         // Need installer lock especially for dex file removal.
         abstract void cleanUpResourcesLI();
         abstract boolean doPostDeleteLI(boolean delete);
         abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException;
 
-        String[] getSplitCodePaths() {
-            return null;
-        }
-
         /**
          * Called before the source arguments are copied. This is used mostly
          * for MoveParams when it needs to read the source file to put it in the
@@ -9078,20 +9083,27 @@ public class PackageManagerService extends IPackageManager.Stub {
         }
     }
 
+    /**
+     * Logic to handle installation of non-ASEC applications, including copying
+     * and renaming logic.
+     */
     class FileInstallArgs extends InstallArgs {
+        // TODO: teach about handling cluster directories
+
         File installDir;
         String codeFileName;
         String resourceFileName;
         String libraryPath;
         boolean created = false;
 
+        /** New install */
         FileInstallArgs(InstallParams params) {
-            super(params.getPackageUri(), params.observer, params.observer2, params.flags,
-                    params.installerPackageName, params.getManifestDigest(),
-                    params.getUser(), params.packageInstructionSetOverride,
-                    params.packageAbiOverride);
+            super(params.getFromFile(), params.observer, params.observer2, params.flags,
+                    params.installerPackageName, params.getManifestDigest(), params.getUser(),
+                    params.packageInstructionSetOverride, params.packageAbiOverride);
         }
 
+        /** Existing install */
         FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
                 String instructionSet) {
             super(null, null, null, 0, null, null, null, instructionSet, null);
@@ -9102,8 +9114,9 @@ public class PackageManagerService extends IPackageManager.Stub {
             libraryPath = nativeLibraryPath;
         }
 
-        FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) {
-            super(packageURI, null, null, 0, null, null, null, instructionSet, null);
+        /** New install from existing */
+        FileInstallArgs(File fromFile, String pkgName, String dataDir, String instructionSet) {
+            super(fromFile, null, null, 0, null, null, null, instructionSet, null);
             installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
             String apkName = getNextCodePath(null, pkgName, ".apk");
             codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -9128,13 +9141,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                 lowThreshold = dsm.getMemoryLowThreshold();
             }
 
-            try {
-                mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
-                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
-                return imcs.checkInternalFreeStorage(packageURI, isFwdLocked(), lowThreshold);
-            } finally {
-                mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            }
+            return imcs.checkInternalFreeStorage(fromFile.getAbsolutePath(), isFwdLocked(),
+                    lowThreshold);
         }
 
         void createCopyFile() {
@@ -9175,12 +9183,9 @@ public class PackageManagerService extends IPackageManager.Stub {
             // Copy the resource now
             int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
             try {
-                mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
-                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
-                ret = imcs.copyResource(packageURI, null, out);
+                ret = imcs.copyResource(fromFile.getAbsolutePath(), null, out);
             } finally {
                 IoUtils.closeQuietly(out);
-                mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
             }
 
             if (isFwdLocked()) {
@@ -9432,7 +9437,13 @@ public class PackageManagerService extends IPackageManager.Stub {
         return subStr1.substring(sidx+1, eidx);
     }
 
+    /**
+     * Logic to handle installation of ASEC applications, including copying and
+     * renaming logic.
+     */
     class AsecInstallArgs extends InstallArgs {
+        // TODO: teach about handling cluster directories
+
         static final String RES_FILE_NAME = "pkg.apk";
         static final String PUBLIC_RES_FILE_NAME = "res.zip";
 
@@ -9441,13 +9452,14 @@ public class PackageManagerService extends IPackageManager.Stub {
         String resourcePath;
         String libraryPath;
 
+        /** New install */
         AsecInstallArgs(InstallParams params) {
-            super(params.getPackageUri(), params.observer, params.observer2, params.flags,
-                    params.installerPackageName, params.getManifestDigest(),
-                    params.getUser(), params.packageInstructionSetOverride,
-                    params.packageAbiOverride);
+            super(params.getFromFile(), params.observer, params.observer2, params.flags,
+                    params.installerPackageName, params.getManifestDigest(), params.getUser(),
+                    params.packageInstructionSetOverride, params.packageAbiOverride);
         }
 
+        /** Existing install */
         AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
                 String instructionSet, boolean isExternal, boolean isForwardLocked) {
             super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
@@ -9469,9 +9481,10 @@ public class PackageManagerService extends IPackageManager.Stub {
             setCachePath(PackageHelper.getSdDir(cid));
         }
 
-        AsecInstallArgs(Uri packageURI, String cid, String instructionSet,
+        /** New install from existing */
+        AsecInstallArgs(File fromFile, String cid, String instructionSet,
                 boolean isExternal, boolean isForwardLocked) {
-            super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
+            super(fromFile, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
                     null, null, null, instructionSet, null);
             this.cid = cid;
@@ -9482,13 +9495,8 @@ public class PackageManagerService extends IPackageManager.Stub {
         }
 
         boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
-            try {
-                mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
-                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
-                return imcs.checkExternalFreeStorage(packageURI, isFwdLocked(), abiOverride);
-            } finally {
-                mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            }
+            return imcs.checkExternalFreeStorage(fromFile.getAbsolutePath(), isFwdLocked(),
+                    abiOverride);
         }
 
         private final boolean isExternal() {
@@ -9506,16 +9514,9 @@ public class PackageManagerService extends IPackageManager.Stub {
                 PackageHelper.destroySdDir(cid);
             }
 
-            final String newCachePath;
-            try {
-                mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
-                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
-                newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(),
-                        RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked(),
-                        abiOverride);
-            } finally {
-                mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            }
+            final String newCachePath = imcs.copyResourceToContainer(fromFile.getAbsolutePath(),
+                    cid, getEncryptKey(), RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(),
+                    isFwdLocked(), abiOverride);
 
             if (newCachePath != null) {
                 setCachePath(newCachePath);
@@ -10078,7 +10079,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                 // We didn't need to disable the .apk as a current system package,
                 // which means we are replacing another update that is already
                 // installed.  We need to make sure to delete the older one's .apk.
-                res.removedInfo.args = createInstallArgs(0,
+                res.removedInfo.args = createInstallArgsForExisting(0,
                         deletedPackage.applicationInfo.sourceDir,
                         deletedPackage.applicationInfo.publicSourceDir,
                         deletedPackage.applicationInfo.nativeLibraryDir,
@@ -10143,7 +10144,7 @@ public class PackageManagerService extends IPackageManager.Stub {
         // TODO: extend to move split APK dex files
         if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
             final String instructionSet = getAppInstructionSet(newPackage.applicationInfo);
-            int retCode = mInstaller.movedex(oldCodePath, newPackage.codePath,
+            int retCode = mInstaller.movedex(oldCodePath, newPackage.baseCodePath,
                                              instructionSet);
             if (retCode != 0) {
                 /*
@@ -10156,7 +10157,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                  */
                 newPackage.mDexOptNeeded = true;
                 mInstaller.rmdex(oldCodePath, instructionSet);
-                mInstaller.rmdex(newPackage.codePath, instructionSet);
+                mInstaller.rmdex(newPackage.baseCodePath, instructionSet);
             }
         }
         return PackageManager.INSTALL_SUCCEEDED;
@@ -10221,8 +10222,7 @@ public class PackageManagerService extends IPackageManager.Stub {
         }
     }
 
-    private void installPackageLI(InstallArgs args,
-            boolean newInstall, PackageInstalledInfo res) {
+    private void installPackageLI(InstallArgs args, boolean newInstall, PackageInstalledInfo res) {
         int pFlags = args.flags;
         String installerPackageName = args.installerPackageName;
         File tmpPackageFile = new File(args.getCodePath());
@@ -10367,14 +10367,18 @@ public class PackageManagerService extends IPackageManager.Stub {
             res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
             return;
         }
+
         // Set application objects path explicitly after the rename
+        // TODO: derive split paths from original scan after rename
         pkg.codePath = args.getCodePath();
+        pkg.baseCodePath = args.getCodePath();
+        pkg.splitCodePaths = null;
         pkg.applicationInfo.sourceDir = args.getCodePath();
         pkg.applicationInfo.publicSourceDir = args.getResourcePath();
-        pkg.applicationInfo.splitSourceDirs = args.getSplitCodePaths();
-        pkg.applicationInfo.splitPublicSourceDirs = deriveSplitResPaths(
-                pkg.applicationInfo.splitSourceDirs);
+        pkg.applicationInfo.splitSourceDirs = null;
+        pkg.applicationInfo.splitPublicSourceDirs = null;
         pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
+
         if (replace) {
             replacePackageLI(pkg, parseFlags, scanMode, args.user,
                     installerPackageName, res, args.abiOverride);
@@ -10847,8 +10851,8 @@ public class PackageManagerService extends IPackageManager.Stub {
 
         // Delete application code and resources
         if (deleteCodeAndResources && (outInfo != null)) {
-            outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString,
-                    ps.resourcePathString, ps.nativeLibraryPathString,
+            outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
+                    ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString,
                     getAppInstructionSetFromSettings(ps));
         }
         return true;
@@ -11253,7 +11257,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                 publicSrcDir = applicationInfo.publicSourceDir;
             }
         }
-        int res = mInstaller.getSizeInfo(packageName, userHandle, p.codePath, libDirPath,
+        // TODO: extend to measure size of split APKs
+        int res = mInstaller.getSizeInfo(packageName, userHandle, p.baseCodePath, libDirPath,
                 publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps),
                 pStats);
         if (res < 0) {
@@ -12843,9 +12848,9 @@ public class PackageManagerService extends IPackageManager.Stub {
             } else {
                 Message msg = mHandler.obtainMessage(INIT_COPY);
                 final String instructionSet = getAppInstructionSet(pkg.applicationInfo);
-                InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
-                        pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir,
-                        instructionSet);
+                InstallArgs srcArgs = createInstallArgsForExisting(currFlags,
+                        pkg.applicationInfo.sourceDir, pkg.applicationInfo.publicSourceDir,
+                        pkg.applicationInfo.nativeLibraryDir, instructionSet);
                 MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
                         pkg.applicationInfo.dataDir, instructionSet, pkg.applicationInfo.uid, user);
                 msg.obj = mp;
@@ -12943,11 +12948,13 @@ public class PackageManagerService extends IPackageManager.Stub {
 
                                     if (returnCode == PackageManager.MOVE_SUCCEEDED) {
                                         pkg.codePath = newCodePath;
+                                        pkg.baseCodePath = newCodePath;
                                         // Move dex files around
                                         if (moveDexFilesLI(oldCodePath, pkg) != PackageManager.INSTALL_SUCCEEDED) {
                                             // Moving of dex files failed. Set
                                             // error code and abort move.
                                             pkg.codePath = oldCodePath;
+                                            pkg.baseCodePath = oldCodePath;
                                             returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
                                         }
                                     }
index 7fee372..2091c16 100644 (file)
@@ -45,6 +45,12 @@ class PackageSettingBase extends GrantedPermissions {
 
     final String name;
     final String realName;
+
+    /**
+     * Path where this package was found on disk. For monolithic packages
+     * this is path to single base APK file; for cluster packages this is
+     * path to the cluster directory.
+     */
     File codePath;
     String codePathString;
     File resourcePath;