Merge "Add network health check watchdog triggered rollback tests" into qt-dev
am: 80b53bd68b

Change-Id: I760b8f0d948702a51b96c3aaffa6ccb268b891fe
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 1cf3d36..aec4055 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -95,7 +95,7 @@
         ":RollbackTestAppASplitV2",
     ],
     test_config: "RollbackTest.xml",
-    sdk_version: "test_current",
+    // TODO: sdk_version: "test_current" when Intent#resolveSystemservice is TestApi
 }
 
 java_test_host {
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index 81629aa..a9e20cd 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
@@ -82,13 +83,31 @@
      * Returns -1 if the package is not currently installed.
      */
     static long getInstalledVersion(String packageName) {
+        PackageInfo pi = getPackageInfo(packageName);
+        if (pi == null) {
+            return -1;
+        } else {
+            return pi.getLongVersionCode();
+        }
+    }
+
+    private static boolean isSystemAppWithoutUpdate(String packageName) {
+        PackageInfo pi = getPackageInfo(packageName);
+        if (pi == null) {
+            return false;
+        } else {
+            return ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)
+                    && ((pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0);
+        }
+    }
+
+    private static PackageInfo getPackageInfo(String packageName) {
         Context context = InstrumentationRegistry.getContext();
         PackageManager pm = context.getPackageManager();
         try {
-            PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
-            return info.getLongVersionCode();
+            return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
         } catch (PackageManager.NameNotFoundException e) {
-            return -1;
+            return null;
         }
     }
 
@@ -109,8 +128,8 @@
      * @throws AssertionError if package can't be uninstalled.
      */
     static void uninstall(String packageName) throws InterruptedException, IOException {
-        // No need to uninstall if the package isn't installed.
-        if (getInstalledVersion(packageName) == -1) {
+        // No need to uninstall if the package isn't installed or is installed on /system.
+        if (getInstalledVersion(packageName) == -1 || isSystemAppWithoutUpdate(packageName)) {
             return;
         }
 
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 1ddfa6e..1a29c4c 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -21,7 +21,9 @@
 
 import android.Manifest;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.VersionedPackage;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
@@ -30,6 +32,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -54,6 +58,8 @@
     private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
     private static final String TEST_APP_A_V1 = "RollbackTestAppAv1.apk";
     private static final String TEST_APP_A_CRASHING_V2 = "RollbackTestAppACrashingV2.apk";
+    private static final String NETWORK_STACK_CONNECTOR_CLASS =
+            "android.net.INetworkStackConnector";
 
     /**
      * Adopts common shell permissions needed for rollback tests.
@@ -157,4 +163,44 @@
         assertTrue(rollback.isStaged());
         assertNotEquals(-1, rollback.getCommittedSessionId());
     }
+
+    @Test
+    public void resetNetworkStack() throws Exception {
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+        String networkStack = getNetworkStackPackageName();
+
+        rm.expireRollbackForPackage(networkStack);
+        RollbackTestUtils.uninstall(networkStack);
+
+        assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                        networkStack));
+    }
+
+    @Test
+    public void assertNetworkStackRollbackAvailable() throws Exception {
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+        assertNotNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                        getNetworkStackPackageName()));
+    }
+
+    @Test
+    public void assertNetworkStackRollbackCommitted() throws Exception {
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+        assertNotNull(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+                        getNetworkStackPackageName()));
+    }
+
+    @Test
+    public void assertNoNetworkStackRollbackCommitted() throws Exception {
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+        assertNull(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+                        getNetworkStackPackageName()));
+    }
+
+    private String getNetworkStackPackageName() {
+        Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
+        ComponentName comp = intent.resolveSystemService(
+                InstrumentationRegistry.getContext().getPackageManager(), 0);
+        return comp.getPackageName();
+    }
 }
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 75a95ad..bad2947 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
@@ -23,6 +23,8 @@
 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;
 
@@ -43,6 +45,20 @@
                     phase));
     }
 
+    @Before
+    public void setUp() throws Exception {
+        // Disconnect internet so we can test network health triggered rollbacks
+        getDevice().executeShellCommand("svc wifi disable");
+        getDevice().executeShellCommand("svc data disable");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Reconnect internet after testing network health triggered rollbacks
+        getDevice().executeShellCommand("svc wifi enable");
+        getDevice().executeShellCommand("svc data enable");
+    }
+
     /**
      * Tests watchdog triggered staged rollbacks involving only apks.
      */
@@ -63,6 +79,90 @@
         }
 
         getDevice().waitForDeviceAvailable();
+
         runPhase("testBadApkOnlyConfirmRollback");
     }
+
+    /**
+     * Tests failed network health check triggers watchdog staged rollbacks.
+     */
+    @Test
+    public void testNetworkFailedRollback() throws Exception {
+        // Remove available rollbacks and uninstall NetworkStack on /data/
+        runPhase("resetNetworkStack");
+        // Reduce health check deadline
+        getDevice().executeShellCommand("device_config put rollback "
+                + "watchdog_request_timeout_millis 300000");
+        // Simulate re-installation of new NetworkStack with rollbacks enabled
+        getDevice().executeShellCommand("pm install -r --staged --enable-rollback "
+                + "/system/priv-app/NetworkStack/NetworkStack.apk");
+
+        // Sleep to allow writes to disk before reboot
+        Thread.sleep(5000);
+        // Reboot device to activate staged package
+        getDevice().reboot();
+        getDevice().waitForDeviceAvailable();
+
+        // Verify rollback was enabled
+        runPhase("assertNetworkStackRollbackAvailable");
+
+        // Sleep for < health check deadline
+        Thread.sleep(5000);
+        // Verify rollback was not executed before health check deadline
+        runPhase("assertNoNetworkStackRollbackCommitted");
+        try {
+            // This is expected to fail due to the device being rebooted out
+            // from underneath the test. If this fails for reasons other than
+            // the device reboot, those failures should result in failure of
+            // the assertNetworkStackExecutedRollback phase.
+            CLog.logAndDisplay(LogLevel.INFO, "Sleep and expect to fail while sleeping");
+            // Sleep for > health check deadline
+            Thread.sleep(260000);
+        } catch (AssertionError e) {
+            // AssertionError is expected.
+        }
+
+        getDevice().waitForDeviceAvailable();
+        // Verify rollback was executed after health check deadline
+        runPhase("assertNetworkStackRollbackCommitted");
+    }
+
+    /**
+     * Tests passed network health check does not trigger watchdog staged rollbacks.
+     */
+    @Test
+    public void testNetworkPassedDoesNotRollback() throws Exception {
+        // Remove available rollbacks and uninstall NetworkStack on /data/
+        runPhase("resetNetworkStack");
+        // Reduce health check deadline, here unlike the network failed case, we use
+        // a longer deadline because joining a network can take a much longer time for
+        // reasons external to the device than 'not joining'
+        getDevice().executeShellCommand("device_config put rollback "
+                + "watchdog_request_timeout_millis 300000");
+        // Simulate re-installation of new NetworkStack with rollbacks enabled
+        getDevice().executeShellCommand("pm install -r --staged --enable-rollback "
+                + "/system/priv-app/NetworkStack/NetworkStack.apk");
+
+        // Sleep to allow writes to disk before reboot
+        Thread.sleep(5000);
+        // Reboot device to activate staged package
+        getDevice().reboot();
+        getDevice().waitForDeviceAvailable();
+
+        // Verify rollback was enabled
+        runPhase("assertNetworkStackRollbackAvailable");
+
+        // Connect to internet so network health check passes
+        getDevice().executeShellCommand("svc wifi enable");
+        getDevice().executeShellCommand("svc data enable");
+
+        // Wait for device available because emulator device may restart after turning
+        // on mobile data
+        getDevice().waitForDeviceAvailable();
+
+        // Sleep for > health check deadline
+        Thread.sleep(310000);
+        // Verify rollback was not executed after health check deadline
+        runPhase("assertNoNetworkStackRollbackCommitted");
+    }
 }