Move package from internal to external and vice versa.
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.");
+                   }
+               }
+           }
+       });
+   }
 }