diff options
4 files changed, 461 insertions, 434 deletions
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java index a9f44b9bbeb1..1123f70b4334 100644 --- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java +++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java @@ -51,7 +51,7 @@ public class AppDataRollbackHelper { * {@code userIds}. Updates said {@code packageRollbackInfo} with the inodes of the CE user data * snapshot folders. */ - @GuardedBy("rollback.getLock") + @GuardedBy("rollback.mLock") // TODO(b/136241838): Move into Rollback and synchronize there. public void snapshotAppData( int snapshotId, PackageRollbackInfo packageRollbackInfo, int[] userIds) { @@ -88,7 +88,7 @@ public class AppDataRollbackHelper { * to {@code packageRollbackInfo} are restricted to the removal or addition of {@code * userId} to the list of pending backups or restores. */ - @GuardedBy("rollback.getLock") + @GuardedBy("rollback.mLock") // TODO(b/136241838): Move into Rollback and synchronize there. public boolean restoreAppData(int rollbackId, PackageRollbackInfo packageRollbackInfo, int userId, int appId, String seInfo) { @@ -133,7 +133,7 @@ public class AppDataRollbackHelper { * Deletes an app data snapshot with a given {@code rollbackId} for a specified package * {@code packageName} for a given {@code user}. */ - @GuardedBy("rollback.getLock") + @GuardedBy("rollback.mLock") // TODO(b/136241838): Move into Rollback and synchronize there. public void destroyAppDataSnapshot(int rollbackId, PackageRollbackInfo packageRollbackInfo, int user) { @@ -162,7 +162,7 @@ public class AppDataRollbackHelper { * * @return true if any backups or restores were found for the userId */ - @GuardedBy("rollback.getLock") + @GuardedBy("rollback.mLock") boolean commitPendingBackupAndRestoreForUser(int userId, Rollback rollback) { boolean foundBackupOrRestore = false; for (PackageRollbackInfo info : rollback.info.getPackages()) { diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 78b8b2e78772..1cf07cb47f2f 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -40,6 +40,7 @@ import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.IndentingPrintWriter; import java.io.File; import java.io.IOException; @@ -53,11 +54,6 @@ import java.util.List; /** * Information about a rollback available for a set of atomically installed packages. - * - * <p>When accessing the state of a Rollback object, the caller is responsible for synchronization. - * The lock object provided by {@link #getLock} should be acquired when accessing any of the mutable - * state of a Rollback, including from the {@link RollbackInfo} and any of the - * {@link PackageRollbackInfo} objects held within. */ class Rollback { @@ -101,11 +97,7 @@ class Rollback { /** * The rollback info for this rollback. - * - * <p>Any access to this field that touches any mutable state should be synchronized on - * {@link #getLock}. */ - @GuardedBy("getLock") public final RollbackInfo info; /** @@ -146,8 +138,6 @@ class Rollback { /** * Lock object to guard all access to Rollback state. - * - * @see #getLock */ private final Object mLock = new Object(); @@ -185,23 +175,8 @@ class Rollback { } /** - * Returns a lock object that should be acquired before accessing any Rollback state from - * {@link RollbackManagerServiceImpl}. - * - * <p>Note that while holding this lock, the lock for {@link RollbackManagerServiceImpl} should - * not be acquired (but it is ok to acquire this lock while already holding the lock for that - * class). - */ - // TODO(b/136241838): Move rollback functionality into this class and synchronize on the lock - // internally. Remove this method once this has been done for all cases. - Object getLock() { - return mLock; - } - - /** * Whether the rollback is for rollback of a staged install. */ - @GuardedBy("getLock") boolean isStaged() { return info.isStaged(); } @@ -216,17 +191,20 @@ class Rollback { /** * Returns the time when the upgrade occurred, for purposes of expiring rollback data. */ - @GuardedBy("getLock") Instant getTimestamp() { - return mTimestamp; + synchronized (mLock) { + return mTimestamp; + } } /** * Sets the time at which upgrade occurred. */ - @GuardedBy("getLock") void setTimestamp(Instant timestamp) { - mTimestamp = timestamp; + synchronized (mLock) { + mTimestamp = timestamp; + RollbackStore.saveRollback(this); + } } /** @@ -240,33 +218,46 @@ class Rollback { /** * Returns true if the rollback is in the ENABLING state. */ - @GuardedBy("getLock") boolean isEnabling() { - return mState == ROLLBACK_STATE_ENABLING; + synchronized (mLock) { + return mState == ROLLBACK_STATE_ENABLING; + } } /** * Returns true if the rollback is in the AVAILABLE state. */ - @GuardedBy("getLock") boolean isAvailable() { - return mState == ROLLBACK_STATE_AVAILABLE; + synchronized (mLock) { + return mState == ROLLBACK_STATE_AVAILABLE; + } } /** * Returns true if the rollback is in the COMMITTED state. */ - @GuardedBy("getLock") boolean isCommitted() { - return mState == ROLLBACK_STATE_COMMITTED; + synchronized (mLock) { + return mState == ROLLBACK_STATE_COMMITTED; + } } /** * Returns true if the rollback is in the DELETED state. */ - @GuardedBy("getLock") boolean isDeleted() { - return mState == ROLLBACK_STATE_DELETED; + synchronized (mLock) { + return mState == ROLLBACK_STATE_DELETED; + } + } + + /** + * Saves this rollback to persistent storage. + */ + void saveRollback() { + synchronized (mLock) { + RollbackStore.saveRollback(this); + } } /** @@ -274,7 +265,6 @@ class Rollback { * * @return boolean True if the rollback was enabled successfully for the specified package. */ - @GuardedBy("getLock") boolean enableForPackage(String packageName, long newVersion, long installedVersion, boolean isApex, String sourceDir, String[] splitSourceDirs) { try { @@ -295,7 +285,9 @@ class Rollback { new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */, isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */); - info.getPackages().add(packageRollbackInfo); + synchronized (mLock) { + info.getPackages().add(packageRollbackInfo); + } return true; } @@ -304,19 +296,33 @@ class Rollback { * Snapshots user data for the provided package and user ids. Does nothing if this rollback is * not in the ENABLING state. */ - @GuardedBy("getLock") void snapshotUserData(String packageName, int[] userIds, AppDataRollbackHelper dataHelper) { - if (!isEnabling()) { - return; - } + synchronized (mLock) { + if (!isEnabling()) { + return; + } + + for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { + if (pkgRollbackInfo.getPackageName().equals(packageName)) { + dataHelper.snapshotAppData(info.getRollbackId(), pkgRollbackInfo, userIds); - for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { - if (pkgRollbackInfo.getPackageName().equals(packageName)) { - dataHelper.snapshotAppData(info.getRollbackId(), pkgRollbackInfo, userIds); + RollbackStore.saveRollback(this); + pkgRollbackInfo.getSnapshottedUsers().addAll(IntArray.wrap(userIds)); + break; + } + } + } + } + /** + * Commits the pending backups and restores for a given {@code userId}. If this rollback has a + * pending backup, it is updated with a mapping from {@code userId} to inode of the CE user data + * snapshot. + */ + void commitPendingBackupAndRestoreForUser(int userId, AppDataRollbackHelper dataHelper) { + synchronized (mLock) { + if (dataHelper.commitPendingBackupAndRestoreForUser(userId, this)) { RollbackStore.saveRollback(this); - pkgRollbackInfo.getSnapshottedUsers().addAll(IntArray.wrap(userIds)); - break; } } } @@ -326,168 +332,170 @@ class Rollback { * current time and saves the rollback. Does nothing if this rollback is already in the * DELETED state. */ - @GuardedBy("getLock") void makeAvailable() { - if (isDeleted()) { - Slog.w(TAG, "Cannot make deleted rollback available."); - return; + synchronized (mLock) { + if (isDeleted()) { + Slog.w(TAG, "Cannot make deleted rollback available."); + return; + } + mState = ROLLBACK_STATE_AVAILABLE; + mTimestamp = Instant.now(); + RollbackStore.saveRollback(this); } - mState = ROLLBACK_STATE_AVAILABLE; - mTimestamp = Instant.now(); - RollbackStore.saveRollback(this); } /** * Commits the rollback. */ - @GuardedBy("getLock") void commit(final Context context, List<VersionedPackage> causePackages, String callerPackageName, IntentSender statusReceiver) { - - if (!isAvailable()) { - sendFailure(context, statusReceiver, - RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, - "Rollback unavailable"); - return; - } - - // Get a context to use to install the downgraded version of the package. - Context pkgContext; - try { - pkgContext = context.createPackageContext(callerPackageName, 0); - } catch (PackageManager.NameNotFoundException e) { - sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, - "Invalid callerPackageName"); - return; - } - - PackageManager pm = pkgContext.getPackageManager(); - try { - PackageInstaller packageInstaller = pm.getPackageInstaller(); - PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams( - PackageInstaller.SessionParams.MODE_FULL_INSTALL); - parentParams.setRequestDowngrade(true); - parentParams.setMultiPackage(); - if (isStaged()) { - parentParams.setStaged(); + synchronized (mLock) { + if (!isAvailable()) { + sendFailure(context, statusReceiver, + RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, + "Rollback unavailable"); + return; } - int parentSessionId = packageInstaller.createSession(parentParams); - PackageInstaller.Session parentSession = packageInstaller.openSession( - parentSessionId); + // Get a context to use to install the downgraded version of the package. + Context pkgContext; + try { + pkgContext = context.createPackageContext(callerPackageName, 0); + } catch (PackageManager.NameNotFoundException e) { + sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, + "Invalid callerPackageName"); + return; + } - for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { - PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( + PackageManager pm = pkgContext.getPackageManager(); + try { + PackageInstaller packageInstaller = pm.getPackageInstaller(); + PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); - // TODO: We can't get the installerPackageName for apex - // (b/123920130). Is it okay to ignore the installer package - // for apex? - if (!pkgRollbackInfo.isApex()) { - String installerPackageName = - pm.getInstallerPackageName(pkgRollbackInfo.getPackageName()); - if (installerPackageName != null) { - params.setInstallerPackageName(installerPackageName); - } - } - params.setRequestDowngrade(true); - params.setRequiredInstalledVersionCode( - pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode()); + parentParams.setRequestDowngrade(true); + parentParams.setMultiPackage(); if (isStaged()) { - params.setStaged(); - } - if (pkgRollbackInfo.isApex()) { - params.setInstallAsApex(); - } - int sessionId = packageInstaller.createSession(params); - PackageInstaller.Session session = packageInstaller.openSession(sessionId); - File[] packageCodePaths = RollbackStore.getPackageCodePaths( - this, pkgRollbackInfo.getPackageName()); - if (packageCodePaths == null) { - sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, - "Backup copy of package inaccessible"); - return; + parentParams.setStaged(); } - for (File packageCodePath : packageCodePaths) { - try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath, - ParcelFileDescriptor.MODE_READ_ONLY)) { - final long token = Binder.clearCallingIdentity(); - try { - session.write(packageCodePath.getName(), 0, - packageCodePath.length(), - fd); - } finally { - Binder.restoreCallingIdentity(token); + int parentSessionId = packageInstaller.createSession(parentParams); + PackageInstaller.Session parentSession = packageInstaller.openSession( + parentSessionId); + + for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { + PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( + PackageInstaller.SessionParams.MODE_FULL_INSTALL); + // TODO: We can't get the installerPackageName for apex + // (b/123920130). Is it okay to ignore the installer package + // for apex? + if (!pkgRollbackInfo.isApex()) { + String installerPackageName = + pm.getInstallerPackageName(pkgRollbackInfo.getPackageName()); + if (installerPackageName != null) { + params.setInstallerPackageName(installerPackageName); } } - } - parentSession.addChildSessionId(sessionId); - } + params.setRequestDowngrade(true); + params.setRequiredInstalledVersionCode( + pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode()); + if (isStaged()) { + params.setStaged(); + } + if (pkgRollbackInfo.isApex()) { + params.setInstallAsApex(); + } + int sessionId = packageInstaller.createSession(params); + PackageInstaller.Session session = packageInstaller.openSession(sessionId); + File[] packageCodePaths = RollbackStore.getPackageCodePaths( + this, pkgRollbackInfo.getPackageName()); + if (packageCodePaths == null) { + sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, + "Backup copy of package inaccessible"); + return; + } - final LocalIntentReceiver receiver = new LocalIntentReceiver( - (Intent result) -> { - int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status != PackageInstaller.STATUS_SUCCESS) { - // Committing the rollback failed, but we still have all the info we - // need to try rolling back again, so restore the rollback state to how - // it was before we tried committing. - // TODO: Should we just kill this rollback if commit failed? - // Why would we expect commit not to fail again? - // TODO: Could this cause a rollback to be resurrected - // if it should otherwise have expired by now? - synchronized (mLock) { - mState = ROLLBACK_STATE_AVAILABLE; - mRestoreUserDataInProgress = false; + for (File packageCodePath : packageCodePaths) { + try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath, + ParcelFileDescriptor.MODE_READ_ONLY)) { + final long token = Binder.clearCallingIdentity(); + try { + session.write(packageCodePath.getName(), 0, + packageCodePath.length(), + fd); + } finally { + Binder.restoreCallingIdentity(token); } - sendFailure(context, statusReceiver, - RollbackManager.STATUS_FAILURE_INSTALL, - "Rollback downgrade install failed: " - + result.getStringExtra( - PackageInstaller.EXTRA_STATUS_MESSAGE)); - return; } + } + parentSession.addChildSessionId(sessionId); + } - synchronized (mLock) { - if (!isStaged()) { - // All calls to restoreUserData should have - // completed by now for a non-staged install. - mRestoreUserDataInProgress = false; + final LocalIntentReceiver receiver = new LocalIntentReceiver( + (Intent result) -> { + int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status != PackageInstaller.STATUS_SUCCESS) { + // Committing the rollback failed, but we still have all the info we + // need to try rolling back again, so restore the rollback state to + // how it was before we tried committing. + // TODO: Should we just kill this rollback if commit failed? + // Why would we expect commit not to fail again? + // TODO: Could this cause a rollback to be resurrected + // if it should otherwise have expired by now? + synchronized (mLock) { + mState = ROLLBACK_STATE_AVAILABLE; + mRestoreUserDataInProgress = false; + } + sendFailure(context, statusReceiver, + RollbackManager.STATUS_FAILURE_INSTALL, + "Rollback downgrade install failed: " + + result.getStringExtra( + PackageInstaller.EXTRA_STATUS_MESSAGE)); + return; } - info.setCommittedSessionId(parentSessionId); - info.getCausePackages().addAll(causePackages); - RollbackStore.deletePackageCodePaths(this); - RollbackStore.saveRollback(this); - } + synchronized (mLock) { + if (!isStaged()) { + // All calls to restoreUserData should have + // completed by now for a non-staged install. + mRestoreUserDataInProgress = false; + } + + info.setCommittedSessionId(parentSessionId); + info.getCausePackages().addAll(causePackages); + RollbackStore.deletePackageCodePaths(this); + RollbackStore.saveRollback(this); + } - // Send success. - try { - final Intent fillIn = new Intent(); - fillIn.putExtra( - RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_SUCCESS); - statusReceiver.sendIntent(context, 0, fillIn, null, null); - } catch (IntentSender.SendIntentException e) { - // Nowhere to send the result back to, so don't bother. - } + // Send success. + try { + final Intent fillIn = new Intent(); + fillIn.putExtra( + RollbackManager.EXTRA_STATUS, + RollbackManager.STATUS_SUCCESS); + statusReceiver.sendIntent(context, 0, fillIn, null, null); + } catch (IntentSender.SendIntentException e) { + // Nowhere to send the result back to, so don't bother. + } - Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED); + Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED); - for (UserInfo userInfo : UserManager.get(context).getUsers(true)) { - context.sendBroadcastAsUser(broadcast, - userInfo.getUserHandle(), - Manifest.permission.MANAGE_ROLLBACKS); + for (UserInfo userInfo : UserManager.get(context).getUsers(true)) { + context.sendBroadcastAsUser(broadcast, + userInfo.getUserHandle(), + Manifest.permission.MANAGE_ROLLBACKS); + } } - } - ); - - mState = ROLLBACK_STATE_COMMITTED; - mRestoreUserDataInProgress = true; - parentSession.commit(receiver.getIntentSender()); - } catch (IOException e) { - Slog.e(TAG, "Rollback failed", e); - sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, - "IOException: " + e.toString()); + ); + + mState = ROLLBACK_STATE_COMMITTED; + mRestoreUserDataInProgress = true; + parentSession.commit(receiver.getIntentSender()); + } catch (IOException e) { + Slog.e(TAG, "Rollback failed", e); + sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, + "IOException: " + e.toString()); + } } } @@ -498,138 +506,156 @@ class Rollback { * @return boolean True if this rollback has a restore in progress and contains the specified * package. */ - @GuardedBy("getLock") boolean restoreUserDataForPackageIfInProgress(String packageName, int[] userIds, int appId, String seInfo, AppDataRollbackHelper dataHelper) { - if (!isRestoreUserDataInProgress()) { - return false; - } + synchronized (mLock) { + if (!isRestoreUserDataInProgress()) { + return false; + } - boolean foundPackage = false; - for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { - if (pkgRollbackInfo.getPackageName().equals(packageName)) { - foundPackage = true; - boolean changedRollback = false; - for (int userId : userIds) { - changedRollback |= dataHelper.restoreAppData( - info.getRollbackId(), pkgRollbackInfo, userId, appId, seInfo); - } - // We've updated metadata about this rollback, so save it to flash. - if (changedRollback) { - RollbackStore.saveRollback(this); + boolean foundPackage = false; + for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { + if (pkgRollbackInfo.getPackageName().equals(packageName)) { + foundPackage = true; + boolean changedRollback = false; + for (int userId : userIds) { + changedRollback |= dataHelper.restoreAppData( + info.getRollbackId(), pkgRollbackInfo, userId, appId, seInfo); + } + // We've updated metadata about this rollback, so save it to flash. + if (changedRollback) { + RollbackStore.saveRollback(this); + } + break; } - break; } + return foundPackage; } - return foundPackage; } /** * Deletes app data snapshots associated with this rollback, and moves to the DELETED state. */ - @GuardedBy("getLock") void delete(AppDataRollbackHelper dataHelper) { - for (PackageRollbackInfo pkgInfo : info.getPackages()) { - IntArray snapshottedUsers = pkgInfo.getSnapshottedUsers(); - for (int i = 0; i < snapshottedUsers.size(); i++) { - // Destroy app data snapshot. - int userId = snapshottedUsers.get(i); - - dataHelper.destroyAppDataSnapshot(info.getRollbackId(), pkgInfo, userId); + synchronized (mLock) { + for (PackageRollbackInfo pkgInfo : info.getPackages()) { + IntArray snapshottedUsers = pkgInfo.getSnapshottedUsers(); + for (int i = 0; i < snapshottedUsers.size(); i++) { + // Destroy app data snapshot. + int userId = snapshottedUsers.get(i); + + dataHelper.destroyAppDataSnapshot(info.getRollbackId(), pkgInfo, userId); + } } - } - RollbackStore.deleteRollback(this); - mState = ROLLBACK_STATE_DELETED; + RollbackStore.deleteRollback(this); + mState = ROLLBACK_STATE_DELETED; + } } /** * Returns the id of the post-reboot apk session for a staged install, if any. */ - @GuardedBy("getLock") int getApkSessionId() { - return mApkSessionId; + synchronized (mLock) { + return mApkSessionId; + } } /** * Sets the id of the post-reboot apk session for a staged install. */ - @GuardedBy("getLock") void setApkSessionId(int apkSessionId) { - mApkSessionId = apkSessionId; + synchronized (mLock) { + mApkSessionId = apkSessionId; + RollbackStore.saveRollback(this); + } } /** * Returns true if we are expecting the package manager to call restoreUserData for this * rollback because it has just been committed but the rollback has not yet been fully applied. */ - @GuardedBy("getLock") boolean isRestoreUserDataInProgress() { - return mRestoreUserDataInProgress; + synchronized (mLock) { + return mRestoreUserDataInProgress; + } } /** * Sets whether we are expecting the package manager to call restoreUserData for this * rollback because it has just been committed but the rollback has not yet been fully applied. */ - @GuardedBy("getLock") void setRestoreUserDataInProgress(boolean restoreUserDataInProgress) { - mRestoreUserDataInProgress = restoreUserDataInProgress; + synchronized (mLock) { + mRestoreUserDataInProgress = restoreUserDataInProgress; + RollbackStore.saveRollback(this); + } } /** * Returns true if this rollback includes the package with the provided {@code packageName}. */ - @GuardedBy("getLock") boolean includesPackage(String packageName) { - for (PackageRollbackInfo packageRollbackInfo : info.getPackages()) { - if (packageRollbackInfo.getPackageName().equals(packageName)) { - return true; + synchronized (mLock) { + for (PackageRollbackInfo packageRollbackInfo : info.getPackages()) { + if (packageRollbackInfo.getPackageName().equals(packageName)) { + return true; + } } + return false; } - return false; } /** * Returns true if this rollback includes the package with the provided {@code packageName} * with a <i>version rolled back from</i> that is not {@code versionCode}. */ - @GuardedBy("getLock") boolean includesPackageWithDifferentVersion(String packageName, long versionCode) { - for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { - if (pkgRollbackInfo.getPackageName().equals(packageName) - && pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode() - != versionCode) { - return true; + synchronized (mLock) { + for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { + if (pkgRollbackInfo.getPackageName().equals(packageName) + && pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode() + != versionCode) { + return true; + } } + return false; } - return false; } /** * Returns a list containing the names of all the packages included in this rollback. */ - @GuardedBy("getLock") List<String> getPackageNames() { - List<String> result = new ArrayList<>(); - for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { - result.add(pkgRollbackInfo.getPackageName()); + synchronized (mLock) { + List<String> result = new ArrayList<>(); + for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { + result.add(pkgRollbackInfo.getPackageName()); + } + return result; } - return result; } /** * Returns a list containing the names of all the apex packages included in this rollback. */ - @GuardedBy("getLock") List<String> getApexPackageNames() { - List<String> result = new ArrayList<>(); - for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { - if (pkgRollbackInfo.isApex()) { - result.add(pkgRollbackInfo.getPackageName()); + synchronized (mLock) { + List<String> result = new ArrayList<>(); + for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { + if (pkgRollbackInfo.isApex()) { + result.add(pkgRollbackInfo.getPackageName()); + } } + return result; + } + } + + int getPackageCount() { + synchronized (mLock) { + return info.getPackages().size(); } - return result; } static String rollbackStateToString(@RollbackState int state) { @@ -651,8 +677,39 @@ class Rollback { throw new ParseException("Invalid rollback state: " + state, 0); } - @GuardedBy("getLock") String getStateAsString() { - return rollbackStateToString(mState); + synchronized (mLock) { + return rollbackStateToString(mState); + } + } + + void dump(IndentingPrintWriter ipw) { + synchronized (mLock) { + ipw.println(info.getRollbackId() + ":"); + ipw.increaseIndent(); + ipw.println("-state: " + getStateAsString()); + ipw.println("-timestamp: " + getTimestamp()); + if (getStagedSessionId() != -1) { + ipw.println("-stagedSessionId: " + getStagedSessionId()); + } + ipw.println("-packages:"); + ipw.increaseIndent(); + for (PackageRollbackInfo pkg : info.getPackages()) { + ipw.println(pkg.getPackageName() + + " " + pkg.getVersionRolledBackFrom().getLongVersionCode() + + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode()); + } + ipw.decreaseIndent(); + if (isCommitted()) { + ipw.println("-causePackages:"); + ipw.increaseIndent(); + for (VersionedPackage cPkg : info.getCausePackages()) { + ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode()); + } + ipw.decreaseIndent(); + ipw.println("-committedSessionId: " + info.getCommittedSessionId()); + } + ipw.decreaseIndent(); + } } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 2221dff79336..cd44f64ada4f 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -36,7 +36,6 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; import android.content.rollback.IRollbackManager; -import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Binder; @@ -215,7 +214,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { synchronized (mLock) { for (NewRollback rollback : mNewRollbacks) { if (rollback.hasToken(token)) { - rollback.isCancelled = true; + rollback.setCancelled(); return; } } @@ -278,10 +277,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { List<RollbackInfo> rollbacks = new ArrayList<>(); for (int i = 0; i < mRollbacks.size(); ++i) { Rollback rollback = mRollbacks.get(i); - synchronized (rollback.getLock()) { - if (rollback.isAvailable()) { - rollbacks.add(rollback.info); - } + if (rollback.isAvailable()) { + rollbacks.add(rollback.info); } } return new ParceledListSlice<>(rollbacks); @@ -296,10 +293,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { List<RollbackInfo> rollbacks = new ArrayList<>(); for (int i = 0; i < mRollbacks.size(); ++i) { Rollback rollback = mRollbacks.get(i); - synchronized (rollback.getLock()) { - if (rollback.isCommitted()) { - rollbacks.add(rollback.info); - } + if (rollback.isCommitted()) { + rollbacks.add(rollback.info); } } return new ParceledListSlice<>(rollbacks); @@ -332,11 +327,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); - synchronized (rollback.getLock()) { - rollback.setTimestamp( - rollback.getTimestamp().plusMillis(timeDifference)); - RollbackStore.saveRollback(rollback); - } + rollback.setTimestamp( + rollback.getTimestamp().plusMillis(timeDifference)); } } } @@ -367,9 +359,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { "Rollback unavailable"); return; } - synchronized (rollback.getLock()) { - rollback.commit(mContext, causePackages, callerPackageName, statusReceiver); - } + rollback.commit(mContext, causePackages, callerPackageName, statusReceiver); } @Override @@ -404,18 +394,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); - synchronized (rollback.getLock()) { - if (rollback.includesPackage(packageName)) { - iter.remove(); - rollback.delete(mAppDataRollbackHelper); - } + if (rollback.includesPackage(packageName)) { + iter.remove(); + rollback.delete(mAppDataRollbackHelper); } } for (NewRollback newRollback : mNewRollbacks) { - synchronized (newRollback.rollback.getLock()) { - if (newRollback.rollback.includesPackage(packageName)) { - newRollback.isCancelled = true; - } + if (newRollback.rollback.includesPackage(packageName)) { + newRollback.setCancelled(); } } } @@ -448,12 +434,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (int i = 0; i < rollbacks.size(); i++) { Rollback rollback = rollbacks.get(i); - synchronized (rollback.getLock()) { - if (mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser( - userId, rollback)) { - RollbackStore.saveRollback(rollback); - } - } + rollback.commitPendingBackupAndRestoreForUser(userId, mAppDataRollbackHelper); } latch.countDown(); @@ -489,48 +470,41 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Set<String> apexPackageNames = new HashSet<>(); synchronized (mLock) { for (Rollback rollback : mRollbacks) { - synchronized (rollback.getLock()) { - if (rollback.isStaged()) { - if (rollback.isEnabling()) { - enabling.add(rollback); - } else if (rollback.isRestoreUserDataInProgress()) { - restoreInProgress.add(rollback); - } - - apexPackageNames.addAll(rollback.getApexPackageNames()); + if (rollback.isStaged()) { + if (rollback.isEnabling()) { + enabling.add(rollback); + } else if (rollback.isRestoreUserDataInProgress()) { + restoreInProgress.add(rollback); } + + apexPackageNames.addAll(rollback.getApexPackageNames()); } } } for (Rollback rollback : enabling) { PackageInstaller installer = mContext.getPackageManager().getPackageInstaller(); - synchronized (rollback.getLock()) { - PackageInstaller.SessionInfo session = - installer.getSessionInfo(rollback.getStagedSessionId()); - if (session == null || session.isStagedSessionFailed()) { - // TODO: Do we need to remove this from - // mRollbacks, or is it okay to leave as - // unavailable until the next reboot when it will go - // away on its own? - rollback.delete(mAppDataRollbackHelper); - } else if (session.isStagedSessionApplied()) { - makeRollbackAvailable(rollback); - } + PackageInstaller.SessionInfo session = + installer.getSessionInfo(rollback.getStagedSessionId()); + if (session == null || session.isStagedSessionFailed()) { + // TODO: Do we need to remove this from + // mRollbacks, or is it okay to leave as + // unavailable until the next reboot when it will go + // away on its own? + rollback.delete(mAppDataRollbackHelper); + } else if (session.isStagedSessionApplied()) { + makeRollbackAvailable(rollback); } } for (Rollback rollback : restoreInProgress) { PackageInstaller installer = mContext.getPackageManager().getPackageInstaller(); - synchronized (rollback.getLock()) { - PackageInstaller.SessionInfo session = - installer.getSessionInfo(rollback.getStagedSessionId()); - // TODO: What if session is null? - if (session != null) { - if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) { - rollback.setRestoreUserDataInProgress(false); - RollbackStore.saveRollback(rollback); - } + PackageInstaller.SessionInfo session = + installer.getSessionInfo(rollback.getStagedSessionId()); + // TODO: What if session is null? + if (session != null) { + if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) { + rollback.setRestoreUserDataInProgress(false); } } } @@ -565,14 +539,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); - synchronized (rollback.getLock()) { - // TODO: Should we remove rollbacks in the ENABLING state here? - if ((rollback.isEnabling() || rollback.isAvailable()) - && rollback.includesPackageWithDifferentVersion(packageName, - installedVersion)) { - iter.remove(); - rollback.delete(mAppDataRollbackHelper); - } + // TODO: Should we remove rollbacks in the ENABLING state here? + if ((rollback.isEnabling() || rollback.isAvailable()) + && rollback.includesPackageWithDifferentVersion(packageName, + installedVersion)) { + iter.remove(); + rollback.delete(mAppDataRollbackHelper); } } } @@ -615,18 +587,17 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); - synchronized (rollback.getLock()) { - if (!rollback.isAvailable()) { - continue; - } - if (!now.isBefore( - rollback.getTimestamp() - .plusMillis(mRollbackLifetimeDurationInMillis))) { - iter.remove(); - rollback.delete(mAppDataRollbackHelper); - } else if (oldest == null || oldest.isAfter(rollback.getTimestamp())) { - oldest = rollback.getTimestamp(); - } + if (!rollback.isAvailable()) { + continue; + } + Instant rollbackTimestamp = rollback.getTimestamp(); + if (!now.isBefore( + rollbackTimestamp + .plusMillis(mRollbackLifetimeDurationInMillis))) { + iter.remove(); + rollback.delete(mAppDataRollbackHelper); + } else if (oldest == null || oldest.isAfter(rollbackTimestamp)) { + oldest = rollbackTimestamp; } } } @@ -734,12 +705,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { synchronized (mLock) { for (int i = 0; i < mRollbacks.size(); ++i) { Rollback rollback = mRollbacks.get(i); - synchronized (rollback.getLock()) { - if (rollback.getApkSessionId() == parentSession.getSessionId()) { - // This is the apk session for a staged session with rollback enabled. We do - // not need to create a new rollback for this session. - return true; - } + if (rollback.getApkSessionId() == parentSession.getSessionId()) { + // This is the apk session for a staged session with rollback enabled. We do + // not need to create a new rollback for this session. + return true; } } } @@ -829,11 +798,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } ApplicationInfo appInfo = pkgInfo.applicationInfo; - synchronized (rollback.getLock()) { - return rollback.enableForPackage(packageName, newPackage.versionCode, - pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir, - appInfo.splitSourceDirs); - } + return rollback.enableForPackage(packageName, newPackage.versionCode, + pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir, + appInfo.splitSourceDirs); } @Override @@ -858,16 +825,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // staged installs for (int i = 0; i < mRollbacks.size(); i++) { Rollback rollback = mRollbacks.get(i); - synchronized (rollback.getLock()) { - rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper); - } + rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper); } // non-staged installs for (NewRollback rollback : mNewRollbacks) { - synchronized (rollback.rollback.getLock()) { - rollback.rollback.snapshotUserData( - packageName, userIds, mAppDataRollbackHelper); - } + rollback.rollback.snapshotUserData( + packageName, userIds, mAppDataRollbackHelper); } } } @@ -877,11 +840,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { synchronized (mLock) { for (int i = 0; i < mRollbacks.size(); ++i) { Rollback rollback = mRollbacks.get(i); - synchronized (rollback.getLock()) { - if (rollback.restoreUserDataForPackageIfInProgress( - packageName, userIds, appId, seInfo, mAppDataRollbackHelper)) { - return; - } + if (rollback.restoreUserDataForPackageIfInProgress( + packageName, userIds, appId, seInfo, mAppDataRollbackHelper)) { + return; } } } @@ -970,10 +931,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } if (rollback != null) { - synchronized (rollback.getLock()) { - rollback.setApkSessionId(apkSessionId); - RollbackStore.saveRollback(rollback); - } + rollback.setApkSessionId(apkSessionId); } }); } @@ -1076,12 +1034,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (newRollback != null) { Rollback rollback = completeEnableRollback(newRollback, success); - if (rollback != null) { - synchronized (rollback.getLock()) { - if (!rollback.isStaged()) { - makeRollbackAvailable(rollback); - } - } + if (rollback != null && !rollback.isStaged()) { + makeRollbackAvailable(rollback); } } } @@ -1091,34 +1045,30 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * Add a rollback to the list of rollbacks. This should be called after rollback has been * enabled for all packages in the rollback. It does not make the rollback available yet. * - * <p>Note that no rollback-specific locks should be held when this method is called. - * * @return the Rollback instance for a successfully enable-completed rollback, * or null on error. */ private Rollback completeEnableRollback(NewRollback newRollback, boolean success) { Rollback rollback = newRollback.rollback; - synchronized (rollback.getLock()) { - if (!success) { - // The install session was aborted, clean up the pending install. - rollback.delete(mAppDataRollbackHelper); - return null; - } - if (newRollback.isCancelled) { - Slog.e(TAG, "Rollback has been cancelled by PackageManager"); - rollback.delete(mAppDataRollbackHelper); - return null; - } - + if (!success) { + // The install session was aborted, clean up the pending install. + rollback.delete(mAppDataRollbackHelper); + return null; + } - if (rollback.info.getPackages().size() != newRollback.packageSessionIds.length) { - Slog.e(TAG, "Failed to enable rollback for all packages in session."); - rollback.delete(mAppDataRollbackHelper); - return null; - } + if (newRollback.isCancelled()) { + Slog.e(TAG, "Rollback has been cancelled by PackageManager"); + rollback.delete(mAppDataRollbackHelper); + return null; + } - RollbackStore.saveRollback(rollback); + if (rollback.getPackageCount() != newRollback.getPackageSessionIdCount()) { + Slog.e(TAG, "Failed to enable rollback for all packages in session."); + rollback.delete(mAppDataRollbackHelper); + return null; } + + rollback.saveRollback(); synchronized (mLock) { // Note: There is a small window of time between when // the session has been committed by the package @@ -1186,34 +1136,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); synchronized (mLock) { for (Rollback rollback : mRollbacks) { - synchronized (rollback.getLock()) { - RollbackInfo info = rollback.info; - ipw.println(info.getRollbackId() + ":"); - ipw.increaseIndent(); - ipw.println("-state: " + rollback.getStateAsString()); - ipw.println("-timestamp: " + rollback.getTimestamp()); - if (rollback.getStagedSessionId() != -1) { - ipw.println("-stagedSessionId: " + rollback.getStagedSessionId()); - } - ipw.println("-packages:"); - ipw.increaseIndent(); - for (PackageRollbackInfo pkg : info.getPackages()) { - ipw.println(pkg.getPackageName() - + " " + pkg.getVersionRolledBackFrom().getLongVersionCode() - + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode()); - } - ipw.decreaseIndent(); - if (rollback.isCommitted()) { - ipw.println("-causePackages:"); - ipw.increaseIndent(); - for (VersionedPackage cPkg : info.getCausePackages()) { - ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode()); - } - ipw.decreaseIndent(); - ipw.println("-committedSessionId: " + info.getCommittedSessionId()); - } - ipw.decreaseIndent(); - } + rollback.dump(ipw); } } } @@ -1233,22 +1156,51 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { public final Rollback rollback; /** - * This array holds all of the rollback tokens associated with package sessions included - * in this rollback. This is used to identify which rollback should be cancelled in case - * {@link PackageManager} sends an {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent. + * This array holds all of the rollback tokens associated with package sessions included in + * this rollback. */ + @GuardedBy("mNewRollbackLock") private final IntArray mTokens = new IntArray(); /** - * Session ids for all packages in the install. - * For multi-package sessions, this is the list of child session ids. - * For normal sessions, this list is a single element with the normal + * Session ids for all packages in the install. For multi-package sessions, this is the list + * of child session ids. For normal sessions, this list is a single element with the normal * session id. */ - public final int[] packageSessionIds; + private final int[] mPackageSessionIds; + + @GuardedBy("mNewRollbackLock") + private boolean mIsCancelled = false; + + private final Object mNewRollbackLock = new Object(); + + NewRollback(Rollback rollback, int[] packageSessionIds) { + this.rollback = rollback; + this.mPackageSessionIds = packageSessionIds; + } + + /** + * Adds a rollback token to be associated with this NewRollback. This may be used to + * identify which rollback should be cancelled in case {@link PackageManager} sends an + * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent. + */ + void addToken(int token) { + synchronized (mNewRollbackLock) { + mTokens.add(token); + } + } + + /** + * Returns true if this NewRollback is associated with the provided {@code token}. + */ + boolean hasToken(int token) { + synchronized (mNewRollbackLock) { + return mTokens.indexOf(token) != -1; + } + } /** - * Flag to determine whether the rollback has been cancelled. + * Returns true if this NewRollback has been cancelled. * * <p>Rollback could be invalidated and cancelled if RollbackManager receives * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}. @@ -1258,19 +1210,38 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}. */ - public boolean isCancelled = false; + boolean isCancelled() { + synchronized (mNewRollbackLock) { + return mIsCancelled; + } + } - NewRollback(Rollback rollback, int[] packageSessionIds) { - this.rollback = rollback; - this.packageSessionIds = packageSessionIds; + /** + * Sets this NewRollback to be marked as cancelled. + */ + void setCancelled() { + synchronized (mNewRollbackLock) { + mIsCancelled = true; + } } - public void addToken(int token) { - mTokens.add(token); + /** + * Returns true if this NewRollback contains the provided {@code packageSessionId}. + */ + boolean containsSessionId(int packageSessionId) { + for (int id : mPackageSessionIds) { + if (id == packageSessionId) { + return true; + } + } + return false; } - public boolean hasToken(int token) { - return mTokens.indexOf(token) != -1; + /** + * Returns the number of package session ids in this NewRollback. + */ + int getPackageSessionIdCount() { + return mPackageSessionIds.length; } } @@ -1301,14 +1272,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * Returns null if no NewRollback is found for the given package * session. */ + @GuardedBy("mLock") NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) { // We expect mNewRollbacks to be a very small list; linear search // should be plenty fast. for (NewRollback newRollback: mNewRollbacks) { - for (int id : newRollback.packageSessionIds) { - if (id == packageSessionId) { - return newRollback; - } + if (newRollback.containsSessionId(packageSessionId)) { + return newRollback; } } return null; diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index a9331aa5648f..eadd09cf46ee 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -252,7 +252,7 @@ class RollbackStore { /** * Saves the given rollback to persistent storage. */ - @GuardedBy("rollback.getLock") + @GuardedBy("rollback.mLock") static void saveRollback(Rollback rollback) { try { JSONObject dataJson = new JSONObject(); |