Save deleted rollbacks for debugging purpose (2/n)

The rollbacks will be saved to /data/rollback-history before deletion.

Bug: 172644981
Test: atest RollbackStoreTest
Change-Id: I608316d2260ce8970d0f625eb8ba286d33d730c9
This commit is contained in:
JW Wang
2020-11-13 14:50:01 +08:00
parent 85e86853c2
commit 0d043b0d49
3 changed files with 72 additions and 18 deletions

View File

@@ -179,7 +179,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
mInstaller = new Installer(mContext);
mInstaller.onStart();
mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
mRollbackStore = new RollbackStore(
new File(Environment.getDataDirectory(), "rollback"),
new File(Environment.getDataDirectory(), "rollback-history"));
mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);
@@ -201,7 +203,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
} else {
// Delete rollbacks when build fingerprint has changed.
for (Rollback rollback : mRollbacks) {
rollback.delete(mAppDataRollbackHelper);
deleteRollback(rollback);
}
mRollbacks.clear();
}
@@ -271,7 +273,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
Rollback rollback = getRollbackForSession(sessionId);
if (rollback != null && rollback.isEnabling()) {
mRollbacks.remove(rollback);
rollback.delete(mAppDataRollbackHelper);
deleteRollback(rollback);
}
}
}
@@ -484,7 +486,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
Rollback rollback = iter.next();
if (rollback.includesPackage(packageName)) {
iter.remove();
rollback.delete(mAppDataRollbackHelper);
deleteRollback(rollback);
}
}
}
@@ -612,7 +614,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
.getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
if (session == null || session.isStagedSessionFailed()) {
iter.remove();
rollback.delete(mAppDataRollbackHelper);
deleteRollback(rollback);
continue;
}
@@ -666,7 +668,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
&& rollback.includesPackageWithDifferentVersion(packageName,
installedVersion)) {
iter.remove();
rollback.delete(mAppDataRollbackHelper);
deleteRollback(rollback);
}
}
}
@@ -720,7 +722,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
if (!now.isBefore(rollbackTimestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
Slog.i(TAG, "runExpiration id=" + rollback.info.getRollbackId());
iter.remove();
rollback.delete(mAppDataRollbackHelper);
deleteRollback(rollback);
} else if (oldest == null || oldest.isAfter(rollbackTimestamp)) {
oldest = rollbackTimestamp;
}
@@ -1132,7 +1134,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
+ " for failed session id=" + sessionId);
mRollbacks.remove(rollback);
rollback.delete(mAppDataRollbackHelper);
deleteRollback(rollback);
}
}
}
@@ -1159,7 +1161,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
if (!rollback.allPackagesEnabled()) {
Slog.e(TAG, "Failed to enable rollback for all packages in session.");
mRollbacks.remove(rollback);
rollback.delete(mAppDataRollbackHelper);
deleteRollback(rollback);
return false;
}
@@ -1329,4 +1331,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
}
return null;
}
@WorkerThread
private void deleteRollback(Rollback rollback) {
assertInWorkerThread();
mRollbackStore.saveRollbackToHistory(rollback);
rollback.delete(mAppDataRollbackHelper);
}
}

View File

@@ -69,18 +69,20 @@ class RollbackStore {
// * XXX, YYY are the rollbackIds for the corresponding rollbacks.
// * rollback.json contains all relevant metadata for the rollback.
private final File mRollbackDataDir;
private final File mRollbackHistoryDir;
RollbackStore(File rollbackDataDir) {
RollbackStore(File rollbackDataDir, File rollbackHistoryDir) {
mRollbackDataDir = rollbackDataDir;
mRollbackHistoryDir = rollbackHistoryDir;
}
/**
* Reads the rollbacks from persistent storage.
*/
List<Rollback> loadRollbacks() {
private static List<Rollback> loadRollbacks(File rollbackDataDir) {
List<Rollback> rollbacks = new ArrayList<>();
mRollbackDataDir.mkdirs();
for (File rollbackDir : mRollbackDataDir.listFiles()) {
rollbackDataDir.mkdirs();
for (File rollbackDir : rollbackDataDir.listFiles()) {
if (rollbackDir.isDirectory()) {
try {
rollbacks.add(loadRollback(rollbackDir));
@@ -93,6 +95,14 @@ class RollbackStore {
return rollbacks;
}
List<Rollback> loadRollbacks() {
return loadRollbacks(mRollbackDataDir);
}
List<Rollback> loadHistorialRollbacks() {
return loadRollbacks(mRollbackHistoryDir);
}
/**
* Converts a {@code JSONArray} of integers to a {@code List<Integer>}.
*/
@@ -258,10 +268,11 @@ class RollbackStore {
/**
* Saves the given rollback to persistent storage.
*/
static void saveRollback(Rollback rollback) {
private static void saveRollback(Rollback rollback, File backDir) {
FileOutputStream fos = null;
AtomicFile file = new AtomicFile(new File(rollback.getBackupDir(), "rollback.json"));
AtomicFile file = new AtomicFile(new File(backDir, "rollback.json"));
try {
backDir.mkdirs();
JSONObject dataJson = new JSONObject();
dataJson.put("info", rollbackInfoToJson(rollback.info));
dataJson.put("timestamp", rollback.getTimestamp().toString());
@@ -286,6 +297,22 @@ class RollbackStore {
}
}
static void saveRollback(Rollback rollback) {
saveRollback(rollback, rollback.getBackupDir());
}
/**
* Saves the rollback to mRollbackHistoryDir/ROLLBACKID-HEX for debugging purpose.
*/
void saveRollbackToHistory(Rollback rollback) {
// The same id might be allocated to different historical rollbacks.
// Let's add a suffix to avoid naming collision.
String suffix = Long.toHexString(rollback.getTimestamp().getEpochSecond());
String dirName = Integer.toString(rollback.info.getRollbackId());
File backupDir = new File(mRollbackHistoryDir, dirName + "-" + suffix);
saveRollback(rollback, backupDir);
}
/**
* Removes all persistent storage associated with the given rollback.
*/

View File

@@ -110,6 +110,8 @@ public class RollbackStoreTest {
@Rule
public TemporaryFolder mFolder = new TemporaryFolder();
@Rule
public TemporaryFolder mHistoryDir = new TemporaryFolder();
private File mRollbackDir;
@@ -117,7 +119,7 @@ public class RollbackStoreTest {
@Before
public void setUp() throws Exception {
mRollbackStore = new RollbackStore(mFolder.getRoot());
mRollbackStore = new RollbackStore(mFolder.getRoot(), mHistoryDir.getRoot());
mRollbackDir = mFolder.newFolder(ID + "");
mFolder.newFile("rollback.json");
}
@@ -324,10 +326,26 @@ public class RollbackStoreTest {
assertThat(expectedFile.exists()).isFalse();
}
private void assertRollbacksAreEquivalent(Rollback b, Rollback a) {
assertThat(b.info.getRollbackId()).isEqualTo(ID);
@Test
public void saveToHistoryAndLoad() {
Rollback origRb = mRollbackStore.createNonStagedRollback(
ID, USER, INSTALLER, null, new SparseIntArray(0));
mRollbackStore.saveRollbackToHistory(origRb);
List<Rollback> loadedRollbacks = mRollbackStore.loadHistorialRollbacks();
assertThat(loadedRollbacks).hasSize(1);
Rollback loadedRb = loadedRollbacks.get(0);
assertRollbacksAreEquivalentExcludingBackupDir(loadedRb, origRb);
}
private void assertRollbacksAreEquivalent(Rollback b, Rollback a) {
assertThat(b.getBackupDir()).isEqualTo(a.getBackupDir());
assertRollbacksAreEquivalentExcludingBackupDir(b, a);
}
private void assertRollbacksAreEquivalentExcludingBackupDir(Rollback b, Rollback a) {
assertThat(b.info.getRollbackId()).isEqualTo(ID);
assertThat(b.isRestoreUserDataInProgress())
.isEqualTo(a.isRestoreUserDataInProgress());