Merge "AppsOnSd feature - Add default container"
diff --git a/Android.mk b/Android.mk
index 568352e..5f9bea4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -136,6 +136,7 @@
 	core/java/android/speech/tts/ITtsCallback.aidl \
 	core/java/com/android/internal/app/IBatteryStats.aidl \
 	core/java/com/android/internal/app/IUsageStats.aidl \
+	core/java/com/android/internal/app/IMediaContainerService.aidl \
 	core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
 	core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
 	core/java/com/android/internal/backup/IBackupTransport.aidl \
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index 5db5545..555c19e 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -33,6 +33,7 @@
     }
 
     x = pkgname;
+    int alpha = -1;
     while (*x) {
         if (isalnum(*x) || (*x == '_')) {
                 /* alphanumeric or underscore are fine */
@@ -42,13 +43,28 @@
                 LOGE("invalid package name '%s'\n", pkgname);
                 return -1;
             }
-        } else {
+        } else if (*x == '-') {
+            /* Suffix -X is fine to let versioning of packages.
+               But whatever follows should be alphanumeric.*/
+            alpha = 1;
+        }else {
                 /* anything not A-Z, a-z, 0-9, _, or . is invalid */
             LOGE("invalid package name '%s'\n", pkgname);
             return -1;
         }
         x++;
     }
+    if (alpha == 1) {
+        // Skip current character
+        x++;
+        while (*x) {
+            if (!isalnum(*x)) {
+                LOGE("invalid package name '%s' should include only numbers after -\n", pkgname);
+                return -1;
+            }
+            x++;
+        }
+    }
 
     sprintf(path, "%s%s%s", prefix, pkgname, postfix);
     return 0;
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
new file mode 100755
index 0000000..726e28f
--- /dev/null
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+
+interface IMediaContainerService {
+    String copyResourceToContainer(in Uri packageURI,
+                String containerId,
+                String key, String resFileName);
+    boolean copyResource(in Uri packageURI,
+                in ParcelFileDescriptor outStream);
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ac6467d..54e15a5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1202,6 +1202,16 @@
         android:description="@string/permdesc_cache_filesystem"
         android:protectionLevel="signatureOrSystem" />
 
+    <!-- Must be required by default container service so that only
+         the system can bind to it and use it to copy
+         protected data to secure containers or files
+         accessible to the system.
+         @hide -->
+    <permission android:name="android.permission.COPY_PROTECTED_DATA"
+        android:label="@string/permlab_copyProtectedData"
+        android:description="@string/permlab_copyProtectedData"
+        android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8c46f58..31f71d3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2102,6 +2102,12 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_pkgUsageStats">Allows the modification of collected component usage statistics. Not for use by normal applications.</string>
 
+    <!-- permission attributes related to default container service -->
+    <!-- Title of an application permission that lets an application use default container service. -->
+    <string name="permlab_copyProtectedData">Allows to invoke default container service to copy content. Not for use by normal applications.</string>
+    <!-- Description of an application permission,  used to invoke default container service to copy content. -->
+    <string name="permdesc_copyProtectedData">Allows to invoke default container service to copy content. Not for use by normal applications.</string>
+
     <!-- Shown in the tutorial for tap twice for zoom control. -->
     <string name="tutorial_double_tap_to_zoom_message_short">Tap twice for zoom control</string>
 
diff --git a/packages/DefaultContainerService/Android.mk b/packages/DefaultContainerService/Android.mk
new file mode 100755
index 0000000..2f1d6ab
--- /dev/null
+++ b/packages/DefaultContainerService/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := DefaultContainerService
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
new file mode 100755
index 0000000..fd77148
--- /dev/null
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.defcontainer">
+    <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/>
+
+    <application android:process="def.container.service"
+                 android:label="@string/service_name">
+
+        <service android:name=".DefaultContainerService"
+                 android:enabled="true"
+                 android:exported="true"
+                 android:permission="android.permission.COPY_PROTECTED_DATA"/>
+    </application>
+
+</manifest>
diff --git a/packages/DefaultContainerService/res/values/strings.xml b/packages/DefaultContainerService/res/values/strings.xml
new file mode 100644
index 0000000..2897f34
--- /dev/null
+++ b/packages/DefaultContainerService/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- service name  -->
+    <string name="service_name">Media Container Service</string>
+</resources>
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
new file mode 100644
index 0000000..d36207b
--- /dev/null
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -0,0 +1,305 @@
+package com.android.defcontainer;
+
+import com.android.internal.app.IMediaContainerService;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.IMountService;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.app.Service;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import android.os.FileUtils;
+
+
+/*
+ * This service copies a downloaded apk to a file passed in as
+ * a ParcelFileDescriptor or to a newly created container specified
+ * by parameters. The DownloadManager gives access to this process
+ * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER
+ * permission to access apks downloaded via the download manager.
+ */
+public class DefaultContainerService extends Service {
+    private static final String TAG = "DefContainer";
+    private static final boolean localLOGV = false;
+
+    private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
+        /*
+         * Creates a new container and copies resource there.
+         * @param paackageURI the uri of resource to be copied. Can be either
+         * a content uri or a file uri
+         * @param containerId the id of the secure container that should
+         * be used for creating a secure container into which the resource
+         * will be copied.
+         * @param key Refers to key used for encrypting the secure container
+         * @param resFileName Name of the target resource file(relative to newly
+         * created secure container)
+         * @return Returns the new cache path where the resource has been copied into
+         *
+         */
+        public String copyResourceToContainer(final Uri packageURI,
+                final String containerId,
+                final String key, final String resFileName) {
+            if (packageURI == null || containerId == null) {
+                return null;
+            }
+            return copyResourceInner(packageURI, containerId, key, resFileName);
+        }
+
+        /*
+         * Copy specified resource to output stream
+         * @param packageURI the uri of resource to be copied. Should be a
+         * file uri
+         * @param outStream Remote file descriptor to be used for copying
+         * @return Returns true if copy succeded or false otherwise.
+         */
+        public boolean copyResource(final Uri packageURI,
+                ParcelFileDescriptor outStream) {
+            if (packageURI == null ||  outStream == null) {
+                return false;
+            }
+            ParcelFileDescriptor.AutoCloseOutputStream
+            autoOut = new ParcelFileDescriptor.AutoCloseOutputStream(outStream);
+            return copyFile(packageURI, autoOut);
+        }
+    };
+
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    private IMountService getMountService() {
+        return IMountService.Stub.asInterface(ServiceManager.getService("mount"));
+    }
+
+    private String copyResourceInner(Uri packageURI, String newCacheId, String key, String resFileName) {
+        // Create new container at newCachePath
+        String codePath = packageURI.getPath();
+        String newCachePath = null;
+        final int CREATE_FAILED = 1;
+        final int COPY_FAILED = 2;
+        final int FINALIZE_FAILED = 3;
+        final int PASS = 4;
+        int errCode = CREATE_FAILED;
+        // Create new container
+        if ((newCachePath = createSdDir(packageURI, newCacheId, key)) != null) {
+            File resFile = new File(newCachePath, resFileName);
+            errCode = COPY_FAILED;
+            if (localLOGV) Log.i(TAG, "Trying to copy " + codePath + " to " + resFile);
+            // Copy file from codePath
+            if (FileUtils.copyFile(new File(codePath), resFile)) {
+                errCode = FINALIZE_FAILED;
+                if (finalizeSdDir(newCacheId)) {
+                    errCode = PASS;
+                }
+            }
+        }
+        // Print error based on errCode
+        String errMsg = "";
+        switch (errCode) {
+            case CREATE_FAILED:
+                errMsg = "CREATE_FAILED";
+                break;
+            case COPY_FAILED:
+                errMsg = "COPY_FAILED";
+                destroySdDir(newCacheId);
+                break;
+            case FINALIZE_FAILED:
+                errMsg = "FINALIZE_FAILED";
+                destroySdDir(newCacheId);
+                break;
+            default:
+                errMsg = "PASS";
+                unMountSdDir(newCacheId);
+                break;
+        }
+        Log.i(TAG, "Status: " + errMsg);
+        if (errCode != PASS) {
+            return null;
+        }
+        return newCachePath;
+    }
+
+    private String createSdDir(final Uri packageURI,
+            String containerId, String sdEncKey) {
+        File tmpPackageFile = new File(packageURI.getPath());
+        // Create mount point via MountService
+        IMountService mountService = getMountService();
+        long len = tmpPackageFile.length();
+        int mbLen = (int) (len/(1024*1024));
+        if ((len - (mbLen * 1024 * 1024)) > 0) {
+            mbLen++;
+        }
+        if (localLOGV) Log.i(TAG, "mbLen="+mbLen);
+        String cachePath = null;
+        int ownerUid = Process.myUid();
+        try {
+            cachePath = mountService.createSecureContainer(containerId,
+                mbLen,
+                "vfat", sdEncKey, ownerUid);
+            if (localLOGV) Log.i(TAG, "Trying to create secure container for  "
+                    + containerId + ", cachePath =" + cachePath);
+            return cachePath;
+        } catch(IllegalStateException e) {
+            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
+        } catch(RemoteException e) {
+            Log.e(TAG, "MounteService not running?");
+            return null;
+        }
+        // TODO just fail here and let the user delete later on.
+        try {
+            mountService.destroySecureContainer(containerId);
+            if (localLOGV) Log.i(TAG, "Destroying cache for " + containerId
+                    + ", cachePath =" + cachePath);
+        } catch(IllegalStateException e) {
+            Log.e(TAG, "Failed to destroy existing cache: " + e);
+            return null;
+        } catch(RemoteException e) {
+            Log.e(TAG, "MounteService not running?");
+            return null;
+        }
+       try {
+            cachePath = mountService.createSecureContainer(containerId,
+                mbLen,
+                "vfat", sdEncKey, ownerUid);
+            if (localLOGV) Log.i(TAG, "Trying to install again " + containerId
+                   + ", cachePath =" + cachePath);
+            return cachePath;
+        } catch(IllegalStateException e) {
+            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
+        } catch(RemoteException e) {
+            Log.e(TAG, "MounteService not running?");
+        }
+        return null;
+    }
+
+    private boolean destroySdDir(String containerId) {
+        try {
+            // We need to destroy right away
+            getMountService().destroySecureContainer(containerId);
+            return true;
+        } catch (IllegalStateException e) {
+            Log.i(TAG, "Failed to destroy container : " + containerId);
+        } catch(RemoteException e) {
+            Log.e(TAG, "MounteService not running?");
+        }
+        return false;
+    }
+
+    private boolean finalizeSdDir(String containerId){
+        try {
+            getMountService().finalizeSecureContainer(containerId);
+            return true;
+        } catch (IllegalStateException e) {
+            Log.i(TAG, "Failed to finalize container for pkg : " + containerId);
+        } catch(RemoteException e) {
+            Log.e(TAG, "MounteService not running?");
+        }
+        return false;
+    }
+
+    private boolean unMountSdDir(String containerId) {
+        try {
+            getMountService().unmountSecureContainer(containerId);
+            return true;
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "Failed to unmount id:  " + containerId + " with exception " + e);
+        } catch(RemoteException e) {
+            Log.e(TAG, "MounteService not running?");
+        }
+        return false;
+    }
+
+    private String mountSdDir(String containerId, String key) {
+        try {
+            return getMountService().mountSecureContainer(containerId, key, Process.myUid());
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "Failed to mount id: " +
+                    containerId + " with exception " + e);
+        } catch(RemoteException e) {
+            Log.e(TAG, "MounteService not running?");
+        }
+        return null;
+    }
+
+    public static boolean copyToFile(InputStream inputStream, FileOutputStream out) {
+        try {
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+            while ((bytesRead = inputStream.read(buffer)) >= 0) {
+                out.write(buffer, 0, bytesRead);
+            }
+            return true;
+        } catch (IOException e) {
+            Log.i(TAG, "Exception : " + e + " when copying file");
+            return false;
+        }
+    }
+
+    public static boolean copyToFile(File srcFile, FileOutputStream out) {
+        InputStream inputStream = null;
+        try {
+            inputStream = new FileInputStream(srcFile);
+            return copyToFile(inputStream, out);
+        } catch (IOException e) {
+            return false;
+        } finally {
+            try { if (inputStream != null) inputStream.close(); } catch (IOException e) {}
+        }
+    }
+
+    private  boolean copyFile(Uri pPackageURI, FileOutputStream outStream) {
+        if (pPackageURI.getScheme().equals("file")) {
+            final File srcPackageFile = new File(pPackageURI.getPath());
+            // We copy the source package file to a temp file and then rename it to the
+            // destination file in order to eliminate a window where the package directory
+            // scanner notices the new package file but it's not completely copied yet.
+            if (!copyToFile(srcPackageFile, outStream)) {
+                Log.e(TAG, "Couldn't copy file: " + srcPackageFile);
+                return false;
+            }
+        } else if (pPackageURI.getScheme().equals("content")) {
+            ParcelFileDescriptor fd = null;
+            try {
+                fd = getContentResolver().openFileDescriptor(pPackageURI, "r");
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e);
+                return false;
+            }
+            if (fd == null) {
+                Log.e(TAG, "Couldn't open file descriptor from download service (null).");
+                return false;
+            } else {
+                if (localLOGV) {
+                    Log.v(TAG, "Opened file descriptor from download service.");
+                }
+                ParcelFileDescriptor.AutoCloseInputStream
+                dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd);
+                // We copy the source package file to a temp file and then rename it to the
+                // destination file in order to eliminate a window where the package directory
+                // scanner notices the new package file but it's not completely copied yet.
+                if (!copyToFile(dlStream, outStream)) {
+                    Log.e(TAG, "Couldn't copy " + pPackageURI + " to temp file.");
+                    return false;
+                }
+            }
+        } else {
+            Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java
index 6a7d432..88b1f02 100644
--- a/services/java/com/android/server/Installer.java
+++ b/services/java/com/android/server/Installer.java
@@ -263,10 +263,15 @@
         return execute(builder.toString());
     }
 
-    public int setForwardLockPerm(String packageName, int gid) {
+    /*
+     * @param packagePathSuffix The name of the path relative to install
+     * directory. Say if the path name is /data/app/com.test-1.apk,
+     * the package suffix path will be com.test-1
+     */
+    public int setForwardLockPerm(String packagePathSuffix, int gid) {
         StringBuilder builder = new StringBuilder("protect");
         builder.append(' ');
-        builder.append(packageName);
+        builder.append(packagePathSuffix);
         builder.append(' ');
         builder.append(gid);
         return execute(builder.toString());
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1307972..1cf3bad 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import com.android.internal.app.IMediaContainerService;
 import com.android.internal.app.ResolverActivity;
 import com.android.common.FastXmlSerializer;
 import com.android.common.XmlUtils;
@@ -31,6 +32,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.ServiceConnection;
 import android.content.IntentSender.SendIntentException;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -62,6 +64,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
@@ -283,26 +286,79 @@
     final HashMap<String, ArrayList<String>> mPendingBroadcasts
             = new HashMap<String, ArrayList<String>>();
     static final int SEND_PENDING_BROADCAST = 1;
-    static final int DESTROY_SD_CONTAINER = 2;
+    static final int MCS_BOUND = 3;
+    static final int END_COPY = 4;
+    static final int INIT_COPY = 5;
+    static final int MCS_UNBIND = 6;
     // Delay time in millisecs
     static final int BROADCAST_DELAY = 10 * 1000;
-    static final int DESTROY_SD_CONTAINER_DELAY = 30 * 1000;
+    private ServiceConnection mDefContainerConn = new ServiceConnection() {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            IMediaContainerService imcs =
+                IMediaContainerService.Stub.asInterface(service);
+            Message msg = mHandler.obtainMessage(MCS_BOUND, imcs);
+            mHandler.sendMessage(msg);
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+        }
+    };
 
     class PackageHandler extends Handler {
+        final ArrayList<InstallArgs> mPendingInstalls =
+            new ArrayList<InstallArgs>();
+        // Service Connection to remote media container service to copy
+        // package uri's from external media onto secure containers
+        // or internal storage.
+        private IMediaContainerService mContainerService = null;
+
         PackageHandler(Looper looper) {
             super(looper);
         }
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case DESTROY_SD_CONTAINER:
-                    String pkgName = (String) msg.obj;
-                    if (pkgName != null) {
-                        // Too bad we cannot handle the errors from destroying the containers.
-                        if (!destroySdDir(pkgName)) {
-                            Log.e(TAG, "Failed to destroy container for pkg : " + pkgName);
+                case INIT_COPY: {
+                    InstallArgs args = (InstallArgs) msg.obj;
+                    args.createCopyFile();
+                    Intent service = new Intent().setComponent(new ComponentName(
+                            "com.android.defcontainer",
+                            "com.android.defcontainer.DefaultContainerService"));
+                    if (mContainerService != null) {
+                        // No need to add to pending list. Use remote stub directly
+                        handleStartCopy(args);
+                    } else {
+                        if (mContext.bindService(service, mDefContainerConn,
+                                Context.BIND_AUTO_CREATE)) {
+                            mPendingInstalls.add(args);
+                        } else {
+                            Log.e(TAG, "Failed to bind to media container service");
+                            // Indicate install failure TODO add new error code
+                            processPendingInstall(args,
+                                    PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE);
                         }
                     }
                     break;
+                }
+                case MCS_BOUND: {
+                    // Initialize mContainerService if needed.
+                    if (msg.obj != null) {
+                        mContainerService = (IMediaContainerService) msg.obj;
+                    }
+                    if (mPendingInstalls.size() > 0) {
+                        InstallArgs args = mPendingInstalls.remove(0);
+                        if (args != null) {
+                            handleStartCopy(args);
+                        }
+                    }
+                    break;
+                }
+                case MCS_UNBIND : {
+                    if (mPendingInstalls.size() == 0) {
+                        mContext.unbindService(mDefContainerConn);
+                        mContainerService = null;
+                    }
+                    break;
+                }
                 case SEND_PENDING_BROADCAST : {
                     String packages[];
                     ArrayList components[];
@@ -343,7 +399,37 @@
                 }
             }
         }
+
+        // Utility method to initiate copying apk via media
+        // container service.
+        private void handleStartCopy(InstallArgs args) {
+            int ret = PackageManager.INSTALL_SUCCEEDED;
+            if (mContainerService == null) {
+                // Install error
+                ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            } else {
+                ret = args.copyApk(mContainerService);
+            }
+            mHandler.sendEmptyMessage(MCS_UNBIND);
+            processPendingInstall(args, ret);
+        }
     }
+
+    static boolean installOnSd(int flags) {
+        if (((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) ||
+                ((flags & PackageManager.INSTALL_ON_SDCARD) == 0)) {
+            return false;
+        }
+        return true;
+    }
+
+    static boolean isFwdLocked(int flags) {
+        if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
+            return true;
+        }
+        return false;
+    }
+
     public static final IPackageManager main(Context context, boolean factoryTest) {
         PackageManagerService m = new PackageManagerService(context, factoryTest);
         ServiceManager.addService("package", m);
@@ -2022,11 +2108,12 @@
             int parseFlags,
             int scanMode) {
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
+        String scanPath = scanFile.getPath();
         parseFlags |= mDefParseFlags;
-        PackageParser pp = new PackageParser(scanFile.getPath());
+        PackageParser pp = new PackageParser(scanPath);
         pp.setSeparateProcesses(mSeparateProcesses);
         final PackageParser.Package pkg = pp.parsePackage(scanFile,
-                scanFile.getPath(),
+                scanPath,
                 mMetrics, parseFlags);
         if (pkg == null) {
             mLastScanError = pp.getParseError();
@@ -2066,7 +2153,8 @@
                         // Just remove the loaded entries from package lists.
                         mPackages.remove(ps.name);
                     }
-                    deletePackageResourcesLI(ps.name, ps.codePathString, ps.resourcePathString);
+                    InstallArgs args = new FileInstallArgs(ps.codePathString, ps.resourcePathString);
+                    args.cleanUpResourcesLI();
                     mSettings.enableSystemPackageLP(ps.name);
                 }
             }
@@ -2077,6 +2165,18 @@
         if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
             parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
         }
+
+        if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) {
+            if (ps != null && ps.resourcePathString != null) {
+                pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
+            } else {
+                // Should not happen at all. Just log an error.
+                Log.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
+            }
+        } else {
+            pkg.applicationInfo.publicSourceDir = pkg.mScanPath;
+        }
+        pkg.applicationInfo.sourceDir = pkg.mScanPath;
         // Note that we invoke the following method only if we are about to unpack an application
         return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
     }
@@ -2230,41 +2330,8 @@
         }
 
         // Initialize package source and resource directories
-        File destResourceFile = null;
-        File destCodeFile = null;
-        if ((scanMode & SCAN_NO_PATHS) == 0) {
-            boolean fwdLocked = (parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0;
-            final String pkgFileName = pkgName + ".apk";
-            File destDir = null;
-
-            if (fwdLocked) {
-                destDir = mDrmAppPrivateInstallDir;
-                destResourceFile = new File(mAppInstallDir, pkgName + ".zip");
-            } else {
-                boolean onSd = (parseFlags & PackageParser.PARSE_ON_SDCARD) != 0;
-                if (!onSd) {
-                    destDir = mAppInstallDir;
-                } else {
-                    String cachePath = getSdDir(pkgName);
-                    if (cachePath == null) {
-                        Log.e(TAG, "Secure container path for pkg: " + pkgName + " at location: " + cachePath +
-                                " not found");
-                        mLastScanError = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
-                        return null;
-                    }
-                    destDir = new File(cachePath);
-                }
-                destResourceFile = new File(destDir, pkgFileName);
-            }
-            destCodeFile = new File(destDir, pkgFileName);
-            pkg.mPath = destCodeFile.getAbsolutePath();
-        } else {
-            pkg.mPath = pkg.mScanPath;
-            destCodeFile = new File(pkg.mScanPath);
-            destResourceFile = new File(pkg.mScanPath);
-        }
-        pkg.applicationInfo.sourceDir = destCodeFile.getAbsolutePath();
-        pkg.applicationInfo.publicSourceDir = destResourceFile.getAbsolutePath();
+        File destCodeFile = new File(pkg.applicationInfo.sourceDir);
+        File destResourceFile = new File(pkg.applicationInfo.publicSourceDir);
 
         SharedUserSetting suid = null;
         PackageSetting pkgSetting = null;
@@ -2568,21 +2635,17 @@
 
         // We don't expect installation to fail beyond this point,
         if ((scanMode&SCAN_MONITOR) != 0) {
-            pkg.mPath = destCodeFile.getAbsolutePath();
             mAppDirs.put(pkg.mPath, pkg);
         }
 
         // Request the ActivityManager to kill the process(only for existing packages)
         // so that we do not end up in a confused state while the user is still using the older
         // version of the application while the new one gets installed.
-        IActivityManager am = ActivityManagerNative.getDefault();
-        if ((am != null) && ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING ) != 0)) {
-            try {
-                am.killApplicationWithUid(pkg.applicationInfo.packageName,
+        if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING ) != 0) {
+            killApplication(pkg.applicationInfo.packageName,
                         pkg.applicationInfo.uid);
-            } catch (RemoteException e) {
-            }
         }
+
         synchronized (mPackages) {
             // Add the new setting to mSettings
             mSettings.insertPackageSettingLP(pkgSetting, pkg);
@@ -2835,6 +2898,19 @@
         return pkg;
     }
 
+    private void killApplication(String pkgName, int uid) {
+        // Request the ActivityManager to kill the process(only for existing packages)
+        // so that we do not end up in a confused state while the user is still using the older
+        // version of the application while the new one gets installed.
+        IActivityManager am = ActivityManagerNative.getDefault();
+        if (am != null) {
+            try {
+                am.killApplicationWithUid(pkgName, uid);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     // The following constants are returned by cachePackageSharedLibsForAbiLI
     // to indicate if native shared libraries were found in the package.
     // Values are:
@@ -3866,26 +3942,51 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.INSTALL_PACKAGES, null);
 
+        Message msg = mHandler.obtainMessage(INIT_COPY);
+        msg.obj = createInstallArgs(packageURI, observer, flags, installerPackageName);
+        mHandler.sendMessage(msg);
+    }
+
+    private InstallArgs createInstallArgs(Uri packageURI, IPackageInstallObserver observer,
+            int flags, String installerPackageName) {
+        if (installOnSd(flags)) {
+            return new SdInstallArgs(packageURI, observer, flags,
+                    installerPackageName);
+        } else {
+            return new FileInstallArgs(packageURI, observer, flags,
+                    installerPackageName);
+        }
+    }
+
+    private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath) {
+        if (installOnSd(flags)) {
+            return new SdInstallArgs(fullCodePath, fullResourcePath);
+        } else {
+            return new FileInstallArgs(fullCodePath, fullResourcePath);
+        }
+    }
+
+    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
         // Queue up an async operation since the package installation may take a little while.
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
                  // Result object to be returned
                 PackageInstalledInfo res = new PackageInstalledInfo();
-                res.returnCode = PackageManager.INSTALL_SUCCEEDED;
+                res.returnCode = currentStatus;
                 res.uid = -1;
                 res.pkg = null;
                 res.removedInfo = new PackageRemovedInfo();
-                // Make a temporary copy of file from given packageURI
-                File tmpPackageFile = copyTempInstallFile(packageURI, res);
-                if (tmpPackageFile != null) {
+                args.doPreInstall(res.returnCode);
+                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                     synchronized (mInstallLock) {
-                        installPackageLI(packageURI, flags, true, installerPackageName, tmpPackageFile, res);
+                        installPackageLI(args, true, res);
                     }
+                    args.doPostInstall(res.returnCode);
                 }
-                if (observer != null) {
+                if (args.observer != null) {
                     try {
-                        observer.packageInstalled(res.name, res.returnCode);
+                        args.observer.packageInstalled(res.name, res.returnCode);
                     } catch (RemoteException e) {
                         Log.i(TAG, "Observer no longer exists.");
                     }
@@ -3908,12 +4009,403 @@
                                 res.pkg.applicationInfo.packageName,
                                 extras);
                     }
+                    if (res.removedInfo.args != null) {
+                        // Remove the replaced package's older resources safely now
+                        synchronized (mInstallLock) {
+                            res.removedInfo.args.cleanUpResourcesLI();
+                        }
+                    }
                 }
                 Runtime.getRuntime().gc();
             }
         });
     }
 
+    static abstract class InstallArgs {
+        final IPackageInstallObserver observer;
+        final int flags;
+        final Uri packageURI;
+        final String installerPackageName;
+
+        InstallArgs(Uri packageURI,
+                IPackageInstallObserver observer, int flags,
+                String installerPackageName) {
+            this.packageURI = packageURI;
+            this.flags = flags;
+            this.observer = observer;
+            this.installerPackageName = installerPackageName;
+        }
+
+        abstract void createCopyFile();
+        abstract int copyApk(IMediaContainerService imcs);
+        abstract void doPreInstall(int status);
+        abstract boolean doRename(int status, String pkgName, String oldCodePath);
+        abstract void doPostInstall(int status);
+        abstract String getCodePath();
+        abstract String getResourcePath();
+        // Need installer lock especially for dex file removal.
+        abstract void cleanUpResourcesLI();
+    }
+
+    class FileInstallArgs extends InstallArgs {
+        File installDir;
+        String codeFileName;
+        String resourceFileName;
+
+        FileInstallArgs(Uri packageURI,
+                IPackageInstallObserver observer, int flags,
+                String installerPackageName) {
+            super(packageURI, observer, flags, installerPackageName);
+        }
+
+        FileInstallArgs(String fullCodePath, String fullResourcePath) {
+            super(null, null, 0, null);
+            File codeFile = new File(fullCodePath);
+            installDir = codeFile.getParentFile();
+            codeFileName = fullCodePath;
+            resourceFileName = fullResourcePath;
+        }
+
+        void createCopyFile() {
+            boolean fwdLocked = isFwdLocked(flags);
+            installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
+            codeFileName = createTempPackageFile(installDir).getPath();
+            resourceFileName = getResourcePathFromCodePath();
+        }
+
+        String getCodePath() {
+            return codeFileName;
+        }
+
+        int copyApk(IMediaContainerService imcs) {
+            // Get a ParcelFileDescriptor to write to the output file
+            File codeFile = new File(codeFileName);
+            ParcelFileDescriptor out = null;
+            try {
+            out = ParcelFileDescriptor.open(codeFile,
+                    ParcelFileDescriptor.MODE_READ_WRITE);
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "Failed to create file descritpor for : " + codeFileName);
+                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            }
+            // Copy the resource now
+            int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            try {
+                if (imcs.copyResource(packageURI, out)) {
+                    ret = PackageManager.INSTALL_SUCCEEDED;
+                }
+            } catch (RemoteException e) {
+            } finally {
+                try { if (out != null) out.close(); } catch (IOException e) {}
+            }
+            return ret;
+        }
+
+        void doPreInstall(int status) {
+            if (status != PackageManager.INSTALL_SUCCEEDED) {
+                cleanUp();
+            }
+        }
+
+        boolean doRename(int status, final String pkgName, String oldCodePath) {
+            if (status != PackageManager.INSTALL_SUCCEEDED) {
+                cleanUp();
+                return false;
+            } else {
+                // Rename based on packageName
+                File codeFile = new File(getCodePath());
+                String apkName = getNextCodePath(oldCodePath, pkgName, ".apk");
+                File desFile = new File(installDir, apkName + ".apk");
+                if (!codeFile.renameTo(desFile)) {
+                    return false;
+                }
+                // Reset paths since the file has been renamed.
+                codeFileName = desFile.getPath();
+                resourceFileName = getResourcePathFromCodePath();
+                // Set permissions
+                if (!setPermissions(pkgName)) {
+                    // Failed setting permissions.
+                    return false;
+                }
+                return true;
+            }
+        }
+
+        void doPostInstall(int status) {
+            if (status != PackageManager.INSTALL_SUCCEEDED) {
+                cleanUp();
+            }
+        }
+
+        String getResourcePath() {
+            return resourceFileName;
+        }
+
+        String getResourcePathFromCodePath() {
+            String codePath = getCodePath();
+            if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
+                String apkNameOnly = getApkName(codePath);
+                return mAppInstallDir.getPath() + "/" + apkNameOnly + ".zip";
+            } else {
+                return codePath;
+            }
+        }
+
+        private boolean cleanUp() {
+            boolean ret = true;
+            String sourceDir = getCodePath();
+            String publicSourceDir = getResourcePath();
+            if (sourceDir != null) {
+                File sourceFile = new File(sourceDir);
+                if (!sourceFile.exists()) {
+                    Log.w(TAG, "Package source " + sourceDir + " does not exist.");
+                    ret = false;
+                }
+                // Delete application's code and resources
+                sourceFile.delete();
+            }
+            if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) {
+                final File publicSourceFile = new File(publicSourceDir);
+                if (!publicSourceFile.exists()) {
+                    Log.w(TAG, "Package public source " + publicSourceFile + " does not exist.");
+                }
+                if (publicSourceFile.exists()) {
+                    publicSourceFile.delete();
+                }
+            }
+            return ret;
+        }
+
+        void cleanUpResourcesLI() {
+            String sourceDir = getCodePath();
+            if (cleanUp() && mInstaller != null) {
+                int retCode = mInstaller.rmdex(sourceDir);
+                if (retCode < 0) {
+                    Log.w(TAG, "Couldn't remove dex file for package: "
+                            +  " at location "
+                            + sourceDir + ", retcode=" + retCode);
+                    // we don't consider this to be a failure of the core package deletion
+                }
+            }
+        }
+
+        private boolean setPermissions(String pkgName) {
+            // TODO Do this in a more elegant way later on. for now just a hack
+            if (!isFwdLocked(flags)) {
+                final int filePermissions =
+                    FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
+                    |FileUtils.S_IROTH;
+                int retCode = FileUtils.setPermissions(getCodePath(), filePermissions, -1, -1);
+                if (retCode != 0) {
+                    Log.e(TAG, "Couldn't set new package file permissions for " +
+                            getCodePath()
+                            + ". The return code was: " + retCode);
+                    // TODO Define new internal error
+                    return false;
+                }
+                return true;
+            }
+            return true;
+        }
+    }
+
+    class SdInstallArgs extends InstallArgs {
+        String cid;
+        String cachePath;
+        static final String RES_FILE_NAME = "pkg.apk";
+
+        SdInstallArgs(Uri packageURI,
+                IPackageInstallObserver observer, int flags,
+                String installerPackageName) {
+           super(packageURI, observer, flags, installerPackageName);
+        }
+
+        SdInstallArgs(String fullCodePath, String fullResourcePath) {
+            super(null, null, 0, null);
+            // Extract cid from fullCodePath
+            int eidx = fullCodePath.lastIndexOf("/");
+            String subStr1 = fullCodePath.substring(0, eidx);
+            int sidx = subStr1.lastIndexOf("/");
+            cid = subStr1.substring(sidx+1, eidx);
+            cachePath = subStr1;
+        }
+
+        void createCopyFile() {
+            cid = getTempContainerId();
+        }
+
+        int copyApk(IMediaContainerService imcs) {
+            try {
+                cachePath = imcs.copyResourceToContainer(
+                        packageURI, cid,
+                        getEncryptKey(), RES_FILE_NAME);
+            } catch (RemoteException e) {
+            }
+
+            if (cachePath != null) {
+                // Mount container once its created with system_uid
+                cachePath = mountSdDir(cid, Process.SYSTEM_UID);
+            }
+            if (cachePath == null) {
+                return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+            } else {
+                return PackageManager.INSTALL_SUCCEEDED;
+            }
+        }
+
+        @Override
+        String getCodePath() {
+            return cachePath + "/" + RES_FILE_NAME;
+        }
+
+        @Override
+        String getResourcePath() {
+            return cachePath + "/" + RES_FILE_NAME;
+        }
+
+        void doPreInstall(int status) {
+            if (status != PackageManager.INSTALL_SUCCEEDED) {
+                // Destroy container
+                destroySdDir(cid);
+            }
+        }
+
+        boolean doRename(int status, final String pkgName,
+                String oldCodePath) {
+            String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME);
+            // STOPSHIP TEMPORARY HACK FOR RENAME
+            // Create new container at newCachePath
+            String codePath = getCodePath();
+            String newCachePath = null;
+            final int CREATE_FAILED = 1;
+            final int COPY_FAILED = 3;
+            final int FINALIZE_FAILED = 5;
+            final int PASS = 7;
+            int errCode = CREATE_FAILED;
+            if ((newCachePath = createSdDir(new File(codePath), newCacheId)) != null) {
+                errCode = COPY_FAILED;
+                // Copy file from codePath
+                if (FileUtils.copyFile(new File(codePath), new File(newCachePath, RES_FILE_NAME))) {
+                    errCode = FINALIZE_FAILED;
+                    if (finalizeSdDir(newCacheId)) {
+                        errCode = PASS;
+                    }
+                }
+            }
+            // Print error based on errCode
+            String errMsg = "";
+            switch (errCode) {
+                case CREATE_FAILED:
+                    errMsg = "CREATE_FAILED";
+                    break;
+                case COPY_FAILED:
+                    errMsg = "COPY_FAILED";
+                    destroySdDir(newCacheId);
+                    break;
+                case FINALIZE_FAILED:
+                    errMsg = "FINALIZE_FAILED";
+                    destroySdDir(newCacheId);
+                    break;
+                default:
+                    errMsg = "PASS";
+                break;
+            }
+            // Destroy the temporary container
+            destroySdDir(cid);
+            Log.i(TAG, "Status: " + errMsg);
+            if (errCode != PASS) {
+                return false;
+            }
+            cid = newCacheId;
+            cachePath = newCachePath;
+
+            return true;
+        }
+
+        void doPostInstall(int status) {
+            if (status != PackageManager.INSTALL_SUCCEEDED) {
+                cleanUp();
+            } else {
+                // Unmount container
+                // Rename and remount based on package name and new uid
+            }
+        }
+
+        private void cleanUp() {
+            // Destroy secure container
+            destroySdDir(cid);
+        }
+
+        void cleanUpResourcesLI() {
+            String sourceFile = getCodePath();
+            // Remove dex file
+            if (mInstaller != null) {
+                int retCode = mInstaller.rmdex(sourceFile.toString());
+                if (retCode < 0) {
+                    Log.w(TAG, "Couldn't remove dex file for package: "
+                            + " at location "
+                            + sourceFile.toString() + ", retcode=" + retCode);
+                    // we don't consider this to be a failure of the core package deletion
+                }
+            }
+            cleanUp();
+        }
+    };
+
+    // Utility method used to create code paths based on package name and available index.
+    private static String getNextCodePath(String oldCodePath, String prefix, String suffix) {
+        String idxStr = "";
+        int idx = 1;
+        if (oldCodePath != null) {
+            int eidx = -1;
+            if (suffix != null) {
+                eidx = oldCodePath.indexOf(suffix);
+            }
+            if (eidx == -1) {
+                eidx = oldCodePath.length();
+            }
+            int sidx = oldCodePath.indexOf(prefix);
+            if (sidx == -1) {
+                sidx = 0;
+            }
+            String subStr = oldCodePath.substring(sidx + prefix.length(), eidx);
+            if (subStr != null) {
+                if (subStr.startsWith("-")) {
+                    subStr = subStr.substring(1);
+                }
+                try {
+                    idx = Integer.parseInt(subStr);
+                    if (idx <= 1) {
+                        idx++;
+                    } else {
+                        idx--;
+                    }
+                } catch(NumberFormatException e) {
+                }
+            }
+        }
+        idxStr = "-" + Integer.toString(idx);
+        return prefix + idxStr;
+    }
+
+    // Utility method that returns the relative package path with respect
+    // to the installation directory. Like say for /data/data/com.test-1.apk
+    // string com.test-1 is returned.
+    static String getApkName(String codePath) {
+        if (codePath == null) {
+            return null;
+        }
+        int sidx = codePath.lastIndexOf("/");
+        int eidx = codePath.lastIndexOf(".");
+        if (eidx == -1) {
+            eidx = codePath.length();
+        } else if (eidx == 0) {
+            Log.w(TAG, " Invalid code path, "+ codePath + " Not a valid apk name");
+            return null;
+        }
+        return codePath.substring(sidx+1, eidx);
+    }
+
     class PackageInstalledInfo {
         String name;
         int uid;
@@ -3932,7 +4424,6 @@
         // Remember this for later, in case we need to rollback this install
         boolean dataDirExists;
         String pkgName = pkg.packageName;
-        boolean onSd = (parseFlags & PackageParser.PARSE_ON_SDCARD) != 0;
 
         if (useEncryptedFilesystemForPackage(pkg)) {
             dataDirExists = (new File(mSecureAppDataDir, pkgName)).exists();
@@ -3950,14 +4441,6 @@
             }
         }
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
-        if (onSd) {
-            // Create secure container mount point for package
-            String cPath = createSdDir(new File(pkg.mScanPath), pkgName);
-            if (cPath == null) {
-                mLastScanError = res.returnCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
-                return;
-            }
-        }
         PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode);
         if (newPackage == null) {
             Log.w(TAG, "Package couldn't be installed in " + pkg.mPath);
@@ -3965,21 +4448,9 @@
                 res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
             }
         } else {
-            File destPackageFile = new File(pkg.mPath);
-            if (destPackageFile.exists()) {
-                // It's safe to do this because we know (from the above check) that the file
-                // isn't currently used for an installed package.
-                destPackageFile.delete();
-            }
             updateSettingsLI(newPackage,
                     installerPackageName,
                     res);
-            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                // Check if container can be finalized
-                if(onSd && !finalizeSdDir(pkgName)) {
-                    res.returnCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
-                }
-            }
             // delete the partially installed application. the data directory will have to be
             // restored if it was already existing
             if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -3988,15 +4459,11 @@
                 // scanPackageLocked, unless those directories existed before we even tried to
                 // install.
                 deletePackageLI(
-                        pkgName, true,
+                        pkgName, false,
                         dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,
                                 res.removedInfo);
             }
         }
-        if (onSd && res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
-            // Destroy cache
-            destroySdDir(pkgName);
-        }
     }
 
     private void replacePackageLI(PackageParser.Package pkg,
@@ -4039,7 +4506,7 @@
 
         parseFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
         // First delete the existing package while retaining the data directory
-        if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
+        if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA,
                 res.removedInfo)) {
             // If the existing package was'nt successfully deleted
             res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
@@ -4063,17 +4530,10 @@
 
         if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
             // If we deleted an exisiting package, the old source and resource files that we
-            // were keeping around in case we needed them (see below) can now be deleted
-            final ApplicationInfo deletedPackageAppInfo = deletedPackage.applicationInfo;
-            final ApplicationInfo installedPackageAppInfo =
-                newPackage.applicationInfo;
-            deletePackageResourcesLI(pkgName,
-                    !deletedPackageAppInfo.sourceDir
-                            .equals(installedPackageAppInfo.sourceDir)
-                            ? deletedPackageAppInfo.sourceDir : null,
-                    !deletedPackageAppInfo.publicSourceDir
-                            .equals(installedPackageAppInfo.publicSourceDir)
-                            ? deletedPackageAppInfo.publicSourceDir : null);
+            // were keeping around in case we needed them (see below) can now be deleted.
+            // This info will be set on the res.removedInfo to clean up later on as post
+            // install action.
+
             //update signature on the new package setting
             //this should always succeed, since we checked the
             //signature earlier.
@@ -4100,22 +4560,15 @@
                     Log.e(TAG, "Failed allocating storage when restoring pkg : " + pkgName);
                     return;
                 }
-                File restoreTmpFile = createTempPackageFile();
-                if (restoreTmpFile == null) {
-                    Log.e(TAG, "Failed creating temp file when restoring pkg :  " + pkgName);
-                    return;
-                }
-                if (!FileUtils.copyFile(restoreFile, restoreTmpFile)) {
-                    Log.e(TAG, "Failed copying temp file when restoring pkg : " + pkgName);
-                    return;
-                }
                 PackageInstalledInfo restoreRes = new PackageInstalledInfo();
                 restoreRes.removedInfo = new PackageRemovedInfo();
-                installPackageLI(
-                        Uri.fromFile(restoreFile),
-                        isForwardLocked(deletedPackage)
-                        ? PackageManager.INSTALL_FORWARD_LOCK
-                                : 0, false, oldInstallerPackageName, restoreTmpFile, restoreRes);
+                // Parse old package
+                parseFlags |= ~PackageManager.INSTALL_REPLACE_EXISTING;
+                scanPackageLI(restoreFile, parseFlags, scanMode);
+                synchronized (mPackages) {
+                    grantPermissionsLP(deletedPackage, false);
+                    mSettings.writeLP();
+                }
                 if (restoreRes.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                     Log.e(TAG, "Failed restoring pkg : " + pkgName + " after failed upgrade");
                 }
@@ -4220,24 +4673,11 @@
                 return;
             }
         }
-        // XXX There are probably some big issues here: upon doing
-        // the rename, we have reached the point of no return (the
-        // original .apk is gone!), so we can't fail.  Yet... we can.
-        File scanFile = new File(newPackage.mScanPath);
-        if (!scanFile.renameTo(new File(newPackage.mPath))) {
-            Log.e(TAG, "Couldn't move package file: " + newPackage.mScanPath + " to: " + newPackage.mPath);
-            // TODO rename should work. Workaround
-            if (!FileUtils.copyFile(scanFile, new File(newPackage.mPath))) {
-                Log.e(TAG, "Couldn't move package file to: " + newPackage.mPath);
-                res.returnCode =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-            }
+        res.returnCode = setPermissionsLI(newPackage);
+        if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+            return;
         } else {
-            res.returnCode = setPermissionsLI(newPackage);
-            if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
-                return;
-            } else {
-                Log.d(TAG, "New package installed in " + newPackage.mPath);
-            }
+            Log.d(TAG, "New package installed in " + newPackage.mPath);
         }
         if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
             if (mInstaller != null) {
@@ -4258,67 +4698,11 @@
         }
     }
 
-    private File copyTempInstallFile(Uri pPackageURI,
-            PackageInstalledInfo res) {
-        File tmpPackageFile = createTempPackageFile();
-        int retCode = PackageManager.INSTALL_SUCCEEDED;
-        if (tmpPackageFile == null) {
-            res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-            return null;
-        }
-
-        if (pPackageURI.getScheme().equals("file")) {
-            final File srcPackageFile = new File(pPackageURI.getPath());
-            // We copy the source package file to a temp file and then rename it to the
-            // destination file in order to eliminate a window where the package directory
-            // scanner notices the new package file but it's not completely copied yet.
-            if (!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) {
-                Log.e(TAG, "Couldn't copy package file to temp file.");
-                retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-            }
-        } else if (pPackageURI.getScheme().equals("content")) {
-            ParcelFileDescriptor fd = null;
-            try {
-                fd = mContext.getContentResolver().openFileDescriptor(pPackageURI, "r");
-            } catch (FileNotFoundException e) {
-                Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e);
-                retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-            }
-            if (fd == null) {
-                Log.e(TAG, "Couldn't open file descriptor from download service (null).");
-                retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-            } else {
-                if (Config.LOGV) {
-                    Log.v(TAG, "Opened file descriptor from download service.");
-                }
-                ParcelFileDescriptor.AutoCloseInputStream
-                        dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd);
-                // We copy the source package file to a temp file and then rename it to the
-                // destination file in order to eliminate a window where the package directory
-                // scanner notices the new package file but it's not completely copied yet.
-                if (!FileUtils.copyToFile(dlStream, tmpPackageFile)) {
-                    Log.e(TAG, "Couldn't copy package stream to temp file.");
-                    retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                }
-            }
-        } else {
-            Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
-            retCode = PackageManager.INSTALL_FAILED_INVALID_URI;
-        }
-
-        res.returnCode = retCode;
-        if (retCode != PackageManager.INSTALL_SUCCEEDED) {
-            if (tmpPackageFile != null && tmpPackageFile.exists()) {
-                tmpPackageFile.delete();
-            }
-            return null;
-        }
-        return tmpPackageFile;
-    }
-
-    private void installPackageLI(Uri pPackageURI,
-            int pFlags, boolean newInstall, String installerPackageName,
-            File tmpPackageFile, 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());
         boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
         boolean onSd = ((pFlags & PackageManager.INSTALL_ON_SDCARD) != 0);
         boolean replacingExistingPackage = false;
@@ -4353,14 +4737,25 @@
                 break main_flow;
             }
 
+            // Get rid of all references to package scan path via parser.
+            pp = null;
+            String oldCodePath = null;
             synchronized (mPackages) {
                 //check if installing already existing package
                 if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0
                         && mPackages.containsKey(pkgName)) {
                     replacingExistingPackage = true;
+                    oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
                 }
             }
 
+            if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
+                res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                break main_flow;
+            }
+            // TODO rename  pkg.mScanPath In scanPackageLI let it just set values based on mScanPath
+            pkg.applicationInfo.sourceDir = pkg.mScanPath= pkg.mPath = args.getCodePath();
+            pkg.applicationInfo.publicSourceDir = args.getResourcePath();
             if(replacingExistingPackage) {
                 replacePackageLI(pkg, parseFlags, scanMode,
                         installerPackageName, res);
@@ -4369,15 +4764,16 @@
                         installerPackageName,res);
             }
         } finally {
-            if (tmpPackageFile != null && tmpPackageFile.exists()) {
-                tmpPackageFile.delete();
+            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
             }
         }
     }
 
     private int setPermissionsLI(PackageParser.Package newPackage) {
         String pkgName = newPackage.packageName;
-        int retCode;
+        int retCode = 0;
+        // TODO Gross hack but fix later. Ideally move this to be a post installation
+        // check after alloting uid.
         if ((newPackage.applicationInfo.flags
                 & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
             File destResourceFile = new File(newPackage.applicationInfo.publicSourceDir);
@@ -4391,7 +4787,7 @@
                 //TODO clean up the extracted public files
             }
             if (mInstaller != null) {
-                retCode = mInstaller.setForwardLockPerm(pkgName,
+                retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath),
                         newPackage.applicationInfo.uid);
             } else {
                 final int filePermissions =
@@ -4400,15 +4796,16 @@
                                                    newPackage.applicationInfo.uid);
             }
         } else {
-            final int filePermissions =
-                    FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
-                    |FileUtils.S_IROTH;
-            retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1, -1);
+            // The permissions on the resource file was set when it was copied for
+            // non forward locked apps and apps on sdcard
         }
+
         if (retCode != 0) {
             Log.e(TAG, "Couldn't set new package file permissions for " +
                     newPackage.mPath
                        + ". The return code was: " + retCode);
+            // TODO Define new internal error
+            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
         }
         return PackageManager.INSTALL_SUCCEEDED;
     }
@@ -4491,10 +4888,10 @@
         }
     }
 
-    private File createTempPackageFile() {
+    private File createTempPackageFile(File installDir) {
         File tmpPackageFile;
         try {
-            tmpPackageFile = File.createTempFile("vmdl", ".tmp", mAppInstallDir);
+            tmpPackageFile = File.createTempFile("vmdl", ".tmp", installDir);
         } catch (IOException e) {
             Log.e(TAG, "Couldn't create temp file for downloaded package file.");
             return null;
@@ -4570,6 +4967,13 @@
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras);
             }
         }
+        // Delete the resources here after sending the broadcast to let
+        // other processes clean up before deleting resources.
+        synchronized (mInstallLock) {
+            if (info.args != null) {
+                info.args.cleanUpResourcesLI();
+            }
+        }
         return res;
     }
 
@@ -4578,6 +4982,8 @@
         int uid = -1;
         int removedUid = -1;
         boolean isRemovedPackageSystemUpdate = false;
+        // Clean up resources deleted packages.
+        InstallArgs args = null;
 
         void sendBroadcast(boolean fullRemove, boolean replacing) {
             Bundle extras = new Bundle(1);
@@ -4707,36 +5113,6 @@
         return true;
     }
 
-    private void deletePackageResourcesLI(String packageName,
-            String sourceDir, String publicSourceDir) {
-        if (sourceDir != null) {
-            File sourceFile = new File(sourceDir);
-            if (!sourceFile.exists()) {
-                Log.w(TAG, "Package source " + sourceDir + " does not exist.");
-            }
-            // Delete application's code and resources
-            sourceFile.delete();
-            if (mInstaller != null) {
-                int retCode = mInstaller.rmdex(sourceFile.toString());
-                if (retCode < 0) {
-                    Log.w(TAG, "Couldn't remove dex file for package: "
-                            + packageName + " at location "
-                            + sourceFile.toString() + ", retcode=" + retCode);
-                    // we don't consider this to be a failure of the core package deletion
-                }
-            }
-        }
-        if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) {
-            final File publicSourceFile = new File(publicSourceDir);
-            if (!publicSourceFile.exists()) {
-                Log.w(TAG, "Package public source " + publicSourceFile + " does not exist.");
-            }
-            if (publicSourceFile.exists()) {
-                publicSourceFile.delete();
-            }
-        }
-    }
-
     private boolean deleteInstalledPackageLI(PackageParser.Package p,
             boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) {
         ApplicationInfo applicationInfo = p.applicationInfo;
@@ -4753,7 +5129,12 @@
 
         // Delete application code and resources
         if (deleteCodeAndResources) {
-            deletePackageResourcesLI(applicationInfo.packageName,
+            // TODO can pick up from PackageSettings as well
+            int installFlags = ((p.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD)!=0) ?
+                    PackageManager.INSTALL_ON_SDCARD : 0;
+            installFlags |= ((p.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK)!=0) ?
+                    PackageManager.INSTALL_FORWARD_LOCK : 0;
+            outInfo.args = createInstallArgs(installFlags,
                     applicationInfo.sourceDir, applicationInfo.publicSourceDir);
         }
         return true;
@@ -4798,12 +5179,6 @@
             Log.w(TAG, "Package " + p.packageName + " has no applicationInfo.");
             return false;
         }
-        boolean onSd = (p.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0;
-        // Mount sd container if needed
-        if (onSd) {
-            // TODO Better error handling from MountService api later
-            mountSdDir(p.packageName, Process.SYSTEM_UID) ;
-        }
         boolean ret = false;
         if ( (p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
             Log.i(TAG, "Removing system package:"+p.packageName);
@@ -4812,22 +5187,10 @@
             ret = deleteSystemPackageLI(p, flags, outInfo);
         } else {
             Log.i(TAG, "Removing non-system package:"+p.packageName);
+            // Kill application pre-emptively especially for apps on sd.
+            killApplication(packageName, p.applicationInfo.uid);
             ret = deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
         }
-        if (ret && onSd) {
-            if (deleteCodeAndResources) {
-                // Post a delayed destroy on the container since there might
-                // be active processes holding open file handles to package
-                // resources which will get killed by the process killer when
-                // destroying the container. This might even kill the current
-                // process and crash the system. Delay the destroy a bit so
-                // that the active processes get to handle the uninstall broadcasts.
-                sendDelayedDestroySdDir(packageName);
-            } else {
-                // Just unmount the directory
-                unMountSdDir(packageName);
-            }
-        }
         return ret;
     }
 
@@ -7520,11 +7883,31 @@
     final private String mSdEncryptKey = "AppsOnSD";
     final private String mSdEncryptAlg = "AES";
     private boolean mMediaMounted = false;
+    private static final int MAX_CONTAINERS = 250;
 
-    private MountService getMountService() {
+
+    static MountService getMountService() {
         return (MountService) ServiceManager.getService("mount");
     }
 
+    private String getEncryptKey() {
+        try {
+            String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey);
+            if (sdEncKey == null) {
+                sdEncKey = SystemKeyStore.getInstance().
+                        generateNewKeyHexString(128, mSdEncryptAlg, mSdEncryptKey);
+                if (sdEncKey == null) {
+                    Log.e(TAG, "Failed to create encryption keys");
+                    return null;
+                }
+            }
+            return sdEncKey;
+        } catch (NoSuchAlgorithmException nsae) {
+            Log.e(TAG, "Failed to create encryption keys with exception: " + nsae);
+            return null;
+        }
+    }
+
    private String createSdDir(File tmpPackageFile, String pkgName) {
         // Create mount point via MountService
         MountService mountService = getMountService();
@@ -7535,8 +7918,6 @@
         }
         if (DEBUG_SD_INSTALL) Log.i(TAG, "mbLen="+mbLen);
         String cachePath = null;
-        // Remove any pending destroy messages
-        mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);
         String sdEncKey;
         try {
             sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey);
@@ -7597,7 +7978,13 @@
 
    private boolean unMountSdDir(String pkgName) {
        // STOPSHIP unmount directory
-       return true;
+       try {
+           getMountService().unmountSecureContainer(pkgName);
+           return true;
+       } catch (IllegalStateException e) {
+           Log.e(TAG, "Failed to unmount : " + pkgName + " with exception " + e);
+       }
+       return false;
    }
 
    private String getSdDir(String pkgName) {
@@ -7622,10 +8009,6 @@
 
    private boolean destroySdDir(String pkgName) {
        try {
-           if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) {
-               // Don't have to send message again
-               mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);
-           }
            // We need to destroy right away
            getMountService().destroySecureContainer(pkgName);
            return true;
@@ -7635,7 +8018,7 @@
        }
    }
 
-   private String[] getSecureContainerList() {
+   static String[] getSecureContainerList() {
        try {
            return getMountService().getSecureContainerList();
        } catch (IllegalStateException e) {
@@ -7644,13 +8027,47 @@
        return null;
    }
 
-   private void sendDelayedDestroySdDir(String pkgName) {
-       if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) {
-           // Don't have to send message again
-           return;
+   static String getTempContainerId() {
+       String prefix = "smdl1tmp";
+       int tmpIdx = 1;
+       String list[] = getSecureContainerList();
+       if (list != null) {
+           int idx = 0;
+           int idList[] = new int[MAX_CONTAINERS];
+           boolean neverFound = true;
+           for (String name : list) {
+               // Ignore null entries
+               if (name == null) {
+                   continue;
+               }
+               int sidx = name.indexOf(prefix);
+               if (sidx == -1) {
+                   // Not a temp file. just ignore
+                   continue;
+               }
+               String subStr = name.substring(sidx + prefix.length());
+               idList[idx] = -1;
+               if (subStr != null) {
+                   try {
+                       int cid = Integer.parseInt(subStr);
+                       idList[idx++] = cid;
+                       neverFound = false;
+                   } catch (NumberFormatException e) {
+                   }
+               }
+           }
+           if (!neverFound) {
+               // Sort idList
+               Arrays.sort(idList);
+               for (int j = 1; j <= idList.length; j++) {
+                   if (idList[j-1] != j) {
+                       tmpIdx = j;
+                       break;
+                   }
+               }
+           }
        }
-       Message msg = mHandler.obtainMessage(DESTROY_SD_CONTAINER, pkgName);
-       mHandler.sendMessageDelayed(msg, DESTROY_SD_CONTAINER_DELAY);
+       return prefix + tmpIdx;
    }
 
    public void updateExternalMediaStatus(final boolean mediaStatus) {