New "frozen" state during app move/upgrade.

This replaces mOperationPending, which was in an odd place.  It adds
a new PackageSetting.frozen flag that is a last-ditch effort to
prevent ActivityManager from starting an app while it's being moved
or upgraded.

Also provides clearer guarding around all upgrades by freezing,
killing, upgrading, then unfreezing.

Bug: 20275579
Change-Id: I28bb0359a6f4e05080fb336b18dd2a249509d989
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ae59bfc..0b24594 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -58,6 +58,7 @@
  *  {@hide}
  */
 interface IPackageManager {
+    boolean isPackageFrozen(String packageName);
     boolean isPackageAvailable(String packageName, int userId);
     PackageInfo getPackageInfo(String packageName, int flags, int userId);
     int getPackageUid(String packageName, int userId);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index fed9261..9596c42 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4315,9 +4315,6 @@
         // Additional data supplied by callers.
         public Object mExtras;
 
-        // Whether an operation is currently pending on this package
-        public boolean mOperationPending;
-
         // Applications hardware preferences
         public ArrayList<ConfigurationInfo> configPreferences = null;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 50f7328..ddcfb15 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3113,8 +3113,16 @@
         checkTime(startTime, "startProcess: done updating cpu stats");
 
         try {
-            int uid = app.uid;
+            try {
+                if (AppGlobals.getPackageManager().isPackageFrozen(app.info.packageName)) {
+                    // This is caught below as if we had failed to fork zygote
+                    throw new RuntimeException("Package " + app.info.packageName + " is frozen!");
+                }
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
 
+            int uid = app.uid;
             int[] gids = null;
             int mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
             if (!app.isolated) {
@@ -3124,7 +3132,7 @@
                     permGids = AppGlobals.getPackageManager().getPackageGids(app.info.packageName,
                             app.userId);
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "Unable to retrieve gids", e);
+                    throw e.rethrowAsRuntimeException();
                 }
 
                 /*
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 453b817..ac3f53f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -299,7 +299,6 @@
     static final int SCAN_BOOTING = 1<<8;
     static final int SCAN_TRUSTED_OVERLAY = 1<<9;
     static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<10;
-    static final int SCAN_REPLACING = 1<<11;
     static final int SCAN_REQUIRE_KNOWN = 1<<12;
 
     static final int REMOVE_CHATTY = 1<<16;
@@ -2380,6 +2379,18 @@
     }
 
     @Override
+    public boolean isPackageFrozen(String packageName) {
+        synchronized (mPackages) {
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps != null) {
+                return ps.frozen;
+            }
+        }
+        Slog.w(TAG, "Package " + packageName + " is missing; assuming frozen");
+        return true;
+    }
+
+    @Override
     public boolean isPackageAvailable(String packageName, int userId) {
         if (!sUserManager.exists(userId)) return false;
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "is package available");
@@ -6482,14 +6493,6 @@
             }
         }
 
-        // 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.
-        if ((scanFlags & SCAN_REPLACING) != 0) {
-            killApplication(pkg.applicationInfo.packageName,
-                        pkg.applicationInfo.uid, "update pkg");
-        }
-
         // Also need to kill any apps that are dependent on the library.
         if (clientLibPkgs != null) {
             for (int i=0; i<clientLibPkgs.size(); i++) {
@@ -10797,16 +10800,17 @@
     private void replacePackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
             UserHandle user, String installerPackageName, String volumeUuid,
             PackageInstalledInfo res) {
-        PackageParser.Package oldPackage;
-        String pkgName = pkg.packageName;
-        int[] allUsers;
-        boolean[] perUserInstalled;
+        final PackageParser.Package oldPackage;
+        final String pkgName = pkg.packageName;
+        final int[] allUsers;
+        final boolean[] perUserInstalled;
+        final boolean weFroze;
 
         // First find the old package info and check signatures
         synchronized(mPackages) {
             oldPackage = mPackages.get(pkgName);
             if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
-            PackageSetting ps = mSettings.mPackages.get(pkgName);
+            final PackageSetting ps = mSettings.mPackages.get(pkgName);
             if (ps == null || !ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {
                 // default to original signature matching
                 if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
@@ -10830,15 +10834,35 @@
             for (int i = 0; i < allUsers.length; i++) {
                 perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;
             }
+
+            // Mark the app as frozen to prevent launching during the upgrade
+            // process, and then kill all running instances
+            if (!ps.frozen) {
+                ps.frozen = true;
+                weFroze = true;
+            } else {
+                weFroze = false;
+            }
         }
 
-        boolean sysPkg = (isSystemApp(oldPackage));
-        if (sysPkg) {
-            replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
-                    user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
-        } else {
-            replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
-                    user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
+        // Now that we're guarded by frozen state, kill app during upgrade
+        killApplication(pkgName, oldPackage.applicationInfo.uid, "replace pkg");
+
+        try {
+            boolean sysPkg = (isSystemApp(oldPackage));
+            if (sysPkg) {
+                replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
+                        user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
+            } else {
+                replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
+                        user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
+            }
+        } finally {
+            // Regardless of success or failure of upgrade steps above, always
+            // unfreeze the package if we froze it
+            if (weFroze) {
+                unfreezePackage(pkgName);
+            }
         }
     }
 
@@ -10968,8 +10992,6 @@
             }
         }
 
-        killApplication(packageName, oldPkg.applicationInfo.uid, "replace sys pkg");
-
         res.removedInfo.uid = oldPkg.applicationInfo.uid;
         res.removedInfo.removedPackage = packageName;
         // Remove existing system package
@@ -11314,7 +11336,7 @@
         startIntentFilterVerifications(args.user.getIdentifier(), pkg);
 
         if (replace) {
-            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
+            replacePackageLI(pkg, parseFlags, scanFlags, args.user,
                     installerPackageName, volumeUuid, res);
         } else {
             installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
@@ -14200,6 +14222,15 @@
         sendResourcesChangedBroadcast(false, false, unloaded, null);
     }
 
+    private void unfreezePackage(String packageName) {
+        synchronized (mPackages) {
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps != null) {
+                ps.frozen = false;
+            }
+        }
+    }
+
     @Override
     public int movePackage(final String packageName, final String volumeUuid) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
@@ -14240,14 +14271,19 @@
             if (pkg.applicationInfo.isSystemApp()) {
                 throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
                         "Cannot move system application");
-            } else if (pkg.mOperationPending) {
-                throw new PackageManagerException(MOVE_FAILED_OPERATION_PENDING,
-                        "Attempt to move package which has pending operations");
             }
 
-            // TODO: yell if already in desired location
+            if (Objects.equals(ps.volumeUuid, volumeUuid)) {
+                throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+                        "Package already moved to " + volumeUuid);
+            }
 
-            pkg.mOperationPending = true;
+            if (ps.frozen) {
+                throw new PackageManagerException(MOVE_FAILED_OPERATION_PENDING,
+                        "Failed to move already frozen package");
+            }
+
+            ps.frozen = true;
 
             currentAsec = pkg.applicationInfo.isForwardLocked()
                     || pkg.applicationInfo.isExternalAsec();
@@ -14260,6 +14296,9 @@
             label = String.valueOf(pm.getApplicationLabel(pkg.applicationInfo));
         }
 
+        // Now that we're guarded by frozen state, kill app during upgrade
+        killApplication(packageName, appId, "move pkg");
+
         final Bundle extras = new Bundle();
         extras.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
         extras.putString(Intent.EXTRA_TITLE, label);
@@ -14279,6 +14318,7 @@
             final VolumeInfo volume = storage.findVolumeByUuid(volumeUuid);
             if (volume == null || volume.getType() != VolumeInfo.TYPE_PRIVATE
                     || !volume.isMountedWritable()) {
+                unfreezePackage(packageName);
                 throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
                         "Move location not mounted private volume");
             }
@@ -14297,15 +14337,9 @@
                 // TODO: split this into separate copy and delete operations
                 if (mInstaller.moveUserDataDirs(currentVolumeUuid, volumeUuid, packageName, appId,
                         seinfo) != 0) {
-                    synchronized (mPackages) {
-                        final PackageParser.Package pkg = mPackages.get(packageName);
-                        if (pkg != null) {
-                            pkg.mOperationPending = false;
-                        }
-                    }
-
+                    unfreezePackage(packageName);
                     throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
-                            "Failed to move private data");
+                            "Failed to move private data to " + volumeUuid);
                 }
             }
         }
@@ -14324,15 +14358,9 @@
                 Slog.d(TAG, "Install result for move: "
                         + PackageManager.installStatusToString(returnCode, msg));
 
-                // We usually have a new package now after the install, but if
-                // we failed we need to clear the pending flag on the original
-                // package object.
-                synchronized (mPackages) {
-                    final PackageParser.Package pkg = mPackages.get(packageName);
-                    if (pkg != null) {
-                        pkg.mOperationPending = false;
-                    }
-                }
+                // Regardless of success or failure of the move operation,
+                // always unfreeze the package
+                unfreezePackage(packageName);
 
                 final int status = PackageManager.installStatusToPublicStatus(returnCode);
                 switch (status) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 5429517..f62c00c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -108,6 +108,13 @@
 
     int installStatus = PKG_INSTALL_COMPLETE;
 
+    /**
+     * Non-persisted value indicating this package has been temporarily frozen,
+     * usually during a critical section of the package update pipeline. The
+     * platform will refuse to launch packages in a frozen state.
+     */
+    boolean frozen = false;
+
     PackageSettingBase origPackage;
 
     /** Package name of the app that installed this package */
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 252c16a..f9c248d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3759,6 +3759,10 @@
             pw.print(Integer.toHexString(System.identityHashCode(ps)));
             pw.println("):");
 
+        if (ps.frozen) {
+            pw.print(prefix); pw.println("  FROZEN!");
+        }
+
         if (ps.realName != null) {
             pw.print(prefix); pw.print("  compat name=");
             pw.println(ps.name);
@@ -3790,9 +3794,6 @@
             pw.print(prefix); pw.print("  priavateFlags="); printFlags(pw,
                     ps.pkg.applicationInfo.privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println();
             pw.print(prefix); pw.print("  dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
-            if (ps.pkg.mOperationPending) {
-                pw.print(prefix); pw.println("  mOperationPending=true");
-            }
             pw.print(prefix); pw.print("  supportsScreens=[");
             boolean first = true;
             if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {