Add multi-user test for the general non-staged case

This test makes assertions about the case where there are two
users running a test application. It verifies that each user can
have its own unique userdata for the same app, and that upgrades
and downgrades are applicable for all users on the device.

Test: atest MultiUserRollbackTest
Test: atest RollbackTest
Bug: 140401995
Change-Id: I5e5c154758b3f0e4731696c813519ac798fbbae9
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 2bd5931..231d045b 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -31,9 +31,9 @@
 }
 
 java_test_host {
-    name: "SecondaryUserRollbackTest",
-    srcs: ["SecondaryUserRollbackTest/src/**/*.java"],
+    name: "MultiUserRollbackTest",
+    srcs: ["MultiUserRollbackTest/src/**/*.java"],
     libs: ["tradefed"],
     test_suites: ["general-tests"],
-    test_config: "SecondaryUserRollbackTest.xml",
+    test_config: "MultiUserRollbackTest.xml",
 }
diff --git a/tests/RollbackTest/SecondaryUserRollbackTest.xml b/tests/RollbackTest/MultiUserRollbackTest.xml
similarity index 67%
rename from tests/RollbackTest/SecondaryUserRollbackTest.xml
rename to tests/RollbackTest/MultiUserRollbackTest.xml
index 6b3f05c..41cec46 100644
--- a/tests/RollbackTest/SecondaryUserRollbackTest.xml
+++ b/tests/RollbackTest/MultiUserRollbackTest.xml
@@ -13,17 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Runs the rollback test from a secondary user">
-    <option name="test-suite-tag" value="SecondaryUserRollbackTest" />
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="RollbackTest.apk" />
-    </target_preparer>
+<configuration description="Runs rollback tests for multiple users">
+    <option name="test-suite-tag" value="MultiUserRollbackTest" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
-        <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
-        <option name="class" value="com.android.tests.rollback.host.SecondaryUserRollbackTest" />
+        <option name="class" value="com.android.tests.rollback.host.MultiUserRollbackTest" />
     </test>
 </configuration>
diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
new file mode 100644
index 0000000..52f6eba
--- /dev/null
+++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.tests.rollback.host;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Runs rollback tests for multiple users.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MultiUserRollbackTest extends BaseHostJUnit4Test {
+    // The user that was running originally when the test starts.
+    private int mOriginalUserId;
+    private int mSecondaryUserId = -1;
+    private static final long SWITCH_USER_COMPLETED_NUMBER_OF_POLLS = 60;
+    private static final long SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS = 1000;
+
+
+    @After
+    public void tearDown() throws Exception {
+        getDevice().switchUser(mOriginalUserId);
+        getDevice().executeShellCommand("pm uninstall com.android.cts.install.lib.testapp.A");
+        removeSecondaryUserIfNecessary();
+    }
+
+    @Before
+    public void setup() throws Exception {
+        mOriginalUserId = getDevice().getCurrentUser();
+        installPackageAsUser("RollbackTest.apk", true, mOriginalUserId);
+        createAndSwitchToSecondaryUserIfNecessary();
+        installPackageAsUser("RollbackTest.apk", true, mSecondaryUserId);
+    }
+
+    @Test
+    public void testBasicForSecondaryUser() throws Exception {
+        runPhaseForUsers("testBasic", mSecondaryUserId);
+    }
+
+    @Test
+    public void testMultipleUsers() throws Exception {
+        runPhaseForUsers("testMultipleUsersInstallV1", mOriginalUserId, mSecondaryUserId);
+        runPhaseForUsers("testMultipleUsersUpgradeToV2", mOriginalUserId);
+        runPhaseForUsers("testMultipleUsersUpdateUserData", mOriginalUserId, mSecondaryUserId);
+        switchToUser(mOriginalUserId);
+        getDevice().executeShellCommand("pm rollback-app com.android.cts.install.lib.testapp.A");
+        runPhaseForUsers("testMultipleUsersVerifyUserdataRollback", mOriginalUserId,
+                mSecondaryUserId);
+    }
+
+    /**
+     * Run the phase for the given user ids, in the order they are given.
+     */
+    private void runPhaseForUsers(String phase, int... userIds) throws Exception {
+        for (int userId: userIds) {
+            switchToUser(userId);
+            assertTrue(runDeviceTests("com.android.tests.rollback",
+                    "com.android.tests.rollback.MultiUserRollbackTest",
+                    phase));
+        }
+    }
+
+    private void removeSecondaryUserIfNecessary() throws Exception {
+        if (mSecondaryUserId != -1) {
+            getDevice().removeUser(mSecondaryUserId);
+            mSecondaryUserId = -1;
+        }
+    }
+
+    private void createAndSwitchToSecondaryUserIfNecessary() throws Exception {
+        if (mSecondaryUserId == -1) {
+            mOriginalUserId = getDevice().getCurrentUser();
+            mSecondaryUserId = getDevice().createUser("MultiUserRollbackTest_User"
+                    + System.currentTimeMillis());
+            switchToUser(mSecondaryUserId);
+        }
+    }
+
+    private void switchToUser(int userId) throws Exception {
+        if (getDevice().getCurrentUser() == userId) {
+            return;
+        }
+
+        assertTrue(getDevice().switchUser(userId));
+        for (int i = 0; i < SWITCH_USER_COMPLETED_NUMBER_OF_POLLS; ++i) {
+            String userState = getDevice().executeShellCommand("am get-started-user-state "
+                    + userId);
+            if (userState.contains("RUNNING_UNLOCKED")) {
+                return;
+            }
+            Thread.sleep(SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS);
+        }
+        fail("User switch to user " + userId + " timed out");
+    }
+}
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 70cd867..a14b01c 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -22,8 +22,9 @@
         <option name="package" value="com.android.tests.rollback" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
 
-        <!-- Exclude the StagedRollbackTest tests, which needs to be specially
-             driven from the StagedRollbackTest host test -->
+        <!-- Exclude the StagedRollbackTest and MultiUserRollbackTest tests, which need to be
+             specially driven from the StagedRollbackTest and MultiUserRollbackTest host test -->
         <option name="exclude-filter" value="com.android.tests.rollback.StagedRollbackTest" />
+        <option name="exclude-filter" value="com.android.tests.rollback.MultiUserRollbackTest" />
     </test>
 </configuration>
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java
new file mode 100644
index 0000000..0ffe041
--- /dev/null
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.tests.rollback;
+
+import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat;
+import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoForPackage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.rollback.lib.Rollback;
+import com.android.cts.rollback.lib.RollbackUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+
+@RunWith(JUnit4.class)
+public class MultiUserRollbackTest {
+
+    @Before
+    public void adoptShellPermissions() {
+        InstallUtils.adoptShellPermissionIdentity(
+                Manifest.permission.INSTALL_PACKAGES,
+                Manifest.permission.DELETE_PACKAGES,
+                Manifest.permission.TEST_MANAGE_ROLLBACKS,
+                Manifest.permission.MANAGE_ROLLBACKS);
+    }
+
+    @After
+    public void dropShellPermissions() {
+        InstallUtils.dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void testBasic() throws Exception {
+        new RollbackTest().testBasic();
+    }
+
+    /**
+     * Install version 1 of the test app. This method is run for both users.
+     */
+    @Test
+    public void testMultipleUsersInstallV1() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        Install.single(TestApp.A1).commit();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        InstallUtils.processUserData(TestApp.A);
+    }
+
+    /**
+     * Upgrade the test app to version 2. This method should only run once as the system user,
+     * and will update the app for both users.
+     */
+    @Test
+    public void testMultipleUsersUpgradeToV2() throws Exception {
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        Install.single(TestApp.A2).setEnableRollback().commit();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+                rm.getAvailableRollbacks(), TestApp.A);
+        assertThat(rollback).isNotNull();
+        assertThat(rollback).packagesContainsExactly(
+                Rollback.from(TestApp.A2).to(TestApp.A1));
+    }
+
+    /**
+     * This method is run for both users. Assert that the test app has upgraded for both users, and
+     * update their userdata to reflect this new version.
+     */
+    @Test
+    public void testMultipleUsersUpdateUserData() {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        InstallUtils.processUserData(TestApp.A);
+    }
+
+    /**
+     * The system will have rolled back the test app at this stage. Verify that the rollback has
+     * taken place, and that the userdata has been correctly rolled back. This method is run for
+     * both users.
+     */
+    @Test
+    public void testMultipleUsersVerifyUserdataRollback() {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        InstallUtils.processUserData(TestApp.A);
+    }
+}
diff --git a/tests/RollbackTest/SecondaryUserRollbackTest/src/com/android/tests/rollback/host/SecondaryUserRollbackTest.java b/tests/RollbackTest/SecondaryUserRollbackTest/src/com/android/tests/rollback/host/SecondaryUserRollbackTest.java
deleted file mode 100644
index 11a0fbb..0000000
--- a/tests/RollbackTest/SecondaryUserRollbackTest/src/com/android/tests/rollback/host/SecondaryUserRollbackTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.tests.rollback.host;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Runs rollback tests from a secondary user.
- */
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class SecondaryUserRollbackTest extends BaseHostJUnit4Test {
-    private static final int SYSTEM_USER_ID = 0;
-    // The user that was running originally when the test starts.
-    private int mOriginalUser = SYSTEM_USER_ID;
-    private int mSecondaryUserId = -1;
-    private static final long SWITCH_USER_COMPLETED_NUMBER_OF_POLLS = 60;
-    private static final long SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS = 1000;
-
-
-    @After
-    public void tearDown() throws Exception {
-        getDevice().switchUser(mOriginalUser);
-        getDevice().executeShellCommand("pm uninstall com.android.cts.install.lib.testapp.A");
-        getDevice().executeShellCommand("pm uninstall com.android.cts.install.lib.testapp.B");
-        removeSecondaryUserIfNecessary();
-    }
-
-    @Before
-    public void setup() throws Exception {
-        createAndSwitchToSecondaryUserIfNecessary();
-        installPackageAsUser("RollbackTest.apk", true, mSecondaryUserId, "--user current");
-    }
-
-    @Test
-    public void testBasic() throws Exception {
-        assertTrue(runDeviceTests("com.android.tests.rollback",
-                "com.android.tests.rollback.RollbackTest",
-                "testBasic"));
-    }
-
-    private void removeSecondaryUserIfNecessary() throws Exception {
-        if (mSecondaryUserId != -1) {
-            getDevice().removeUser(mSecondaryUserId);
-            mSecondaryUserId = -1;
-        }
-    }
-
-    private void createAndSwitchToSecondaryUserIfNecessary() throws Exception {
-        if (mSecondaryUserId == -1) {
-            mOriginalUser = getDevice().getCurrentUser();
-            mSecondaryUserId = getDevice().createUser("SecondaryUserRollbackTest_User");
-            assertTrue(getDevice().switchUser(mSecondaryUserId));
-            // give time for user to be switched
-            waitForSwitchUserCompleted(mSecondaryUserId);
-        }
-    }
-
-    private void waitForSwitchUserCompleted(int userId) throws Exception {
-        for (int i = 0; i < SWITCH_USER_COMPLETED_NUMBER_OF_POLLS; ++i) {
-            String logs = getDevice().executeAdbCommand("logcat", "-v", "brief", "-d",
-                    "ActivityManager:D");
-            if (logs.contains("Posting BOOT_COMPLETED user #" + userId)) {
-                return;
-            }
-            Thread.sleep(SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS);
-        }
-        fail("User switch to user " + userId + " timed out");
-    }
-}
diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING
index 7ae03e6..fefde5b 100644
--- a/tests/RollbackTest/TEST_MAPPING
+++ b/tests/RollbackTest/TEST_MAPPING
@@ -7,7 +7,7 @@
       "name": "StagedRollbackTest"
     },
     {
-      "name": "SecondaryUserRollbackTest"
+      "name": "MultiUserRollbackTest"
     }
   ]
 }