Use Virt Manager for test

Bug: 171278952
Test: atest VirtualizationHostTestCases
Change-Id: Ib603194acb90d4e0952cf276c136efd246d0ce31
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 63b3a3d..f099b9e 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -31,6 +31,8 @@
         ":virt_hostside_tests_kernel",
         ":virt_hostside_tests_initramfs-arm64",
         ":virt_hostside_tests_initramfs-x86_64",
+        ":vm_config.arm64.json",
+        ":vm_config.x86_64.json",
     ],
     required: [
         "virt_hostside_tests_vsock_server",
@@ -52,6 +54,17 @@
     cmd: "$(location scripts/place_files.sh) $(in) -- $(out)",
 }
 
+// Copy config files to output directory so that AndroidTest.xml can copy them to the device.
+filegroup {
+    name: "vm_config.arm64.json",
+    srcs: ["vm_config.arm64.json"],
+}
+
+filegroup {
+    name: "vm_config.x86_64.json",
+    srcs: ["vm_config.x86_64.json"],
+}
+
 // Ramdisk containing /init and test binaries/resources needed inside guest.
 genrule {
     name: "virt_hostside_tests_initramfs_base",
diff --git a/tests/hostside/AndroidTest.xml b/tests/hostside/AndroidTest.xml
index 1fd86ef..42dd680 100644
--- a/tests/hostside/AndroidTest.xml
+++ b/tests/hostside/AndroidTest.xml
@@ -24,7 +24,9 @@
         <!-- Kernel has vhost-vsock enabled. -->
         <option name="run-command" value="ls /dev/vhost-vsock" />
         <!-- CrosVM is installed. -->
-        <option name="run-command" value="which crosvm" />
+        <option name="run-command" value="ls /apex/com.android.virt/bin/crosvm" />
+        <!-- Virt Manager is installed. -->
+        <option name="run-command" value="which virtmanager" />
     </target_preparer>
 
     <!-- Push test binaries to the device. -->
@@ -33,8 +35,10 @@
         <option name="abort-on-push-failure" value="true" />
         <option name="push-file" key="virt_hostside_tests_kernel-arm64" value="/data/local/tmp/virt-test/arm64/kernel" />
         <option name="push-file" key="virt_hostside_tests_initramfs-arm64" value="/data/local/tmp/virt-test/arm64/initramfs" />
+        <option name="push-file" key="vm_config.arm64.json" value="/data/local/tmp/virt-test/arm64/vm_config.json" />
         <option name="push-file" key="virt_hostside_tests_kernel-x86_64" value="/data/local/tmp/virt-test/x86_64/kernel" />
         <option name="push-file" key="virt_hostside_tests_initramfs-x86_64" value="/data/local/tmp/virt-test/x86_64/initramfs" />
+        <option name="push-file" key="vm_config.x86_64.json" value="/data/local/tmp/virt-test/x86_64/vm_config.json" />
         <option name="push-file" key="virt_hostside_tests_vsock_server" value="/data/local/tmp/virt-test" />
     </target_preparer>
 
diff --git a/tests/hostside/java/android/virt/test/VirtTestCase.java b/tests/hostside/java/android/virt/test/VirtTestCase.java
index 7ba6409..1817847 100644
--- a/tests/hostside/java/android/virt/test/VirtTestCase.java
+++ b/tests/hostside/java/android/virt/test/VirtTestCase.java
@@ -27,11 +27,8 @@
 import java.util.ArrayList;
 
 public abstract class VirtTestCase extends DeviceTestCase implements IAbiReceiver {
-
     private static final String DEVICE_DIR = "/data/local/tmp/virt-test";
 
-    private static final int CID_RESERVED = 2;
-
     private IAbi mAbi;
 
     @Before
@@ -72,34 +69,6 @@
         return String.join(" ", strings);
     }
 
-    protected String getVmCommand(String guestCmd, Integer cid) throws Exception {
-        ArrayList<String> cmd = new ArrayList<>();
-
-        cmd.add("crosvm");
-        cmd.add("run");
-
-        cmd.add("--disable-sandbox");
-
-        if (cid != null) {
-            if (cid > CID_RESERVED) {
-                cmd.add("--cid");
-                cmd.add(cid.toString());
-            } else {
-                throw new IllegalArgumentException("Invalid CID " + cid);
-            }
-        }
-
-        cmd.add("--initrd");
-        cmd.add(getDevicePathForTestBinary("initramfs"));
-
-        cmd.add("--params");
-        cmd.add(String.format("'%s'", guestCmd));
-
-        cmd.add(getDevicePathForTestBinary("kernel"));
-
-        return String.join(" ", cmd);
-    }
-
     @Override
     public void setAbi(IAbi abi) {
         mAbi = abi;
diff --git a/tests/hostside/java/android/virt/test/VsockTest.java b/tests/hostside/java/android/virt/test/VsockTest.java
index c82db77..397b41e 100644
--- a/tests/hostside/java/android/virt/test/VsockTest.java
+++ b/tests/hostside/java/android/virt/test/VsockTest.java
@@ -18,7 +18,6 @@
 
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
 
 import org.junit.Test;
 
@@ -33,21 +32,30 @@
     private static final int      RETRIES = 0;
 
     private static final Integer  HOST_CID = 2;
-    private static final Integer  GUEST_CID = 42;
     private static final Integer  GUEST_PORT = 45678;
     private static final String   TEST_MESSAGE = "HelloWorld";
 
     private static final String   CLIENT_PATH = "bin/vsock_client";
     private static final String   SERVER_TARGET = "virt_hostside_tests_vsock_server";
+    private static final String   VIRT_MANAGER_COMMAND = "virtmanager";
 
     @Test
     public void testVsockServer() throws Exception {
         ExecutorService executor = Executors.newFixedThreadPool(2);
 
         final String serverPath = getDevicePathForTestBinary(SERVER_TARGET);
-        final String serverCmd = createCommand(serverPath, GUEST_PORT);
+        final String vmConfigPath = getDevicePathForTestBinary("vm_config.json");
+        final String serverCmd = createCommand(serverPath, GUEST_PORT, vmConfigPath);
         final String clientCmd = createCommand(CLIENT_PATH, HOST_CID, GUEST_PORT, TEST_MESSAGE);
-        final String vmCmd = getVmCommand(clientCmd, GUEST_CID);
+
+        // Start Virt Manager. This will eventually be a system service, but for now we run it
+        // manually.
+        Future<?> virtManagerTask = executor.submit(() -> {
+            CommandResult res = getDevice().executeShellV2Command(
+                    VIRT_MANAGER_COMMAND, TIMEOUT, TIMEOUT_UNIT, RETRIES);
+            CLog.d(res.getStdout());
+            return null;
+        });
 
         // Start server in Android that listens for vsock connections.
         // It will receive a message from a client in the guest VM.
@@ -58,27 +66,6 @@
             return null;
         });
 
-        // Run VM that will connect to the server and send a message to it.
-        Future<?> vmTask = executor.submit(() -> {
-            CommandResult res = getDevice().executeShellV2Command(
-                    vmCmd, TIMEOUT, TIMEOUT_UNIT, RETRIES);
-            CLog.d(res.getStdout()); // print VMM output into host_log
-            assertEquals(CommandStatus.SUCCESS, res.getStatus());
-            return null;
-        });
-
-        // Wait for the VMM to finish sending the message.
-        try {
-            vmTask.get(TIMEOUT, TIMEOUT_UNIT);
-        } catch (Throwable ex) {
-            // The VMM either exited with a non-zero code or it timed out.
-            // Kill the server process, the test has failed.
-            // Note: executeShellV2Command cannot be interrupted. This will wait
-            // until `serverTask` times out.
-            executor.shutdownNow();
-            throw ex;
-        }
-
         // Wait for the server to finish processing the message.
         serverTask.get(TIMEOUT, TIMEOUT_UNIT);
     }
diff --git a/tests/hostside/native/vsock/Android.bp b/tests/hostside/native/vsock/Android.bp
index 13b46d5..966bc04 100644
--- a/tests/hostside/native/vsock/Android.bp
+++ b/tests/hostside/native/vsock/Android.bp
@@ -19,11 +19,13 @@
 cc_test {
     name: "virt_hostside_tests_vsock_server",
     srcs: ["server.cc"],
-    static_libs: [
+    shared_libs: [
+        "android.system.virtmanager-cpp",
         "libbase",
+        "libbinder",
         "liblog",
+        "libutils",
     ],
-    static_executable: true,
     test_suites: ["device-tests"],
 }
 
diff --git a/tests/hostside/native/vsock/server.cc b/tests/hostside/native/vsock/server.cc
index d4a99d2..1a1aa37 100644
--- a/tests/hostside/native/vsock/server.cc
+++ b/tests/hostside/native/vsock/server.cc
@@ -26,15 +26,21 @@
 #include "android-base/logging.h"
 #include "android-base/parseint.h"
 #include "android-base/unique_fd.h"
+#include "android/system/virtmanager/IVirtManager.h"
+#include "android/system/virtmanager/IVirtualMachine.h"
+#include "binder/IServiceManager.h"
 
+using namespace android;
 using namespace android::base;
+using namespace android::system::virtmanager;
 
 int main(int argc, const char *argv[]) {
     unsigned int port;
-    if (argc != 2 || !ParseUint(argv[1], &port)) {
-        LOG(ERROR) << "Usage: " << argv[0] << " <port>";
+    if (argc != 3 || !ParseUint(argv[1], &port)) {
+        LOG(ERROR) << "Usage: " << argv[0] << " <port> <vm_config.json>";
         return EXIT_FAILURE;
     }
+    String16 vm_config(argv[2]);
 
     unique_fd server_fd(TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM, 0)));
     if (server_fd < 0) {
@@ -61,6 +67,27 @@
         return EXIT_FAILURE;
     }
 
+    LOG(INFO) << "Getting Virt Manager";
+    sp<IVirtManager> virt_manager;
+    status_t err = getService<IVirtManager>(String16("android.system.virtmanager"), &virt_manager);
+    if (err != 0) {
+        LOG(ERROR) << "Error getting Virt Manager from Service Manager: " << err;
+        return EXIT_FAILURE;
+    }
+    sp<IVirtualMachine> vm;
+    binder::Status status = virt_manager->startVm(vm_config, &vm);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Error starting VM: " << status;
+        return EXIT_FAILURE;
+    }
+    int32_t cid;
+    status = vm->getCid(&cid);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Error getting CID: " << status;
+        return EXIT_FAILURE;
+    }
+    LOG(INFO) << "VM starting with CID " << cid;
+
     LOG(INFO) << "Accepting connection...";
     struct sockaddr_vm client_sa;
     socklen_t client_sa_len = sizeof(client_sa);
diff --git a/tests/hostside/vm_config.arm64.json b/tests/hostside/vm_config.arm64.json
new file mode 100644
index 0000000..92f2fb8
--- /dev/null
+++ b/tests/hostside/vm_config.arm64.json
@@ -0,0 +1,5 @@
+{
+  "kernel": "/data/local/tmp/virt-test/arm64/kernel",
+  "initrd": "/data/local/tmp/virt-test/arm64/initramfs",
+  "params": "bin/vsock_client 2 45678 HelloWorld"
+}
\ No newline at end of file
diff --git a/tests/hostside/vm_config.x86_64.json b/tests/hostside/vm_config.x86_64.json
new file mode 100644
index 0000000..9ea95ab
--- /dev/null
+++ b/tests/hostside/vm_config.x86_64.json
@@ -0,0 +1,5 @@
+{
+  "kernel": "/data/local/tmp/virt-test/x86_64/kernel",
+  "initrd": "/data/local/tmp/virt-test/x86_64/initramfs",
+  "params": "bin/vsock_client 2 45678 HelloWorld"
+}
\ No newline at end of file
diff --git a/virtmanager/src/main.rs b/virtmanager/src/main.rs
index 7b0423b..88d6ed7 100644
--- a/virtmanager/src/main.rs
+++ b/virtmanager/src/main.rs
@@ -34,6 +34,7 @@
 const FIRST_GUEST_CID: Cid = 10;
 
 const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtmanager";
+const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
 
 /// The unique ID of a VM used (together with a port number) for vsock communication.
 type Cid = u32;
@@ -169,7 +170,7 @@
 
 /// Start an instance of `crosvm` to manage a new VM.
 fn run_vm(config: &VmConfig, cid: Cid) -> Result<Child, io::Error> {
-    let mut command = Command::new("crosvm");
+    let mut command = Command::new(CROSVM_PATH);
     // TODO(qwandor): Remove --disable-sandbox.
     command.arg("run").arg("--disable-sandbox").arg("--cid").arg(cid.to_string());
     if let Some(initrd) = &config.initrd {