WIP: crosvm launcher mode

There is still some clean up left to do here and a vmlinux image needs
to be given to the launcher through --kernel_image_path

Bug: 122978436
Test: build & boot locally
Change-Id: Ib6900a45ed37444bcad9615cccc8efd9cae9f4f0
diff --git a/host/commands/launch/flags.cc b/host/commands/launch/flags.cc
index 96a4fb2..5b67f2b 100644
--- a/host/commands/launch/flags.cc
+++ b/host/commands/launch/flags.cc
@@ -12,6 +12,7 @@
 #include "host/commands/launch/data_image.h"
 #include "host/commands/launch/launch.h"
 #include "host/commands/launch/launcher_defs.h"
+#include "host/libs/vm_manager/crosvm_manager.h"
 #include "host/libs/vm_manager/qemu_manager.h"
 #include "host/libs/vm_manager/vm_manager.h"
 
@@ -48,8 +49,9 @@
 DEFINE_string(console, "ttyS0", "Console device for the guest kernel.");
 DEFINE_string(androidboot_console, "ttyS1",
               "Console device for the Android framework");
-DEFINE_string(hardware_name, "vsoc",
-              "The codename of the device's hardware");
+DEFINE_string(
+    hardware_name, "",
+    "The codename of the device's hardware, one of {vsoc, virtio}");
 DEFINE_string(guest_security, "selinux",
               "The security module to use in the guest");
 DEFINE_bool(guest_enforce_security, false,
@@ -74,7 +76,7 @@
               "A directory to put all instance specific files");
 DEFINE_string(
     vm_manager, vm_manager::QemuManager::name(),
-    "What virtual machine manager to use, one of {qemu_cli}");
+    "What virtual machine manager to use, one of {qemu_cli, crosvm}");
 DEFINE_string(system_image_dir, vsoc::DefaultGuestImagePath(""),
               "Location of the system partition images.");
 DEFINE_string(vendor_image, "", "Location of the vendor partition image.");
@@ -110,7 +112,7 @@
 DEFINE_string(socket_vsock_proxy_binary,
               vsoc::DefaultHostArtifactsPath("bin/socket_vsock_proxy"),
               "Location of the socket_vsock_proxy binary.");
-DEFINE_string(adb_mode, "tunnel",
+DEFINE_string(adb_mode, "",
               "Mode for adb connection. Can be 'usb' for usb forwarding, "
               "'tunnel' for tcp connection, 'vsock_tunnel' for vsock tcp,"
               "or a comma separated list of types as in 'usb,tunnel'");
@@ -138,6 +140,9 @@
 
 // TODO(b/72969289) This should be generated
 DEFINE_string(dtb, "", "Path to the cuttlefish.dtb file");
+DEFINE_string(gsi_fstab,
+              vsoc::DefaultHostArtifactsPath("config/gsi.fstab"),
+              "Path to the GSI fstab file");
 
 DEFINE_string(uuid, vsoc::GetPerInstanceDefault(vsoc::kDefaultUuidPrefix),
               "UUID to use for the device. Random if not specified");
@@ -153,6 +158,9 @@
 DEFINE_string(qemu_binary,
               "/usr/bin/qemu-system-x86_64",
               "The qemu binary to use");
+DEFINE_string(crosvm_binary,
+              vsoc::DefaultHostArtifactsPath("bin/crosvm"),
+              "The Crosvm binary to use");
 DEFINE_bool(restart_subprocesses, true, "Restart any crashed host process");
 DEFINE_bool(run_e2e_test, true, "Run e2e test after device launches");
 DEFINE_string(e2e_test_binary,
@@ -278,6 +286,7 @@
     tmp_config_obj.add_kernel_cmdline(
         concat("androidboot.hardware=", FLAGS_hardware_name));
   }
+  tmp_config_obj.set_hardware_name(FLAGS_hardware_name);
   if (!FLAGS_guest_security.empty()) {
     tmp_config_obj.add_kernel_cmdline(concat("security=", FLAGS_guest_security));
     if (FLAGS_guest_enforce_security) {
@@ -305,6 +314,7 @@
   tmp_config_obj.set_data_image_path(FLAGS_data_image);
   tmp_config_obj.set_vendor_image_path(FLAGS_vendor_image);
   tmp_config_obj.set_dtb_path(FLAGS_dtb);
+  tmp_config_obj.set_gsi_fstab_path(FLAGS_gsi_fstab);
 
   tmp_config_obj.set_mempath(FLAGS_mempath);
   tmp_config_obj.set_ivshmem_qemu_socket_path(
@@ -342,6 +352,7 @@
   tmp_config_obj.set_uuid(FLAGS_uuid);
 
   tmp_config_obj.set_qemu_binary(FLAGS_qemu_binary);
+  tmp_config_obj.set_crosvm_binary(FLAGS_crosvm_binary);
   tmp_config_obj.set_ivserver_binary(FLAGS_ivserver_binary);
   tmp_config_obj.set_kernel_log_monitor_binary(FLAGS_kernel_log_monitor_binary);
 
@@ -414,6 +425,40 @@
   SetCommandLineOptionWithMode("instance_dir",
                                default_instance_dir.c_str(),
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("hardware_name",
+                               "vsoc",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("adb_mode", "tunnel",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+}
+
+void SetDefaultFlagsForCrosvm() {
+  auto default_mobile_interface = GetPerInstanceDefault("cvd-mbr-");
+  SetCommandLineOptionWithMode("mobile_interface",
+                               default_mobile_interface.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_mobile_tap_name = GetPerInstanceDefault("cvd-mtap-");
+  SetCommandLineOptionWithMode("mobile_tap_name",
+                               default_mobile_tap_name.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_wifi_interface = GetPerInstanceDefault("cvd-wbr-");
+  SetCommandLineOptionWithMode("wifi_interface",
+                               default_wifi_interface.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_wifi_tap_name = GetPerInstanceDefault("cvd-wtap-");
+  SetCommandLineOptionWithMode("wifi_tap_name",
+                               default_wifi_tap_name.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_instance_dir =
+      cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
+  SetCommandLineOptionWithMode("instance_dir",
+                               default_instance_dir.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("hardware_name",
+                               "virtio",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("adb_mode", "vsock_tunnel",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
 }
 
 bool ParseCommandLineFlags(int* argc, char*** argv) {
@@ -426,6 +471,8 @@
   bool invalid_manager = false;
   if (FLAGS_vm_manager == vm_manager::QemuManager::name()) {
     SetDefaultFlagsForQemu();
+  } else if (FLAGS_vm_manager == vm_manager::CrosvmManager::name()) {
+    SetDefaultFlagsForCrosvm();
   } else {
     std::cerr << "Unknown Virtual Machine Manager: " << FLAGS_vm_manager
               << std::endl;
diff --git a/host/commands/launch/launch.cc b/host/commands/launch/launch.cc
index e5a13b1..064981b 100644
--- a/host/commands/launch/launch.cc
+++ b/host/commands/launch/launch.cc
@@ -7,6 +7,7 @@
 #include "common/libs/utils/size_utils.h"
 #include "common/vsoc/shm/screen_layout.h"
 #include "host/commands/launch/launcher_defs.h"
+#include "host/commands/launch/pre_launch_initializers.h"
 #include "host/commands/launch/vsoc_shared_memory.h"
 
 using cvd::LauncherExitCodes;
@@ -36,13 +37,17 @@
   return std::string{"--ports="} + std::to_string(GetHostPort());
 }
 
+bool VSoCEnabled(const vsoc::CuttlefishConfig& config) {
+  return config.hardware_name() == "vsoc";
+}
+
 bool AdbModeEnabled(const vsoc::CuttlefishConfig& config, const char* mode) {
   auto modes = cvd::StrSplit(config.adb_mode(), ',');
   return std::find(modes.begin(), modes.end(), mode) != modes.end();
 }
 
 bool AdbTunnelEnabled(const vsoc::CuttlefishConfig& config) {
-  return AdbModeEnabled(config, kAdbModeTunnel);
+  return VSoCEnabled(config) && AdbModeEnabled(config, kAdbModeTunnel);
 }
 
 bool AdbVsockTunnelEnabled(const vsoc::CuttlefishConfig& config) {
@@ -71,7 +76,7 @@
 }
 
 bool AdbUsbEnabled(const vsoc::CuttlefishConfig& config) {
-  return AdbModeEnabled(config, kAdbModeUsb);
+  return VSoCEnabled(config) && AdbModeEnabled(config, kAdbModeUsb);
 }
 
 void ValidateAdbModeFlag(const vsoc::CuttlefishConfig& config) {
@@ -150,7 +155,7 @@
 void LaunchVNCServerIfEnabled(const vsoc::CuttlefishConfig& config,
                               cvd::ProcessMonitor* process_monitor,
                               std::function<bool(MonitorEntry*)> callback) {
-  if (config.enable_vnc_server()) {
+  if (VSoCEnabled(config) && config.enable_vnc_server()) {
     // Launch the vnc server, don't wait for it to complete
     auto port_options = "-port=" + std::to_string(config.vnc_server_port());
     cvd::Command vnc_server(config.vnc_server_binary());
@@ -162,7 +167,7 @@
 void LaunchStreamAudioIfEnabled(const vsoc::CuttlefishConfig& config,
                                 cvd::ProcessMonitor* process_monitor,
                                 std::function<bool(MonitorEntry*)> callback) {
-  if (config.enable_stream_audio()) {
+  if (VSoCEnabled(config) && config.enable_stream_audio()) {
     auto port_options = "-port=" + std::to_string(config.stream_audio_port());
     cvd::Command stream_audio(config.stream_audio_binary());
     stream_audio.AddParameter(port_options);
@@ -204,3 +209,15 @@
                                      GetOnSubprocessExitCallback(config));
   }
 }
+
+void LaunchIvServerIfEnabled(cvd::ProcessMonitor* process_monitor,
+                             const vsoc::CuttlefishConfig& config) {
+  if (!VSoCEnabled(config)) {
+    return;
+  }
+  process_monitor->StartSubprocess(GetIvServerCommand(config),
+                                   GetOnSubprocessExitCallback(config));
+
+  // Initialize the regions that require so before the VM starts.
+  PreLaunchInitializers::Initialize(config);
+}
\ No newline at end of file
diff --git a/host/commands/launch/launch.h b/host/commands/launch/launch.h
index 739790a..02f3a38 100644
--- a/host/commands/launch/launch.h
+++ b/host/commands/launch/launch.h
@@ -27,3 +27,5 @@
                                  const vsoc::CuttlefishConfig& config);
 void LaunchSocketVsockProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
                                  const vsoc::CuttlefishConfig& config);
+void LaunchIvServerIfEnabled(cvd::ProcessMonitor* process_monitor,
+                             const vsoc::CuttlefishConfig& config);
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index b97305e..3a0e72d 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -37,6 +37,7 @@
 
 #include <gflags/gflags.h>
 #include <glog/logging.h>
+#include <host/libs/vm_manager/crosvm_manager.h>
 
 #include "common/libs/fs/shared_fd.h"
 #include "common/libs/fs/shared_select.h"
@@ -52,7 +53,6 @@
 #include "host/commands/launch/flags.h"
 #include "host/commands/launch/launch.h"
 #include "host/commands/launch/launcher_defs.h"
-#include "host/commands/launch/pre_launch_initializers.h"
 #include "host/commands/launch/process_monitor.h"
 #include "host/commands/launch/vsoc_shared_memory.h"
 #include "host/libs/config/cuttlefish_config.h"
@@ -169,12 +169,15 @@
       });
 }
 
-void LaunchE2eTest(cvd::ProcessMonitor* process_monitor,
-                   std::shared_ptr<CvdBootStateMachine> state_machine,
-                   const vsoc::CuttlefishConfig& config) {
-  // Run a command that always succeeds if we are not running e2e tests
-  std::string e2e_test_cmd =
-      config.run_e2e_test() ? config.e2e_test_binary() : "/bin/true";
+void LaunchE2eTestIfEnabled(cvd::ProcessMonitor* process_monitor,
+                            std::shared_ptr<CvdBootStateMachine> state_machine,
+                            const vsoc::CuttlefishConfig& config) {
+  std::string e2e_test_cmd = config.e2e_test_binary();
+  if (!config.run_e2e_test() || config.hardware_name() != "vsoc") {
+    // Run a command that always succeeds if we are not running e2e tests
+    // TODO make the state machine aware of the fact that the tests may not run
+    e2e_test_cmd = "/bin/true";
+  }
   process_monitor->StartSubprocess(
       cvd::Command(e2e_test_cmd),
       [state_machine](cvd::MonitorEntry* entry) {
@@ -414,15 +417,10 @@
 
   LaunchUsbServerIfEnabled(*config, &process_monitor);
 
-  process_monitor.StartSubprocess(
-      GetIvServerCommand(*config),
-      GetOnSubprocessExitCallback(*config));
-
-  // Initialize the regions that require so before the VM starts.
-  PreLaunchInitializers::Initialize(*config);
-
-  // Launch the e2e test after the shared memory is initialized
-  LaunchE2eTest(&process_monitor, boot_state_machine, *config);
+  LaunchIvServerIfEnabled(&process_monitor, *config);
+  // Launch the e2e tests after the ivserver is ready
+  // TODO move this to launch.cc and make it
+  LaunchE2eTestIfEnabled(&process_monitor, boot_state_machine, *config);
 
   // Start the guest VM
   process_monitor.StartSubprocess(vm_manager->StartCommand(),
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 8c3cb96..7fb9c7a 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -31,6 +31,8 @@
 
 #include "common/libs/utils/environment.h"
 #include "common/libs/utils/files.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+
 
 namespace {
 
@@ -67,6 +69,7 @@
 const char* kSerialNumber = "serial_number";
 const char* kInstanceDir = "instance_dir";
 const char* kVmManager = "vm_manager";
+const char* kHardwareName = "hardware_name";
 const char* kDeviceTitle = "device_title";
 
 const char* kCpus = "cpus";
@@ -97,6 +100,7 @@
 const char* kLauncherLogPath = "launcher_log_path";
 const char* kLauncherMonitorPath = "launcher_monitor_socket";
 const char* kDtbPath = "dtb_path";
+const char* kGsiFstabPath = "gsi.fstab_path";
 
 const char* kMempath = "mempath";
 const char* kIvshmemQemuSocketPath = "ivshmem_qemu_socket_path";
@@ -120,6 +124,7 @@
 const char* kSetupWizardMode = "setupwizard_mode";
 
 const char* kQemuBinary = "qemu_binary";
+const char* kCrosvmBinary = "crosvm_binary";
 const char* kIvServerBinary = "ivserver_binary";
 const char* kKernelLogMonitorBinary = "kernel_log_monitor_binary";
 
@@ -163,6 +168,13 @@
   (*dictionary_)[kVmManager] = name;
 }
 
+std::string CuttlefishConfig::hardware_name() const {
+  return (*dictionary_)[kHardwareName].asString();
+}
+void CuttlefishConfig::set_hardware_name(const std::string& name) {
+  (*dictionary_)[kHardwareName] = name;
+}
+
 std::string CuttlefishConfig::serial_number() const {
   return (*dictionary_)[kSerialNumber].asString();
 }
@@ -324,6 +336,13 @@
   SetPath(kDtbPath, dtb_path);
 }
 
+std::string CuttlefishConfig::gsi_fstab_path() const {
+  return (*dictionary_)[kGsiFstabPath].asString();
+}
+void CuttlefishConfig::set_gsi_fstab_path(const std::string& path){
+  SetPath(kGsiFstabPath, path);
+}
+
 std::string CuttlefishConfig::mempath() const {
   return (*dictionary_)[kMempath].asString();
 }
@@ -548,6 +567,14 @@
   (*dictionary_)[kQemuBinary] = qemu_binary;
 }
 
+std::string CuttlefishConfig::crosvm_binary() const {
+  return (*dictionary_)[kCrosvmBinary].asString();
+}
+
+void CuttlefishConfig::set_crosvm_binary(const std::string& crosvm_binary) {
+  (*dictionary_)[kCrosvmBinary] = crosvm_binary;
+}
+
 std::string CuttlefishConfig::ivserver_binary() const {
   return (*dictionary_)[kIvServerBinary].asString();
 }
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 2a4e558..300250e 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -57,6 +57,9 @@
   std::string vm_manager() const;
   void set_vm_manager(const std::string& name);
 
+  std::string hardware_name() const;
+  void set_hardware_name(const std::string& name);
+
   std::string serial_number() const;
   void set_serial_number(const std::string& serial_number);
 
@@ -114,6 +117,9 @@
   std::string dtb_path() const;
   void set_dtb_path(const std::string& dtb_path);
 
+  std::string gsi_fstab_path() const;
+  void set_gsi_fstab_path(const std::string& path);
+
   std::string mempath() const;
   void set_mempath(const std::string& mempath);
 
@@ -205,6 +211,9 @@
   void set_qemu_binary(const std::string& qemu_binary);
   std::string qemu_binary() const;
 
+  void set_crosvm_binary(const std::string& crosvm_binary);
+  std::string crosvm_binary() const;
+
   void set_ivserver_binary(const std::string& ivserver_binary);
   std::string ivserver_binary() const;
 
diff --git a/host/libs/vm_manager/Android.bp b/host/libs/vm_manager/Android.bp
index 8a1f1c8..6c638ea 100644
--- a/host/libs/vm_manager/Android.bp
+++ b/host/libs/vm_manager/Android.bp
@@ -16,6 +16,7 @@
 cc_library_host_static {
     name: "libcuttlefish_vm_manager",
     srcs: [
+        "crosvm_manager.cpp",
         "qemu_manager.cpp",
         "vm_manager.cpp",
     ],
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
new file mode 100644
index 0000000..78286f9
--- /dev/null
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 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/crosvm_manager.h"
+
+#include <string>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "common/libs/utils/subprocess.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+
+namespace vm_manager {
+
+namespace {
+
+std::string GetControlSocketPath(const vsoc::CuttlefishConfig* config) {
+  return config->PerInstancePath("crosvm_control.sock");
+}
+
+cvd::SharedFD ConnectToLogMonitor(const std::string& log_monitor_name) {
+  return cvd::SharedFD::SocketLocalClient(log_monitor_name.c_str(), false,
+                                          SOCK_STREAM);
+}
+
+}  // namespace
+
+const std::string CrosvmManager::name() { return "crosvm"; }
+
+CrosvmManager::CrosvmManager(const vsoc::CuttlefishConfig* config)
+    : VmManager(config) {}
+
+cvd::Command CrosvmManager::StartCommand() {
+  if (!config_->ramdisk_image_path().empty()) {
+    // TODO re-enable ramdisk when crosvm supports it
+    LOG(FATAL) << "initramfs not supported";
+    return cvd::Command("/bin/false");
+  }
+
+  // TODO Add aarch64 support
+  // TODO Add the tap interfaces (--tap-fd)
+  // TODO Redirect logcat output
+
+  // Run crosvm directly instead of through a cf_crosvm.sh script. The kernel
+  // logs are on crosvm's standard output, so we need to redirect it to the log
+  // monitor socket, a helper script will print more than just the logs to
+  // standard output.
+  cvd::Command command(config_->crosvm_binary());
+  command.AddParameter("run");
+
+  command.AddParameter("--mem=", config_->memory_mb());
+  command.AddParameter("--cpus=", config_->cpus());
+  command.AddParameter("--params=", config_->kernel_cmdline_as_string());
+  command.AddParameter("--disk=", config_->system_image_path());
+  command.AddParameter("--rwdisk=", config_->data_image_path());
+  command.AddParameter("--rwdisk=", config_->cache_image_path());
+  command.AddParameter("--disk=", config_->vendor_image_path());
+  command.AddParameter("--socket=", GetControlSocketPath(config_));
+  command.AddParameter("--android-fstab=", config_->gsi_fstab_path());
+
+  // TODO remove this (use crosvm's seccomp files)
+  command.AddParameter("--disable-sandbox");
+
+  if (config_->vsock_guest_cid() >= 2) {
+    command.AddParameter("--cid=", config_->vsock_guest_cid());
+  }
+
+  auto kernel_log_connection =
+      ConnectToLogMonitor(config_->kernel_log_socket_name());
+  if (!kernel_log_connection->IsOpen()) {
+    LOG(WARNING) << "Unable to connect to log monitor: "
+                 << kernel_log_connection->StrError();
+  } else {
+    command.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdOut,
+                          kernel_log_connection);
+  }
+
+  // This needs to be the last parameter
+  command.AddParameter(config_->kernel_image_path());
+
+  return command;
+}
+
+bool CrosvmManager::Stop() {
+  cvd::Command command(config_->crosvm_binary());
+  command.AddParameter("stop");
+  command.AddParameter(GetControlSocketPath(config_));
+
+  auto process = command.Start();
+
+  return process.Wait() == 0;
+}
+
+}  // namespace vm_manager
diff --git a/host/libs/vm_manager/crosvm_manager.h b/host/libs/vm_manager/crosvm_manager.h
new file mode 100644
index 0000000..7574e51
--- /dev/null
+++ b/host/libs/vm_manager/crosvm_manager.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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"
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/subprocess.h"
+
+namespace vm_manager {
+
+// Starts a guest VM with crosvm. It requires the host package to support the
+// qemu-cli capability (for network only).
+class CrosvmManager : public VmManager {
+ public:
+  static const std::string name();
+  static bool EnsureInstanceDirExists(const std::string& instance_dir);
+
+  CrosvmManager(const vsoc::CuttlefishConfig* config);
+  virtual ~CrosvmManager() = default;
+
+  cvd::Command StartCommand() override;
+  bool Stop() override;
+};
+
+}  // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 4263fa9..4a64a11 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -120,12 +120,4 @@
   return true;
 }
 
-bool QemuManager::ValidateHostConfiguration(
-    std::vector<std::string>* config_commands) const {
-  // the check for cvdnetwork needs to happen even if the user is not in kvm, so
-  // we cant just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
-  auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
-  auto in_cvdnetwork = VmManager::UserInGroup("cvdnetwork", config_commands);
-  return in_kvm && in_cvdnetwork;
-}
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.h b/host/libs/vm_manager/qemu_manager.h
index b129c47..a0c6540 100644
--- a/host/libs/vm_manager/qemu_manager.h
+++ b/host/libs/vm_manager/qemu_manager.h
@@ -32,9 +32,6 @@
 
   cvd::Command StartCommand() override;
   bool Stop() override;
-
-  bool ValidateHostConfiguration(
-      std::vector<std::string>* config_commands) const override;
 };
 
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.cpp b/host/libs/vm_manager/vm_manager.cpp
index 8811902..5433d6d 100644
--- a/host/libs/vm_manager/vm_manager.cpp
+++ b/host/libs/vm_manager/vm_manager.cpp
@@ -23,6 +23,7 @@
 #include "common/libs/utils/users.h"
 #include "host/libs/config/cuttlefish_config.h"
 #include "host/libs/vm_manager/qemu_manager.h"
+#include "host/libs/vm_manager/crosvm_manager.h"
 
 namespace vm_manager {
 
@@ -43,6 +44,12 @@
          {[](const vsoc::CuttlefishConfig* config) {
             return GetManagerSingleton<QemuManager>(config);
           },
+          []() { return vsoc::HostSupportsQemuCli(); }}},
+        {CrosvmManager::name(),
+         {[](const vsoc::CuttlefishConfig* config) {
+            return GetManagerSingleton<CrosvmManager>(config);
+          },
+        // Same as Qemu for the time being
           []() { return vsoc::HostSupportsQemuCli(); }}}};
 
 VmManager* VmManager::Get(const std::string& vm_manager_name,
@@ -81,4 +88,13 @@
   }
   return true;
 }
+
+bool VmManager::ValidateHostConfiguration(
+    std::vector<std::string>* config_commands) const {
+  // the check for cvdnetwork needs to happen even if the user is not in kvm, so
+  // we cant just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
+  auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
+  auto in_cvdnetwork = VmManager::UserInGroup("cvdnetwork", config_commands);
+  return in_kvm && in_cvdnetwork;
+}
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
index c2ba761..84bf77c 100644
--- a/host/libs/vm_manager/vm_manager.h
+++ b/host/libs/vm_manager/vm_manager.h
@@ -44,7 +44,7 @@
   virtual bool Stop() = 0;
 
   virtual bool ValidateHostConfiguration(
-      std::vector<std::string>* config_commands) const = 0;
+      std::vector<std::string>* config_commands) const;
 
  protected:
   static bool UserInGroup(const std::string& group,