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) {