Only allow one movePackage operation in-flight

When a movePackage operation is requested, don't allow multiple requests
to pile up for one package. Once a move is completed, an observer will
receive the message and be allowed to call movePackage again.

Change-Id: Ie3842b6d96446febc0037bf9b8f1ca250735edc2
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 15a446b..b14555a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -610,6 +610,16 @@
     public static final int MOVE_FAILED_INTERNAL_ERROR = -6;
 
     /**
+     * Error code that is passed to the {@link IPackageMoveObserver} by
+     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} if the
+     * specified package already has an operation pending in the
+     * {@link PackageHandler} queue.
+     * 
+     * @hide
+     */
+    public static final int MOVE_FAILED_OPERATION_PENDING = -7;
+
+    /**
      * Flag parameter for {@link #movePackage} to indicate that
      * the package should be moved to internal storage if its
      * been installed on external media.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0b35d8b..47e668d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2791,7 +2791,10 @@
 
         // Additional data supplied by callers.
         public Object mExtras;
-        
+
+        // Whether an operation is currently pending on this package
+        public boolean mOperationPending;
+
         /*
          *  Applications hardware preferences
          */
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index ff4ff74..7827d26 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -7202,6 +7202,9 @@
                     pw.print("    pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
                             pw.print(" installStatus="); pw.print(ps.installStatus);
                             pw.print(" enabled="); pw.println(ps.enabled);
+                    if (ps.pkg.mOperationPending) {
+                        pw.println("    mOperationPending=true");
+                    }
                     if (ps.disabledComponents.size() > 0) {
                         pw.println("    disabledComponents:");
                         for (String s : ps.disabledComponents) {
@@ -9889,6 +9892,9 @@
                        (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
                    Slog.w(TAG, "Cannot move forward locked app.");
                    returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED;
+               } else if (pkg.mOperationPending) {
+                   Slog.w(TAG, "Attempt to move package which has pending operations");
+                   returnCode = PackageManager.MOVE_FAILED_OPERATION_PENDING;
                } else {
                    // Find install location first
                    if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 &&
@@ -9905,6 +9911,9 @@
                            returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
                        }
                    }
+                   if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+                       pkg.mOperationPending = true;
+                   }
                }
            }
            if (returnCode != PackageManager.MOVE_SUCCEEDED) {
@@ -10017,6 +10026,18 @@
                        mp.srcArgs.doPostDeleteLI(true);
                    }
                }
+
+               // Allow more operations on this file if we didn't fail because
+               // an operation was already pending for this package.
+               if (returnCode != PackageManager.MOVE_FAILED_OPERATION_PENDING) {
+                   synchronized (mPackages) {
+                       PackageParser.Package pkg = mPackages.get(mp.packageName);
+                       if (pkg != null) {
+                           pkg.mOperationPending = false;
+                       }
+                   }
+               }
+
                IPackageMoveObserver observer = mp.observer;
                if (observer != null) {
                    try {