aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjahinimtiaz <jahinimtiaz@google.com>2022-01-30 19:25:50 +0000
committerjahinimtiaz <jahinimtiaz@google.com>2022-02-04 22:16:14 +0000
commit10a63ea72caabfba138b01b259e533be6772c1bb (patch)
treed2a1006eabc9a8865905825cf69705702d0e6332
parent20234f041e66605c420272eb2ac958b8d8e01edf (diff)
downloadtradefederation-10a63ea72caabfba138b01b259e533be6772c1bb.tar.gz
Adding a builder pattern for starting Microdroid
Instead of using fixed parameters for starting a Microdroid in Android device, adding a builder pattern so that if in future we need to support more flags, we can do it without breaking exisiting code. Also modifyig the design so that it is easy to support multiple microdroid devices in future if we need to. Bug: 182478337 Bug: 216664481 Test: abtd, "tradefed.sh run host --class com.android.tradefed.device.MicrodroidFuncTest" Change-Id: Ifac2d5559d6468393ddf44ffc7aebf06ee830fd2
-rw-r--r--javatests/com/android/tradefed/device/MicrodroidFuncTest.java22
-rw-r--r--src/com/android/tradefed/device/TestDevice.java266
2 files changed, 214 insertions, 74 deletions
diff --git a/javatests/com/android/tradefed/device/MicrodroidFuncTest.java b/javatests/com/android/tradefed/device/MicrodroidFuncTest.java
index 0d6d8fba1..c460f4b3f 100644
--- a/javatests/com/android/tradefed/device/MicrodroidFuncTest.java
+++ b/javatests/com/android/tradefed/device/MicrodroidFuncTest.java
@@ -47,7 +47,6 @@ import java.util.Arrays;
@RunWith(DeviceJUnit4ClassRunner.class)
public class MicrodroidFuncTest implements IDeviceTest, ITestInformationReceiver {
private static final String APK_NAME = "MicrodroidTestApp.apk";
- private static final String PACKAGE_NAME = "com.android.microdroid.test";
private static final String CONFIG_PATH = "assets/vm_config.json"; // path inside the APK
private TestDevice mTestDevice;
@@ -85,12 +84,12 @@ public class MicrodroidFuncTest implements IDeviceTest, ITestInformationReceiver
public void testStartAndShutdownMicrodroid()
throws DeviceNotAvailableException, FileNotFoundException {
final ITestDevice microdroid =
- mTestDevice.startMicrodroid(
- findTestFile(mTestInformation, APK_NAME),
- PACKAGE_NAME,
- CONFIG_PATH,
- /* debug */ true,
- /* use default memoryMib */ 0);
+ TestDevice.MicrodroidBuilder.fromFile(
+ findTestFile(mTestInformation, APK_NAME), CONFIG_PATH)
+ .debugLevel("full")
+ .memoryMib(0)
+ .numCpus(1)
+ .build(mTestDevice);
assertNotNull(microdroid);
MicrodroidHelper microdroidHelper = new MicrodroidHelper();
@@ -122,17 +121,16 @@ public class MicrodroidFuncTest implements IDeviceTest, ITestInformationReceiver
assertThat(
runOnDevice(microdroid, "getprop", "debug.microdroid.app.sublib.run"), is("true"));
- // Check that keystore was found by the payload. Wait until the property is set.
- assertThat(
- runOnDevice(microdroid, "getprop", "debug.microdroid.test.keystore"), is("PASS"));
-
// Check that no denials have happened so far
assertThat(runOnDevice(microdroid, "logcat -d -e 'avc:[[:space:]]{1,2}denied'"), is(""));
+ assertThat(
+ runOnDevice(microdroid, "cat /proc/cpuinfo | grep processor | wc -l"),
+ is(Integer.toString(1)));
assertThat(
microdroidHelper.runOnMicrodroid(microdroid.getSerialNumber(), "echo true"),
is("true"));
- mTestDevice.shutdownMicrodroid();
+ mTestDevice.shutdownMicrodroid(microdroid);
assertNull(microdroidHelper.tryRunOnMicrodroid(microdroid.getSerialNumber(), "echo true"));
}
diff --git a/src/com/android/tradefed/device/TestDevice.java b/src/com/android/tradefed/device/TestDevice.java
index 258eee1de..cc60a2112 100644
--- a/src/com/android/tradefed/device/TestDevice.java
+++ b/src/com/android/tradefed/device/TestDevice.java
@@ -59,6 +59,7 @@ import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
@@ -134,9 +135,11 @@ public class TestDevice extends NativeDevice {
private static final String APEX_SUFFIX = ".apex";
private static final String APEX_ARG = "--apex";
- private ITestDevice microdroid = null;
+ /** If the device is a Microdroid, this will refer to the CID. Otherwise, it will be null. */
private String microdroidCID = null;
- private String microdroidPackageName = null;
+ /** Contains a set of Microdroid CIDs which is running in this TestDevice. */
+ private Set<String> startedMicrodroids = new HashSet<>();
+
private static final String TEST_ROOT = "/data/local/tmp/virt/";
private static final String VIRT_APEX = "/apex/com.android.virt/";
private static final String INSTANCE_IMG = "instance.img";
@@ -2067,21 +2070,29 @@ public class TestDevice extends NativeDevice {
* Starts a Microdroid TestDevice.
*
* @param apkFile APK file of the Microdroid app to install.
- * @param packageName package name of your app
+ * @param apkPath path to the apk file.
* @param configPath the name of the VM config file that you embedded in the APK.
- * @param debug debugging flag
+ * @param debugLevel debugging level.
* @param memoryMib minimum memory size
+ * @param extraIdsigPaths Paths to extra idsig files.
* @return returns a ITestDevice for the microdroid, can return null.
*/
- public ITestDevice startMicrodroid(
- File apkFile, String packageName, String configPath, boolean debug, int memoryMib)
+ private ITestDevice startMicrodroid(
+ File apkFile,
+ String apkPath,
+ String configPath,
+ String debugLevel,
+ int memoryMib,
+ int numCpus,
+ String cpuAffinity,
+ List<String> extraIdsigPaths)
throws DeviceNotAvailableException {
- if (microdroidCID != null)
+ if (!startedMicrodroids.isEmpty())
throw new IllegalStateException(
String.format(
"Microdroid with cid '%s' already exists in device. Can not create"
+ " another one.",
- microdroidCID));
+ startedMicrodroids.iterator().next()));
String microdroidSerial;
int vmAdbPort = -1;
@@ -2097,9 +2108,6 @@ public class TestDevice extends NativeDevice {
microdroidSerial = "localhost:" + vmAdbPort;
- // kill stale crosvm processes
- executeShellV2Command("killall crosvm");
-
// disconnect from microdroid
getRunUtil()
.runTimedCmd(
@@ -2109,57 +2117,54 @@ public class TestDevice extends NativeDevice {
microdroidSerial);
// remove any leftover files under test root
- executeShellV2Command("rm -rf " + vmAdbPort + "*");
-
- // Install APK
- installPackage(apkFile, true);
+ executeShellV2Command("rm -rf " + TEST_ROOT + "*");
- // Get the path to the installed apk. Note that
- // getDevice().getAppPackageInfo(...).getCodePath() doesn't work due to the incorrect
- // parsing of the "=" character. (b/190975227). So we use the `pm path` command directly.
- CommandResult result = executeShellV2Command("pm path " + packageName);
- if (result.getStatus() != CommandStatus.SUCCESS) {
- throw new DeviceRuntimeException(
- "pm path " + packageName + " has failed: " + result,
- DeviceErrorIdentifier.SHELL_COMMAND_ERROR);
- }
- String apkPath = result.getStdout().trim();
- if (!apkPath.startsWith("package:")) {
- throw new DeviceRuntimeException(
- String.format("Path for package '%s' was not found.", packageName),
- DeviceErrorIdentifier.SHELL_COMMAND_ERROR);
- }
- apkPath = apkPath.substring("package:".length());
-
- result = executeShellV2Command("mkdir -p " + TEST_ROOT);
+ CommandResult result = executeShellV2Command("mkdir -p " + TEST_ROOT);
if (result.getStatus() != CommandStatus.SUCCESS) {
throw new DeviceRuntimeException(
"mkdir -p " + TEST_ROOT + " has failed: " + result,
DeviceErrorIdentifier.SHELL_COMMAND_ERROR);
}
+ // Push the apk file to the test directory
+ if (apkFile != null) {
+ pushFile(apkFile, TEST_ROOT + apkFile.getName());
+ apkPath = TEST_ROOT + apkFile.getName();
+ } else if (apkPath == null) {
+ // if both apkFile and apkPath is null, we can not start a microdroid device
+ throw new IllegalArgumentException(
+ "apkFile and apkPath is both null. Can not start microdroid.");
+ }
// This file is not what we provide. It will be created by the vm tool.
- final String outApkIdsigPath = TEST_ROOT + apkFile.getName() + ".idsig";
-
+ final String outApkIdsigPath =
+ TEST_ROOT + (apkFile != null ? apkFile.getName() : "NULL") + ".idsig";
final String instanceImg = TEST_ROOT + INSTANCE_IMG;
final String logPath = TEST_ROOT + "log.txt";
- final String debugFlag = debug ? "--debug full" : "";
+ final String debugFlag = Strings.isNullOrEmpty(debugLevel) ? "" : "--debug " + debugLevel;
+ final String cpuAffinityFlag =
+ Strings.isNullOrEmpty(cpuAffinity) ? "" : "--cpu-affinity " + cpuAffinity;
+
+ List<String> args =
+ Arrays.asList(
+ VIRT_APEX + "bin/vm",
+ "run-app",
+ "--daemonize",
+ "--log " + logPath,
+ "--mem " + memoryMib,
+ debugFlag,
+ "--cpus " + numCpus,
+ cpuAffinityFlag,
+ apkPath,
+ outApkIdsigPath,
+ instanceImg,
+ configPath);
+ for (String path : extraIdsigPaths) {
+ args.add("--extra-idsig");
+ args.add(path);
+ }
// Run the VM
- String vmRunCmd =
- String.join(
- " ",
- Arrays.asList(
- VIRT_APEX + "bin/vm",
- "run-app",
- "--daemonize",
- "--log " + logPath,
- "--mem " + memoryMib,
- debugFlag,
- apkPath,
- outApkIdsigPath,
- instanceImg,
- configPath));
+ String vmRunCmd = String.join(" ", args);
result = executeShellV2Command(vmRunCmd);
if (result.getStatus() != CommandStatus.SUCCESS) {
throw new DeviceRuntimeException(
@@ -2200,13 +2205,14 @@ public class TestDevice extends NativeDevice {
Pattern pattern = Pattern.compile("with CID (\\d+)");
Matcher matcher = pattern.matcher(ret);
if (matcher.find()) {
- microdroidCID = matcher.group(1);
- microdroidPackageName = packageName;
- adbConnectToMicrodroid(microdroidCID, microdroidSerial, vmAdbPort);
+ String cid = matcher.group(1);
+ adbConnectToMicrodroid(cid, microdroidSerial, vmAdbPort);
- microdroid =
+ final ITestDevice microdroid =
GlobalConfiguration.getDeviceManagerInstance()
.forceAllocateDevice(microdroidSerial);
+ ((TestDevice) microdroid).setMicrodroidCID(cid);
+ startedMicrodroids.add(cid);
return microdroid;
} else {
return null;
@@ -2299,24 +2305,160 @@ public class TestDevice extends NativeDevice {
*
* @throws DeviceNotAvailableException
*/
- public void shutdownMicrodroid() throws DeviceNotAvailableException {
- if (microdroidCID == null) return;
+ public void shutdownMicrodroid(@Nonnull ITestDevice microdroidDevice)
+ throws DeviceNotAvailableException {
+ String cid = ((TestDevice) microdroidDevice).getMicrodroidCID();
+ if (cid == null) {
+ throw new IllegalArgumentException("CID is null. TestDevice is not a Microdroid. ");
+ }
+ if (!startedMicrodroids.contains(cid)) {
+ throw new IllegalArgumentException(
+ "Microdroid device was not started in this TestDevice.");
+ }
// Shutdown the VM
- CommandResult result = executeShellV2Command(VIRT_APEX + "bin/vm stop " + microdroidCID);
+ CommandResult result = executeShellV2Command(VIRT_APEX + "bin/vm stop " + cid);
if (result.getStatus() != CommandStatus.SUCCESS) {
throw new DeviceRuntimeException(
- VIRT_APEX + "bin/vm stop " + microdroidCID + " has failed: " + result,
+ VIRT_APEX + "bin/vm stop " + cid + " has failed: " + result,
DeviceErrorIdentifier.SHELL_COMMAND_ERROR);
}
- uninstallPackage(microdroidPackageName);
// See(b/192660485) for the reason of this wait.
getRunUtil().sleep(1000);
+ // disconnect from microdroid
+ getRunUtil()
+ .runTimedCmd(
+ 10000,
+ GlobalConfiguration.getDeviceManagerInstance().getAdbPath(),
+ "disconnect",
+ microdroidDevice.getSerialNumber());
+
+ // Make sure we're connected to the host adb; this connection seems to get dropped when a VM
+ // exits.(b/195765441)
+ waitForDeviceAvailable();
+
GlobalConfiguration.getDeviceManagerInstance()
- .freeDevice(microdroid, FreeDeviceState.AVAILABLE);
- microdroidCID = null;
- microdroid = null;
+ .freeDevice(microdroidDevice, FreeDeviceState.AVAILABLE);
+ startedMicrodroids.remove(cid);
+ }
+
+ /**
+ * Marks the TestDevice as microdroid and sets its CID.
+ *
+ * @param cid CID of the microdroid vm.
+ */
+ private void setMicrodroidCID(String cid) {
+ microdroidCID = cid;
+ }
+
+ /**
+ * @return Returns the CID of the microdroid vm. If TestDevice is not a microdroid, return null.
+ */
+ public String getMicrodroidCID() {
+ return microdroidCID;
+ }
+
+ /** A builder used to create a Microdroid TestDevice. */
+ public static class MicrodroidBuilder {
+ private File mApkFile;
+ private String mApkPath;
+ private String mConfigPath;
+ private String mDebugLevel;
+ private int mMemoryMib;
+ private int mNumCpus;
+ private String mCpuAffinity;
+ private List<String> mExtraIdsigPaths;
+
+ /** Creates a builder for the given APK/apkPath and the payload config file in APK. */
+ private MicrodroidBuilder(File apkFile, String apkPath, @Nonnull String configPath) {
+ mApkFile = apkFile;
+ mApkPath = apkPath;
+ mConfigPath = configPath;
+ mDebugLevel = null;
+ mMemoryMib = 0;
+ mNumCpus = 1;
+ mCpuAffinity = null;
+ mExtraIdsigPaths = new ArrayList<>();
+ }
+
+ /** Creates a Microdroid builder for the given APK and the payload config file in APK. */
+ public static MicrodroidBuilder fromFile(
+ @Nonnull File apkFile, @Nonnull String configPath) {
+ return new MicrodroidBuilder(apkFile, null, configPath);
+ }
+
+ /**
+ * Creates a Microdroid builder for the given apkPath and the payload config file in APK.
+ */
+ public static MicrodroidBuilder fromDevicePath(
+ @Nonnull String apkPath, @Nonnull String configPath) {
+ return new MicrodroidBuilder(null, apkPath, configPath);
+ }
+
+ /** Sets the debug level. Supported values: "none", "app_only", and "full". */
+ public MicrodroidBuilder debugLevel(String debugLevel) {
+ mDebugLevel = debugLevel;
+ return this;
+ }
+
+ /**
+ * Sets the amount of RAM to give the VM. If this is zero or negative then the default will
+ * be used.
+ */
+ public MicrodroidBuilder memoryMib(int memoryMib) {
+ mMemoryMib = memoryMib;
+ return this;
+ }
+
+ /** Sets the number of vCPUs in the VM. Defaults to 1. */
+ public MicrodroidBuilder numCpus(int num) {
+ mNumCpus = num;
+ return this;
+ }
+
+ /**
+ * Sets on which host CPUs the vCPUs can run. The format is a comma-separated list of CPUs
+ * or CPU ranges to run vCPUs on. e.g. "0,1-3,5" to choose host CPUs 0, 1, 2, 3, and 5. Or
+ * this can be a colon-separated list of assignments of vCPU to host CPU assignments. e.g.
+ * "0=0:1=1:2=2" to map vCPU 0 to host CPU 0, and so on.
+ */
+ public MicrodroidBuilder cpuAffinity(String affinity) {
+ mCpuAffinity = affinity;
+ return this;
+ }
+
+ /** Adds extra idsig file to the list. */
+ public MicrodroidBuilder addExtraIdsigPath(String extraIdsigPath) {
+ if (extraIdsigPath != null && !extraIdsigPath.isEmpty()) {
+ mExtraIdsigPaths.add(extraIdsigPath);
+ }
+ return this;
+ }
+
+ /** Starts a Micrdroid TestDevice on the given TestDevice. */
+ public ITestDevice build(@Nonnull TestDevice device) throws DeviceNotAvailableException {
+ if (mNumCpus < 1) {
+ throw new IllegalArgumentException("Number of vCPUs can not be less than 1.");
+ }
+
+ if (mCpuAffinity != null
+ && !Pattern.matches("[\\d]+(-[\\d]+)?(,[\\d]+(-[\\d]+)?)*", mCpuAffinity)
+ && !Pattern.matches("[\\d]+=[\\d]+(:[\\d]+=[\\d]+)*", mCpuAffinity)) {
+ throw new IllegalArgumentException(
+ "CPU affinity [" + mCpuAffinity + "]" + " is invalid");
+ }
+
+ return device.startMicrodroid(
+ mApkFile,
+ mApkPath,
+ mConfigPath,
+ mDebugLevel,
+ mMemoryMib,
+ mNumCpus,
+ mCpuAffinity,
+ mExtraIdsigPaths);
+ }
}
}