+ * In case of staged installs, this function will return succesfully after
+ * the staged install has been committed and is ready for the device to
+ * reboot.
*
+ * @param staged if the rollback should be staged.
* @param enableRollback if rollback should be enabled.
* @param resourceNames names of the class loader resource for the apks to
* install.
* @throws AssertionError if the installation fails.
*/
- static void installMultiPackage(boolean enableRollback, String... resourceNames)
- throws InterruptedException, IOException {
+ private static void install(boolean staged, boolean enableRollback,
+ String... resourceNames) throws InterruptedException, IOException {
Context context = InstrumentationRegistry.getContext();
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams multiPackageParams = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
multiPackageParams.setMultiPackage();
+ if (staged) {
+ multiPackageParams.setStaged();
+ }
if (enableRollback) {
// TODO: Do we set this on the parent params, the child params, or
// both?
@@ -183,6 +203,9 @@ class RollbackTestUtils {
PackageInstaller.Session session = null;
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ if (staged) {
+ params.setStaged();
+ }
if (enableRollback) {
params.setEnableRollback();
}
@@ -204,6 +227,36 @@ class RollbackTestUtils {
// Commit the session (this will start the installation workflow).
multiPackage.commit(LocalIntentSender.getIntentSender());
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+
+ if (staged) {
+ waitForSessionReady(multiPackageId);
+ }
+ }
+
+ /**
+ * Installs the apks with the given resource names as an atomic set.
+ *
+ * @param enableRollback if rollback should be enabled.
+ * @param resourceNames names of the class loader resource for the apks to
+ * install.
+ * @throws AssertionError if the installation fails.
+ */
+ static void installMultiPackage(boolean enableRollback, String... resourceNames)
+ throws InterruptedException, IOException {
+ install(false, enableRollback, resourceNames);
+ }
+
+ /**
+ * Installs the apks with the given resource names as a staged atomic set.
+ *
+ * @param enableRollback if rollback should be enabled.
+ * @param resourceNames names of the class loader resource for the apks to
+ * install.
+ * @throws AssertionError if the installation fails.
+ */
+ static void installStaged(boolean enableRollback, String... resourceNames)
+ throws InterruptedException, IOException {
+ install(true, enableRollback, resourceNames);
}
static void adoptShellPermissionIdentity(String... permissions) {
@@ -219,4 +272,104 @@ class RollbackTestUtils {
.getUiAutomation()
.dropShellPermissionIdentity();
}
+
+ /**
+ * Returns the RollbackInfo with a given package in the list of rollbacks.
+ * Throws an assertion failure if there is more than one such rollback
+ * info. Returns null if there are no such rollback infos.
+ */
+ static RollbackInfo getUniqueRollbackInfoForPackage(List
+ * Note: These tests require reboot in between test phases. They are run
+ * specially so that the testFooEnableRollback, testFooCommitRollback, and
+ * testFooConfirmRollback phases of each test are run in order with reboots in
+ * between them.
+ */
+@RunWith(JUnit4.class)
+public class StagedRollbackTest {
+
+ private static final String TAG = "RollbackTest";
+ private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
+
+ /**
+ * Adopts common shell permissions needed for rollback tests.
+ */
+ @Before
+ public void adoptShellPermissions() {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
+ }
+
+ /**
+ * Drops shell permissions needed for rollback tests.
+ */
+ @After
+ public void dropShellPermissions() {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+
+
+ /**
+ * Test basic rollbacks. Enable rollback phase.
+ */
+ @Test
+ public void testBasicEnableRollback() throws Exception {
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackTestUtils.installStaged(true, "RollbackTestAppAv2.apk");
+
+ // At this point, the host test driver will reboot the device and run
+ // testBasicCommitRollback().
+ }
+
+ /**
+ * Test basic rollbacks. Commit rollback phase.
+ */
+ @Test
+ public void testBasicCommitRollback() throws Exception {
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TEST_APP_A);
+ assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+ assertTrue(rollback.isStaged());
+
+ RollbackTestUtils.rollback(rollback.getRollbackId());
+
+ rollback = getUniqueRollbackInfoForPackage(
+ rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
+ assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+ assertTrue(rollback.isStaged());
+ assertNotEquals(-1, rollback.getCommittedSessionId());
+
+ RollbackTestUtils.waitForSessionReady(rollback.getCommittedSessionId());
+
+ // The app should not be rolled back until after reboot.
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // At this point, the host test driver will reboot the device and run
+ // testBasicConfirmRollback().
+ }
+
+ /**
+ * Test basic rollbacks. Confirm rollback phase.
+ */
+ @Test
+ public void testBasicConfirmRollback() throws Exception {
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
+ assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+ assertTrue(rollback.isStaged());
+ assertNotEquals(-1, rollback.getCommittedSessionId());
+ }
+}
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 5c006931128c9..6cb0dd091392e 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
@@ -16,6 +16,8 @@
package com.android.tests.rollback.host;
+import static org.junit.Assert.assertTrue;
+
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -28,11 +30,27 @@ import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
public class StagedRollbackTest extends BaseHostJUnit4Test {
+ /**
+ * Runs the given phase of a test by calling into the device.
+ * Throws an exception if the test phase fails.
+ *
+ * For example, runPhase("testBasicEnableRollback");
+ */
+ private void runPhase(String phase) throws Exception {
+ assertTrue(runDeviceTests("com.android.tests.rollback",
+ "com.android.tests.rollback.StagedRollbackTest",
+ phase));
+ }
+
/**
* Tests staged rollbacks.
*/
@Test
- public void testStagedRollback() {
- // TODO: Actually test staged rollbacks.
+ public void testBasic() throws Exception {
+ runPhase("testBasicEnableRollback");
+ getDevice().reboot();
+ runPhase("testBasicCommitRollback");
+ getDevice().reboot();
+ runPhase("testBasicConfirmRollback");
}
}