blob: 2a37038bb8f94c4e6abbca0b2eefc51ce57918f7 [file] [log] [blame]
/*
* Copyright (C) 2020 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 android.cts.install.lib.host;
import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import com.android.ddmlib.Log;
import com.android.tradefed.build.BuildInfoKey;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.IRunUtil;
import com.android.tradefed.util.RunUtil;
import com.android.tradefed.util.SystemUtil;
import com.google.common.base.Stopwatch;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utilities to facilitate installation in tests on host side.
*/
public class InstallUtilsHost {
private static final String TAG = InstallUtilsHost.class.getSimpleName();
private static final String APEX_INFO_EXTRACT_REGEX =
".*package:\\sname='(\\S+)\\'\\sversionCode='(\\d+)'\\s.*";
private final IRunUtil mRunUtil = new RunUtil();
private BaseHostJUnit4Test mTest = null;
private TestInformation mTestInfo = null;
public InstallUtilsHost(BaseHostJUnit4Test test) {
mTest = test;
}
public InstallUtilsHost(TestInformation testInfo) {
assertThat(testInfo).isNotNull();
mTestInfo = testInfo;
}
/**
* Return {@code true} if and only if device supports updating apex.
*/
public boolean isApexUpdateSupported() throws Exception {
return getTestInfo().getDevice().getBooleanProperty("ro.apex.updatable", false);
}
/**
* Return {@code true} if and only if device supports file system checkpoint.
*/
public boolean isCheckpointSupported() throws Exception {
CommandResult result = getTestInfo().getDevice().executeShellV2Command(
"sm supports-checkpoint");
assertWithMessage("Failed to check if file system checkpoint is supported : %s",
result.getStderr()).that(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
return "true".equals(result.getStdout().trim());
}
/**
* Uninstalls a shim apex only if it's latest version is installed on /data partition (i.e.
* it has a version higher than {@code 1}).
*
* <p>This is purely to optimize tests run time. Since uninstalling an apex requires a reboot,
* and only a small subset of tests successfully install an apex, this code avoids ~10
* unnecessary reboots.
*/
public void uninstallShimApexIfNecessary() throws Exception {
if (!isApexUpdateSupported()) {
// Device doesn't support updating apex. Nothing to uninstall.
return;
}
final ITestDevice.ApexInfo shimApex = getShimApex().orElseThrow(
() -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME));
if (shimApex.sourceDir.startsWith("/system")) {
// System version is active, nothing to uninstall.
return;
}
// Non system version is active, need to uninstall it and reboot the device.
Log.i(TAG, "Uninstalling shim apex");
final String errorMessage =
getTestInfo().getDevice().uninstallPackage(SHIM_APEX_PACKAGE_NAME);
if (errorMessage != null) {
Log.e(TAG, "Failed to uninstall " + SHIM_APEX_PACKAGE_NAME + " : " + errorMessage);
} else {
getTestInfo().getDevice().reboot();
final ITestDevice.ApexInfo shim = getShimApex().orElseThrow(
() -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME));
assertThat(shim.versionCode).isEqualTo(1L);
assertThat(shim.sourceDir).startsWith("/system");
}
}
/**
* Returns the active shim apex as optional.
*/
public Optional<ITestDevice.ApexInfo> getShimApex() throws DeviceNotAvailableException {
return getTestInfo().getDevice().getActiveApexes().stream().filter(
apex -> apex.name.equals(SHIM_APEX_PACKAGE_NAME)).findAny();
}
/**
* Retrieve package name and version code from test apex file.
*
* @param apex input apex file to retrieve the info from
*/
public ITestDevice.ApexInfo getApexInfo(File apex) {
String aaptOutput = runCmd(String.format("aapt dump badging %s", apex.getAbsolutePath()));
String[] lines = aaptOutput.split("\n");
Pattern p = Pattern.compile(APEX_INFO_EXTRACT_REGEX);
for (String l : lines) {
Matcher m = p.matcher(l);
if (m.matches()) {
return new ITestDevice.ApexInfo(m.group(1), Long.parseLong(m.group(2)));
}
}
return null;
}
/**
* Installs packages using staged install flow and waits for pre-reboot verification to complete
*/
public String installStagedPackage(File pkg) throws Exception {
return getTestInfo().getDevice().installPackage(pkg, false, "--staged");
}
/**
* Installs packages using rebootless (non-staged) install flow and waits
* for verification to complete
*/
public String installRebootlessPackage(File pkg) throws Exception {
return getTestInfo().getDevice().installPackage(pkg, false, "--non-staged");
}
/**
* Install multiple package at the same time
*/
public void installApexes(String... filenames) throws Exception {
String[] args = new String[filenames.length + 1];
args[0] = "install-multi-package";
for (int i = 0; i < filenames.length; i++) {
args[i + 1] = getTestFile(filenames[i]).getAbsolutePath();
}
String stdout = getTestInfo().getDevice().executeAdbCommand(args);
assertThat(stdout).isNotNull();
}
/**
* Waits for given {@code timeout} for {@code filePath} to be deleted.
*/
public void waitForFileDeleted(String filePath, Duration timeout) throws Exception {
Stopwatch stopwatch = Stopwatch.createStarted();
while (true) {
if (!getTestInfo().getDevice().doesFileExist(filePath)) {
return;
}
if (stopwatch.elapsed().compareTo(timeout) > 0) {
break;
}
Thread.sleep(500);
}
throw new AssertionError("Timed out waiting for " + filePath + " to be deleted");
}
/**
* Get the test file.
*
* @param testFileName name of the file
*/
public File getTestFile(String testFileName) throws IOException {
File testFile = null;
final List<File> testCasesDirs = SystemUtil.getTestCasesDirs(getTestInfo().getBuildInfo());
for (File testCasesDir : testCasesDirs) {
testFile = searchTestFile(testCasesDir, testFileName);
if (testFile != null) {
return testFile;
}
}
File hostLinkedDir = getTestInfo().getBuildInfo().getFile(
BuildInfoKey.BuildInfoFileKey.HOST_LINKED_DIR);
if (hostLinkedDir != null) {
testFile = searchTestFile(hostLinkedDir, testFileName);
}
if (testFile != null) {
return testFile;
}
// Find the file in the buildinfo.
File buildInfoFile = getTestInfo().getBuildInfo().getFile(testFileName);
if (buildInfoFile != null) {
return buildInfoFile;
}
throw new IOException("Cannot find " + testFileName);
}
/**
* Searches the file with the given name under the given directory, returns null if not found.
*/
private File searchTestFile(File baseSearchFile, String testFileName) {
if (baseSearchFile != null && baseSearchFile.isDirectory()) {
File testFile = FileUtil.findFile(baseSearchFile, testFileName);
if (testFile != null && testFile.isFile()) {
return testFile;
}
}
return null;
}
private String runCmd(String cmd) {
Log.d("About to run command: %s", cmd);
CommandResult result = mRunUtil.runTimedCmd(1000 * 60 * 5, cmd.split("\\s+"));
assertThat(result).isNotNull();
assertWithMessage(String.format("Command %s failed", cmd)).that(result.getStatus())
.isEqualTo(CommandStatus.SUCCESS);
Log.d("output:\n%s", result.getStdout());
return result.getStdout();
}
private TestInformation getTestInfo() {
if (mTestInfo == null) {
mTestInfo = mTest.getTestInformation();
assertThat(mTestInfo).isNotNull();
}
return mTestInfo;
}
}