Backport of ag/14170751 and ag/14170752.
1. An available rollback shouldn't be deleted when its session expires.
2. Add a test to check we don't crash or delete a rollback when its
session expires.
Bug: 181087240
Bug: 185132440
Test: atest StagedRollbackTest
Merged-In: I3c2b718905d2d7619d6f299ee5402fd858de030e
Merged-In: I1bd786b38e2df1f4bdc80d0d371d5d2d52e21e06
Change-Id: Id55f1bde0ee69a6f26bc8e109918fb26d1da4e3f
(cherry picked from commit 29d5f72db216eeae13cb7d395b2359544012ffce)
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index f075790..d5dbf6b 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -554,8 +554,10 @@
PackageInstaller.SessionInfo session = mContext.getPackageManager()
.getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
if (session == null || session.isStagedSessionFailed()) {
- iter.remove();
- rollback.delete(mAppDataRollbackHelper);
+ if (rollback.isEnabling()) {
+ iter.remove();
+ rollback.delete(mAppDataRollbackHelper);
+ }
continue;
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 00bd4cf..eaf9c7b 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -482,6 +482,33 @@
}
@Test
+ public void testExpireSession_Phase1_Install() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ Install.single(TestApp.A1).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
+ }
+
+ @Test
+ public void testExpireSession_Phase2_VerifyInstall() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TestApp.A);
+ assertThat(rollback).isNotNull();
+ assertThat(rollback).packagesContainsExactly(Rollback.from(TestApp.A2).to(TestApp.A1));
+ assertThat(rollback.isStaged()).isTrue();
+ }
+
+ @Test
+ public void testExpireSession_Phase3_VerifyRollback() throws Exception {
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TestApp.A);
+ assertThat(rollback).isNotNull();
+ }
+
+ @Test
public void hasMainlineModule() throws Exception {
String pkgName = getModuleMetadataPackageName();
boolean existed = InstrumentationRegistry.getInstrumentation().getContext()
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 9169ef5..bebb991 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -38,7 +38,9 @@
import org.junit.runner.RunWith;
import java.io.File;
+import java.time.Instant;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -441,6 +443,27 @@
after.forEach(dir -> assertDirectoryIsEmpty(dir));
}
+ /**
+ * Tests an available rollback shouldn't be deleted when its session expires.
+ */
+ @Test
+ public void testExpireSession() throws Exception {
+ runPhase("testExpireSession_Phase1_Install");
+ getDevice().reboot();
+ runPhase("testExpireSession_Phase2_VerifyInstall");
+
+ // Advance system clock by 7 days to expire the staged session
+ Instant t1 = Instant.ofEpochMilli(getDevice().getDeviceDate());
+ Instant t2 = t1.plusMillis(TimeUnit.DAYS.toMillis(7));
+ runAsRoot(() -> getDevice().setDate(Date.from(t2)));
+
+ // Somehow we need to wait for a while before reboot. Otherwise the change to the
+ // system clock will be reset after reboot.
+ Thread.sleep(3000);
+ getDevice().reboot();
+ runPhase("testExpireSession_Phase3_VerifyRollback");
+ }
+
private void pushTestApex() throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
@@ -521,4 +544,18 @@
return false;
}
}
+
+ @FunctionalInterface
+ private interface ExceptionalRunnable {
+ void run() throws Exception;
+ }
+
+ private void runAsRoot(ExceptionalRunnable runnable) throws Exception {
+ try {
+ getDevice().enableAdbRoot();
+ runnable.run();
+ } finally {
+ getDevice().disableAdbRoot();
+ }
+ }
}