Call qemu directly when host packages support it

Bug: 79170615
Test: Run on gce
Change-Id: Ic082ff9fc1c34687fbe895ffe6d155f362c61250
Merged-In: Ic082ff9fc1c34687fbe895ffe6d155f362c61250
(cherry picked from commit a8142f95a29b5c3d2b0a80457062428bc582916d)
diff --git a/common/vsoc/lib/vsoc_memory.cpp b/common/vsoc/lib/vsoc_memory.cpp
index 04f90bd..76ecb26 100644
--- a/common/vsoc/lib/vsoc_memory.cpp
+++ b/common/vsoc/lib/vsoc_memory.cpp
@@ -198,6 +198,7 @@
     }
 
     region.SetRegionSize(new_min_size);
+    LOG(INFO) << region_name << ": resized to " << new_min_size;
 
     // Get new offset for next region
     auto offset = region.begin_offset() + region.region_size();
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index 55ba4a9..1a2cffe 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -48,23 +48,7 @@
 #include "host/libs/monitor/kernel_log_server.h"
 #include "host/libs/usbip/server.h"
 #include "host/libs/vadb/virtual_adb_server.h"
-#include "host/libs/vm_manager/libvirt_manager.h"
-
-namespace {
-std::string StringFromEnv(const char* varname, std::string defval) {
-  const char* const valstr = getenv(varname);
-  if (!valstr) {
-    return defval;
-  }
-  return valstr;
-}
-
-std::string DefaultHostArtifactsPath(const char* file_name) {
-  return (StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) + "/") +
-         file_name;
-}
-
-}  // namespace
+#include "host/libs/vm_manager/vm_manager.h"
 
 using vsoc::GetPerInstanceDefault;
 
@@ -103,18 +87,25 @@
 std::string g_default_mempath{GetPerInstanceDefault("/var/run/shm/cvd-")};
 DEFINE_string(mempath, g_default_mempath.c_str(),
               "Target location for the shmem file.");
-std::string g_default_mobile_interface{GetPerInstanceDefault("cvd-mobile-")};
+// The cvd-mobile-{tap|br}-xx interfaces are created by default, but libvirt
+// needs to create its own on tap interfaces on every run so we use a different
+// set for it.
+std::string g_default_mobile_interface{
+    vsoc::HostSupportsQemuCli() ? GetPerInstanceDefault("cvd-mbr-")
+                                : GetPerInstanceDefault("cvd-mobile-")};
 DEFINE_string(mobile_interface, g_default_mobile_interface.c_str(),
               "Network interface to use for mobile networking");
-DEFINE_string(mobile_tap_name, GetPerInstanceDefault("amobile"),
+std::string g_default_mobile_tap_interface =
+    vsoc::HostSupportsQemuCli() ? GetPerInstanceDefault("cvd-mtap-")
+                                : GetPerInstanceDefault("amobile");
+DEFINE_string(mobile_tap_name, g_default_mobile_tap_interface.c_str(),
               "The name of the tap interface to use for mobile");
 std::string g_default_serial_number{GetPerInstanceDefault("CUTTLEFISHCVD")};
 DEFINE_string(serial_number, g_default_serial_number.c_str(),
               "Serial number to use for the device");
 DEFINE_string(instance_dir, vsoc::GetDefaultPerInstanceDir(),
               "A directory to put all instance specific files");
-DEFINE_string(system_image_dir,
-              StringFromEnv("ANDROID_PRODUCT_OUT", StringFromEnv("HOME", ".")),
+DEFINE_string(system_image_dir, vsoc::DefaultGuestImagePath(""),
               "Location of the system partition images.");
 DEFINE_string(vendor_image, "", "Location of the vendor partition image.");
 
@@ -123,12 +114,12 @@
             " Will be deprecated soon.");
 DEFINE_bool(start_vnc_server, true, "Whether to start the vnc server process.");
 DEFINE_string(vnc_server_binary,
-              DefaultHostArtifactsPath("/bin/vnc_server"),
+              vsoc::DefaultHostArtifactsPath("bin/vnc_server"),
               "Location of the vnc server binary.");
 DEFINE_int32(vnc_server_port, GetPerInstanceDefault(6444),
              "The port on which the vnc server should listen");
 DEFINE_string(socket_forward_proxy_binary,
-              DefaultHostArtifactsPath("/bin/socket_forward_proxy"),
+              vsoc::DefaultHostArtifactsPath("bin/socket_forward_proxy"),
               "Location of the socket_forward_proxy binary.");
 DEFINE_string(adb_mode, "tunnel",
               "Mode for adb connection. Can be usb for usb forwarding, or "
@@ -143,7 +134,7 @@
               "MAC address of the wifi interface running on the host.");
 DEFINE_bool(start_wifi_relay, true, "Whether to start the wifi_relay process.");
 DEFINE_string(wifi_relay_binary,
-              DefaultHostArtifactsPath("/bin/wifi_relay"),
+              vsoc::DefaultHostArtifactsPath("bin/wifi_relay"),
               "Location of the wifi_relay binary.");
 std::string g_default_wifi_interface{GetPerInstanceDefault("cvd-wifi-")};
 DEFINE_string(wifi_interface, g_default_wifi_interface.c_str(),
@@ -683,9 +674,9 @@
   PreLaunchInitializers::Initialize();
 
   // Start the guest VM
-  vm_manager::LibvirtManager libvirt;
-  if (!libvirt.Start()) {
-    LOG(FATAL) << "Unable to start libvirt";
+  auto vm_manager = vm_manager::VmManager::Get();
+  if (!vm_manager->Start()) {
+    LOG(FATAL) << "Unable to start vm_manager";
     return -1;
   }
 
diff --git a/host/commands/stop_cvd/Android.bp b/host/commands/stop_cvd/Android.bp
index 3607e8e..e9db668 100644
--- a/host/commands/stop_cvd/Android.bp
+++ b/host/commands/stop_cvd/Android.bp
@@ -24,6 +24,7 @@
     shared_libs: [
         "libbase",
         "libcuttlefish_fs",
+        "libcuttlefish_utils",
         "cuttlefish_auto_resources",
         "libicuuc",
     ],
diff --git a/host/commands/stop_cvd/main.cc b/host/commands/stop_cvd/main.cc
index 441ec60..ce2d02d 100644
--- a/host/commands/stop_cvd/main.cc
+++ b/host/commands/stop_cvd/main.cc
@@ -36,7 +36,7 @@
 #include <glog/logging.h>
 
 #include "host/libs/config/cuttlefish_config.h"
-#include "host/libs/vm_manager/libvirt_manager.h"
+#include "host/libs/vm_manager/vm_manager.h"
 
 namespace {
 void RunCommand(const char* command) {
@@ -55,8 +55,8 @@
   int exit_code = 0;
 
   // TODO(b/78512938): Should ask the monitor to do the shutdown instead
-  vm_manager::LibvirtManager libvirt_manager;
-  if (!libvirt_manager.Stop()) {
+  auto vm_manager = vm_manager::VmManager::Get();
+  if (!vm_manager->Stop()) {
     LOG(ERROR)
         << "Error when stopping guest virtual machine. Is it still running?";
     exit_code = 1;
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 178ff0f..a2a3e0b 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -449,12 +449,34 @@
 int GetPerInstanceDefault(int base) { return base + GetInstance() - 1; }
 
 std::string GetDefaultPerInstanceDir() {
-  // TODO(79170615): Change to a directory in home once libvirt is no longer
-  // default.
   std::ostringstream stream;
-  stream << "/var/run/libvirt-" << kDefaultUuidPrefix << std::setfill('0')
-         << std::setw(2) << GetInstance();
+  if (HostSupportsQemuCli()) {
+    stream << std::getenv("HOME") << "/runfiles";
+  } else {
+    stream << "/var/run/libvirt-" << kDefaultUuidPrefix << std::setfill('0')
+           << std::setw(2) << GetInstance();
+  }
   return stream.str();
 }
 
+std::string DefaultHostArtifactsPath(const std::string& file_name) {
+  return (cvd::StringFromEnv("ANDROID_HOST_OUT",
+                             cvd::StringFromEnv("HOME", ".")) +
+          "/") +
+         file_name;
+}
+
+std::string DefaultGuestImagePath(const std::string& file_name) {
+  return (cvd::StringFromEnv("ANDROID_PRODUCT_OUT",
+                        cvd::StringFromEnv("HOME", ".")) +
+          "/") +
+         file_name;
+}
+
+bool HostSupportsQemuCli() {
+  static bool supported =
+      std::system(
+          "/usr/lib/cuttlefish-common/bin/capability_query.py qemu_cli") == 0;
+  return supported;
+}
 }  // namespace vsoc
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 4451653..3eb5532 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -32,7 +32,7 @@
 
   // Saves the configuration object in a file, it can then be read in other
   // processes by passing the --config_file option.
-  bool SaveToFile(const std::string& file) const ;
+  bool SaveToFile(const std::string& file) const;
 
   // Returns the path to a file with the given name in the instance directory..
   std::string PerInstancePath(const char* file_name) const;
@@ -188,4 +188,10 @@
 
 std::string GetDefaultPerInstanceDir();
 
+std::string DefaultHostArtifactsPath(const std::string& file);
+std::string DefaultGuestImagePath(const std::string& file);
+
+// Whether the installed host packages support calling qemu directly instead of
+// through libvirt
+bool HostSupportsQemuCli();
 }  // namespace vsoc
diff --git a/host/libs/vm_manager/Android.bp b/host/libs/vm_manager/Android.bp
index a4bfc66..3ab302d 100644
--- a/host/libs/vm_manager/Android.bp
+++ b/host/libs/vm_manager/Android.bp
@@ -17,6 +17,8 @@
     name: "libcuttlefish_vm_manager",
     srcs: [
         "libvirt_manager.cpp",
+        "qemu_manager.cpp",
+        "vm_manager.cpp",
     ],
     header_libs: [
         "cuttlefish_glog",
@@ -35,4 +37,10 @@
         "libjsoncpp",
     ],
     defaults: ["cuttlefish_host_only"],
+}
+
+cc_prebuilt_binary {
+    name: "cf_qemu.sh",
+    srcs: ["cf_qemu.sh"],
+    defaults: ["cuttlefish_host_only"],
 }
\ No newline at end of file
diff --git a/host/libs/vm_manager/cf_qemu.sh b/host/libs/vm_manager/cf_qemu.sh
new file mode 100644
index 0000000..8d49b04
--- /dev/null
+++ b/host/libs/vm_manager/cf_qemu.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+#
+# Copyright (C) 2018 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.
+#
+
+default_instance_number() {
+    if [[ "${USER::5}" == "vsoc-" ]]; then
+        echo "${USER: -2}"
+    else
+        echo "01"
+    fi
+}
+CUTTLEFISH_INSTANCE="${CUTTLEFISH_INSTANCE:-$(default_instance_number)}"
+default_instance_name="cvd-${CUTTLEFISH_INSTANCE}"
+default_uuid="699acfc4-c8c4-11e7-882b-5065f31dc1${CUTTLEFISH_INSTANCE}"
+default_dir="${HOME}/runfiles"
+default_mobile_tap_name="cvd-mtap-${CUTTLEFISH_INSTANCE}"
+
+if [[ -z "${ivshmem_vector_count}" ]]; then
+    echo "The required ivshmem_vector_count environment variable is not set" >&2
+    exit 1
+fi
+
+exec "${qemu_binary=/usr/bin/qemu-system-x86_64}" \
+    -enable-kvm \
+    -name "guest=${instance_name=${default_instance_name}},debug-threads=on" \
+    -machine "pc-i440fx-2.8,accel=kvm,usb=off,dump-guest-core=off" \
+    -m "${memory_mb=2048}" \
+    -realtime mlock=off \
+    -smp "${cpus=2},sockets=${cpus=2},cores=1,threads=1" \
+    -uuid "${uuid=${default_uuid}}"\
+    -display none \
+    -no-user-config \
+    -nodefaults \
+    -chardev "socket,id=charmonitor,path=${monitor_path=${default_dir}/qemu_monitor.sock},server,nowait" \
+    -mon "chardev=charmonitor,id=monitor,mode=control" \
+    -rtc "base=utc" \
+    -no-shutdown \
+    -boot "strict=on" \
+    -kernel "${kernel_image_path=${HOME}/kernel}" \
+    -initrd "${ramdisk_image_path=${HOME}/ramdisk.img}" \
+    -append "${kernel_args="loop.max_part=7 console=ttyS0 androidboot.console=ttyS1 androidboot.hardware=vsoc enforcing=0 audit=1 androidboot.selinux=permissive mac80211_hwsim.radios=0 security=selinux buildvariant=userdebug  androidboot.serialno=CUTTLEFISHCVD01 androidboot.lcd_density=160"}" \
+    -dtb "${dtb_path=${HOME}/config/cuttlefish.dtb}" \
+    -device "piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2" \
+    -device "virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x3" \
+    -drive "file=${system_image_path=${HOME}/system.img},format=raw,if=none,id=drive-virtio-disk0,aio=threads" \
+    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1" \
+    -drive "file=${data_image_path=${HOME}/userdata.img},format=raw,if=none,id=drive-virtio-disk1,aio=threads" \
+    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk1,id=virtio-disk1" \
+    -drive "file=${cache_image_path=${HOME}/cache.img},format=raw,if=none,id=drive-virtio-disk2,aio=threads" \
+    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk2,id=virtio-disk2" \
+    -drive "file=${vendor_image_path=${HOME}/vendor.img},format=raw,if=none,id=drive-virtio-disk3,aio=threads" \
+    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x7,drive=drive-virtio-disk3,id=virtio-disk3" \
+    -netdev "tap,id=hostnet0,ifname=${mobile_tap_name=${default_mobile_tap_name}},script=no,downscript=no" \
+    -device "virtio-net-pci,netdev=hostnet0,id=net0,mac=00:43:56:44:01:01,bus=pci.0,addr=0x2" \
+    -chardev "socket,id=charserial0,path=${kernel_log_socket_name=${default_dir}/kernel-log}" \
+    -device "isa-serial,chardev=charserial0,id=serial0" \
+    -chardev "socket,id=charserial1,path=${console_path=${default_dir}/console},server,nowait" \
+    -device "isa-serial,chardev=charserial1,id=serial1" \
+    -chardev "file,id=charchannel0,path=${logcat_path=${default_dir}/logcat},append=on" \
+    -device "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=cf-logcat" \
+    -device "virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x8" \
+    -object "rng-random,id=objrng0,filename=/dev/urandom" \
+    -device "virtio-rng-pci,rng=objrng0,id=rng0,max-bytes=1024,period=2000,bus=pci.0,addr=0x9" \
+    -chardev "socket,path=${ivshmem_qemu_socket_path=${default_dir}/ivshmem_socket_qemu},id=ivsocket" \
+    -device "ivshmem-doorbell,chardev=ivsocket,vectors=${ivshmem_vector_count}" \
+    -cpu host \
+    -msg "timestamp=on"
diff --git a/host/libs/vm_manager/libvirt_manager.cpp b/host/libs/vm_manager/libvirt_manager.cpp
index bebe12d..306f130 100644
--- a/host/libs/vm_manager/libvirt_manager.cpp
+++ b/host/libs/vm_manager/libvirt_manager.cpp
@@ -20,11 +20,14 @@
 #include <cstdlib>
 #include <iomanip>
 #include <sstream>
+#include <string>
 
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 #include <libxml/tree.h>
 
+#include "host/libs/config/cuttlefish_config.h"
+
 DEFINE_string(hypervisor_uri, "qemu:///system", "Hypervisor cannonical uri.");
 DEFINE_bool(log_xml, false, "Log the XML machine configuration");
 
@@ -259,9 +262,7 @@
   return cmd;
 }
 
-}  // namespace
-
-std::string LibvirtManager::BuildXmlConfig() const {
+std::string BuildXmlConfig() {
   auto config = vsoc::CuttlefishConfig::Get();
   std::string instance_name = config->instance_name();
 
@@ -328,6 +329,8 @@
   xmlFree(tgt);
   return out;
 }
+}  // namespace
+
 
 bool LibvirtManager::Start() const {
   std::string start_command = GetLibvirtCommand();
diff --git a/host/libs/vm_manager/libvirt_manager.h b/host/libs/vm_manager/libvirt_manager.h
index dbe7dcf..9537af6 100644
--- a/host/libs/vm_manager/libvirt_manager.h
+++ b/host/libs/vm_manager/libvirt_manager.h
@@ -15,22 +15,17 @@
  */
 #pragma once
 
-#include <string>
-
-#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/vm_manager/vm_manager.h"
 
 namespace vm_manager {
 
-class LibvirtManager {
+class LibvirtManager : public VmManager {
  public:
   LibvirtManager() = default;
-  ~LibvirtManager() = default;
+  virtual ~LibvirtManager() = default;
 
-  bool Start() const;
-  bool Stop() const;
-
- protected:
-  std::string BuildXmlConfig() const;
+  bool Start() const override;
+  bool Stop() const override;
 };
 
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
new file mode 100644
index 0000000..a12bc61
--- /dev/null
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "host/libs/vm_manager/qemu_manager.h"
+
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cstdlib>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/utils/subprocess.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+extern char** environ;
+
+DEFINE_string(qemu_binary,
+              "/usr/bin/qemu-system-x86_64",
+              "The qemu binary to use");
+
+namespace vm_manager {
+
+namespace {
+
+std::string GetMonitorPath() {
+  return vsoc::CuttlefishConfig::Get()->PerInstancePath("qemu_monitor.sock");
+}
+
+int BuildAndRunQemuCmd() {
+  auto config = vsoc::CuttlefishConfig::Get();
+  std::vector<std::string> qemu_envp;
+  // Copy this process' environment
+  char** env = environ;
+  while (*env) {
+    qemu_envp.push_back(*env);
+    ++env;
+  }
+  // Set the config values in the environment
+  qemu_envp.push_back("qemu_binary=" + FLAGS_qemu_binary);
+  qemu_envp.push_back("instance_name=" + config->instance_name());
+  qemu_envp.push_back("memory_mb=" + std::to_string(config->memory_mb()));
+  qemu_envp.push_back("cpus=" + std::to_string(config->cpus()));
+  qemu_envp.push_back("uuid=" + config->uuid());
+  qemu_envp.push_back("monitor_path=" +
+                      config->PerInstancePath("qemu_monitor.sock"));
+  qemu_envp.push_back("kernel_image_path=" + config->kernel_image_path());
+  qemu_envp.push_back("ramdisk_image_path=" + config->ramdisk_image_path());
+  qemu_envp.push_back("kernel_args=" + config->kernel_args());
+  qemu_envp.push_back("dtb_path=" + config->dtb_path());
+  qemu_envp.push_back("system_image_path=" + config->system_image_path());
+  qemu_envp.push_back("data_image_path=" + config->data_image_path());
+  qemu_envp.push_back("cache_image_path=" + config->cache_image_path());
+  qemu_envp.push_back("vendor_image_path=" + config->vendor_image_path());
+  qemu_envp.push_back("mobile_tap_name=" + config->mobile_tap_name());
+  qemu_envp.push_back("kernel_log_socket_name=" +
+                      config->kernel_log_socket_name());
+  qemu_envp.push_back("console_path=" + config->console_path());
+  qemu_envp.push_back("logcat_path=" + config->logcat_path());
+  qemu_envp.push_back("ivshmem_qemu_socket_path=" +
+                      config->ivshmem_qemu_socket_path());
+  qemu_envp.push_back("ivshmem_vector_count=" +
+                      std::to_string(config->ivshmem_vector_count()));
+  for (const auto& arg : qemu_envp) {
+    LOG(INFO) << arg;
+  }
+  return cvd::execute({vsoc::DefaultHostArtifactsPath("bin/cf_qemu.sh")},
+                      qemu_envp);
+}
+
+}  // namespace
+
+bool QemuManager::Start() const {
+  // Create a thread that will make the launcher abort if the qemu process
+  // crashes, this avoids having the launcher waiting forever for
+  // VIRTUAL_DEVICE_BOOT_COMPLETED in this cases.
+  std::thread waiting_thread([]() {
+    int status = BuildAndRunQemuCmd();
+    if (status != 0) {
+      LOG(FATAL) << "Qemu process exited prematurely";
+    } else {
+      LOG(ERROR) << "Qemu process exited normally, it shouldn't happen";
+    }
+  });
+  waiting_thread.detach();
+  return true;
+}
+bool QemuManager::Stop() const {
+  int errno_;
+  int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (fd == -1) {
+    errno_ = errno;
+    LOG(ERROR) << "Error creating socket: " << strerror(errno_);
+    return false;
+  }
+
+  struct sockaddr_un addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sun_family = AF_UNIX;
+  std::string monitor_path = GetMonitorPath();
+  strncpy(addr.sun_path, monitor_path.c_str(), sizeof(addr.sun_path) - 1);
+
+  if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
+    errno_ = errno;
+    LOG(ERROR) << "Error connecting to qemu monitor: " << strerror(errno_);
+    return false;
+  }
+
+  char msg[] = "{\"execute\":\"qmp_capabilities\"}{\"execute\":\"quit\"}";
+  ssize_t len = sizeof(msg) - 1;
+  while (len > 0) {
+    int tmp = TEMP_FAILURE_RETRY(write(fd, msg, len));
+    if (tmp < 0) {
+      LOG(ERROR) << "Error writing to socket";
+    }
+    len -= tmp;
+  }
+  // Log the reply
+  char buff[1000];
+  while ((len = TEMP_FAILURE_RETRY(read(fd, buff, sizeof(buff) - 1))) > 0) {
+    buff[len] = '\0';
+    LOG(INFO) << "From qemu monitor: " << buff;
+  }
+
+  return true;
+}
+
+}  // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.h b/host/libs/vm_manager/qemu_manager.h
new file mode 100644
index 0000000..566c106
--- /dev/null
+++ b/host/libs/vm_manager/qemu_manager.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#pragma once
+
+#include "host/libs/vm_manager/vm_manager.h"
+
+namespace vm_manager {
+
+// Starts a guest VM using the qemu command directly. It requires the host
+// package to support the qemu-cli capability.
+class QemuManager : public VmManager {
+ public:
+  QemuManager() = default;
+  virtual ~QemuManager() = default;
+
+  bool Start() const override;
+  bool Stop() const override;
+};
+
+}  // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.cpp b/host/libs/vm_manager/vm_manager.cpp
new file mode 100644
index 0000000..787e90b
--- /dev/null
+++ b/host/libs/vm_manager/vm_manager.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "host/libs/vm_manager/vm_manager.h"
+
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/vm_manager/libvirt_manager.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+
+namespace vm_manager {
+std::shared_ptr<VmManager> VmManager::Get() {
+  static std::shared_ptr<VmManager> vm_manager(
+      vsoc::HostSupportsQemuCli()
+          ? static_cast<VmManager*>(new QemuManager())
+          : static_cast<VmManager*>(new LibvirtManager()));
+  return vm_manager;
+}
+}  // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
new file mode 100644
index 0000000..a329571
--- /dev/null
+++ b/host/libs/vm_manager/vm_manager.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#pragma once
+
+#include <memory>
+#include <string>
+
+namespace vm_manager {
+
+// Superclass of every guest VM manager. It provides a static getter that
+// chooses the best subclass to instantiate based on the capabilities supported
+// by the host packages.
+class VmManager {
+ public:
+  // Returns the most suitable vm manager as a singleton.
+  static std::shared_ptr<VmManager> Get();
+  virtual ~VmManager() = default;
+
+  virtual bool Start() const = 0;
+  virtual bool Stop() const = 0;
+};
+
+}  // namespace vm_manager