1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tests.rollback.host; 18 19 import static org.junit.Assert.assertTrue; 20 21 import com.android.ddmlib.Log.LogLevel; 22 import com.android.tradefed.device.DeviceNotAvailableException; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 25 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 26 27 import org.junit.After; 28 import org.junit.Before; 29 import org.junit.Ignore; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 33 import java.util.concurrent.TimeUnit; 34 35 /** 36 * Runs the staged rollback tests. 37 */ 38 @RunWith(DeviceJUnit4ClassRunner.class) 39 public class StagedRollbackTest extends BaseHostJUnit4Test { 40 private static final int NATIVE_CRASHES_THRESHOLD = 5; 41 42 /** 43 * Runs the given phase of a test by calling into the device. 44 * Throws an exception if the test phase fails. 45 * <p> 46 * For example, <code>runPhase("testApkOnlyEnableRollback");</code> 47 */ runPhase(String phase)48 private void runPhase(String phase) throws Exception { 49 assertTrue(runDeviceTests("com.android.tests.rollback", 50 "com.android.tests.rollback.StagedRollbackTest", 51 phase)); 52 } 53 54 @Before setUp()55 public void setUp() throws Exception { 56 // Disconnect internet so we can test network health triggered rollbacks 57 getDevice().executeShellCommand("svc wifi disable"); 58 getDevice().executeShellCommand("svc data disable"); 59 } 60 61 @After tearDown()62 public void tearDown() throws Exception { 63 // Reconnect internet after testing network health triggered rollbacks 64 getDevice().executeShellCommand("svc wifi enable"); 65 getDevice().executeShellCommand("svc data enable"); 66 } 67 68 /** 69 * Tests watchdog triggered staged rollbacks involving only apks. 70 */ 71 @Test 72 @Ignore("b/139175593 flaky test") testBadApkOnly()73 public void testBadApkOnly() throws Exception { 74 runPhase("testBadApkOnlyEnableRollback"); 75 getDevice().reboot(); 76 runPhase("testBadApkOnlyConfirmEnableRollback"); 77 try { 78 // This is expected to fail due to the device being rebooted out 79 // from underneath the test. If this fails for reasons other than 80 // the device reboot, those failures should result in failure of 81 // the testApkOnlyConfirmRollback phase. 82 CLog.logAndDisplay(LogLevel.INFO, "testBadApkOnlyTriggerRollback is expected to fail"); 83 runPhase("testBadApkOnlyTriggerRollback"); 84 } catch (AssertionError e) { 85 // AssertionError is expected. 86 } 87 88 getDevice().waitForDeviceAvailable(); 89 90 runPhase("testBadApkOnlyConfirmRollback"); 91 } 92 93 @Test testNativeWatchdogTriggersRollback()94 public void testNativeWatchdogTriggersRollback() throws Exception { 95 //Stage install ModuleMetadata package - this simulates a Mainline module update 96 runPhase("installModuleMetadataPackage"); 97 98 // Reboot device to activate staged package 99 getDevice().reboot(); 100 getDevice().waitForDeviceAvailable(); 101 102 runPhase("assertModuleMetadataRollbackAvailable"); 103 104 // crash system_server enough times to trigger a rollback 105 crashProcess("system_server", NATIVE_CRASHES_THRESHOLD); 106 107 // Rollback should be committed automatically now. 108 // Give time for rollback to be committed. This could take a while, 109 // because we need all of the following to happen: 110 // 1. system_server comes back up and boot completes. 111 // 2. Rollback health observer detects updatable crashing signal. 112 // 3. Staged rollback session becomes ready. 113 // 4. Device actually reboots. 114 // So we give a generous timeout here. 115 assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))); 116 getDevice().waitForDeviceAvailable(); 117 118 // verify rollback committed 119 runPhase("assertModuleMetadataRollbackCommitted"); 120 } 121 122 /** 123 * Tests failed network health check triggers watchdog staged rollbacks. 124 */ 125 @Test testNetworkFailedRollback()126 public void testNetworkFailedRollback() throws Exception { 127 // Remove available rollbacks and uninstall NetworkStack on /data/ 128 runPhase("resetNetworkStack"); 129 // Reduce health check deadline 130 getDevice().executeShellCommand("device_config put rollback " 131 + "watchdog_request_timeout_millis 120000"); 132 // Simulate re-installation of new NetworkStack with rollbacks enabled 133 getDevice().executeShellCommand("pm install -r --staged --enable-rollback " 134 + getNetworkStackPath()); 135 136 // Sleep to allow writes to disk before reboot 137 Thread.sleep(5000); 138 // Reboot device to activate staged package 139 getDevice().reboot(); 140 getDevice().waitForDeviceAvailable(); 141 142 // Verify rollback was enabled 143 runPhase("assertNetworkStackRollbackAvailable"); 144 145 // Sleep for < health check deadline 146 Thread.sleep(5000); 147 // Verify rollback was not executed before health check deadline 148 runPhase("assertNoNetworkStackRollbackCommitted"); 149 150 // Wait for reboot to happen 151 assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))); 152 // Wait for reboot to complete and device to become available 153 getDevice().waitForDeviceAvailable(); 154 // Verify rollback was executed after health check deadline 155 runPhase("assertNetworkStackRollbackCommitted"); 156 } 157 158 /** 159 * Tests passed network health check does not trigger watchdog staged rollbacks. 160 */ 161 @Test testNetworkPassedDoesNotRollback()162 public void testNetworkPassedDoesNotRollback() throws Exception { 163 // Remove available rollbacks and uninstall NetworkStack on /data/ 164 runPhase("resetNetworkStack"); 165 // Reduce health check deadline, here unlike the network failed case, we use 166 // a longer deadline because joining a network can take a much longer time for 167 // reasons external to the device than 'not joining' 168 getDevice().executeShellCommand("device_config put rollback " 169 + "watchdog_request_timeout_millis 300000"); 170 // Simulate re-installation of new NetworkStack with rollbacks enabled 171 getDevice().executeShellCommand("pm install -r --staged --enable-rollback " 172 + getNetworkStackPath()); 173 174 // Sleep to allow writes to disk before reboot 175 Thread.sleep(5000); 176 // Reboot device to activate staged package 177 getDevice().reboot(); 178 getDevice().waitForDeviceAvailable(); 179 180 // Verify rollback was enabled 181 runPhase("assertNetworkStackRollbackAvailable"); 182 183 // Connect to internet so network health check passes 184 getDevice().executeShellCommand("svc wifi enable"); 185 getDevice().executeShellCommand("svc data enable"); 186 187 // Wait for device available because emulator device may restart after turning 188 // on mobile data 189 getDevice().waitForDeviceAvailable(); 190 191 // Sleep for > health check deadline 192 Thread.sleep(310000); 193 // Verify rollback was not executed after health check deadline 194 runPhase("assertNoNetworkStackRollbackCommitted"); 195 } 196 197 /** 198 * Tests rolling back user data where there are multiple rollbacks for that package. 199 */ 200 @Test testPreviouslyAbandonedRollbacks()201 public void testPreviouslyAbandonedRollbacks() throws Exception { 202 runPhase("testPreviouslyAbandonedRollbacksEnableRollback"); 203 getDevice().reboot(); 204 runPhase("testPreviouslyAbandonedRollbacksCommitRollback"); 205 getDevice().reboot(); 206 runPhase("testPreviouslyAbandonedRollbacksCheckUserdataRollback"); 207 } 208 crashProcess(String processName, int numberOfCrashes)209 private void crashProcess(String processName, int numberOfCrashes) throws Exception { 210 String pid = ""; 211 String lastPid = "invalid"; 212 for (int i = 0; i < numberOfCrashes; ++i) { 213 // This condition makes sure before we kill the process, the process is running AND 214 // the last crash was finished. 215 while ("".equals(pid) || lastPid.equals(pid)) { 216 pid = getDevice().executeShellCommand("pidof " + processName); 217 } 218 getDevice().executeShellCommand("kill " + pid); 219 lastPid = pid; 220 } 221 } 222 getNetworkStackPath()223 private String getNetworkStackPath() throws DeviceNotAvailableException { 224 // Find the NetworkStack path (can be NetworkStack.apk or NetworkStackNext.apk) 225 return getDevice().executeShellCommand("ls /system/priv-app/NetworkStack*/*.apk"); 226 } 227 } 228