Use the vm tool

MicrodroidHostTestCases now uses the vm tool to create the VM. It no
longer directly interacts with crosvm and mk_cdisk.

The READMD.md file is rewritten to fully reflect the recent changes, and
also to add description about building an app for microdroid.

Bug: 185891097
Test: atest MicrodroidHostTestCases
Change-Id: I5fdc854390fd362ebead22a4a36af75c30500a81
diff --git a/apex/Android.bp b/apex/Android.bp
index 146e4eb..bf38860 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -54,8 +54,7 @@
     ],
     prebuilts: [
         "com.android.virt.init.rc",
-        "microdroid_cdisk.json",
-        "microdroid_cdisk_env.json",
+        "microdroid.json",
         "microdroid_uboot_env",
         "microdroid_bootloader",
     ],
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 41ee0d2..6424988 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -391,13 +391,8 @@
 }
 
 prebuilt_etc {
-    name: "microdroid_cdisk.json",
-    src: "microdroid_cdisk.json",
-}
-
-prebuilt_etc {
-    name: "microdroid_cdisk_env.json",
-    src: "microdroid_cdisk_env.json",
+    name: "microdroid.json",
+    src: "microdroid.json",
 }
 
 prebuilt_etc {
diff --git a/microdroid/README.md b/microdroid/README.md
index aae6e66..6d3491c 100644
--- a/microdroid/README.md
+++ b/microdroid/README.md
@@ -5,140 +5,221 @@
 Android, but it is much smaller; no system server, no HALs, no GUI, etc. It is
 intended to host headless & native workloads only.
 
-## Building (VIM3L)
+## Prerequisites
 
-You need a VIM3L board. Instructions for building Android for the target, and
-flashing the image can be found [here](../docs/getting_started/yukawa.md).
+Any 64-bit target (either x86_64 or arm64) is supported. 32-bit target is not
+supported. Note that we currently don't support user builds; only userdebug
+builds are supported.
 
-Then you install `com.android.virt` APEX. All files needed to run microdroid are
-included in the APEX, which is already in the `yukawa` (VIM3L) target. You can
-of course build and install the APEX manually.
+The only remaining requirment is that `com.android.virt` APEX has to be
+pre-installed. To do this, add the following line in your product makefile.
 
-```sh
-$ source build/envsetup.sh
-$ choosecombo 1 aosp_arm64 userdebug // actually, any arm64-based target is ok
-$ TARGET_BUILD_APPS=com.android.virt m
-$ adb install $ANDROID_PRODUCT_OUT/system/apex/com.android.virt.apex
-$ adb reboot
+```make
+$(call inherit-product, packages/modules/Virtualization/apex/product_packages.mk)
 ```
 
-## Building (Cloud Android)
+Build the target after adding the line, and flash it. This step needs to be done
+only once for the target.
 
-You may use Cloud Android (cuttlefish) as well. Instructions for running a
-cuttlefish device on your workstation can be found
-[here](https://android.googlesource.com/device/google/cuttlefish/).
+If you are using `yukawa` (VIM3L) or `aosp_cf_x86_64_phone` (Cuttlefish), adding
+above line is not necessary as it's already done.
 
-The `aosp_cf_x86_64_phone` target already includes the `com.android.virt` APEX.
-So you don't need to build the APEX separately. If you want to rebuild the APEX
-and install it to cuttlefish,
+Instructions for building and flashing Android for `yukawa` can be found
+[here](../docs/getting_started/yukawa.md).
+
+## Building and installing microdroid
+
+Microdroid is part of the `com.android.virt` APEX. To build it and install to
+the device:
 
 ```sh
-$ source build/envsetup.sh
-$ choosecombo 1 aosp_cf_x86_64_phone userdebug
-$ TARGET_BUILD_APPS=com.android.virt m
-$ adb install $ANDROID_PRODUCT_OUT/system/apex/com.android.virt.apex
-$ adb reboot
+banchan com.android.virt aosp_arm64
+m apps_only dist
+adb install out/dist/com.android.virt.apex
+adb reboot
 ```
 
-## Running
+If your target is x86_64 (e.g. `aosp_cf_x86_64_phone`), replace `aosp_arm64`
+with `aosp_x86_64`.
 
-Create two config files, `microdroid.json` and `payload.json`:
+## Building an app
+
+An app in microdroid is a shared library file embedded in an APK. The shared
+library should have an entry point `android_native_main` as shown below:
+
+```C++
+extern "C" int android_native_main(int argc, char* argv[]) {
+  printf("Hello Microdroid!\n");
+}
+```
+
+Then build it as a shared library:
+
+```
+cc_library_shared {
+  name: "MyMicrodroidApp",
+  srcs: ["**/*.cpp"],
+  sdk_version: "current",
+}
+```
+
+Then you need a configuration file in JSON format that defines what to load and
+execute in microdroid. The name of the file can be anything and you may have
+multiple configuration files if needed.
 
 ```json
-microdroid.json:
-
 {
-  "bootloader": "/apex/com.android.virt/etc/microdroid_bootloader",
-  "disks": [
-    {
-      "partitions": [
-        {
-          "label": "boot_a",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_boot-5.10.img"
-        },
-        {
-          "label": "vendor_boot_a",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_vendor_boot-5.10.img"
-        },
-        {
-          "label": "vbmeta_a",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta.img"
-        },
-        {
-          "label": "vbmeta_system_a",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta_system.img"
-        },
-        {
-          "label": "super",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_super.img"
-        }
-      ],
-      "writable": false
-    },
-    {
-      "partitions": [
-        {
-          "label": "uboot_env",
-          "path": "/apex/com.android.virt/etc/uboot_env.img"
-        }
-      ],
-      "writable": false
-    },
-    {
-      "image": "/data/local/tmp/microdroid/payload.img",
-      "writable": false
-    }
+  "os": {"name": "microdroid"},
+  "task": {
+    "type": "microdroid_launcher",
+    "command": "MyMicrodroidApp.so",
+  },
+  "apexes": [
+    {"name": "com.android.adbd"},
+    {"name": "com.android.i18n"},
+    {"name": "com.android.os.statsd"},
+    {"name": "com.android.sdkext"}
   ]
 }
+```
 
-payload.json:
+The value of `task.command` should match with the name of the shared library
+defined above. The `apexes` array is the APEXes that will be imported to
+microdroid. The above four APEXes are essential ones and therefore shouldn't be
+omitted. In the future, you wouldn't need to add the default ones manually. If
+more APEXes are required for you app, add their names too.
 
+Embed the shared library and the VM configuration file in an APK:
+
+```
+android_app {
+  name: "MyApp",
+  srcs: ["**/*.java"], // if there is any java code
+  jni_libs: ["MyMicrodroidApp"],
+  use_embedded_native_libs: true,
+  sdk_version: "current",
+}
+
+// The VM configuration file can be embedded by simply placing it at `./assets`
+directory.
+```
+
+Finally, you build and sign the APK.
+
+```sh
+TARGET_BUILD_APPS=MyMicrodroidApp m dist
+m apksigner
+apksigner sign --ks path_to_keystore out/dist/MyMicrodroidApp.apk
+```
+
+`path_to_keystore` should be replaced with the actual path to the keystore,
+which can be created as follows:
+
+```sh
+keytool -keystore my_keystore -genkey -alias my_key
+```
+
+Make sure that `.apk.idsig` file is also generated in the same directory as the
+signed APK.
+
+## Running the app on microdroid
+
+First of all, install the signed APK to the target device.
+
+```sh
+adb install out/dist/MyMicrodroidApp.apk
+```
+
+### Creating `payload.img` manually (temporary step)
+
+This is a step that needs to be done manually for now. Eventually, this will be
+automatically done by a service named `virtualizationservice` which is part of
+the `com.android.virt` APEX.
+
+Create `payload.json` file:
+
+```json
 {
-  "system_apexes" : [
+  "payload_config_path": "/mnt/apk/assets/VM_CONFIG_NAME,
+  "system_apexes": [
     "com.android.adbd",
     "com.android.i18n",
     "com.android.os.statsd",
     "com.android.sdkext"
-  ]
+  ],
+  "apk": {
+    "name": "PACKAGE_NAME_OF_YOUR_APP"
+    "path": "PATH_TO_YOUR_APP",
+  }
 }
 ```
 
-Copy the these files to the temp directory, create the composite image using
-`mk_cdisk` and copy the VM config file. For now, some other files have to be
-manually created. In the future, you won't need these, and this shall be done
-via [`virtualizationservice`](../virtualizationservice/).
+`ALL_CAP`s in the above are placeholders. They need to be replaced with correct
+values:
+
+* `VM_CONFIG_FILE`: the name of the VM config file that you embedded in the APK.
+* `PACKAGE_NAME_OF_YOUR_APP`: package name of your app(e.g. `com.acme.app`).
+* `PATH_TO_YOUR_APP`: path to the installed APK on the device. Can be obtained
+  via the following command.
 
 ```sh
-$ adb root
-$ adb shell 'mkdir /data/local/tmp/microdroid'
-$ adb push payload.json /data/local/tmp/microdroid/payload.json
-$ adb shell 'cd /data/local/tmp/microdroid; /apex/com.android.virt/bin/mk_payload payload.json payload.img'
-$ adb shell 'chmod go+r /data/local/tmp/microdroid/payload*'
-$ adb push microdroid.json /data/local/tmp/microdroid/microdroid.json
+adb shell pm path PACKAGE_NAME_OF_YOUR_APP
 ```
 
-Ensure SELinux is in permissive mode to allow virtualizationservice and crosvm to open
-files from `/data/local/tmp`. Opening files from this directory is
-neverallow-ed and file descriptors should be passed instead but, before that is
-supported, `adb shell setenforce 0` will put the device in permissive mode.
+It shall report a cryptic path similar to
+`/data/app/~~OgZq==/com.acme.app-HudMahQ==/base.apk`.
 
-Now, run the VM and look for `adbd` starting in the logs.
+Once the file is done, execute the following command to push it to the device
+and run `mk_payload` to create `payload.img`:
 
 ```sh
-$ adb shell "start virtualizationservice"
-$ adb shell "RUST_BACKTRACE=1 RUST_LOG=trace /apex/com.android.virt/bin/vm run /data/local/tmp/microdroid/microdroid.json"
+TEST_ROOT=/data/local/tmp/virt
+adb push out/dist/MyMicrodroidApp.apk.idsig $TEST_ROOT/MyMicrodroidApp.apk.idsig
+adb push path_to_payload.json $TEST_ROOT/payload.json
+adb shell /apex/com.android.virt/bin/my_payload $TEST_ROOT/payload.json $TEST_ROOT/payload.img
+adb shell chmod go+r $TEST_ROOT/payload*
 ```
 
+### Running the VM
+
+Execute the following commands to launch a VM. The VM will boot to microdroid
+and then automatically execute your app (the shared library
+`MyMicrodroidApp.so`).
+
+```sh
+TEST_ROOT=/data/local/tmp/virt
+adb push packages/modules/Virtualization/microdroid/microdroid.json $TEST_ROOT/microdroid.json
+adb root
+adb shell setenforce 0
+adb shell start virtualizationservice
+adb shell /apex/com.android.virt/bin/vm run $TEST_ROOT/microdroid.json
+```
+
+The last command lets you know the CID assigned to the VM.
+
+Note: the disabling of SELinux is a temporary step. The restriction will
+eventually be removed.
+
+Stopping the VM can be done as follows:
+
+```sh
+adb shell /apex/com.android.virt/bin/vm stop CID
+```
+
+, where `CID` is the reported CID value.
+
 ## ADB
 
+On userdebug builds, you can have an adb connection to microdroid. To do so,
+
 ```sh
-$ CID=10
-$ adb forward tcp:8000 vsock:$CID:5555
-$ adb connect localhost:8000
+adb forward tcp:8000 vsock:$CID:5555
+adb connect localhost:8000
 ```
 
-`CID` should be the CID that `vm` reported was assigned to the VM. You can also
-check it with `adb shell "/apex/com.android.virt/bin/vm list"`. `5555` must be
+`CID` should be the CID that `vm` reported upon execution of the `vm run`
+command in the above. You can also check it with `adb shell
+"/apex/com.android.virt/bin/vm list"`. `5555` must be
 the value. `8000` however can be any port in the development machine.
 
 Done. Now you can log into microdroid. Have fun!
diff --git a/microdroid/microdroid.json b/microdroid/microdroid.json
new file mode 100644
index 0000000..7dc4b6a
--- /dev/null
+++ b/microdroid/microdroid.json
@@ -0,0 +1,43 @@
+{
+  "bootloader": "/apex/com.android.virt/etc/microdroid_bootloader",
+  "disks": [
+    {
+      "partitions": [
+        {
+          "label": "boot_a",
+          "path": "/apex/com.android.virt/etc/fs/microdroid_boot-5.10.img"
+        },
+        {
+          "label": "vendor_boot_a",
+          "path": "/apex/com.android.virt/etc/fs/microdroid_vendor_boot-5.10.img"
+        },
+        {
+          "label": "vbmeta_a",
+          "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta.img"
+        },
+        {
+          "label": "vbmeta_system_a",
+          "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta_system.img"
+        },
+        {
+          "label": "super",
+          "path": "/apex/com.android.virt/etc/fs/microdroid_super.img"
+        }
+      ],
+      "writable": false
+    },
+    {
+      "partitions": [
+        {
+          "label": "uboot_env",
+          "path": "/apex/com.android.virt/etc/uboot_env.img"
+        }
+      ],
+      "writable": false
+    },
+    {
+      "image": "/data/local/tmp/virt/payload.img",
+      "writable": false
+    }
+  ]
+}
diff --git a/microdroid/microdroid_cdisk.json b/microdroid/microdroid_cdisk.json
deleted file mode 100644
index d0a5025..0000000
--- a/microdroid/microdroid_cdisk.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "partitions": [
-    {
-      "label": "boot_a",
-      "path": "/apex/com.android.virt/etc/fs/microdroid_boot-5.10.img"
-    },
-    {
-      "label": "vendor_boot_a",
-      "path": "/apex/com.android.virt/etc/fs/microdroid_vendor_boot-5.10.img"
-    },
-    {
-      "label": "vbmeta_a",
-      "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta.img"
-    },
-    {
-      "label": "vbmeta_system_a",
-      "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta_system.img"
-    },
-    {
-      "label": "super",
-      "path": "/apex/com.android.virt/etc/fs/microdroid_super.img"
-    }
-  ]
-}
diff --git a/microdroid/microdroid_cdisk_env.json b/microdroid/microdroid_cdisk_env.json
deleted file mode 100644
index 8c84ed5..0000000
--- a/microdroid/microdroid_cdisk_env.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "partitions": [
-    {
-      "label": "uboot_env",
-      "path": "/apex/com.android.virt/etc/uboot_env.img"
-    }
-  ]
-}
diff --git a/tests/hostside/AndroidTest.xml b/tests/hostside/AndroidTest.xml
index 247923d..adad01d 100644
--- a/tests/hostside/AndroidTest.xml
+++ b/tests/hostside/AndroidTest.xml
@@ -18,6 +18,10 @@
         <option name="force-root" value="true" />
     </target_preparer>
 
+    <!-- virtualizationservice doesn't have access to shell_data_file. Instead of giving it
+      a test-only permission, run it without selinux -->
+    <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer"/>
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="MicrodroidTestApp.apk" />
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 5449af0..4aa8eb5 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -46,9 +46,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.zip.ZipFile;
 
@@ -56,7 +55,6 @@
 public class MicrodroidTestCase extends BaseHostJUnit4Test {
     private static final String TEST_ROOT = "/data/local/tmp/virt/";
     private static final String VIRT_APEX = "/apex/com.android.virt/";
-    private static final int TEST_VM_CID = 10;
     private static final int TEST_VM_ADB_PORT = 8000;
     private static final String MICRODROID_SERIAL = "localhost:" + TEST_VM_ADB_PORT;
 
@@ -69,9 +67,8 @@
         final String apkName = "MicrodroidTestApp.apk";
         final String packageName = "com.android.microdroid.test";
         final String configPath = "assets/vm_config.json"; // path inside the APK
-        startMicrodroid(apkName, packageName, configPath);
-        waitForMicrodroidBoot(MICRODROID_BOOT_TIMEOUT_MINUTES);
-        adbConnectToMicrodroid();
+        final String cid = startMicrodroid(apkName, packageName, configPath);
+        adbConnectToMicrodroid(cid, MICRODROID_BOOT_TIMEOUT_MINUTES);
 
         // Check if it actually booted by reading a sysprop.
         assertThat(runOnMicrodroid("getprop", "ro.hardware"), is("microdroid"));
@@ -108,15 +105,12 @@
                 is("Hello Microdroid " + testLib + " arg1 arg2"));
 
         // Shutdown microdroid
-        runOnMicrodroid("reboot");
+        runOnAndroid(VIRT_APEX + "bin/vm", "stop", cid);
     }
 
     // Run an arbitrary command in the host side and returns the result
     private String runOnHost(String... cmd) {
-        final long timeout = 10000;
-        CommandResult result = RunUtil.getDefault().runTimedCmd(timeout, cmd);
-        assertThat(result.getStatus(), is(CommandStatus.SUCCESS));
-        return result.getStdout().trim();
+        return runOnHostWithTimeout(10000, cmd);
     }
 
     // Same as runOnHost, but failure is not an error
@@ -126,6 +120,14 @@
         return result.getStdout().trim();
     }
 
+    // Same as runOnHost, but with custom timeout
+    private String runOnHostWithTimeout(long timeoutMillis, String... cmd) {
+        assertTrue(timeoutMillis >= 0);
+        CommandResult result = RunUtil.getDefault().runTimedCmd(timeoutMillis, cmd);
+        assertThat(result.getStatus(), is(CommandStatus.SUCCESS));
+        return result.getStdout().trim();
+    }
+
     // Run a shell command on Android
     private String runOnAndroid(String... cmd) throws Exception {
         CommandResult result = getDevice().executeShellV2Command(join(cmd));
@@ -213,6 +215,9 @@
         runOnAndroid(mkPayload, payloadJson, payloadImg);
         assertThat(runOnAndroid("du", "-b", payloadImg), is(not("")));
 
+        // The generated files are owned by root. Allow the virtualizationservice to read them.
+        runOnAndroid("chmod", "go+r", TEST_ROOT + "payload*");
+
         return payloadImg;
     }
 
@@ -220,65 +225,64 @@
         return (new CompatibilityBuildHelper(getBuild())).getTestFile(name);
     }
 
-    private void startMicrodroid(String apkName, String packageName, String configPath)
+    private String startMicrodroid(String apkName, String packageName, String configPath)
             throws Exception {
         // Create payload.img
-        final String payloadImg = createPayloadImage(apkName, packageName, configPath);
+        createPayloadImage(apkName, packageName, configPath);
 
-        // Tools and executables
-        final String mkCdisk = VIRT_APEX + "bin/mk_cdisk";
-        final String crosvm = VIRT_APEX + "bin/crosvm";
+        // Run the VM
+        runOnAndroid("start", "virtualizationservice");
+        String ret =
+                runOnAndroid(
+                        VIRT_APEX + "bin/vm",
+                        "run",
+                        "--daemonize",
+                        VIRT_APEX + "etc/microdroid.json");
 
-        // Create os_composisite.img and env_composite.img
-        // TODO(jiyong): remove this when running a VM is done by `vm`
-        final String cdiskJson = VIRT_APEX + "etc/microdroid_cdisk.json";
-        final String cdiskEnvJson = VIRT_APEX + "etc/microdroid_cdisk_env.json";
-        final String osImg = TEST_ROOT + "os_composite.img";
-        final String envImg = TEST_ROOT + "env_composite.img";
-        final String bootloader = VIRT_APEX + "etc/microdroid_bootloader";
-        runOnAndroid(mkCdisk, cdiskJson, osImg);
-        runOnAndroid(mkCdisk, cdiskEnvJson, envImg);
-
-        // Start microdroid using crosvm
-        // TODO(jiyong): do this via the `vm` command
-        ExecutorService executor = Executors.newFixedThreadPool(1);
-        executor.execute(
-                () -> {
-                    try {
-                        runOnAndroid(
-                                crosvm,
-                                "run",
-                                "--cid=" + TEST_VM_CID,
-                                "--disable-sandbox",
-                                "--bios=" + bootloader,
-                                "--serial=type=syslog",
-                                "--disk=" + osImg,
-                                "--disk=" + envImg,
-                                "--disk=" + payloadImg,
-                                "&");
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
-    }
-
-    private void waitForMicrodroidBoot(long timeoutMinutes) throws Exception {
-        final String pattern = "load_persist_props_action";
-        getDevice()
-                .executeShellV2Command(
-                        "logcat --regex=\"" + pattern + "\" -m 1",
-                        timeoutMinutes,
-                        TimeUnit.MINUTES);
+        // Retrieve the CID from the vm tool output
+        Pattern pattern = Pattern.compile("with CID (\\d+)");
+        Matcher matcher = pattern.matcher(ret);
+        assertTrue(matcher.find());
+        return matcher.group(1);
     }
 
     // Establish an adb connection to microdroid by letting Android forward the connection to
-    // microdroid.
-    private void adbConnectToMicrodroid() {
+    // microdroid. Wait until the connection is established and microdroid is booted.
+    private void adbConnectToMicrodroid(String cid, long timeoutMinutes) throws Exception {
+        long start = System.currentTimeMillis();
+        long timeoutMillis = timeoutMinutes * 60 * 1000;
+        long elapsed = 0;
+
         final String serial = getDevice().getSerialNumber();
         final String from = "tcp:" + TEST_VM_ADB_PORT;
-        final String to = "vsock:" + TEST_VM_CID + ":5555";
+        final String to = "vsock:" + cid + ":5555";
         runOnHost("adb", "-s", serial, "forward", from, to);
-        runOnHost("adb", "connect", MICRODROID_SERIAL);
+
+        boolean disconnected = true;
+        while (disconnected) {
+            elapsed = System.currentTimeMillis() - start;
+            timeoutMillis -= elapsed;
+            start = System.currentTimeMillis();
+            String ret = runOnHostWithTimeout(timeoutMillis, "adb", "connect", MICRODROID_SERIAL);
+            disconnected = ret.equals("failed to connect to " + MICRODROID_SERIAL);
+            if (disconnected) {
+                // adb demands us to disconnect if the prior connection was a failure.
+                runOnHost("adb", "disconnect", MICRODROID_SERIAL);
+            }
+        }
+
+        elapsed = System.currentTimeMillis() - start;
+        timeoutMillis -= elapsed;
+        runOnHostWithTimeout(timeoutMillis, "adb", "-s", MICRODROID_SERIAL, "wait-for-device");
+
+        boolean dataAvailable = false;
+        while (!dataAvailable && timeoutMillis >= 0) {
+            elapsed = System.currentTimeMillis() - start;
+            timeoutMillis -= elapsed;
+            start = System.currentTimeMillis();
+            final String checkCmd = "if [ -d /data/local/tmp ]; then echo 1; fi";
+            dataAvailable = runOnMicrodroid(checkCmd).equals("1");
+        }
     }
 
     private void skipIfFail(String command) throws Exception {
@@ -316,7 +320,9 @@
         // disconnect from microdroid
         tryRunOnHost("adb", "disconnect", MICRODROID_SERIAL);
 
-        // kill stale crosvm processes
+        // kill stale VMs and directories
         tryRunOnAndroid("killall", "crosvm");
+        tryRunOnAndroid("rm", "-rf", "/data/misc/virtualizationservice/*");
+        tryRunOnAndroid("stop", "virtualizationservice");
     }
 }