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.dsu; 18 19 import com.android.tradefed.build.BuildRetrievalError; 20 import com.android.tradefed.build.IBuildInfo; 21 import com.android.tradefed.build.IDeviceBuildInfo; 22 import com.android.tradefed.config.Option; 23 import com.android.tradefed.config.Option.Importance; 24 import com.android.tradefed.device.DeviceNotAvailableException; 25 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 26 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 27 import com.android.tradefed.util.CommandResult; 28 import com.android.tradefed.util.ZipUtil2; 29 30 import org.apache.commons.compress.archivers.zip.ZipFile; 31 32 import org.junit.After; 33 import org.junit.Assert; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 37 import java.io.ByteArrayOutputStream; 38 import java.io.File; 39 import java.io.IOException; 40 import java.lang.Process; 41 import java.lang.Runtime; 42 import java.util.concurrent.TimeUnit; 43 44 /** 45 * Test Dynamic System Updates by booting in and out of a supplied system image 46 */ 47 @RunWith(DeviceJUnit4ClassRunner.class) 48 public class DSUEndtoEndTest extends BaseHostJUnit4Test { 49 private static final long kDefaultUserdataSize = 4L * 1024 * 1024 * 1024; 50 private static final String APK = "LockScreenAutomation.apk"; 51 private static final String PACKAGE = "com.google.android.lockscreenautomation"; 52 private static final String UI_AUTOMATOR_INSTRUMENTATION_RUNNER = 53 "androidx.test.uiautomator.UiAutomatorInstrumentationTestRunner"; 54 private static final String CLASS = "LockScreenAutomation"; 55 private static final String LPUNPACK_PATH = "bin/lpunpack"; 56 private static final String SIMG2IMG_PATH = "bin/simg2img"; 57 58 // Example: atest -v DSUEndtoEndTest -- --test-arg \ 59 // com.android.tradefed.testtype.HostTest:set-option:system_image_path:/full/path/to/system.img 60 @Option(name="system_image_path", 61 shortName='s', 62 description="full path to the system image to use. If not specified, attempt " + 63 "to download the image from the test infrastructure", 64 importance=Importance.ALWAYS) 65 private String mSystemImagePath; 66 67 @Option(name="userdata_size", 68 shortName='u', 69 description="size in bytes of the new userdata partition", 70 importance=Importance.ALWAYS) 71 private long mUserdataSize = kDefaultUserdataSize; 72 73 private File mUnsparseSystemImage; 74 75 @After teardown()76 public void teardown() throws Exception { 77 uninstallPackage(PACKAGE); 78 if (mUnsparseSystemImage != null) { 79 mUnsparseSystemImage.delete(); 80 } 81 } 82 83 @Test testDSU()84 public void testDSU() throws Exception { 85 String simg2imgPath = "simg2img"; 86 if (mSystemImagePath == null) { 87 IBuildInfo buildInfo = getBuild(); 88 File imgs = ((IDeviceBuildInfo) buildInfo).getDeviceImageFile(); 89 Assert.assertNotEquals("Failed to fetch system image. See system_image_path parameter", null, imgs); 90 File otaTools = buildInfo.getFile("otatools.zip"); 91 File tempdir = ZipUtil2.extractZipToTemp(otaTools, "otatools"); 92 File system = ZipUtil2.extractFileFromZip(new ZipFile(imgs), "system.img"); 93 if (system == null) { 94 File superImg = ZipUtil2.extractFileFromZip(new ZipFile(imgs), "super.img"); 95 String lpunpackPath = new File(tempdir, LPUNPACK_PATH).getAbsolutePath(); 96 String outputDir = superImg.getParentFile().getAbsolutePath(); 97 String[] cmd = {lpunpackPath, "-p", "system_a", superImg.getAbsolutePath(), outputDir}; 98 Process p = Runtime.getRuntime().exec(cmd); 99 p.waitFor(); 100 if (p.exitValue() == 0) { 101 mSystemImagePath = new File(outputDir, "system_a.img").getAbsolutePath(); 102 } else { 103 ByteArrayOutputStream stderr = new ByteArrayOutputStream(); 104 int len; 105 byte[] buf = new byte[1024]; 106 while ((len = p.getErrorStream().read(buf)) != -1) { 107 stderr.write(buf, 0, len); 108 } 109 Assert.assertEquals("non-zero exit value (" + stderr.toString("UTF-8") + ")", 0, p.exitValue()); 110 } 111 } else { 112 mSystemImagePath = system.getAbsolutePath(); 113 } 114 simg2imgPath = new File(tempdir, SIMG2IMG_PATH).getAbsolutePath(); 115 } 116 File gsi = new File(mSystemImagePath); 117 Assert.assertTrue("not a valid file", gsi.isFile()); 118 String[] cmd = {simg2imgPath, mSystemImagePath, mSystemImagePath + ".raw"}; 119 Process p = Runtime.getRuntime().exec(cmd); 120 p.waitFor(); 121 if (p.exitValue() == 0) { 122 mUnsparseSystemImage = new File(mSystemImagePath + ".raw"); 123 gsi = mUnsparseSystemImage; 124 } 125 126 boolean wasRoot = getDevice().isAdbRoot(); 127 if (!wasRoot) 128 Assert.assertTrue("Test requires root", getDevice().enableAdbRoot()); 129 130 expectGsiStatus("normal"); 131 132 installPackage(APK); 133 String method = "setPin"; 134 String testClass = PACKAGE + "." + CLASS; 135 String testMethod = testClass + "." + method; 136 Assert.assertTrue(testMethod + " failed.", 137 runDeviceTests(UI_AUTOMATOR_INSTRUMENTATION_RUNNER, PACKAGE, testClass, method)); 138 139 // Sleep after installing to allow time for gsi_tool to reboot. This prevents a race between 140 // the device rebooting and waitForDeviceAvailable() returning. 141 getDevice().executeShellV2Command("gsi_tool install --userdata-size " + mUserdataSize + 142 " --gsi-size " + gsi.length() + " && sleep 10000000", gsi, null, 10, TimeUnit.MINUTES, 1); 143 getDevice().waitForDeviceAvailable(); 144 getDevice().enableAdbRoot(); 145 146 expectGsiStatus("running"); 147 148 rebootAndUnlock(); 149 150 expectGsiStatus("installed"); 151 152 CommandResult result = getDevice().executeShellV2Command("gsi_tool enable"); 153 Assert.assertEquals("gsi_tool enable failed", 0, result.getExitCode().longValue()); 154 155 getDevice().reboot(); 156 157 expectGsiStatus("running"); 158 159 getDevice().reboot(); 160 161 expectGsiStatus("running"); 162 163 getDevice().executeShellV2Command("gsi_tool wipe"); 164 165 rebootAndUnlock(); 166 167 expectGsiStatus("normal"); 168 169 method = "removePin"; 170 testClass = PACKAGE + "." + CLASS; 171 testMethod = testClass + "." + method; 172 Assert.assertTrue(testMethod + " failed.", 173 runDeviceTests(UI_AUTOMATOR_INSTRUMENTATION_RUNNER, PACKAGE, testClass, method)); 174 175 if (wasRoot) { 176 getDevice().enableAdbRoot(); 177 } 178 } 179 expectGsiStatus(String expected)180 private void expectGsiStatus(String expected) throws Exception { 181 CommandResult result = getDevice().executeShellV2Command("gsi_tool status"); 182 String status = result.getStdout().split("\n", 2)[0].trim(); 183 Assert.assertEquals("Device not in expected DSU state", expected, status); 184 } 185 rebootAndUnlock()186 private void rebootAndUnlock() throws Exception { 187 getDevice().rebootUntilOnline(); 188 getDevice().executeShellV2Command("input keyevent 224"); // KeyEvent.KEYCODE_WAKEUP 189 getDevice().executeShellV2Command("wm dismiss-keyguard"); 190 getDevice().executeShellV2Command("input keyevent 7"); // KeyEvent.KEYCODE_0 191 getDevice().executeShellV2Command("input keyevent 7"); 192 getDevice().executeShellV2Command("input keyevent 7"); 193 getDevice().executeShellV2Command("input keyevent 7"); 194 getDevice().executeShellV2Command("input keyevent 66"); // KeyEvent.KEYCODE_ENTER 195 } 196 } 197 198