Move package from internal to external and vice versa.
diff --git a/Android.mk b/Android.mk
index a48ef45..1700965 100644
--- a/Android.mk
+++ b/Android.mk
@@ -111,6 +111,7 @@
 	core/java/android/content/pm/IPackageDeleteObserver.aidl \
 	core/java/android/content/pm/IPackageInstallObserver.aidl \
 	core/java/android/content/pm/IPackageManager.aidl \
+	core/java/android/content/pm/IPackageMoveObserver.aidl \
 	core/java/android/content/pm/IPackageStatsObserver.aidl \
 	core/java/android/database/IContentObserver.aidl \
 	core/java/android/hardware/ISensorService.aidl \
diff --git a/api/current.xml b/api/current.xml
index 40739da..86043df 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -716,6 +716,17 @@
  visibility="public"
 >
 </field>
+<field name="MOVE_PACKAGE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.MOVE_PACKAGE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PERSISTENT_ACTIVITY"
  type="java.lang.String"
  transient="false"
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index ff16c6e..fc5707d 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -600,6 +600,7 @@
             } else if (opt.equals("-t")) {
                 installFlags |= PackageManager.INSTALL_ALLOW_TEST;
             } else if (opt.equals("-s")) {
+                // Override if -s option is specified.
                 installFlags |= PackageManager.INSTALL_EXTERNAL;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index db6a4bf..1d004ee 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -42,6 +42,7 @@
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageManager;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
@@ -2489,6 +2490,15 @@
         }
 
         @Override
+        public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+            try {
+                mPM.movePackage(packageName, observer, flags);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+
+        @Override
         public String getInstallerPackageName(String packageName) {
             try {
                 return mPM.getInstallerPackageName(packageName);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 47789a5..7a02a98 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -26,6 +26,7 @@
 import android.content.pm.IPackageInstallObserver;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
@@ -309,4 +310,6 @@
     void updateExternalMediaStatus(boolean mounted);
 
     String nextPackageToClean(String lastPackage);
+
+    void movePackage(String packageName, IPackageMoveObserver observer, int flags);
 }
diff --git a/core/java/android/content/pm/IPackageMoveObserver.aidl b/core/java/android/content/pm/IPackageMoveObserver.aidl
new file mode 100644
index 0000000..baa1595
--- /dev/null
+++ b/core/java/android/content/pm/IPackageMoveObserver.aidl
@@ -0,0 +1,27 @@
+/*
+**
+** Copyright 2007, 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 android.content.pm;
+
+/**
+ * Callback for moving package resources from the Package Manager.
+ * @hide
+ */
+oneway interface IPackageMoveObserver {
+    void packageMoved(in String packageName, int returnCode);
+}
+
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8576de2..2edb430 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -551,6 +551,69 @@
     public static final int DONT_DELETE_DATA = 0x00000001;
 
     /**
+     * Return code that is passed to the {@link IPackageMoveObserver} by
+     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+     * when the package has been successfully moved by the system.
+     * @hide
+     */
+    public static final int MOVE_SUCCEEDED = 1;
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} by
+     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+     * when the package hasn't been successfully moved by the system
+     * because of insufficient memory on specified media.
+     * @hide
+     */
+    public static final int MOVE_FAILED_INSUFFICIENT_STORAGE = -1;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} by
+     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+     * if the specified package doesn't exist.
+     * @hide
+     */
+    public static final int MOVE_FAILED_DOESNT_EXIST = -2;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} by
+     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+     * if the specified package cannot be moved since its a system package.
+     * @hide
+     */
+    public static final int MOVE_FAILED_SYSTEM_PACKAGE = -3;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} by
+     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+     * if the specified package cannot be moved since its forward locked.
+     * @hide
+     */
+    public static final int MOVE_FAILED_FORWARD_LOCKED = -4;
+
+    /**
+     * Error code that is passed to the {@link IPackageMoveObserver} by
+     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+     * if the specified package cannot be moved to the specified location.
+     * @hide
+     */
+    public static final int MOVE_FAILED_INVALID_LOCATION = -5;
+
+    /**
+     * Flag parameter for {@link #movePackage} to indicate that
+     * the package should be moved to internal storage if its
+     * been installed on external media.
+     * @hide
+     */
+    public static final int MOVE_INTERNAL = 0x00000001;
+
+    /**
+     * Flag parameter for {@link #movePackage} to indicate that
+     * the package should be moved to external media.
+     * @hide
+     */
+    public static final int MOVE_EXTERNAL_MEDIA = 0x00000002;
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has a camera facing away
      * from the screen.
@@ -1922,4 +1985,23 @@
      * Return whether the device has been booted into safe mode.
      */
     public abstract boolean isSafeMode();
+
+    /**
+     * Attempts to move package resources from internal to external media or vice versa.
+     * Since this may take a little while, the result will
+     * be posted back to the given observer.   This call may fail if the calling context
+     * lacks the {@link android.Manifest.permission#MOVE_PACKAGE} permission, if the
+     * named package cannot be found, or if the named package is a "system package".
+     *
+     * @param packageName The name of the package to delete
+     * @param observer An observer callback to get notified when the package move is
+     * complete. {@link android.content.pm.IPackageMoveObserver#packageMoved(boolean)} will be
+     * called when that happens.  observer may be null to indicate that no callback is desired.
+     * @param flags To indicate install location {@link #MOVE_INTERNAL} or
+     * {@link #MOVE_EXTERNAL_MEDIA}
+     *
+     * @hide
+     */
+    public abstract void movePackage(
+            String packageName, IPackageMoveObserver observer, int flags);
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 333db05..63584ed 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1041,6 +1041,12 @@
         android:description="@string/permdesc_deletePackages"
         android:protectionLevel="signatureOrSystem" />
 
+    <!-- Allows an application to move location of installed package. -->
+    <permission android:name="android.permission.MOVE_PACKAGE"
+        android:label="@string/permlab_movePackage"
+        android:description="@string/permdesc_movePackage"
+        android:protectionLevel="signatureOrSystem" />
+
     <!-- Allows an application to change whether an application component (other than its own) is
          enabled or not. -->
     <permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cdf38b9..98b8863 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -680,6 +680,11 @@
         restricted usually to system process.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_movePackage">Move application resources</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_movePackage">Allows an application to move application resources from internal to external media and vice versa.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readLogs">read system log files</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readLogs">Allows an application to read from the
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 933a7e5..95ab684 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -102,7 +102,11 @@
             File sourceFile = new File(archiveFilePath);
             DisplayMetrics metrics = new DisplayMetrics();
             metrics.setToDefaults();
-            PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+            PackageParser.Package pkg = packageParser.parsePackage(sourceFile,
+                    archiveFilePath, metrics, 0);
+            // Nuke the parser reference right away and force a gc
+            Runtime.getRuntime().gc();
+            packageParser = null;
             if (pkg == null) {
                 Log.w(TAG, "Failed to parse package");
                 return PackageHelper.RECOMMEND_FAILED_INVALID_APK;
@@ -115,7 +119,7 @@
                 return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
             } else {
                 // Implies install on internal storage.
-                return 0;
+                return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
             }
         }
     };
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 4326efc..252f2a6 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -44,6 +44,7 @@
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageInstallObserver;
 import android.content.pm.IPackageManager;
+import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
@@ -142,6 +143,9 @@
         FileObserver.CLOSE_WRITE /*| FileObserver.CREATE*/ | FileObserver.MOVED_TO;
 
     private static final int OBSERVER_EVENTS = REMOVE_EVENTS | ADD_EVENTS;
+    // Suffix used during package installation when copying/moving
+    // package apks to install directory.
+    private static final String INSTALL_PACKAGE_SUFFIX = "-";
 
     static final int SCAN_MONITOR = 1<<0;
     static final int SCAN_NO_DEX = 1<<1;
@@ -321,8 +325,8 @@
     };
 
     class PackageHandler extends Handler {
-        final ArrayList<InstallParams> mPendingInstalls =
-            new ArrayList<InstallParams>();
+        final ArrayList<HandlerParams> mPendingInstalls =
+            new ArrayList<HandlerParams>();
         // Service Connection to remote media container service to copy
         // package uri's from external media onto secure containers
         // or internal storage.
@@ -334,20 +338,19 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case INIT_COPY: {
-                    InstallParams params = (InstallParams) msg.obj;
+                    HandlerParams params = (HandlerParams) msg.obj;
                     Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
                     if (mContainerService != null) {
                         // No need to add to pending list. Use remote stub directly
-                        handleStartCopy(params);
+                        params.handleStartCopy(mContainerService);
                     } else {
                         if (mContext.bindService(service, mDefContainerConn,
                                 Context.BIND_AUTO_CREATE)) {
                             mPendingInstalls.add(params);
                         } else {
                             Log.e(TAG, "Failed to bind to media container service");
-                            // Indicate install failure TODO add new error code
-                            processPendingInstall(createInstallArgs(params),
-                                    PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+                            // Indicate service bind error
+                            params.handleServiceError();
                         }
                     }
                     break;
@@ -358,9 +361,9 @@
                         mContainerService = (IMediaContainerService) msg.obj;
                     }
                     if (mPendingInstalls.size() > 0) {
-                        InstallParams params = mPendingInstalls.remove(0);
+                        HandlerParams params = mPendingInstalls.remove(0);
                         if (params != null) {
-                            handleStartCopy(params);
+                            params.handleStartCopy(mContainerService);
                         }
                     }
                     break;
@@ -421,54 +424,6 @@
                 } break;
             }
         }
-
-        // Utility method to initiate copying apk via media
-        // container service.
-        private void handleStartCopy(InstallParams params) {
-            int ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
-            if (mContainerService != null) {
-                // Remote call to find out default install location
-                int loc = params.getInstallLocation(mContainerService);
-                // Use install location to create InstallArgs and temporary
-                // install location
-                if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){
-                    ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
-                    ret = PackageManager.INSTALL_FAILED_INVALID_APK;
-                } else {
-                    if ((params.flags & PackageManager.INSTALL_EXTERNAL) == 0){
-                        if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
-                            // Set the flag to install on external media.
-                            params.flags |= PackageManager.INSTALL_EXTERNAL;
-                        } else {
-                            // Make sure the flag for installing on external
-                            // media is unset
-                            params.flags &= ~PackageManager.INSTALL_EXTERNAL;
-                        }
-                    }
-                    // Disable forward locked apps on sdcard.
-                    if ((params.flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 &&
-                            (params.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
-                        // Make sure forward locked apps can only be installed
-                        // on internal storage
-                        Log.w(TAG, "Cannot install protected apps on sdcard");
-                        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-                    } else {
-                        ret = PackageManager.INSTALL_SUCCEEDED;
-                    }
-                }
-            }
-            mHandler.sendEmptyMessage(MCS_UNBIND);
-            // Create the file args now.
-            InstallArgs args = createInstallArgs(params);
-            if (ret == PackageManager.INSTALL_SUCCEEDED) {
-                // Create copy only if we are not in an erroneous state.
-                args.createCopyFile();
-                // Remote call to initiate copy
-                ret = args.copyApk(mContainerService);
-            }
-            processPendingInstall(args, ret);
-        }
     }
 
     static boolean installOnSd(int flags) {
@@ -2180,8 +2135,8 @@
         int i;
         for (i=0; i<files.length; i++) {
             File file = new File(dir, files[i]);
-            if (files[i] != null && files[i].endsWith(".zip")) {
-                // Public resource for forward locked package. Ignore
+            if (!isPackageFilename(files[i])) {
+                // Ignore entries which are not apk's
                 continue;
             }
             PackageParser.Package pkg = scanPackageLI(file,
@@ -2936,11 +2891,6 @@
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
         }
 
-        // We don't expect installation to fail beyond this point,
-        if ((scanMode&SCAN_MONITOR) != 0) {
-            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.
@@ -2950,6 +2900,10 @@
         }
 
         synchronized (mPackages) {
+            // We don't expect installation to fail beyond this point,
+            if ((scanMode&SCAN_MONITOR) != 0) {
+                mAppDirs.put(pkg.mPath, pkg);
+            }
             // Add the new setting to mSettings
             mSettings.insertPackageSettingLP(pkgSetting, pkg);
             // Add the new setting to mPackages
@@ -4224,19 +4178,24 @@
                     return;
                 }
 
+                // Ignore packages that are being installed or
+                // have just been installed.
+                if (ignoreCodePath(fullPathStr)) {
+                    return;
+                }
+                PackageParser.Package p = null;
+                synchronized (mPackages) {
+                    p = mAppDirs.get(fullPathStr);
+                }
                 if ((event&REMOVE_EVENTS) != 0) {
-                    synchronized (mInstallLock) {
-                        PackageParser.Package p = mAppDirs.get(fullPathStr);
-                        if (p != null) {
-                            removePackageLI(p, true);
-                            removedPackage = p.applicationInfo.packageName;
-                            removedUid = p.applicationInfo.uid;
-                        }
+                    if (p != null) {
+                        removePackageLI(p, true);
+                        removedPackage = p.applicationInfo.packageName;
+                        removedUid = p.applicationInfo.uid;
                     }
                 }
 
                 if ((event&ADD_EVENTS) != 0) {
-                    PackageParser.Package p = mAppDirs.get(fullPathStr);
                     if (p == null) {
                         p = scanPackageLI(fullPath,
                                 (mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) |
@@ -4312,13 +4271,6 @@
                     }
                     args.doPostInstall(res.returnCode);
                 }
-                if (args.observer != null) {
-                    try {
-                        args.observer.packageInstalled(res.name, res.returnCode);
-                    } catch (RemoteException e) {
-                        Log.i(TAG, "Observer no longer exists.");
-                    }
-                }
                 // There appears to be a subtle deadlock condition if the sendPackageBroadcast
                 // call appears in the synchronized block above.
                 if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
@@ -4345,15 +4297,29 @@
                     }
                 }
                 Runtime.getRuntime().gc();
+                if (args.observer != null) {
+                    try {
+                        args.observer.packageInstalled(res.name, res.returnCode);
+                    } catch (RemoteException e) {
+                        Log.i(TAG, "Observer no longer exists.");
+                    }
+                }
             }
         });
     }
 
-    static final class InstallParams {
+    interface HandlerParams {
+        void handleStartCopy(IMediaContainerService imcs);
+        void handleServiceError();
+    }
+
+    class InstallParams implements HandlerParams {
         final IPackageInstallObserver observer;
         int flags;
         final Uri packageURI;
         final String installerPackageName;
+        private InstallArgs mArgs;
+        private int mRet;
         InstallParams(Uri packageURI,
                 IPackageInstallObserver observer, int flags,
                 String installerPackageName) {
@@ -4363,13 +4329,118 @@
             this.installerPackageName = installerPackageName;
         }
 
-        public int getInstallLocation(IMediaContainerService imcs) {
+        private int getInstallLocation(IMediaContainerService imcs) {
             try {
                 return imcs.getRecommendedInstallLocation(packageURI);
             } catch (RemoteException e) {
             }
             return  -1;
         }
+
+        public void handleStartCopy(IMediaContainerService imcs) {
+            int ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+            if (imcs != null) {
+                // Remote call to find out default install location
+                int loc = getInstallLocation(imcs);
+                // Use install location to create InstallArgs and temporary
+                // install location
+                if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){
+                    ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
+                    ret = PackageManager.INSTALL_FAILED_INVALID_APK;
+                } else {
+                    if ((flags & PackageManager.INSTALL_EXTERNAL) == 0){
+                        if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
+                            // Set the flag to install on external media.
+                            flags |= PackageManager.INSTALL_EXTERNAL;
+                        } else {
+                            // Make sure the flag for installing on external
+                            // media is unset
+                            flags &= ~PackageManager.INSTALL_EXTERNAL;
+                        }
+                    }
+                    // Disable forward locked apps on sdcard.
+                    if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 &&
+                            (flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+                        // Make sure forward locked apps can only be installed
+                        // on internal storage
+                        Log.w(TAG, "Cannot install protected apps on sdcard");
+                        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+                    } else {
+                        ret = PackageManager.INSTALL_SUCCEEDED;
+                    }
+                }
+            }
+            // Create the file args now.
+            mArgs = createInstallArgs(this);
+            if (ret == PackageManager.INSTALL_SUCCEEDED) {
+                // Create copy only if we are not in an erroneous state.
+                // Remote call to initiate copy using temporary file
+                ret = mArgs.copyApk(imcs, true);
+            }
+            mRet = ret;
+            mHandler.sendEmptyMessage(MCS_UNBIND);
+            handleReturnCode();
+        }
+
+        void handleReturnCode() {
+            processPendingInstall(mArgs, mRet);
+        }
+
+        public void handleServiceError() {
+            mArgs = createInstallArgs(this);
+            mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+            handleReturnCode();
+        }
+    };
+
+    /*
+     * Utility class used in movePackage api.
+     * srcArgs and targetArgs are not set for invalid flags and make
+     * sure to do null checks when invoking methods on them.
+     * We probably want to return ErrorPrams for both failed installs
+     * and moves.
+     */
+    class MoveParams implements HandlerParams {
+        final IPackageMoveObserver observer;
+        final int flags;
+        final String packageName;
+        final InstallArgs srcArgs;
+        final InstallArgs targetArgs;
+        int mRet;
+        MoveParams(InstallArgs srcArgs,
+                IPackageMoveObserver observer,
+                int flags, String packageName) {
+            this.srcArgs = srcArgs;
+            this.observer = observer;
+            this.flags = flags;
+            this.packageName = packageName;
+            if (srcArgs != null) {
+                Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath()));
+                targetArgs = createInstallArgs(packageUri, flags, packageName);
+            } else {
+                targetArgs = null;
+            }
+        }
+
+        public void handleStartCopy(IMediaContainerService imcs) {
+            // Create the file args now.
+            mRet = targetArgs.copyApk(imcs, false);
+            targetArgs.doPreInstall(mRet);
+            mHandler.sendEmptyMessage(MCS_UNBIND);
+            handleReturnCode();
+        }
+
+        void handleReturnCode() {
+            targetArgs.doPostInstall(mRet);
+            // TODO invoke pending move
+            processPendingMove(this, mRet);
+        }
+
+        public void handleServiceError() {
+            mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+            handleReturnCode();
+        }
     };
 
     private InstallArgs createInstallArgs(InstallParams params) {
@@ -4388,8 +4459,18 @@
         }
     }
 
+    private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName) {
+        if (installOnSd(flags)) {
+            String cid = getNextCodePath(null, pkgName, "/" + SdInstallArgs.RES_FILE_NAME);
+            return new SdInstallArgs(packageURI, cid);
+        } else {
+            return new FileInstallArgs(packageURI, pkgName);
+        }
+    }
+
     static abstract class InstallArgs {
         final IPackageInstallObserver observer;
+        // Always refers to PackageManager flags only
         final int flags;
         final Uri packageURI;
         final String installerPackageName;
@@ -4404,7 +4485,7 @@
         }
 
         abstract void createCopyFile();
-        abstract int copyApk(IMediaContainerService imcs);
+        abstract int copyApk(IMediaContainerService imcs, boolean temp);
         abstract int doPreInstall(int status);
         abstract boolean doRename(int status, String pkgName, String oldCodePath);
         abstract int doPostInstall(int status);
@@ -4419,6 +4500,7 @@
         File installDir;
         String codeFileName;
         String resourceFileName;
+        boolean created = false;
 
         FileInstallArgs(InstallParams params) {
             super(params.packageURI, params.observer,
@@ -4433,6 +4515,15 @@
             resourceFileName = fullResourcePath;
         }
 
+        FileInstallArgs(Uri packageURI, String pkgName) {
+            super(packageURI, null, 0, null);
+            boolean fwdLocked = isFwdLocked(flags);
+            installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
+            String apkName = getNextCodePath(null, pkgName, ".apk");
+            codeFileName = new File(installDir, apkName + ".apk").getPath();
+            resourceFileName = getResourcePathFromCodePath();
+        }
+
         String getCodePath() {
             return codeFileName;
         }
@@ -4442,11 +4533,29 @@
             installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
             codeFileName = createTempPackageFile(installDir).getPath();
             resourceFileName = getResourcePathFromCodePath();
+            created = true;
         }
 
-        int copyApk(IMediaContainerService imcs) {
+        int copyApk(IMediaContainerService imcs, boolean temp) {
+            if (temp) {
+                // Generate temp file name
+                createCopyFile();
+            }
             // Get a ParcelFileDescriptor to write to the output file
             File codeFile = new File(codeFileName);
+            if (!created) {
+                try {
+                    codeFile.createNewFile();
+                    // Set permissions
+                    if (!setPermissions()) {
+                        // Failed setting permissions.
+                        return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                    }
+                } catch (IOException e) {
+                   Log.w(TAG, "Failed to create file " + codeFile);
+                   return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                }
+            }
             ParcelFileDescriptor out = null;
             try {
             out = ParcelFileDescriptor.open(codeFile,
@@ -4491,7 +4600,7 @@
                 codeFileName = desFile.getPath();
                 resourceFileName = getResourcePathFromCodePath();
                 // Set permissions
-                if (!setPermissions(pkgName)) {
+                if (!setPermissions()) {
                     // Failed setting permissions.
                     return false;
                 }
@@ -4558,7 +4667,7 @@
             }
         }
 
-        private boolean setPermissions(String pkgName) {
+        private boolean setPermissions() {
             // TODO Do this in a more elegant way later on. for now just a hack
             if (!isFwdLocked(flags)) {
                 final int filePermissions =
@@ -4594,7 +4703,7 @@
         }
 
         SdInstallArgs(String fullCodePath, String fullResourcePath) {
-            super(null, null, ApplicationInfo.FLAG_ON_SDCARD, null);
+            super(null, null, PackageManager.INSTALL_EXTERNAL, null);
             // Extract cid from fullCodePath
             int eidx = fullCodePath.lastIndexOf("/");
             String subStr1 = fullCodePath.substring(0, eidx);
@@ -4604,7 +4713,12 @@
         }
 
         SdInstallArgs(String cid) {
-            super(null, null,  ApplicationInfo.FLAG_ON_SDCARD, null);
+            super(null, null, PackageManager.INSTALL_EXTERNAL, null);
+            this.cid = cid;
+        }
+
+        SdInstallArgs(Uri packageURI, String cid) {
+            super(packageURI, null, PackageManager.INSTALL_EXTERNAL, null);
             this.cid = cid;
         }
 
@@ -4612,7 +4726,10 @@
             cid = getTempContainerId();
         }
 
-        int copyApk(IMediaContainerService imcs) {
+        int copyApk(IMediaContainerService imcs, boolean temp) {
+            if (temp) {
+                createCopyFile();
+            }
             try {
                 cachePath = imcs.copyResourceToContainer(
                         packageURI, cid,
@@ -4771,8 +4888,8 @@
             if (sidx != -1) {
                 subStr = subStr.substring(sidx + prefix.length());
                 if (subStr != null) {
-                    if (subStr.startsWith("-")) {
-                        subStr = subStr.substring(1);
+                    if (subStr.startsWith(INSTALL_PACKAGE_SUFFIX)) {
+                        subStr = subStr.substring(INSTALL_PACKAGE_SUFFIX.length());
                     }
                     try {
                         idx = Integer.parseInt(subStr);
@@ -4786,10 +4903,26 @@
                 }
             }
         }
-        idxStr = "-" + Integer.toString(idx);
+        idxStr = INSTALL_PACKAGE_SUFFIX + Integer.toString(idx);
         return prefix + idxStr;
     }
 
+    // Utility method used to ignore ADD/REMOVE events
+    // by directory observer.
+    private static boolean ignoreCodePath(String fullPathStr) {
+        String apkName = getApkName(fullPathStr);
+        int idx = apkName.lastIndexOf(INSTALL_PACKAGE_SUFFIX);
+        if (idx != -1 && ((idx+1) < apkName.length())) {
+            // Make sure the package ends with a numeral
+            String version = apkName.substring(idx+1);
+            try {
+                Integer.parseInt(version);
+                return true;
+            } catch (NumberFormatException e) {}
+        }
+        return false;
+    }
+    
     // 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.
@@ -5055,6 +5188,19 @@
         }
     }
 
+    // Utility method used to move dex files during install.
+    private int moveDexFiles(PackageParser.Package newPackage) {
+        int retCode;
+        if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
+            retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
+            if (retCode != 0) {
+                Log.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
+                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            }
+        }
+        return PackageManager.INSTALL_SUCCEEDED;
+    }
+
     private void updateSettingsLI(PackageParser.Package newPackage,
             String installerPackageName, PackageInstalledInfo res) {
         String pkgName = newPackage.packageName;
@@ -5066,27 +5212,20 @@
             mSettings.writeLP();
         }
 
-        int retCode = 0;
-        if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
-            retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
-            if (retCode != 0) {
-                Log.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
-                res.returnCode =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                return;
-            }
+        if ((res.returnCode = moveDexFiles(newPackage))
+                != PackageManager.INSTALL_SUCCEEDED) {
+            // Discontinue if moving dex files failed.
+            return;
         }
-        res.returnCode = setPermissionsLI(newPackage);
-        if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+        if((res.returnCode = setPermissionsLI(newPackage))
+                != PackageManager.INSTALL_SUCCEEDED) {
+            if (mInstaller != null) {
+                mInstaller.rmdex(newPackage.mScanPath);
+            }
             return;
         } else {
             Log.d(TAG, "New package installed in " + newPackage.mPath);
         }
-        if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
-            if (mInstaller != null) {
-                mInstaller.rmdex(newPackage.mScanPath);
-            }
-        }
-
         synchronized (mPackages) {
             grantPermissionsLP(newPackage, true);
             res.name = pkgName;
@@ -6765,9 +6904,13 @@
         HashSet<String> loadedPermissions = new HashSet<String>();
 
         GrantedPermissions(int pkgFlags) {
+            setFlags(pkgFlags);
+        }
+
+        void setFlags(int pkgFlags) {
             this.pkgFlags = (pkgFlags & ApplicationInfo.FLAG_SYSTEM) |
-                    (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
-                    (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD);
+            (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
+            (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD);
         }
     }
 
@@ -8662,11 +8805,6 @@
                    uidArr[di++] = uidList[i];
                }
            }
-           if (true) {
-               for (int j = 0; j < num; j++) {
-                   Log.i(TAG, "uidArr[" + j + "]=" + uidArr[j]);
-               }
-           }
        }
        if (mediaStatus) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
@@ -8779,4 +8917,145 @@
            }
        }
    }
+
+   public void movePackage(final String packageName,
+           final IPackageMoveObserver observer, final int flags) {
+       if (packageName == null) {
+           return;
+       }
+       mContext.enforceCallingOrSelfPermission(
+               android.Manifest.permission.MOVE_PACKAGE, null);
+       int returnCode = PackageManager.MOVE_SUCCEEDED;
+       int currFlags = 0;
+       int newFlags = 0;
+       synchronized (mPackages) {
+           PackageParser.Package pkg = mPackages.get(packageName);
+           if (pkg == null) {
+               returnCode =  PackageManager.MOVE_FAILED_DOESNT_EXIST;
+           }
+           // Disable moving fwd locked apps and system packages
+           if (pkg.applicationInfo != null &&
+                   (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+               Log.w(TAG, "Cannot move system application");
+               returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
+           } else if (pkg.applicationInfo != null &&
+                   (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
+               Log.w(TAG, "Cannot move forward locked app.");
+               returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED;
+           } else {
+               // Find install location first
+               if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 &&
+                       (flags & PackageManager.MOVE_INTERNAL) != 0) {
+                   Log.w(TAG, "Ambigous flags specified for move location.");
+                   returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
+               } else {
+                   newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ?
+                           PackageManager.INSTALL_EXTERNAL : 0;
+                   currFlags = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0 ?
+                           PackageManager.INSTALL_EXTERNAL : 0;
+                   if (newFlags == currFlags) {
+                       Log.w(TAG, "No move required. Trying to move to same location");
+                       returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
+                   }
+               }
+           }
+           if (returnCode != PackageManager.MOVE_SUCCEEDED) {
+               processPendingMove(new MoveParams(null, observer, 0, null), returnCode);
+           } else {
+               Message msg = mHandler.obtainMessage(INIT_COPY);
+               InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
+                       pkg.applicationInfo.publicSourceDir);
+               MoveParams mp = new MoveParams(srcArgs, observer, newFlags,
+                       packageName);
+               msg.obj = mp;
+               mHandler.sendMessage(msg);
+           }
+       }
+   }
+
+   private void processPendingMove(final MoveParams mp, final int currentStatus) {
+       // Queue up an async operation since the package deletion may take a little while.
+       mHandler.post(new Runnable() {
+           public void run() {
+               mHandler.removeCallbacks(this);
+               int returnCode = currentStatus;
+               boolean moveSucceeded = (returnCode == PackageManager.MOVE_SUCCEEDED);
+               if (moveSucceeded) {
+                   int uid = -1;
+                   synchronized (mPackages) {
+                       uid = mPackages.get(mp.packageName).applicationInfo.uid;
+                   }
+                   ArrayList<String> pkgList = new ArrayList<String>();
+                   pkgList.add(mp.packageName);
+                   int uidArr[] = new int[] { uid };
+                   // Send resources unavailable broadcast
+                   sendResourcesChangedBroadcast(false, pkgList, uidArr);
+
+                   // Update package code and resource paths
+                   synchronized (mPackages) {
+                       PackageParser.Package pkg = mPackages.get(mp.packageName);
+                       if (pkg != null) {
+                           String oldCodePath = pkg.mPath;
+                           String newCodePath = mp.targetArgs.getCodePath();
+                           String newResPath = mp.targetArgs.getResourcePath();
+                           pkg.mPath = newCodePath;
+                           // Move dex files around
+                           if (moveDexFiles(pkg)
+                                   != PackageManager.INSTALL_SUCCEEDED) {
+                               // Moving of dex files failed. Set
+                               // error code and abort move.
+                               pkg.mPath = pkg.mScanPath;
+                               returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+                               moveSucceeded = false;
+                           } else {
+                               pkg.mScanPath = newCodePath;
+                               pkg.applicationInfo.sourceDir = newCodePath;
+                               pkg.applicationInfo.publicSourceDir = newResPath;
+                               PackageSetting ps = (PackageSetting) pkg.mExtras;
+                               ps.codePath = new File(pkg.applicationInfo.sourceDir);
+                               ps.codePathString = ps.codePath.getPath();
+                               ps.resourcePath = new File(pkg.applicationInfo.publicSourceDir);
+                               ps.resourcePathString = ps.resourcePath.getPath();
+                               // Set the application info flag correctly.
+                               if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+                                   pkg.applicationInfo.flags |= ApplicationInfo.FLAG_ON_SDCARD;
+                               } else {
+                                   pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_ON_SDCARD;
+                               }
+                               ps.setFlags(pkg.applicationInfo.flags);
+                               mAppDirs.remove(oldCodePath);
+                               mAppDirs.put(newCodePath, pkg);
+                               // Persist settings
+                               mSettings.writeLP();
+                           }
+                       }
+                   }
+                   if (moveSucceeded) {
+                       // Delete older code
+                       synchronized (mInstallLock) {
+                           mp.srcArgs.cleanUpResourcesLI();
+                       }
+                       // Send resources available broadcast
+                       sendResourcesChangedBroadcast(true, pkgList, uidArr);
+                       Runtime.getRuntime().gc();
+                   }
+               }
+               if (!moveSucceeded){
+                   // Clean up failed installation
+                   if (mp.targetArgs != null) {
+                       mp.targetArgs.doPostInstall(
+                               PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+                   }
+               }
+               IPackageMoveObserver observer = mp.observer;
+               if (observer != null) {
+                   try {
+                       observer.packageMoved(mp.packageName, returnCode);
+                   } catch (RemoteException e) {
+                       Log.i(TAG, "Observer no longer exists.");
+                   }
+               }
+           }
+       });
+   }
 }
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index f1ba44a..2ccc9bb 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -26,6 +26,7 @@
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
@@ -308,6 +309,14 @@
             int flags, String installerPackageName) {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+        throw new UnsupportedOperationException();
+    }
     
     @Override
     public String getInstallerPackageName(String packageName) {
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index 28f1e73..e06c3a8 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -28,6 +28,7 @@
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
     <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.MOVE_PACKAGE" />
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.ASEC_ACCESS" />
     <uses-permission android:name="android.permission.ASEC_CREATE" />
diff --git a/tests/AndroidTests/res/raw/install_loc_auto b/tests/AndroidTests/res/raw/install_loc_auto
index 60dda18..d5d2739 100644
--- a/tests/AndroidTests/res/raw/install_loc_auto
+++ b/tests/AndroidTests/res/raw/install_loc_auto
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_internal b/tests/AndroidTests/res/raw/install_loc_internal
index 1bc33ca..eb6279a 100644
--- a/tests/AndroidTests/res/raw/install_loc_internal
+++ b/tests/AndroidTests/res/raw/install_loc_internal
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_sdcard b/tests/AndroidTests/res/raw/install_loc_sdcard
index 6604e35..c774989 100644
--- a/tests/AndroidTests/res/raw/install_loc_sdcard
+++ b/tests/AndroidTests/res/raw/install_loc_sdcard
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_unspecified b/tests/AndroidTests/res/raw/install_loc_unspecified
index 88bbace..ab226c6 100644
--- a/tests/AndroidTests/res/raw/install_loc_unspecified
+++ b/tests/AndroidTests/res/raw/install_loc_unspecified
Binary files differ
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index be9571c..9c5c44d 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -37,6 +37,7 @@
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstallObserver;
 import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageMoveObserver;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
@@ -159,6 +160,7 @@
         PackageInstallObserver observer = new PackageInstallObserver();
         final boolean received = false;
         mContext.registerReceiver(receiver, receiver.filter);
+        final boolean DEBUG = true;
         try {
             // Wait on observer
             synchronized(observer) {
@@ -173,6 +175,7 @@
                         throw new Exception("Timed out waiting for packageInstalled callback");
                     }
                     if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+                        Log.i(TAG, "Failed to install with error code = " + observer.returnCode);
                         return false;
                     }
                     // Verify we received the broadcast
@@ -237,7 +240,9 @@
         File sourceFile = new File(archiveFilePath);
         DisplayMetrics metrics = new DisplayMetrics();
         metrics.setToDefaults();
-        return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+        PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+        packageParser = null;
+        return pkg;
     }
 
     private void assertInstall(String pkgName, int flags) {
@@ -297,6 +302,25 @@
         return installFromRawResource("install.apk", R.raw.install, flags, cleanUp,
                 false, -1);
     }
+
+    public void clearSecureContainersForPkg(String pkgName) {
+        IMountService ms = getMs();
+        try {
+            String list[] = ms.getSecureContainerList();
+            if (list != null) {
+                for (String cid : list) {
+                    boolean delete = false;
+                    // STOPSHIP issues with rename should be fixed.
+                    if (cid.contains(pkgName) ||
+                            cid.contains("smdltmp")) {
+                        Log.i(TAG, "Destroying container " + cid);
+                        ms.destroySecureContainer(cid, true);
+                    }
+                }
+            }
+        } catch (RemoteException e) {}
+    }
+
     /*
      * Utility function that reads a apk bundled as a raw resource
      * copies it into own data directory and invokes
@@ -310,11 +334,15 @@
         PackageParser.Package pkg = parsePackage(packageURI);
         assertNotNull(pkg);
         InstallParams ip = null;
+        // Make sure the package doesn't exist
+        getPm().deletePackage(pkg.packageName, null, 0);
+        // Clean up the containers as well
+        if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+            clearSecureContainersForPkg(pkg.packageName);
+        }
         try {
             try {
                 if (fail) {
-                    // Make sure it doesn't exist
-                    getPm().deletePackage(pkg.packageName, null, 0);
                     assertTrue(invokeInstallPackageFail(packageURI, flags,
                             pkg.packageName, result));
                     assertNotInstalled(pkg.packageName);
@@ -811,7 +839,7 @@
 
     public void testManifestInstallLocationSdcard() {
         installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                PackageManager.INSTALL_EXTERNAL, true, false, -1);
+                0, true, false, -1);
     }
 
     public void testManifestInstallLocationAuto() {
@@ -826,10 +854,186 @@
 
     public void testManifestInstallLocationFwdLockedSdcard() {
         installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                PackageManager.INSTALL_FORWARD_LOCK |
-                PackageManager.INSTALL_EXTERNAL, true, true,
+                PackageManager.INSTALL_FORWARD_LOCK, true, true,
                 PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION);
     }
+
+    public void xxxtestClearAllSecureContainers() {
+        IMountService ms = getMs();
+        try {
+            String list[] = ms.getSecureContainerList();
+            if (list != null) {
+                for (String cid : list) {
+                    Log.i(TAG, "Destroying container " + cid);
+                    ms.destroySecureContainer(cid, false);
+                }
+            }
+        } catch (RemoteException e) {}
+    }
+
+    class MoveReceiver extends GenericReceiver {
+        String pkgName;
+        final static int INVALID = -1;
+        final static int REMOVED = 1;
+        final static int ADDED = 2;
+        int removed = INVALID;
+
+        MoveReceiver(String pkgName) {
+            this.pkgName = pkgName;
+            filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+            filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            Log.i(TAG, "MoveReceiver::" + action);
+            if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                String[] list = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                if (list != null) {
+                    for (String pkg : list) {
+                        if (pkg.equals(pkgName)) {
+                            removed = REMOVED;
+                            break;
+                        }
+                    }
+                }
+                removed = REMOVED;
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+                if (removed != REMOVED) {
+                    return false;
+                }
+                String[] list = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                if (list != null) {
+                    for (String pkg : list) {
+                        if (pkg.equals(pkgName)) {
+                            removed = ADDED;
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    private class PackageMoveObserver extends IPackageMoveObserver.Stub {
+        public int returnCode;
+        private boolean doneFlag = false;
+
+        public void packageMoved(String packageName, int returnCode) {
+            synchronized(this) {
+                this.returnCode = returnCode;
+                doneFlag = true;
+                notifyAll();
+            }
+        }
+
+        public boolean isDone() {
+            return doneFlag;
+        }
+    }
+
+    public boolean invokeMovePackage(String pkgName, int flags,
+            GenericReceiver receiver) throws Exception {
+        PackageMoveObserver observer = new PackageMoveObserver();
+        final boolean received = false;
+        mContext.registerReceiver(receiver, receiver.filter);
+        try {
+            // Wait on observer
+            synchronized(observer) {
+                synchronized (receiver) {
+                    getPm().movePackage(pkgName, observer, flags);
+                    long waitTime = 0;
+                    while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                        observer.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    }
+                    if(!observer.isDone()) {
+                        throw new Exception("Timed out waiting for pkgmove callback");
+                    }
+                    if (observer.returnCode != PackageManager.MOVE_SUCCEEDED) {
+                        return false;
+                    }
+                    // Verify we received the broadcast
+                    waitTime = 0;
+                    while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                        receiver.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    }
+                    if(!receiver.isDone()) {
+                        throw new Exception("Timed out waiting for MOVE notifications");
+                    }
+                    return receiver.received;
+                }
+            }
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    /*
+     * Utility function that reads a apk bundled as a raw resource
+     * copies it into own data directory and invokes
+     * PackageManager api to install first and then replace it
+     * again.
+     */
+    public void moveFromRawResource(int installFlags, int moveFlags,
+            int expRetCode) {
+        // Install first
+        InstallParams ip = sampleInstallFromRawResource(installFlags, false);
+        ApplicationInfo oldAppInfo = null;
+        try {
+            oldAppInfo = getPm().getApplicationInfo(ip.pkg.packageName, 0);
+        } catch (NameNotFoundException e) {
+            failStr("Pkg hasnt been installed correctly");
+        }
+
+        // Create receiver based on expRetCode
+        MoveReceiver receiver = new MoveReceiver(ip.pkg.packageName);
+        try {
+            boolean retCode = invokeMovePackage(ip.pkg.packageName, moveFlags,
+                    receiver);
+            if (expRetCode == PackageManager.MOVE_SUCCEEDED) {
+                assertTrue(retCode);
+                ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.packageName, 0);
+                assertNotNull(info);
+                if ((moveFlags & PackageManager.MOVE_INTERNAL) != 0) {
+                    assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) == 0);
+                } else if ((moveFlags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0){
+                    assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+                }
+            } else {
+                assertFalse(retCode);
+                ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.packageName, 0);
+                assertNotNull(info);
+                assertEquals(oldAppInfo.flags, info.flags);
+            }
+        } catch (Exception e) {
+            failStr("Failed with exception : " + e);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    public void testMoveAppInternalToExternal() {
+        moveFromRawResource(0, PackageManager.MOVE_EXTERNAL_MEDIA,
+                PackageManager.MOVE_SUCCEEDED);
+    }
+
+    public void testMoveAppInternalToInternal() {
+        moveFromRawResource(0, PackageManager.MOVE_INTERNAL,
+                PackageManager.MOVE_FAILED_INVALID_LOCATION);
+    }
+
+    public void testMoveAppExternalToExternal() {
+        moveFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.MOVE_EXTERNAL_MEDIA,
+                PackageManager.MOVE_FAILED_INVALID_LOCATION);
+    }
+    public void testMoveAppExternalToInternal() {
+        moveFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.MOVE_INTERNAL,
+                PackageManager.MOVE_SUCCEEDED);
+    }
     /*
      * TODO's
      * check version numbers for upgrades