xref: /aosp_15_r20/system/gsid/tests/DsuGsiIntegrationTest.java (revision 4e2b41f188908a2ae9d9a2089f1f10779d080021)
1*4e2b41f1SAndroid Build Coastguard Worker /*
2*4e2b41f1SAndroid Build Coastguard Worker  * Copyright (C) 2022 The Android Open Source Project
3*4e2b41f1SAndroid Build Coastguard Worker  *
4*4e2b41f1SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*4e2b41f1SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*4e2b41f1SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*4e2b41f1SAndroid Build Coastguard Worker  *
8*4e2b41f1SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*4e2b41f1SAndroid Build Coastguard Worker  *
10*4e2b41f1SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*4e2b41f1SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*4e2b41f1SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*4e2b41f1SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*4e2b41f1SAndroid Build Coastguard Worker  * limitations under the License.
15*4e2b41f1SAndroid Build Coastguard Worker  */
16*4e2b41f1SAndroid Build Coastguard Worker 
17*4e2b41f1SAndroid Build Coastguard Worker package com.android.tests.dsu;
18*4e2b41f1SAndroid Build Coastguard Worker 
19*4e2b41f1SAndroid Build Coastguard Worker import static org.junit.Assert.assertEquals;
20*4e2b41f1SAndroid Build Coastguard Worker import static org.junit.Assert.assertNotNull;
21*4e2b41f1SAndroid Build Coastguard Worker import static org.junit.Assert.assertTrue;
22*4e2b41f1SAndroid Build Coastguard Worker 
23*4e2b41f1SAndroid Build Coastguard Worker import com.android.tradefed.config.Option;
24*4e2b41f1SAndroid Build Coastguard Worker import com.android.tradefed.device.DeviceNotAvailableException;
25*4e2b41f1SAndroid Build Coastguard Worker import com.android.tradefed.log.LogUtil.CLog;
26*4e2b41f1SAndroid Build Coastguard Worker import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
27*4e2b41f1SAndroid Build Coastguard Worker import com.android.tradefed.util.FileUtil;
28*4e2b41f1SAndroid Build Coastguard Worker import com.android.tradefed.util.StreamUtil;
29*4e2b41f1SAndroid Build Coastguard Worker 
30*4e2b41f1SAndroid Build Coastguard Worker import org.junit.After;
31*4e2b41f1SAndroid Build Coastguard Worker import org.junit.Before;
32*4e2b41f1SAndroid Build Coastguard Worker import org.junit.Test;
33*4e2b41f1SAndroid Build Coastguard Worker import org.junit.runner.RunWith;
34*4e2b41f1SAndroid Build Coastguard Worker 
35*4e2b41f1SAndroid Build Coastguard Worker import java.io.BufferedInputStream;
36*4e2b41f1SAndroid Build Coastguard Worker import java.io.BufferedOutputStream;
37*4e2b41f1SAndroid Build Coastguard Worker import java.io.File;
38*4e2b41f1SAndroid Build Coastguard Worker import java.io.FileInputStream;
39*4e2b41f1SAndroid Build Coastguard Worker import java.io.FileOutputStream;
40*4e2b41f1SAndroid Build Coastguard Worker import java.io.IOException;
41*4e2b41f1SAndroid Build Coastguard Worker import java.io.InputStream;
42*4e2b41f1SAndroid Build Coastguard Worker import java.util.zip.ZipEntry;
43*4e2b41f1SAndroid Build Coastguard Worker import java.util.zip.ZipOutputStream;
44*4e2b41f1SAndroid Build Coastguard Worker 
45*4e2b41f1SAndroid Build Coastguard Worker @RunWith(DeviceJUnit4ClassRunner.class)
46*4e2b41f1SAndroid Build Coastguard Worker public class DsuGsiIntegrationTest extends DsuTestBase {
47*4e2b41f1SAndroid Build Coastguard Worker     private static final long DSU_MAX_WAIT_SEC = 10 * 60;
48*4e2b41f1SAndroid Build Coastguard Worker     private static final long DSU_DEFAULT_USERDATA_SIZE = 8L << 30;
49*4e2b41f1SAndroid Build Coastguard Worker 
50*4e2b41f1SAndroid Build Coastguard Worker     private static final String GSI_IMAGE_NAME = "system.img";
51*4e2b41f1SAndroid Build Coastguard Worker     private static final String DSU_IMAGE_ZIP_PUSH_PATH = "/sdcard/gsi.zip";
52*4e2b41f1SAndroid Build Coastguard Worker 
53*4e2b41f1SAndroid Build Coastguard Worker     private static final String REMOUNT_TEST_PATH = "/system/remount_test";
54*4e2b41f1SAndroid Build Coastguard Worker     private static final String REMOUNT_TEST_FILE = REMOUNT_TEST_PATH + "/test_file";
55*4e2b41f1SAndroid Build Coastguard Worker 
56*4e2b41f1SAndroid Build Coastguard Worker     @Option(
57*4e2b41f1SAndroid Build Coastguard Worker             name = "wipe-dsu-on-failure",
58*4e2b41f1SAndroid Build Coastguard Worker             description = "Wipe the DSU installation on test failure.")
59*4e2b41f1SAndroid Build Coastguard Worker     private boolean mWipeDsuOnFailure = true;
60*4e2b41f1SAndroid Build Coastguard Worker 
61*4e2b41f1SAndroid Build Coastguard Worker     @Option(
62*4e2b41f1SAndroid Build Coastguard Worker             name = "system-image-path",
63*4e2b41f1SAndroid Build Coastguard Worker             description = "Path to the GSI system.img or directory containing the system.img.",
64*4e2b41f1SAndroid Build Coastguard Worker             mandatory = true)
65*4e2b41f1SAndroid Build Coastguard Worker     private File mSystemImagePath;
66*4e2b41f1SAndroid Build Coastguard Worker 
67*4e2b41f1SAndroid Build Coastguard Worker     private File mSystemImageZip;
68*4e2b41f1SAndroid Build Coastguard Worker 
getDsuInstallCommand()69*4e2b41f1SAndroid Build Coastguard Worker     private String getDsuInstallCommand() {
70*4e2b41f1SAndroid Build Coastguard Worker         return String.format(
71*4e2b41f1SAndroid Build Coastguard Worker                 "am start-activity"
72*4e2b41f1SAndroid Build Coastguard Worker                         + " -n com.android.dynsystem/com.android.dynsystem.VerificationActivity"
73*4e2b41f1SAndroid Build Coastguard Worker                         + " -a android.os.image.action.START_INSTALL"
74*4e2b41f1SAndroid Build Coastguard Worker                         + " -d file://%s"
75*4e2b41f1SAndroid Build Coastguard Worker                         + " --el KEY_USERDATA_SIZE %d"
76*4e2b41f1SAndroid Build Coastguard Worker                         + " --ez KEY_ENABLE_WHEN_COMPLETED true",
77*4e2b41f1SAndroid Build Coastguard Worker                 DSU_IMAGE_ZIP_PUSH_PATH, getDsuUserdataSize(DSU_DEFAULT_USERDATA_SIZE));
78*4e2b41f1SAndroid Build Coastguard Worker     }
79*4e2b41f1SAndroid Build Coastguard Worker 
80*4e2b41f1SAndroid Build Coastguard Worker     @Before
setUp()81*4e2b41f1SAndroid Build Coastguard Worker     public void setUp() throws IOException {
82*4e2b41f1SAndroid Build Coastguard Worker         mSystemImageZip = null;
83*4e2b41f1SAndroid Build Coastguard Worker         InputStream stream = null;
84*4e2b41f1SAndroid Build Coastguard Worker         try {
85*4e2b41f1SAndroid Build Coastguard Worker             assertNotNull("--system-image-path is invalid", mSystemImagePath);
86*4e2b41f1SAndroid Build Coastguard Worker             if (mSystemImagePath.isDirectory()) {
87*4e2b41f1SAndroid Build Coastguard Worker                 File gsiImageFile = FileUtil.findFile(mSystemImagePath, GSI_IMAGE_NAME);
88*4e2b41f1SAndroid Build Coastguard Worker                 assertNotNull("Cannot find " + GSI_IMAGE_NAME, gsiImageFile);
89*4e2b41f1SAndroid Build Coastguard Worker                 stream = new FileInputStream(gsiImageFile);
90*4e2b41f1SAndroid Build Coastguard Worker             } else {
91*4e2b41f1SAndroid Build Coastguard Worker                 stream = new FileInputStream(mSystemImagePath);
92*4e2b41f1SAndroid Build Coastguard Worker             }
93*4e2b41f1SAndroid Build Coastguard Worker             stream = new BufferedInputStream(stream);
94*4e2b41f1SAndroid Build Coastguard Worker             mSystemImageZip = FileUtil.createTempFile(this.getClass().getSimpleName(), "gsi.zip");
95*4e2b41f1SAndroid Build Coastguard Worker             try (FileOutputStream foStream = new FileOutputStream(mSystemImageZip);
96*4e2b41f1SAndroid Build Coastguard Worker                     BufferedOutputStream boStream = new BufferedOutputStream(foStream);
97*4e2b41f1SAndroid Build Coastguard Worker                     ZipOutputStream out = new ZipOutputStream(boStream); ) {
98*4e2b41f1SAndroid Build Coastguard Worker                 // Don't bother compressing it as we are going to uncompress it on device anyway.
99*4e2b41f1SAndroid Build Coastguard Worker                 out.setLevel(0);
100*4e2b41f1SAndroid Build Coastguard Worker                 out.putNextEntry(new ZipEntry(GSI_IMAGE_NAME));
101*4e2b41f1SAndroid Build Coastguard Worker                 StreamUtil.copyStreams(stream, out);
102*4e2b41f1SAndroid Build Coastguard Worker                 out.closeEntry();
103*4e2b41f1SAndroid Build Coastguard Worker             }
104*4e2b41f1SAndroid Build Coastguard Worker         } finally {
105*4e2b41f1SAndroid Build Coastguard Worker             StreamUtil.close(stream);
106*4e2b41f1SAndroid Build Coastguard Worker         }
107*4e2b41f1SAndroid Build Coastguard Worker     }
108*4e2b41f1SAndroid Build Coastguard Worker 
109*4e2b41f1SAndroid Build Coastguard Worker     @After
tearDown()110*4e2b41f1SAndroid Build Coastguard Worker     public void tearDown() {
111*4e2b41f1SAndroid Build Coastguard Worker         try {
112*4e2b41f1SAndroid Build Coastguard Worker             FileUtil.deleteFile(mSystemImageZip);
113*4e2b41f1SAndroid Build Coastguard Worker         } catch (SecurityException e) {
114*4e2b41f1SAndroid Build Coastguard Worker             CLog.w("Failed to clean up '%s': %s", mSystemImageZip, e);
115*4e2b41f1SAndroid Build Coastguard Worker         }
116*4e2b41f1SAndroid Build Coastguard Worker         if (mWipeDsuOnFailure) {
117*4e2b41f1SAndroid Build Coastguard Worker             // If test case completed successfully, then the test body should have called `wipe`
118*4e2b41f1SAndroid Build Coastguard Worker             // already and calling `wipe` again would be a noop.
119*4e2b41f1SAndroid Build Coastguard Worker             // If test case failed, then this piece of code would clean up the DSU installation left
120*4e2b41f1SAndroid Build Coastguard Worker             // by the failed test case.
121*4e2b41f1SAndroid Build Coastguard Worker             try {
122*4e2b41f1SAndroid Build Coastguard Worker                 getDevice().executeShellV2Command("gsi_tool wipe");
123*4e2b41f1SAndroid Build Coastguard Worker                 if (isDsuRunning()) {
124*4e2b41f1SAndroid Build Coastguard Worker                     getDevice().reboot();
125*4e2b41f1SAndroid Build Coastguard Worker                     getDevice().disableAdbRoot();
126*4e2b41f1SAndroid Build Coastguard Worker                 }
127*4e2b41f1SAndroid Build Coastguard Worker             } catch (DeviceNotAvailableException e) {
128*4e2b41f1SAndroid Build Coastguard Worker                 CLog.w("Failed to clean up DSU installation on device: %s", e);
129*4e2b41f1SAndroid Build Coastguard Worker             }
130*4e2b41f1SAndroid Build Coastguard Worker         }
131*4e2b41f1SAndroid Build Coastguard Worker         try {
132*4e2b41f1SAndroid Build Coastguard Worker             getDevice().deleteFile(DSU_IMAGE_ZIP_PUSH_PATH);
133*4e2b41f1SAndroid Build Coastguard Worker         } catch (DeviceNotAvailableException e) {
134*4e2b41f1SAndroid Build Coastguard Worker             CLog.w("Failed to clean up device '%s': %s", DSU_IMAGE_ZIP_PUSH_PATH, e);
135*4e2b41f1SAndroid Build Coastguard Worker         }
136*4e2b41f1SAndroid Build Coastguard Worker     }
137*4e2b41f1SAndroid Build Coastguard Worker 
138*4e2b41f1SAndroid Build Coastguard Worker     @Test
testDsuGsi()139*4e2b41f1SAndroid Build Coastguard Worker     public void testDsuGsi() throws DeviceNotAvailableException {
140*4e2b41f1SAndroid Build Coastguard Worker         if (isDsuRunning()) {
141*4e2b41f1SAndroid Build Coastguard Worker             CLog.i("Wipe existing DSU installation");
142*4e2b41f1SAndroid Build Coastguard Worker             assertShellCommand("gsi_tool wipe");
143*4e2b41f1SAndroid Build Coastguard Worker             getDevice().reboot();
144*4e2b41f1SAndroid Build Coastguard Worker             getDevice().disableAdbRoot();
145*4e2b41f1SAndroid Build Coastguard Worker             assertDsuNotRunning();
146*4e2b41f1SAndroid Build Coastguard Worker         }
147*4e2b41f1SAndroid Build Coastguard Worker 
148*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("Pushing '%s' -> '%s'", mSystemImageZip, DSU_IMAGE_ZIP_PUSH_PATH);
149*4e2b41f1SAndroid Build Coastguard Worker         getDevice().pushFile(mSystemImageZip, DSU_IMAGE_ZIP_PUSH_PATH, true);
150*4e2b41f1SAndroid Build Coastguard Worker 
151*4e2b41f1SAndroid Build Coastguard Worker         final long freeSpaceBeforeInstall = getDevice().getPartitionFreeSpace("/data") << 10;
152*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand(getDsuInstallCommand());
153*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("Wait for DSU installation complete and reboot");
154*4e2b41f1SAndroid Build Coastguard Worker         assertTrue(
155*4e2b41f1SAndroid Build Coastguard Worker                 "Timed out waiting for DSU installation complete",
156*4e2b41f1SAndroid Build Coastguard Worker                 getDevice().waitForDeviceNotAvailable(DSU_MAX_WAIT_SEC * 1000));
157*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("DSU installation is complete and device is disconnected");
158*4e2b41f1SAndroid Build Coastguard Worker 
159*4e2b41f1SAndroid Build Coastguard Worker         getDevice().waitForDeviceAvailable();
160*4e2b41f1SAndroid Build Coastguard Worker         assertDsuRunning();
161*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("Successfully booted with DSU");
162*4e2b41f1SAndroid Build Coastguard Worker 
163*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("Test 'gsi_tool enable -s' and 'gsi_tool enable'");
164*4e2b41f1SAndroid Build Coastguard Worker         getDevice().reboot();
165*4e2b41f1SAndroid Build Coastguard Worker         getDevice().disableAdbRoot();
166*4e2b41f1SAndroid Build Coastguard Worker         assertDsuNotRunning();
167*4e2b41f1SAndroid Build Coastguard Worker 
168*4e2b41f1SAndroid Build Coastguard Worker         final long freeSpaceAfterInstall = getDevice().getPartitionFreeSpace("/data") << 10;
169*4e2b41f1SAndroid Build Coastguard Worker         final long estimatedDsuSize = freeSpaceBeforeInstall - freeSpaceAfterInstall;
170*4e2b41f1SAndroid Build Coastguard Worker         assertTrue(
171*4e2b41f1SAndroid Build Coastguard Worker                 String.format(
172*4e2b41f1SAndroid Build Coastguard Worker                         "Expected DSU installation to consume some storage space, free space before"
173*4e2b41f1SAndroid Build Coastguard Worker                                 + " install: %d, free space after install: %d, delta: %d",
174*4e2b41f1SAndroid Build Coastguard Worker                         freeSpaceBeforeInstall, freeSpaceAfterInstall, estimatedDsuSize),
175*4e2b41f1SAndroid Build Coastguard Worker                 estimatedDsuSize > 0);
176*4e2b41f1SAndroid Build Coastguard Worker 
177*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand("gsi_tool enable");
178*4e2b41f1SAndroid Build Coastguard Worker         getDevice().reboot();
179*4e2b41f1SAndroid Build Coastguard Worker         getDevice().disableAdbRoot();
180*4e2b41f1SAndroid Build Coastguard Worker         assertDsuRunning();
181*4e2b41f1SAndroid Build Coastguard Worker 
182*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("Set up 'adb remount' for testing (requires reboot)");
183*4e2b41f1SAndroid Build Coastguard Worker         assertAdbRoot();
184*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand("remount");
185*4e2b41f1SAndroid Build Coastguard Worker         getDevice().reboot();
186*4e2b41f1SAndroid Build Coastguard Worker         getDevice().disableAdbRoot();
187*4e2b41f1SAndroid Build Coastguard Worker         assertDsuRunning();
188*4e2b41f1SAndroid Build Coastguard Worker         assertAdbRoot();
189*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand("remount");
190*4e2b41f1SAndroid Build Coastguard Worker         assertDevicePathNotExist(REMOUNT_TEST_PATH);
191*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand(String.format("mkdir -p '%s'", REMOUNT_TEST_PATH));
192*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand(String.format("cp /system/bin/init '%s'", REMOUNT_TEST_FILE));
193*4e2b41f1SAndroid Build Coastguard Worker         final String initContent = getDevice().pullFileContents("/system/bin/init");
194*4e2b41f1SAndroid Build Coastguard Worker 
195*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("DSU and original system have separate remount overlays");
196*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand("gsi_tool disable");
197*4e2b41f1SAndroid Build Coastguard Worker         getDevice().reboot();
198*4e2b41f1SAndroid Build Coastguard Worker         getDevice().disableAdbRoot();
199*4e2b41f1SAndroid Build Coastguard Worker         assertDsuNotRunning();
200*4e2b41f1SAndroid Build Coastguard Worker         assertDevicePathNotExist(REMOUNT_TEST_PATH);
201*4e2b41f1SAndroid Build Coastguard Worker 
202*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("Test that 'adb remount' is consistent after reboot");
203*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand("gsi_tool enable");
204*4e2b41f1SAndroid Build Coastguard Worker         getDevice().reboot();
205*4e2b41f1SAndroid Build Coastguard Worker         getDevice().disableAdbRoot();
206*4e2b41f1SAndroid Build Coastguard Worker         assertDsuRunning();
207*4e2b41f1SAndroid Build Coastguard Worker         assertDevicePathExist(REMOUNT_TEST_FILE);
208*4e2b41f1SAndroid Build Coastguard Worker         assertEquals(
209*4e2b41f1SAndroid Build Coastguard Worker                 String.format(
210*4e2b41f1SAndroid Build Coastguard Worker                         "Expected contents of '%s' to persist after reboot", REMOUNT_TEST_FILE),
211*4e2b41f1SAndroid Build Coastguard Worker                 initContent,
212*4e2b41f1SAndroid Build Coastguard Worker                 getDevice().pullFileContents(REMOUNT_TEST_FILE));
213*4e2b41f1SAndroid Build Coastguard Worker 
214*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("'enable-verity' should teardown the remount overlay and restore the filesystem");
215*4e2b41f1SAndroid Build Coastguard Worker         assertAdbRoot();
216*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand("enable-verity");
217*4e2b41f1SAndroid Build Coastguard Worker         getDevice().reboot();
218*4e2b41f1SAndroid Build Coastguard Worker         getDevice().disableAdbRoot();
219*4e2b41f1SAndroid Build Coastguard Worker         assertDsuRunning();
220*4e2b41f1SAndroid Build Coastguard Worker         assertDevicePathNotExist(REMOUNT_TEST_PATH);
221*4e2b41f1SAndroid Build Coastguard Worker 
222*4e2b41f1SAndroid Build Coastguard Worker         CLog.i("Test 'gsi_tool wipe'");
223*4e2b41f1SAndroid Build Coastguard Worker         assertShellCommand("gsi_tool wipe");
224*4e2b41f1SAndroid Build Coastguard Worker         getDevice().reboot();
225*4e2b41f1SAndroid Build Coastguard Worker         getDevice().disableAdbRoot();
226*4e2b41f1SAndroid Build Coastguard Worker         assertDsuNotRunning();
227*4e2b41f1SAndroid Build Coastguard Worker 
228*4e2b41f1SAndroid Build Coastguard Worker         final double dampeningCoefficient = 0.9;
229*4e2b41f1SAndroid Build Coastguard Worker         final long freeSpaceAfterWipe = getDevice().getPartitionFreeSpace("/data") << 10;
230*4e2b41f1SAndroid Build Coastguard Worker         final long freeSpaceReturnedByWipe = freeSpaceAfterWipe - freeSpaceAfterInstall;
231*4e2b41f1SAndroid Build Coastguard Worker         assertTrue(
232*4e2b41f1SAndroid Build Coastguard Worker                 String.format(
233*4e2b41f1SAndroid Build Coastguard Worker                         "Expected 'gsi_tool wipe' to return roughly %d of storage space, free space"
234*4e2b41f1SAndroid Build Coastguard Worker                             + " before wipe: %d, free space after wipe: %d, delta: %d",
235*4e2b41f1SAndroid Build Coastguard Worker                         estimatedDsuSize,
236*4e2b41f1SAndroid Build Coastguard Worker                         freeSpaceAfterInstall,
237*4e2b41f1SAndroid Build Coastguard Worker                         freeSpaceAfterWipe,
238*4e2b41f1SAndroid Build Coastguard Worker                         freeSpaceReturnedByWipe),
239*4e2b41f1SAndroid Build Coastguard Worker                 freeSpaceReturnedByWipe > (long) (estimatedDsuSize * dampeningCoefficient));
240*4e2b41f1SAndroid Build Coastguard Worker     }
241*4e2b41f1SAndroid Build Coastguard Worker }
242