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