/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tests.dsu; import com.android.tradefed.build.IBuildInfo; import com.android.tradefed.build.IDeviceBuildInfo; import com.android.tradefed.config.Option; import com.android.tradefed.config.Option.Importance; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.util.FileUtil; import com.android.tradefed.util.SparseImageUtil; import com.android.tradefed.util.StreamUtil; import com.android.tradefed.util.ZipUtil2; import org.apache.commons.compress.archivers.zip.ZipFile; import org.junit.Before; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; /** * Test Dynamic System Updates by booting in and out of a supplied system image */ @RunWith(DeviceJUnit4ClassRunner.class) public class DSUEndtoEndTest extends DsuTestBase { private static final long kDefaultUserdataSize = 4L * 1024 * 1024 * 1024; private static final String LPUNPACK_PATH = "bin/lpunpack"; // Example: atest -v DSUEndtoEndTest -- --test-arg \ // com.android.tradefed.testtype.HostTest:set-option:system_image_path:/full/path/to/system.img @Option(name="system_image_path", shortName='s', description="full path to the system image to use. If not specified, attempt " + "to download the image from the test infrastructure", importance=Importance.ALWAYS) private String mSystemImagePath; private File mTempDir; private File getTempPath(String relativePath) throws IOException { if (mTempDir == null) { mTempDir = FileUtil.createTempDir("DSUEndtoEndTest"); } return new File(mTempDir, relativePath); } /** Extract system.img from build info to a temproary file. */ private File extractSystemImageFromBuildInfo(IBuildInfo buildInfo) throws IOException, InterruptedException { File imgZip = ((IDeviceBuildInfo) buildInfo).getDeviceImageFile(); Assert.assertNotNull( "Failed to fetch system image. See system_image_path parameter", imgZip); File superImg = getTempPath("super.img"); if (imgZip.isDirectory()) { File systemImg = new File(imgZip, "system.img"); if (systemImg.exists()) { return systemImg; } superImg = new File(imgZip, "super.img"); Assert.assertTrue( "No system.img or super.img in img zip.", superImg.exists()); } else { try (ZipFile zip = new ZipFile(imgZip)) { File systemImg = getTempPath("system.img"); if (ZipUtil2.extractFileFromZip(zip, "system.img", systemImg)) { return systemImg; } Assert.assertTrue( "No system.img or super.img in img zip.", ZipUtil2.extractFileFromZip(zip, "super.img", superImg)); } } if (SparseImageUtil.isSparse(superImg)) { File unsparseSuperImage = getTempPath("super.raw"); SparseImageUtil.unsparse(superImg, unsparseSuperImage); superImg = unsparseSuperImage; } File otaTools = buildInfo.getFile("otatools.zip"); Assert.assertNotNull("No otatools.zip in BuildInfo.", otaTools); File otaToolsDir = getTempPath("otatools"); ZipUtil2.extractZip(otaTools, otaToolsDir); String lpunpackPath = new File(otaToolsDir, LPUNPACK_PATH).getAbsolutePath(); File outputDir = getTempPath("lpunpack_output"); outputDir.mkdirs(); String[] cmd = { lpunpackPath, "-p", "system_a", superImg.getAbsolutePath(), outputDir.getAbsolutePath() }; Process p = Runtime.getRuntime().exec(cmd); p.waitFor(); if (p.exitValue() != 0) { String stderr = StreamUtil.getStringFromStream(p.getErrorStream()); Assert.fail(String.format("lpunpack returned %d. (%s)", p.exitValue(), stderr)); } return new File(outputDir, "system_a.img"); } @Before public void setUp() { mTempDir = null; } @After public void tearDown() { FileUtil.recursiveDelete(mTempDir); } @Test public void testDSU() throws Exception { File systemImage; if (mSystemImagePath != null) { systemImage = new File(mSystemImagePath); } else { systemImage = extractSystemImageFromBuildInfo(getBuild()); } Assert.assertTrue("not a valid file", systemImage.isFile()); if (SparseImageUtil.isSparse(systemImage)) { File unsparseSystemImage = getTempPath("system.raw"); SparseImageUtil.unsparse(systemImage, unsparseSystemImage); systemImage = unsparseSystemImage; } boolean wasRoot = getDevice().isAdbRoot(); if (!wasRoot) Assert.assertTrue("Test requires root", getDevice().enableAdbRoot()); assertDsuStatus("normal"); // Sleep after installing to allow time for gsi_tool to reboot. This prevents a race between // the device rebooting and waitForDeviceAvailable() returning. getDevice() .executeShellV2Command( String.format( "gsi_tool install --userdata-size %d" + " --gsi-size %d" + " && sleep 10000000", getDsuUserdataSize(kDefaultUserdataSize), systemImage.length()), systemImage, null, 10, TimeUnit.MINUTES, 1); getDevice().waitForDeviceAvailable(); getDevice().enableAdbRoot(); assertDsuStatus("running"); getDevice().rebootUntilOnline(); assertDsuStatus("installed"); assertShellCommand("gsi_tool enable"); getDevice().reboot(); assertDsuStatus("running"); getDevice().reboot(); assertDsuStatus("running"); assertShellCommand("gsi_tool wipe"); getDevice().rebootUntilOnline(); assertDsuStatus("normal"); if (wasRoot) { getDevice().enableAdbRoot(); } } }