diff options
4 files changed, 233 insertions, 63 deletions
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 0d5746bf547f..6769fe07bbf8 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -65,7 +65,7 @@ class Rollback { /** * The directory where the rollback data is stored. */ - public final File backupDir; + private final File mBackupDir; /** * The time when the upgrade occurred, for purposes of expiring @@ -74,24 +74,24 @@ class Rollback { * The timestamp is not applicable for all rollback states, but we make * sure to keep it non-null to avoid potential errors there. */ - public @NonNull Instant timestamp; + private @NonNull Instant mTimestamp; /** * The session ID for the staged session if this rollback data represents a staged session, * {@code -1} otherwise. */ - public final int stagedSessionId; + private final int mStagedSessionId; /** * The current state of the rollback. * ENABLING, AVAILABLE, or COMMITTED. */ - public @RollbackState int state; + private @RollbackState int mState; /** * The id of the post-reboot apk session for a staged install, if any. */ - public int apkSessionId = -1; + private int mApkSessionId = -1; /** * True if we are expecting the package manager to call restoreUserData @@ -99,7 +99,7 @@ class Rollback { * has not yet been fully applied. */ // NOTE: All accesses to this field are from the RollbackManager handler thread. - public boolean restoreUserDataInProgress = false; + private boolean mRestoreUserDataInProgress = false; /** * Constructs a new, empty Rollback instance. @@ -114,10 +114,10 @@ class Rollback { /* isStaged */ stagedSessionId != -1, /* causePackages */ new ArrayList<>(), /* committedSessionId */ -1); - this.backupDir = backupDir; - this.stagedSessionId = stagedSessionId; - this.state = ROLLBACK_STATE_ENABLING; - this.timestamp = Instant.now(); + mBackupDir = backupDir; + mStagedSessionId = stagedSessionId; + mState = ROLLBACK_STATE_ENABLING; + mTimestamp = Instant.now(); } /** @@ -126,21 +126,115 @@ class Rollback { Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId, @RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress) { this.info = info; - this.backupDir = backupDir; - this.timestamp = timestamp; - this.stagedSessionId = stagedSessionId; - this.state = state; - this.apkSessionId = apkSessionId; - this.restoreUserDataInProgress = restoreUserDataInProgress; + mBackupDir = backupDir; + mTimestamp = timestamp; + mStagedSessionId = stagedSessionId; + mState = state; + mApkSessionId = apkSessionId; + mRestoreUserDataInProgress = restoreUserDataInProgress; } /** * Whether the rollback is for rollback of a staged install. */ - public boolean isStaged() { + boolean isStaged() { return info.isStaged(); } + /** + * Returns the directory in which rollback data should be stored. + */ + File getBackupDir() { + return mBackupDir; + } + + /** + * Returns the time when the upgrade occurred, for purposes of expiring rollback data. + */ + Instant getTimestamp() { + return mTimestamp; + } + + /** + * Sets the time at which upgrade occurred. + */ + void setTimestamp(Instant timestamp) { + mTimestamp = timestamp; + } + + /** + * Returns the session ID for the staged session if this rollback data represents a staged + * session, or {@code -1} otherwise. + */ + int getStagedSessionId() { + return mStagedSessionId; + } + + /** + * Returns true if the rollback is in the ENABLING state. + */ + boolean isEnabling() { + return mState == ROLLBACK_STATE_ENABLING; + } + + /** + * Returns true if the rollback is in the AVAILABLE state. + */ + boolean isAvailable() { + return mState == ROLLBACK_STATE_AVAILABLE; + } + + /** + * Returns true if the rollback is in the COMMITTED state. + */ + boolean isCommitted() { + return mState == ROLLBACK_STATE_COMMITTED; + } + + /** + * Sets the state of the rollback to AVAILABLE. + */ + void setAvailable() { + mState = ROLLBACK_STATE_AVAILABLE; + } + + /** + * Sets the state of the rollback to COMMITTED. + */ + void setCommitted() { + mState = ROLLBACK_STATE_COMMITTED; + } + + /** + * Returns the id of the post-reboot apk session for a staged install, if any. + */ + int getApkSessionId() { + return mApkSessionId; + } + + /** + * Sets the id of the post-reboot apk session for a staged install. + */ + void setApkSessionId(int apkSessionId) { + mApkSessionId = apkSessionId; + } + + /** + * 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. + */ + boolean isRestoreUserDataInProgress() { + 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. + */ + void setRestoreUserDataInProgress(boolean restoreUserDataInProgress) { + mRestoreUserDataInProgress = restoreUserDataInProgress; + } + static String rollbackStateToString(@RollbackState int state) { switch (state) { case Rollback.ROLLBACK_STATE_ENABLING: return "enabling"; @@ -160,7 +254,7 @@ class Rollback { throw new ParseException("Invalid rollback state: " + state, 0); } - public String getStateAsString() { - return rollbackStateToString(state); + String getStateAsString() { + return rollbackStateToString(mState); } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 3147bc629ffa..96d284bb1c58 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -282,7 +282,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { List<RollbackInfo> rollbacks = new ArrayList<>(); for (int i = 0; i < mRollbacks.size(); ++i) { Rollback rollback = mRollbacks.get(i); - if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE) { + if (rollback.isAvailable()) { rollbacks.add(rollback.info); } } @@ -298,7 +298,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { List<RollbackInfo> rollbacks = new ArrayList<>(); for (int i = 0; i < mRollbacks.size(); ++i) { Rollback rollback = mRollbacks.get(i); - if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) { + if (rollback.isCommitted()) { rollbacks.add(rollback.info); } } @@ -332,7 +332,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); - rollback.timestamp = rollback.timestamp.plusMillis(timeDifference); + rollback.setTimestamp(rollback.getTimestamp().plusMillis(timeDifference)); saveRollback(rollback); } } @@ -358,7 +358,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Slog.i(TAG, "Initiating rollback"); Rollback rollback = getRollbackForId(rollbackId); - if (rollback == null || rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) { + if (rollback == null || !rollback.isAvailable()) { sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, "Rollback unavailable"); return; @@ -454,8 +454,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // TODO: Could this cause a rollback to be // resurrected if it should otherwise have // expired by now? - rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE; - rollback.restoreUserDataInProgress = false; + rollback.setAvailable(); + rollback.setRestoreUserDataInProgress(false); } sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL, "Rollback downgrade install failed: " @@ -468,7 +468,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (!rollback.isStaged()) { // All calls to restoreUserData should have // completed by now for a non-staged install. - rollback.restoreUserDataInProgress = false; + rollback.setRestoreUserDataInProgress(false); } rollback.info.setCommittedSessionId(parentSessionId); @@ -490,8 +490,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { ); synchronized (mLock) { - rollback.state = Rollback.ROLLBACK_STATE_COMMITTED; - rollback.restoreUserDataInProgress = true; + rollback.setCommitted(); + rollback.setRestoreUserDataInProgress(true); } parentSession.commit(receiver.getIntentSender()); } catch (IOException e) { @@ -618,9 +618,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { synchronized (mLock) { for (Rollback rollback : mRollbacks) { if (rollback.isStaged()) { - if (rollback.state == Rollback.ROLLBACK_STATE_ENABLING) { + if (rollback.isEnabling()) { enabling.add(rollback); - } else if (rollback.restoreUserDataInProgress) { + } else if (rollback.isRestoreUserDataInProgress()) { restoreInProgress.add(rollback); } @@ -635,8 +635,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (Rollback rollback : enabling) { PackageInstaller installer = mContext.getPackageManager().getPackageInstaller(); - PackageInstaller.SessionInfo session = installer.getSessionInfo( - rollback.stagedSessionId); + 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 @@ -650,13 +650,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (Rollback rollback : restoreInProgress) { PackageInstaller installer = mContext.getPackageManager().getPackageInstaller(); - PackageInstaller.SessionInfo session = installer.getSessionInfo( - rollback.stagedSessionId); + PackageInstaller.SessionInfo session = + installer.getSessionInfo(rollback.getStagedSessionId()); // TODO: What if session is null? if (session != null) { if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) { synchronized (mLock) { - rollback.restoreUserDataInProgress = false; + rollback.setRestoreUserDataInProgress(false); } saveRollback(rollback); } @@ -694,8 +694,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { while (iter.hasNext()) { Rollback rollback = iter.next(); // TODO: Should we remove rollbacks in the ENABLING state here? - if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE - || rollback.state == Rollback.ROLLBACK_STATE_ENABLING) { + if (rollback.isEnabling() || rollback.isAvailable()) { for (PackageRollbackInfo info : rollback.info.getPackages()) { if (info.getPackageName().equals(packageName) && !packageVersionsEqual( @@ -761,15 +760,16 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); - if (rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) { + if (!rollback.isAvailable()) { continue; } if (!now.isBefore( - rollback.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) { + rollback.getTimestamp() + .plusMillis(mRollbackLifetimeDurationInMillis))) { iter.remove(); deleteRollback(rollback); - } else if (oldest == null || oldest.isAfter(rollback.timestamp)) { - oldest = rollback.timestamp; + } else if (oldest == null || oldest.isAfter(rollback.getTimestamp())) { + oldest = rollback.getTimestamp(); } } } @@ -877,7 +877,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { synchronized (mLock) { for (int i = 0; i < mRollbacks.size(); ++i) { Rollback rollback = mRollbacks.get(i); - if (rollback.apkSessionId == parentSession.getSessionId()) { + 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; @@ -1020,7 +1020,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // staged installs for (int i = 0; i < mRollbacks.size(); i++) { Rollback rollback = mRollbacks.get(i); - if (rollback.state != Rollback.ROLLBACK_STATE_ENABLING) { + if (!rollback.isEnabling()) { continue; } @@ -1053,7 +1053,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { synchronized (mLock) { for (int i = 0; i < mRollbacks.size(); ++i) { Rollback candidate = mRollbacks.get(i); - if (candidate.restoreUserDataInProgress) { + if (candidate.isRestoreUserDataInProgress()) { info = getPackageRollbackInfo(candidate, packageName); if (info != null) { rollback = candidate; @@ -1146,8 +1146,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { synchronized (mLock) { for (int i = 0; i < mRollbacks.size(); ++i) { Rollback candidate = mRollbacks.get(i); - if (candidate.stagedSessionId == originalSessionId) { - candidate.apkSessionId = apkSessionId; + if (candidate.getStagedSessionId() == originalSessionId) { + candidate.setApkSessionId(apkSessionId); rollback = candidate; break; } @@ -1333,8 +1333,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // to a new package being installed. Won't this revive an expired // rollback? Consider adding a ROLLBACK_STATE_EXPIRED to address this. synchronized (mLock) { - rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE; - rollback.timestamp = Instant.now(); + rollback.setAvailable(); + rollback.setTimestamp(Instant.now()); } saveRollback(rollback); @@ -1434,9 +1434,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { ipw.println(info.getRollbackId() + ":"); ipw.increaseIndent(); ipw.println("-state: " + rollback.getStateAsString()); - ipw.println("-timestamp: " + rollback.timestamp); - if (rollback.stagedSessionId != -1) { - ipw.println("-stagedSessionId: " + rollback.stagedSessionId); + ipw.println("-timestamp: " + rollback.getTimestamp()); + if (rollback.getStagedSessionId() != -1) { + ipw.println("-stagedSessionId: " + rollback.getStagedSessionId()); } ipw.println("-packages:"); ipw.increaseIndent(); @@ -1446,7 +1446,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode()); } ipw.decreaseIndent(); - if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) { + if (rollback.isCommitted()) { ipw.println("-causePackages:"); ipw.increaseIndent(); for (VersionedPackage cPkg : info.getCausePackages()) { diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index b2448f62bcd7..772c53fec4ce 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -17,7 +17,6 @@ package com.android.server.rollback; import static com.android.server.rollback.Rollback.rollbackStateFromString; -import static com.android.server.rollback.Rollback.rollbackStateToString; import android.annotation.NonNull; import android.content.pm.VersionedPackage; @@ -216,7 +215,7 @@ class RollbackStore { static void backupPackageCodePath(Rollback rollback, String packageName, String codePath) throws IOException { File sourceFile = new File(codePath); - File targetDir = new File(rollback.backupDir, packageName); + File targetDir = new File(rollback.getBackupDir(), packageName); targetDir.mkdirs(); File targetFile = new File(targetDir, sourceFile.getName()); @@ -229,7 +228,7 @@ class RollbackStore { * Includes the base apk and any splits. Returns null if none found. */ static File[] getPackageCodePaths(Rollback rollback, String packageName) { - File targetDir = new File(rollback.backupDir, packageName); + File targetDir = new File(rollback.getBackupDir(), packageName); File[] files = targetDir.listFiles(); if (files == null || files.length == 0) { return null; @@ -243,7 +242,7 @@ class RollbackStore { */ static void deletePackageCodePaths(Rollback rollback) { for (PackageRollbackInfo info : rollback.info.getPackages()) { - File targetDir = new File(rollback.backupDir, info.getPackageName()); + File targetDir = new File(rollback.getBackupDir(), info.getPackageName()); removeFile(targetDir); } } @@ -255,13 +254,13 @@ class RollbackStore { try { JSONObject dataJson = new JSONObject(); dataJson.put("info", rollbackInfoToJson(rollback.info)); - dataJson.put("timestamp", rollback.timestamp.toString()); - dataJson.put("stagedSessionId", rollback.stagedSessionId); - dataJson.put("state", rollbackStateToString(rollback.state)); - dataJson.put("apkSessionId", rollback.apkSessionId); - dataJson.put("restoreUserDataInProgress", rollback.restoreUserDataInProgress); + dataJson.put("timestamp", rollback.getTimestamp().toString()); + dataJson.put("stagedSessionId", rollback.getStagedSessionId()); + dataJson.put("state", rollback.getStateAsString()); + dataJson.put("apkSessionId", rollback.getApkSessionId()); + dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress()); - PrintWriter pw = new PrintWriter(new File(rollback.backupDir, "rollback.json")); + PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json")); pw.println(dataJson.toString()); pw.close(); } catch (JSONException e) { @@ -273,7 +272,7 @@ class RollbackStore { * Removes all persistent storage associated with the given rollback. */ void deleteRollback(Rollback rollback) { - removeFile(rollback.backupDir); + removeFile(rollback.getBackupDir()); } /** diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java new file mode 100644 index 000000000000..d27f1c7e0ce7 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.rollback; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; + +@RunWith(JUnit4.class) +public class RollbackUnitTest { + + @Test + public void newEmptyStagedRollbackDefaults() { + int rollbackId = 123; + int sessionId = 567; + File file = new File("/test/testing"); + + Rollback rollback = new Rollback(rollbackId, file, sessionId); + + assertThat(rollback.isEnabling()).isTrue(); + assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing"); + assertThat(rollback.isStaged()).isTrue(); + assertThat(rollback.getStagedSessionId()).isEqualTo(567); + } + + @Test + public void newEmptyNonStagedRollbackDefaults() { + int rollbackId = 123; + File file = new File("/test/testing"); + + Rollback rollback = new Rollback(rollbackId, file, -1); + + assertThat(rollback.isEnabling()).isTrue(); + assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing"); + assertThat(rollback.isStaged()).isFalse(); + } + + @Test + public void rollbackStateChanges() { + Rollback rollback = new Rollback(123, new File("/test/testing"), -1); + + assertThat(rollback.isEnabling()).isTrue(); + assertThat(rollback.isAvailable()).isFalse(); + assertThat(rollback.isCommitted()).isFalse(); + + rollback.setAvailable(); + + assertThat(rollback.isEnabling()).isFalse(); + assertThat(rollback.isAvailable()).isTrue(); + assertThat(rollback.isCommitted()).isFalse(); + + rollback.setCommitted(); + + assertThat(rollback.isEnabling()).isFalse(); + assertThat(rollback.isAvailable()).isFalse(); + assertThat(rollback.isCommitted()).isTrue(); + } + +} |