diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 1cf3d364a8d55..aec40558ad51e 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -95,7 +95,7 @@ android_test { ":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 81629aaaec762..a9e20cdb191b4 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.ComponentName; 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 @@ class RollbackTestUtils { * 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 @@ class RollbackTestUtils { * @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 1ddfa6ee54ce7..1a29c4c114579 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 static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfo 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 androidx.test.InstrumentationRegistry; 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 @@ public class StagedRollbackTest { 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 @@ public class StagedRollbackTest { 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 75a95adf460a6..bad2947943372 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.log.LogUtil.CLog; 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 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { 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 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { } 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"); + } }