Merge "Minor refactors in modem_simulator" into main
diff --git a/common/libs/utils/Android.bp b/common/libs/utils/Android.bp
index ee5d565..6ac56bf 100644
--- a/common/libs/utils/Android.bp
+++ b/common/libs/utils/Android.bp
@@ -30,6 +30,7 @@
         "network.cpp",
         "proc_file_utils.cpp",
         "shared_fd_flag.cpp",
+        "signals.cpp",
         "subprocess.cpp",
         "tcp_socket.cpp",
         "tee_logging.cpp",
diff --git a/common/libs/utils/signals.cpp b/common/libs/utils/signals.cpp
new file mode 100644
index 0000000..ab6fb93
--- /dev/null
+++ b/common/libs/utils/signals.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 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 "common/libs/utils/signals.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+
+#include <vector>
+
+#include <android-base/logging.h>
+
+namespace cuttlefish {
+
+SignalMasker::SignalMasker(sigset_t signals) {
+  auto res = sigprocmask(SIG_SETMASK, &signals, &old_mask_);
+  auto err = errno;
+  CHECK(res == 0) << "Failed to set thread's blocked signal mask: "
+                  << strerror(err);
+}
+
+SignalMasker::~SignalMasker() {
+  auto res = sigprocmask(SIG_SETMASK, &old_mask_, NULL);
+  auto err = errno;
+  CHECK(res == 0) << "Failed to reset thread's blocked signal mask: "
+                  << strerror(err);
+}
+
+void ChangeSignalHandlers(void (*handler)(int), std::vector<int> signals) {
+  struct sigaction act;
+  act.sa_handler = handler;
+  sigemptyset(&act.sa_mask);
+  for (auto signal: signals) {
+    sigaddset(&act.sa_mask, signal);
+  }
+  act.sa_flags = 0;
+
+  for (auto signal : signals) {
+    sigaction(signal, &act, NULL);
+  }
+}
+
+}  // namespace cuttlefish
+
diff --git a/common/libs/utils/signals.h b/common/libs/utils/signals.h
new file mode 100644
index 0000000..4eb6843
--- /dev/null
+++ b/common/libs/utils/signals.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 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 <signal.h>
+
+#include <vector>
+
+namespace cuttlefish {
+
+/**
+ * Blocks signals for the current thread for the lifetime of the object.
+ *
+ * Provides a RAII interface to sigprocmask.
+ */
+class SignalMasker {
+ public:
+  /**
+   * Blocks the given signals until the object is destroyed.
+   */
+  SignalMasker(sigset_t signals);
+  SignalMasker(const SignalMasker&) = delete;
+  SignalMasker(SignalMasker&&) = delete;
+  SignalMasker operator=(const SignalMasker&) = delete;
+  SignalMasker operator=(SignalMasker&&) = delete;
+  ~SignalMasker();
+
+ private:
+  sigset_t old_mask_;
+};
+
+void ChangeSignalHandlers(void(*handler)(int), std::vector<int> signals);
+
+}  // namespace cuttlefish
diff --git a/common/libs/utils/subprocess.cpp b/common/libs/utils/subprocess.cpp
index 40b13c3..2ef5957 100644
--- a/common/libs/utils/subprocess.cpp
+++ b/common/libs/utils/subprocess.cpp
@@ -230,7 +230,7 @@
     return -1;
   }
   *infop = {};
-  auto retval = waitid(P_PID, pid_, infop, options);
+  auto retval = TEMP_FAILURE_RETRY(waitid(P_PID, pid_, infop, options));
   // We don't want to wait twice for the same process
   bool exited = infop->si_code == CLD_EXITED || infop->si_code == CLD_DUMPED;
   bool reaped = !(options & WNOWAIT);
diff --git a/guest/hals/ril/reference-ril/reference-ril.c b/guest/hals/ril/reference-ril/reference-ril.c
index b7915e1..c946db0 100644
--- a/guest/hals/ril/reference-ril/reference-ril.c
+++ b/guest/hals/ril/reference-ril/reference-ril.c
@@ -918,6 +918,10 @@
             continue;
 
         i = ncid - 1;
+
+        if (i >= n || i < 0)
+            goto error;
+
         // Assume no error
         responses[i].status = 0;
 
@@ -1054,14 +1058,21 @@
         &input, (responses) ? &responses[i].dnses : &sskip);  // dns_prim_addr
     if (err < 0) goto error;
 
+    size_t response_size = 0;
+    RIL_Data_Call_Response_v11 *presponse = NULL;
+    if (responses) {
+        if (i >= n || i < 0)
+            goto error;
+        presponse = &responses[i];
+        response_size = sizeof(*presponse);
+    }
+
     if (t != NULL)
       RIL_onRequestComplete(*t, RIL_E_SUCCESS,
-                            (responses != NULL) ? (responses + i) : responses,
-                            sizeof(RIL_Data_Call_Response_v11));
+                            presponse, response_size);
     else
         RIL_onUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED,
-                                  responses,
-                                  n * sizeof(RIL_Data_Call_Response_v11));
+                                  responses, n * response_size);
 
     at_response_free(p_response);
     return;
diff --git a/host/commands/assemble_cvd/bootconfig_args.cpp b/host/commands/assemble_cvd/bootconfig_args.cpp
index b757102..baa1d90 100644
--- a/host/commands/assemble_cvd/bootconfig_args.cpp
+++ b/host/commands/assemble_cvd/bootconfig_args.cpp
@@ -111,8 +111,7 @@
         std::to_string(instance.tombstone_receiver_port());
   }
 
-  const auto enable_confui =
-      (config.vm_manager() == QemuManager::name() ? 0 : 1);
+  const auto enable_confui = (config.vm_manager() == VmmMode::kQemu ? 0 : 1);
   bootconfig_args["androidboot.enable_confirmationui"] =
       std::to_string(enable_confui);
 
@@ -173,7 +172,7 @@
   if (instance.target_arch() == Arch::X86 ||
       instance.target_arch() == Arch::X86_64) {
     bootconfig_args["androidboot.hypervisor.version"] =
-        "cf-" + config.vm_manager();
+        "cf-" + ToString(config.vm_manager());
     bootconfig_args["androidboot.hypervisor.vm.supported"] = "1";
   } else {
     bootconfig_args["androidboot.hypervisor.vm.supported"] = "0";
diff --git a/host/commands/assemble_cvd/disk/gem5_image_unpacker.cpp b/host/commands/assemble_cvd/disk/gem5_image_unpacker.cpp
index f4d91f8..b60bd39 100644
--- a/host/commands/assemble_cvd/disk/gem5_image_unpacker.cpp
+++ b/host/commands/assemble_cvd/disk/gem5_image_unpacker.cpp
@@ -26,7 +26,7 @@
 
 Result<void> Gem5ImageUnpacker(const CuttlefishConfig& config,
                                KernelRamdiskRepacker& /* dependency */) {
-  if (config.vm_manager() != vm_manager::Gem5Manager::name()) {
+  if (config.vm_manager() != VmmMode::kGem5) {
     return {};
   }
   // TODO: b/281130788 - This should accept InstanceSpecific as an argument
diff --git a/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp b/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp
index 7960b63..04ebafa 100644
--- a/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp
+++ b/host/commands/assemble_cvd/disk/generate_persistent_bootconfig.cpp
@@ -78,7 +78,7 @@
                                 << bootconfig_path
                                 << "` failed:" << bootconfig_fd->StrError());
 
-  if (config.vm_manager() == vm_manager::Gem5Manager::name()) {
+  if (config.vm_manager() == VmmMode::kGem5) {
     const off_t bootconfig_size_bytes_gem5 =
         AlignToPowerOf2(bytesWritten, PARTITION_SIZE_SHIFT);
     CF_EXPECT(bootconfig_fd->Truncate(bootconfig_size_bytes_gem5) == 0);
diff --git a/host/commands/assemble_cvd/disk/initialize_instance_composite_disk.cc b/host/commands/assemble_cvd/disk/initialize_instance_composite_disk.cc
index 6658a2b..ed6d36a 100644
--- a/host/commands/assemble_cvd/disk/initialize_instance_composite_disk.cc
+++ b/host/commands/assemble_cvd/disk/initialize_instance_composite_disk.cc
@@ -84,7 +84,7 @@
 }  // namespace
 
 bool IsVmManagerQemu(const CuttlefishConfig& config) {
-  return config.vm_manager() == vm_manager::QemuManager::name();
+  return config.vm_manager() == VmmMode::kQemu;
 }
 Result<void> InitializeInstanceCompositeDisk(
     const CuttlefishConfig& config,
diff --git a/host/commands/assemble_cvd/disk/kernel_ramdisk_repacker.cpp b/host/commands/assemble_cvd/disk/kernel_ramdisk_repacker.cpp
index 46d49a5..6268d86 100644
--- a/host/commands/assemble_cvd/disk/kernel_ramdisk_repacker.cpp
+++ b/host/commands/assemble_cvd/disk/kernel_ramdisk_repacker.cpp
@@ -136,7 +136,8 @@
     // large to be repacked. Skip repack of boot.img on Gem5, as we need to be
     // able to extract the ramdisk.img in a later stage and so this step must
     // not fail (..and the repacked kernel wouldn't be used anyway).
-    if (instance_.kernel_path().size() && config_.vm_manager() != Gem5Manager::name()) {
+    if (instance_.kernel_path().size() &&
+        config_.vm_manager() != VmmMode::kGem5) {
       CF_EXPECT(RepackBootImage(avb_, instance_.kernel_path(), instance_.boot_image(),
                                 instance_.new_boot_image(), instance_.instance_dir()),
                 "Failed to regenerate the boot image with the new kernel");
diff --git a/host/commands/assemble_cvd/disk_builder.cpp b/host/commands/assemble_cvd/disk_builder.cpp
index b7f6b41..d6ee26b 100644
--- a/host/commands/assemble_cvd/disk_builder.cpp
+++ b/host/commands/assemble_cvd/disk_builder.cpp
@@ -91,11 +91,11 @@
   return *this;
 }
 
-DiskBuilder& DiskBuilder::VmManager(std::string vm_manager) & {
+DiskBuilder& DiskBuilder::VmManager(VmmMode vm_manager) & {
   vm_manager_ = std::move(vm_manager);
   return *this;
 }
-DiskBuilder DiskBuilder::VmManager(std::string vm_manager) && {
+DiskBuilder DiskBuilder::VmManager(VmmMode vm_manager) && {
   vm_manager_ = std::move(vm_manager);
   return *this;
 }
@@ -139,7 +139,7 @@
 Result<std::string> DiskBuilder::TextConfig() {
   std::ostringstream disk_conf;
 
-  CF_EXPECT(!vm_manager_.empty(), "Missing vm_manager");
+  CF_EXPECT(vm_manager_ != VmmMode::kUnknown, "Missing vm_manager");
   disk_conf << vm_manager_ << "\n";
 
   CF_EXPECT(!partitions_.empty() ^ !entire_disk_.empty(),
@@ -195,8 +195,8 @@
     return false;
   }
 
-  CF_EXPECT(!vm_manager_.empty());
-  if (vm_manager_ == vm_manager::CrosvmManager::name()) {
+  CF_EXPECT(vm_manager_ != VmmMode::kUnknown);
+  if (vm_manager_ == VmmMode::kCrosvm) {
     CF_EXPECT(!header_path_.empty(), "No header path");
     CF_EXPECT(!footer_path_.empty(), "No footer path");
     CreateCompositeDisk(partitions_, AbsolutePath(header_path_),
diff --git a/host/commands/assemble_cvd/disk_builder.h b/host/commands/assemble_cvd/disk_builder.h
index 424a5ad..6b3be7b 100644
--- a/host/commands/assemble_cvd/disk_builder.h
+++ b/host/commands/assemble_cvd/disk_builder.h
@@ -42,8 +42,8 @@
   DiskBuilder& CrosvmPath(std::string crosvm_path) &;
   DiskBuilder CrosvmPath(std::string crosvm_path) &&;
 
-  DiskBuilder& VmManager(std::string vm_manager) &;
-  DiskBuilder VmManager(std::string vm_manager) &&;
+  DiskBuilder& VmManager(VmmMode vm_manager) &;
+  DiskBuilder VmManager(VmmMode vm_manager) &&;
 
   DiskBuilder& ConfigPath(std::string config_path) &;
   DiskBuilder ConfigPath(std::string config_path) &&;
@@ -70,7 +70,7 @@
   std::string entire_disk_;
   std::string header_path_;
   std::string footer_path_;
-  std::string vm_manager_;
+  VmmMode vm_manager_ = VmmMode::kUnknown;
   std::string crosvm_path_;
   std::string config_path_;
   std::string composite_disk_path_;
diff --git a/host/commands/assemble_cvd/disk_flags.cc b/host/commands/assemble_cvd/disk_flags.cc
index 6f23e03..d7fac96 100644
--- a/host/commands/assemble_cvd/disk_flags.cc
+++ b/host/commands/assemble_cvd/disk_flags.cc
@@ -625,7 +625,7 @@
   CF_EXPECT(CreateBlankImage(instance.sdcard_path(),
                              instance.blank_sdcard_image_mb(), "sdcard"),
             "Failed to create \"" << instance.sdcard_path() << "\"");
-  if (config.vm_manager() == "qemu_cli") {
+  if (config.vm_manager() == VmmMode::kQemu) {
     const std::string crosvm_path = instance.crosvm_binary();
     CreateQcowOverlay(crosvm_path, instance.sdcard_path(),
                       instance.sdcard_overlay_path());
@@ -927,8 +927,7 @@
     // Repacking a boot.img changes boot_image and vendor_boot_image paths
     const CuttlefishConfig& const_config = const_cast<const CuttlefishConfig&>(config);
     const CuttlefishConfig::InstanceSpecific const_instance = const_config.ForInstance(num);
-    if (cur_kernel_path.size() &&
-        config.vm_manager() != Gem5Manager::name()) {
+    if (cur_kernel_path.size() && config.vm_manager() != VmmMode::kGem5) {
       const std::string new_boot_image_path =
           const_instance.PerInstancePath("boot_repacked.img");
       // change the new flag value to corresponding instance
@@ -1078,7 +1077,7 @@
     }
     // Gem5 Simulate per-instance what the bootloader would usually do
     // Since on other devices this runs every time, just do it here every time
-    if (config.vm_manager() == Gem5Manager::name()) {
+    if (config.vm_manager() == VmmMode::kGem5) {
       RepackGem5BootImage(instance.PerInstancePath("initrd.img"),
                           instance.persistent_bootconfig_path(),
                           config.assembly_dir(), instance.initramfs_path());
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index eef579c..0bbc749 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -973,11 +973,12 @@
   // TODO(weihsu), b/250988697: moved bootconfig_supported and hctr2_supported
   // into each instance, but target_arch is still in todo
   // target_arch should be in instance later
-  auto vmm = GetVmManager(vm_manager_vec[0], guest_configs[0].target_arch);
+  auto vmm_mode = CF_EXPECT(ParseVmm(vm_manager_vec[0]));
+  auto vmm = GetVmManager(vmm_mode, guest_configs[0].target_arch);
   if (!vmm) {
     LOG(FATAL) << "Invalid vm_manager: " << vm_manager_vec[0];
   }
-  tmp_config_obj.set_vm_manager(vm_manager_vec[0]);
+  tmp_config_obj.set_vm_manager(vmm_mode);
   tmp_config_obj.set_ap_vm_manager(vm_manager_vec[0] + "_openwrt");
 
   auto secure_hals_strs =
@@ -1373,7 +1374,7 @@
       std::set<Arch> default_on_arch = {Arch::Arm64};
       if (guest_configs[instance_index].vhost_user_vsock) {
         instance.set_vhost_user_vsock(true);
-      } else if (tmp_config_obj.vm_manager() == CrosvmManager::name() &&
+      } else if (tmp_config_obj.vm_manager() == VmmMode::kCrosvm &&
                  default_on_arch.find(
                      guest_configs[instance_index].target_arch) !=
                      default_on_arch.end()) {
@@ -1383,7 +1384,7 @@
       }
     } else if (vhost_user_vsock_vec[instance_index] ==
                kVhostUserVsockModeTrue) {
-      CHECK(tmp_config_obj.vm_manager() == CrosvmManager::name())
+      CHECK(tmp_config_obj.vm_manager() == VmmMode::kCrosvm)
           << "For now, only crosvm supports vhost_user_vsock";
       instance.set_vhost_user_vsock(true);
     } else if (vhost_user_vsock_vec[instance_index] ==
@@ -1552,7 +1553,7 @@
     const std::string gpu_mode = CF_EXPECT(ConfigureGpuSettings(
         gpu_mode_vec[instance_index], gpu_vhost_user_mode_vec[instance_index],
         gpu_renderer_features_vec[instance_index],
-        gpu_context_types_vec[instance_index], vm_manager_vec[instance_index],
+        gpu_context_types_vec[instance_index], vmm_mode,
         guest_configs[instance_index], instance));
     calculated_gpu_mode_vec[instance_index] = gpu_mode_vec[instance_index];
 
@@ -1633,7 +1634,7 @@
     bool os_overlay = true;
     os_overlay &= !protected_vm_vec[instance_index];
     // Gem5 already uses CoW wrappers around disk images
-    os_overlay &= vm_manager_vec[0] != Gem5Manager::name();
+    os_overlay &= vmm_mode != VmmMode::kGem5;
     os_overlay &= FLAGS_use_overlay;
     if (os_overlay) {
       auto path = const_instance.PerInstancePath("overlay.img");
@@ -1644,13 +1645,14 @@
 
     bool persistent_disk = true;
     persistent_disk &= !protected_vm_vec[instance_index];
-    persistent_disk &= vm_manager_vec[0] != Gem5Manager::name();
+    persistent_disk &= vmm_mode != VmmMode::kGem5;
     if (persistent_disk) {
 #ifdef __APPLE__
       const std::string persistent_composite_img_base =
           "persistent_composite.img";
 #else
-      const bool is_vm_qemu_cli = (tmp_config_obj.vm_manager() == "qemu_cli");
+      const bool is_vm_qemu_cli =
+          (tmp_config_obj.vm_manager() == VmmMode::kQemu);
       const std::string persistent_composite_img_base =
           is_vm_qemu_cli ? "persistent_composite_overlay.img"
                          : "persistent_composite.img";
@@ -1666,7 +1668,7 @@
     sdcard &= use_sdcard_vec[instance_index];
     sdcard &= !protected_vm_vec[instance_index];
     if (sdcard) {
-      if (tmp_config_obj.vm_manager() == "qemu_cli") {
+      if (tmp_config_obj.vm_manager() == VmmMode::kQemu) {
         virtual_disk_paths.push_back(const_instance.sdcard_overlay_path());
       } else {
         virtual_disk_paths.push_back(const_instance.sdcard_path());
@@ -1765,7 +1767,7 @@
     auto external_network_mode = CF_EXPECT(
         ParseExternalNetworkMode(device_external_network_vec[instance_index]));
     CF_EXPECT(external_network_mode == ExternalNetworkMode::kTap ||
-                  vm_manager_vec[instance_index] == QemuManager::name(),
+                  vmm_mode == VmmMode::kQemu,
               "TODO(b/286284441): slirp only works on QEMU");
     instance.set_external_network_mode(external_network_mode);
 
@@ -1824,7 +1826,7 @@
           .ForEnvironment(environment_name);
   CF_EXPECT(CheckSnapshotCompatible(
                 FLAGS_snapshot_compatible &&
-                    (tmp_config_obj.vm_manager() == CrosvmManager::name()) &&
+                    (tmp_config_obj.vm_manager() == VmmMode::kCrosvm) &&
                     instance_nums.size() == 1,
                 calculated_gpu_mode_vec),
             "The set of flags is incompatible with snapshot");
@@ -2057,23 +2059,27 @@
   }
   if (FLAGS_vm_manager == "") {
     if (IsHostCompatible(guest_configs[0].target_arch)) {
-      FLAGS_vm_manager = CrosvmManager::name();
+      FLAGS_vm_manager = ToString(VmmMode::kCrosvm);
     } else {
-      FLAGS_vm_manager = QemuManager::name();
+      FLAGS_vm_manager = ToString(VmmMode::kQemu);
     }
   }
-  // TODO(weihsu), b/250988697:
-  // Currently, all instances should use same vmm
+
   std::vector<std::string> vm_manager_vec =
       android::base::Split(FLAGS_vm_manager, ",");
+
+  // TODO(weihsu), b/250988697:
+  // Currently, all instances should use same vmm
+  auto vmm = CF_EXPECT(ParseVmm(vm_manager_vec[0]));
+
   // get flag default values and store into map
   auto name_to_default_value = CurrentFlagsToDefaultValue();
 
-  if (vm_manager_vec[0] == QemuManager::name()) {
+  if (vmm == VmmMode::kQemu) {
     CF_EXPECT(SetDefaultFlagsForQemu(guest_configs[0].target_arch, name_to_default_value));
-  } else if (vm_manager_vec[0] == CrosvmManager::name()) {
+  } else if (vmm == VmmMode::kCrosvm) {
     CF_EXPECT(SetDefaultFlagsForCrosvm(guest_configs, name_to_default_value));
-  } else if (vm_manager_vec[0] == Gem5Manager::name()) {
+  } else if (vmm == VmmMode::kGem5) {
     // TODO: Get the other architectures working
     if (guest_configs[0].target_arch != Arch::Arm64) {
       return CF_ERR("Gem5 only supports ARM64");
@@ -2082,7 +2088,7 @@
   } else {
     return CF_ERR("Unknown Virtual Machine Manager: " << FLAGS_vm_manager);
   }
-  if (vm_manager_vec[0] != Gem5Manager::name()) {
+  if (vmm != VmmMode::kGem5) {
     // After SetCommandLineOptionWithMode in SetDefaultFlagsForCrosvm/Qemu,
     // default flag values changed, need recalculate name_to_default_value
     name_to_default_value = CurrentFlagsToDefaultValue();
diff --git a/host/commands/assemble_cvd/graphics_flags.cc b/host/commands/assemble_cvd/graphics_flags.cc
index e5e7e44..d843f51 100644
--- a/host/commands/assemble_cvd/graphics_flags.cc
+++ b/host/commands/assemble_cvd/graphics_flags.cc
@@ -24,9 +24,8 @@
 #include <google/protobuf/text_format.h>
 
 #include "common/libs/utils/contains.h"
+#include "common/libs/utils/subprocess.h"
 #include "host/libs/config/cuttlefish_config.h"
-#include "host/libs/vm_manager/crosvm_manager.h"
-#include "host/libs/vm_manager/qemu_manager.h"
 
 #ifdef __APPLE__
 #define CF_UNUSED_ON_MACOS [[maybe_unused]]
@@ -232,7 +231,7 @@
 
 #ifndef __APPLE__
 Result<std::string> SelectGpuMode(
-    const std::string& gpu_mode_arg, const std::string& vm_manager,
+    const std::string& gpu_mode_arg, VmmMode vmm,
     const GuestConfig& guest_config,
     const gfxstream::proto::GraphicsAvailability& graphics_availability) {
   if (gpu_mode_arg != kGpuModeAuto && gpu_mode_arg != kGpuModeDrmVirgl &&
@@ -260,7 +259,7 @@
       LOG(INFO) << "GPU auto mode: detected prerequisites for accelerated "
                 << "rendering support.";
 
-      if (vm_manager == vm_manager::QemuManager::name() && !UseQemuPrebuilt()) {
+      if (vmm == VmmMode::kQemu && !UseQemuPrebuilt()) {
         LOG(INFO) << "Not using QEMU prebuilt (QEMU 8+): selecting guest swiftshader";
         return kGpuModeGuestSwiftshader;
       } else if (!guest_config.gfxstream_supported) {
@@ -290,7 +289,7 @@
                     "--gpu_mode=auto or --gpu_mode=guest_swiftshader.";
     }
 
-    if (vm_manager == vm_manager::QemuManager::name() && !UseQemuPrebuilt()) {
+    if (vmm == VmmMode::kQemu && !UseQemuPrebuilt()) {
       LOG(INFO) << "Not using QEMU prebuilt (QEMU 8+): selecting guest swiftshader";
       return kGpuModeGuestSwiftshader;
     }
@@ -301,7 +300,7 @@
 
 Result<bool> SelectGpuVhostUserMode(const std::string& gpu_mode,
                                     const std::string& gpu_vhost_user_mode_arg,
-                                    const std::string& vm_manager) {
+                                    VmmMode vmm) {
   CF_EXPECT(gpu_vhost_user_mode_arg == kGpuVhostUserModeAuto ||
             gpu_vhost_user_mode_arg == kGpuVhostUserModeOn ||
             gpu_vhost_user_mode_arg == kGpuVhostUserModeOff);
@@ -313,9 +312,9 @@
       return false;
     }
 
-    if (vm_manager != vm_manager::CrosvmManager::name()) {
-      LOG(INFO) << "GPU vhost user auto mode: not yet supported with "
-                << vm_manager << ". Not enabling vhost user gpu.";
+    if (vmm != VmmMode::kCrosvm) {
+      LOG(INFO) << "GPU vhost user auto mode: not yet supported with " << vmm
+                << ". Not enabling vhost user gpu.";
       return false;
     }
 
@@ -490,12 +489,12 @@
 Result<std::string> ConfigureGpuSettings(
     const std::string& gpu_mode_arg, const std::string& gpu_vhost_user_mode_arg,
     const std::string& gpu_renderer_features_arg,
-    std::string& gpu_context_types_arg, const std::string& vm_manager,
+    std::string& gpu_context_types_arg, VmmMode vmm,
     const GuestConfig& guest_config,
     CuttlefishConfig::MutableInstanceSpecific& instance) {
 #ifdef __APPLE__
   (void)gpu_vhost_user_mode_arg;
-  (void)vm_manager;
+  (void)vmm;
   (void)guest_config;
   CF_EXPECT(gpu_mode_arg == kGpuModeAuto ||
             gpu_mode_arg == kGpuModeGuestSwiftshader ||
@@ -521,10 +520,10 @@
                << graphics_availability.DebugString();
   }
 
-  const std::string gpu_mode = CF_EXPECT(SelectGpuMode(
-      gpu_mode_arg, vm_manager, guest_config, graphics_availability));
-  const bool enable_gpu_vhost_user = CF_EXPECT(
-      SelectGpuVhostUserMode(gpu_mode, gpu_vhost_user_mode_arg, vm_manager));
+  const std::string gpu_mode = CF_EXPECT(
+      SelectGpuMode(gpu_mode_arg, vmm, guest_config, graphics_availability));
+  const bool enable_gpu_vhost_user =
+      CF_EXPECT(SelectGpuVhostUserMode(gpu_mode, gpu_vhost_user_mode_arg, vmm));
 
   if (gpu_mode == kGpuModeGfxstream ||
       gpu_mode == kGpuModeGfxstreamGuestAngle ||
diff --git a/host/commands/assemble_cvd/graphics_flags.h b/host/commands/assemble_cvd/graphics_flags.h
index 63a4df1..8329724 100644
--- a/host/commands/assemble_cvd/graphics_flags.h
+++ b/host/commands/assemble_cvd/graphics_flags.h
@@ -27,7 +27,7 @@
 Result<std::string> ConfigureGpuSettings(
     const std::string& gpu_mode_arg, const std::string& gpu_vhost_user_mode_arg,
     const std::string& gpu_renderer_features_arg,
-    std::string& gpu_context_types_arg, const std::string& vm_manager,
+    std::string& gpu_context_types_arg, VmmMode vmm,
     const GuestConfig& guest_config,
     CuttlefishConfig::MutableInstanceSpecific& instance);
 
diff --git a/host/commands/metrics/events.cc b/host/commands/metrics/events.cc
index 3d3a7c1..ec0147e 100644
--- a/host/commands/metrics/events.cc
+++ b/host/commands/metrics/events.cc
@@ -94,10 +94,10 @@
   auto config = cuttlefish::CuttlefishConfig::Get();
   CHECK(config) << "Could not open cuttlefish config";
   auto vmm = config->vm_manager();
-  if (vmm == cuttlefish::vm_manager::CrosvmManager::name()) {
+  if (vmm == cuttlefish::VmmMode::kCrosvm) {
     return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_CROSVM;
   }
-  if (vmm == cuttlefish::vm_manager::QemuManager::name()) {
+  if (vmm == cuttlefish::VmmMode::kQemu) {
     return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_QEMU;
   }
   return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_UNSPECIFIED;
diff --git a/host/commands/run_cvd/launch/open_wrt.cpp b/host/commands/run_cvd/launch/open_wrt.cpp
index 0ca4a9c..5b84a3b 100644
--- a/host/commands/run_cvd/launch/open_wrt.cpp
+++ b/host/commands/run_cvd/launch/open_wrt.cpp
@@ -123,7 +123,7 @@
     auto openwrt_args = OpenwrtArgsFromConfig(instance_);
     switch (instance_.ap_boot_flow()) {
       case APBootFlow::Grub:
-        if (config_.vm_manager() == "qemu_cli") {
+        if (config_.vm_manager() == VmmMode::kQemu) {
           ap_cmd.AddReadWriteDisk(
               instance_.persistent_ap_composite_overlay_path());
         } else {
@@ -156,7 +156,7 @@
   std::string Name() const override { return "OpenWrt"; }
   bool Enabled() const override {
     return instance_.ap_boot_flow() != APBootFlow::None &&
-           config_.vm_manager() == vm_manager::CrosvmManager::name();
+           config_.vm_manager() == VmmMode::kCrosvm;
   }
 
  private:
diff --git a/host/commands/run_cvd/launch/streamer.cpp b/host/commands/run_cvd/launch/streamer.cpp
index 3334efd..b27fb7b 100644
--- a/host/commands/run_cvd/launch/streamer.cpp
+++ b/host/commands/run_cvd/launch/streamer.cpp
@@ -94,7 +94,7 @@
       : config_(config), instance_(instance) {}
 
   void AppendCommandArguments(Command& cmd) {
-    if (config_.vm_manager() == vm_manager::QemuManager::name()) {
+    if (config_.vm_manager() == VmmMode::kQemu) {
       cmd.AddParameter("-write_virtio_input");
     }
     if (!touch_servers_.empty()) {
@@ -126,7 +126,7 @@
   // SetupFeature
   std::string Name() const override { return "StreamerSockets"; }
   bool Enabled() const override {
-    bool is_qemu = config_.vm_manager() == vm_manager::QemuManager::name();
+    bool is_qemu = config_.vm_manager() == VmmMode::kQemu;
     bool is_accelerated = instance_.gpu_mode() != kGpuModeGuestSwiftshader;
     return !(is_qemu && is_accelerated);
   }
@@ -261,7 +261,7 @@
 
     webrtc.UnsetFromEnvironment("http_proxy");
     sockets_.AppendCommandArguments(webrtc);
-    if (config_.vm_manager() == vm_manager::CrosvmManager::name()) {
+    if (config_.vm_manager() == VmmMode::kCrosvm) {
       webrtc.AddParameter("-switches_fd=", switches_server_);
     }
     // Currently there is no way to ensure the signaling server will already
@@ -300,7 +300,7 @@
   }
 
   Result<void> ResultSetup() override {
-    if (config_.vm_manager() == vm_manager::CrosvmManager::name()) {
+    if (config_.vm_manager() == VmmMode::kCrosvm) {
       switches_server_ =
           CreateUnixInputServer(instance_.switches_socket_path());
       CF_EXPECT(switches_server_->IsOpen(), switches_server_->StrError());
diff --git a/host/commands/run_cvd/server_loop_impl.cpp b/host/commands/run_cvd/server_loop_impl.cpp
index 61f1fa6..0828848 100644
--- a/host/commands/run_cvd/server_loop_impl.cpp
+++ b/host/commands/run_cvd/server_loop_impl.cpp
@@ -414,7 +414,7 @@
 }
 
 Result<std::string> ServerLoopImpl::VmControlSocket() const {
-  CF_EXPECT_EQ(config_.vm_manager(), "crosvm",
+  CF_EXPECT_EQ(config_.vm_manager(), VmmMode::kCrosvm,
                "Other VMs but crosvm is not yet supported.");
   return instance_.CrosvmSocketPath();
 }
diff --git a/host/commands/run_cvd/server_loop_impl.h b/host/commands/run_cvd/server_loop_impl.h
index 1577f28..a016fbf 100644
--- a/host/commands/run_cvd/server_loop_impl.h
+++ b/host/commands/run_cvd/server_loop_impl.h
@@ -93,7 +93,7 @@
   static std::unordered_map<std::string, std::string>
   InitializeVmToControlSockPath(const CuttlefishConfig::InstanceSpecific&);
   Result<std::string> VmControlSocket() const;
-  Result<void> TakeGuestSnapshot(const std::string&, const std::string&);
+  Result<void> TakeGuestSnapshot(VmmMode, const std::string&);
   Result<void> TakeCrosvmGuestSnapshot(const Json::Value&);
 
   const CuttlefishConfig& config_;
diff --git a/host/commands/run_cvd/server_loop_impl_snapshot.cpp b/host/commands/run_cvd/server_loop_impl_snapshot.cpp
index 76682ae..9ed21a3 100644
--- a/host/commands/run_cvd/server_loop_impl_snapshot.cpp
+++ b/host/commands/run_cvd/server_loop_impl_snapshot.cpp
@@ -43,9 +43,9 @@
     const CuttlefishConfig::InstanceSpecific& instance) {
   return std::unordered_map<std::string, std::string>{
       // TODO(kwstephenkim): add the following two lines to support QEMU
-      // {QemuManager::name(),
+      // {ToString(VmmMode::kQemu),
       // instance.PerInstanceInternalUdsPath("qemu_monitor.sock")},
-      {vm_manager::CrosvmManager::name(), instance.CrosvmSocketPath()},
+      {ToString(VmmMode::kCrosvm), instance.CrosvmSocketPath()},
       {cuttlefish::kApName, instance.OpenwrtCrosvmSocketPath()},
   };
 }
@@ -105,15 +105,16 @@
     CF_EXPECT(SuspendCrosvm(openwrt_sock),
               "failed to suspend openwrt crosvm instance.");
   }
-  const auto vm_name = config_.vm_manager();
-  if (vm_name == vm_manager::CrosvmManager::name()) {
-    const auto& vm_sock = GetSocketPath(vm_name, vm_name_to_control_sock_);
+  const auto main_vmm = config_.vm_manager();
+  if (main_vmm == VmmMode::kCrosvm) {
+    const auto& vm_sock =
+        GetSocketPath(ToString(main_vmm), vm_name_to_control_sock_);
     if (vm_sock == "") {
-      return CF_ERR("The vm_manager " + vm_name + " is not supported yet");
+      return CF_ERR("The vm_manager " << main_vmm << " is not supported yet");
     }
     return SuspendCrosvm(vm_sock);
   } else {
-    return CF_ERR("The vm_manager " + vm_name + " is not supported yet");
+    return CF_ERR("The vm_manager " << main_vmm << " is not supported yet");
   }
 }
 
@@ -130,15 +131,16 @@
     CF_EXPECT(ResumeCrosvm(openwrt_sock),
               "failed to resume openwrt crosvm instance.");
   }
-  const auto vm_name = config_.vm_manager();
-  if (vm_name == vm_manager::CrosvmManager::name()) {
-    const auto& vm_sock = GetSocketPath(vm_name, vm_name_to_control_sock_);
+  const auto main_vmm = config_.vm_manager();
+  if (main_vmm == VmmMode::kCrosvm) {
+    const auto& vm_sock =
+        GetSocketPath(ToString(main_vmm), vm_name_to_control_sock_);
     if (vm_sock == "") {
-      return CF_ERR("The vm_manager " + vm_name + " is not supported yet");
+      return CF_ERR("The vm_manager " << main_vmm << " is not supported yet");
     }
     return ResumeCrosvm(vm_sock);
   } else {
-    return CF_ERR("The vm_manager " + vm_name + " is not supported yet");
+    return CF_ERR("The vm_manager " << main_vmm << " is not supported yet");
   }
 }
 
@@ -240,7 +242,7 @@
 /*
  * Parse json file at json_path, and take guest snapshot
  */
-Result<void> ServerLoopImpl::TakeGuestSnapshot(const std::string& vm_manager,
+Result<void> ServerLoopImpl::TakeGuestSnapshot(VmmMode vm_manager,
                                                const std::string& json_path) {
   // common code across vm_manager
   CF_EXPECTF(FileExists(json_path), "{} must exist but does not.", json_path);
@@ -251,7 +253,7 @@
                std::string("Failed to read from ") + json_path);
   Json::Value meta_json = CF_EXPECTF(
       ParseJson(json_contents), "Failed to parse json: \n{}", json_contents);
-  CF_EXPECTF(vm_manager == "crosvm",
+  CF_EXPECTF(vm_manager == VmmMode::kCrosvm,
              "{}, which is not crosvm, is not yet supported.", vm_manager);
   CF_EXPECT(TakeCrosvmGuestSnapshot(meta_json),
             "TakeCrosvmGuestSnapshot() failed.");
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 6de4825..091b591 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -111,11 +111,12 @@
 }
 
 static constexpr char kVmManager[] = "vm_manager";
-std::string CuttlefishConfig::vm_manager() const {
-  return (*dictionary_)[kVmManager].asString();
+VmmMode CuttlefishConfig::vm_manager() const {
+  auto str = (*dictionary_)[kVmManager].asString();
+  return ParseVmm(str).value_or(VmmMode::kUnknown);
 }
-void CuttlefishConfig::set_vm_manager(const std::string& name) {
-  (*dictionary_)[kVmManager] = name;
+void CuttlefishConfig::set_vm_manager(VmmMode vmm) {
+  (*dictionary_)[kVmManager] = fmt::format("{}", vmm);
 }
 
 static constexpr char kApVmManager[] = "ap_vm_manager";
@@ -173,7 +174,9 @@
   (*dictionary_)[kCrosvmBinary] = crosvm_binary;
 }
 
-bool CuttlefishConfig::IsCrosvm() const { return vm_manager() == "crosvm"; }
+bool CuttlefishConfig::IsCrosvm() const {
+  return vm_manager() == VmmMode::kCrosvm;
+}
 
 static constexpr char kGem5DebugFlags[] = "gem5_debug_flags";
 std::string CuttlefishConfig::gem5_debug_flags() const {
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 7fec650..ecc93ea 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -52,6 +52,17 @@
   HostOemlockSecure,
 };
 
+enum class VmmMode {
+  kUnknown,
+  kCrosvm,
+  kGem5,
+  kQemu,
+};
+
+std::ostream& operator<<(std::ostream&, VmmMode);
+std::string ToString(VmmMode mode);
+Result<VmmMode> ParseVmm(std::string_view);
+
 enum class ExternalNetworkMode {
   kUnknown,
   kTap,
@@ -100,8 +111,8 @@
   std::string environments_uds_dir() const;
   std::string EnvironmentsUdsPath(const std::string&) const;
 
-  std::string vm_manager() const;
-  void set_vm_manager(const std::string& name);
+  VmmMode vm_manager() const;
+  void set_vm_manager(VmmMode vmm);
 
   std::string ap_vm_manager() const;
   void set_ap_vm_manager(const std::string& name);
@@ -1010,4 +1021,6 @@
 #if FMT_VERSION >= 90000
 template <>
 struct fmt::formatter<cuttlefish::ExternalNetworkMode> : ostream_formatter {};
+template <>
+struct fmt::formatter<cuttlefish::VmmMode> : ostream_formatter {};
 #endif
diff --git a/host/libs/config/cuttlefish_config_instance.cpp b/host/libs/config/cuttlefish_config_instance.cpp
index ace76e8..24b2464 100644
--- a/host/libs/config/cuttlefish_config_instance.cpp
+++ b/host/libs/config/cuttlefish_config_instance.cpp
@@ -62,6 +62,37 @@
   }
 }
 
+std::string ToString(VmmMode mode) {
+  std::stringstream ss;
+  ss << mode;
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, VmmMode vmm) {
+  switch (vmm) {
+    case VmmMode::kUnknown:
+      return out << "unknown";
+    case VmmMode::kCrosvm:
+      return out << "crosvm";
+    case VmmMode::kGem5:
+      return out << "gem5";
+    case VmmMode::kQemu:
+      return out << "qemu_cli";
+  }
+}
+
+Result<VmmMode> ParseVmm(std::string_view str) {
+  if (android::base::EqualsIgnoreCase(str, "crosvm")) {
+    return VmmMode::kCrosvm;
+  } else if (android::base::EqualsIgnoreCase(str, "gem5")) {
+    return VmmMode::kGem5;
+  } else if (android::base::EqualsIgnoreCase(str, "qemu_cli")) {
+    return VmmMode::kQemu;
+  } else {
+    return CF_ERRF("\"{}\" is not a valid Vmm.", str);
+  }
+}
+
 static constexpr char kInstanceDir[] = "instance_dir";
 CuttlefishConfig::MutableInstanceSpecific::MutableInstanceSpecific(
     CuttlefishConfig* config, const std::string& id)
@@ -1199,8 +1230,7 @@
 std::string CuttlefishConfig::InstanceSpecific::console_dev() const {
   auto can_use_virtio_console = !kgdb() && !use_bootloader();
   std::string console_dev;
-  if (can_use_virtio_console ||
-      config_->vm_manager() == vm_manager::Gem5Manager::name()) {
+  if (can_use_virtio_console || config_->vm_manager() == VmmMode::kGem5) {
     // If kgdb and the bootloader are disabled, the Android serial console
     // spawns on a virtio-console port. If the bootloader is enabled, virtio
     // console can't be used since uboot doesn't support it.
@@ -1210,7 +1240,7 @@
     // architectures emulate ns16550a/uart8250 instead.
     Arch target = target_arch();
     if ((target == Arch::Arm64 || target == Arch::Arm) &&
-        config_->vm_manager() != vm_manager::CrosvmManager::name()) {
+        config_->vm_manager() != VmmMode::kCrosvm) {
       console_dev = "ttyAMA0";
     } else {
       console_dev = "ttyS0";
diff --git a/host/libs/config/data_image.cpp b/host/libs/config/data_image.cpp
index 0849fd0..915728e 100644
--- a/host/libs/config/data_image.cpp
+++ b/host/libs/config/data_image.cpp
@@ -271,7 +271,7 @@
       LOG(DEBUG) << "creating esp_image: " << instance_.ap_esp_image_path();
       CF_EXPECT(BuildAPImage());
     }
-    const auto is_not_gem5 = config_.vm_manager() != vm_manager::Gem5Manager::name();
+    const auto is_not_gem5 = config_.vm_manager() != VmmMode::kGem5;
     const auto esp_required_for_boot_flow = EspRequiredForBootFlow();
     if (is_not_gem5 && esp_required_for_boot_flow) {
       LOG(DEBUG) << "creating esp_image: " << instance_.esp_image_path();
diff --git a/host/libs/config/kernel_args.cpp b/host/libs/config/kernel_args.cpp
index 91e0bbd..f5bdf7d 100644
--- a/host/libs/config/kernel_args.cpp
+++ b/host/libs/config/kernel_args.cpp
@@ -42,7 +42,7 @@
     const CuttlefishConfig& config,
     const CuttlefishConfig::InstanceSpecific& instance) {
   std::vector<std::string> vm_manager_cmdline;
-  if (config.vm_manager() == QemuManager::name()) {
+  if (config.vm_manager() == VmmMode::kQemu) {
     Arch target_arch = instance.target_arch();
     if (target_arch == Arch::Arm64 || target_arch == Arch::Arm) {
       if (instance.enable_kernel_log()) {
diff --git a/host/libs/vm_manager/Android.bp b/host/libs/vm_manager/Android.bp
index 7cd0329..1bc3ee0 100644
--- a/host/libs/vm_manager/Android.bp
+++ b/host/libs/vm_manager/Android.bp
@@ -24,6 +24,7 @@
         "crosvm_manager.cpp",
         "gem5_manager.cpp",
         "host_configuration.cpp",
+        "pci.cpp",
         "qemu_manager.cpp",
         "vm_manager.cpp",
     ],
diff --git a/host/libs/vm_manager/crosvm_builder.cpp b/host/libs/vm_manager/crosvm_builder.cpp
index cb863db..4f51e33 100644
--- a/host/libs/vm_manager/crosvm_builder.cpp
+++ b/host/libs/vm_manager/crosvm_builder.cpp
@@ -28,6 +28,17 @@
 #include "host/libs/config/known_paths.h"
 
 namespace cuttlefish {
+namespace {
+
+std::string MacCrosvmArgument(std::optional<std::string_view> mac) {
+  return mac.has_value() ? fmt::format(",mac={}", mac.value()) : "";
+}
+
+std::string PciCrosvmArgument(std::optional<pci::Address> pci) {
+  return pci.has_value() ? fmt::format(",pci-address={}", pci.value().Id()) : "";
+}
+
+}
 
 CrosvmBuilder::CrosvmBuilder() : command_("crosvm") {}
 
@@ -104,10 +115,12 @@
 }
 
 #ifdef __linux__
-SharedFD CrosvmBuilder::AddTap(const std::string& tap_name) {
+SharedFD CrosvmBuilder::AddTap(const std::string& tap_name,
+                               std::optional<std::string_view> mac,
+                               const std::optional<pci::Address>& pci) {
   auto tap_fd = OpenTapInterface(tap_name);
   if (tap_fd->IsOpen()) {
-    command_.AddParameter("--net=tap-fd=", tap_fd);
+    command_.AddParameter("--net=tap-fd=", tap_fd, MacCrosvmArgument(mac), PciCrosvmArgument(pci));
   } else {
     LOG(ERROR) << "Unable to connect to \"" << tap_name
                << "\": " << tap_fd->StrError();
@@ -115,16 +128,6 @@
   return tap_fd;
 }
 
-SharedFD CrosvmBuilder::AddTap(const std::string& tap_name, const std::string& mac) {
-  auto tap_fd = OpenTapInterface(tap_name);
-  if (tap_fd->IsOpen()) {
-    command_.AddParameter("--net=tap-fd=", tap_fd, ",mac=\"", mac, "\"");
-  } else {
-    LOG(ERROR) << "Unable to connect to \"" << tap_name
-               << "\": " << tap_fd->StrError();
-  }
-  return tap_fd;
-}
 #endif
 
 int CrosvmBuilder::HvcNum() { return hvc_num_; }
diff --git a/host/libs/vm_manager/crosvm_builder.h b/host/libs/vm_manager/crosvm_builder.h
index e2f722e..123d01a 100644
--- a/host/libs/vm_manager/crosvm_builder.h
+++ b/host/libs/vm_manager/crosvm_builder.h
@@ -15,12 +15,14 @@
 
 #pragma once
 
+#include <optional>
 #include <string>
 #include <utility>
 
 #include "common/libs/fs/shared_fd.h"
 #include "common/libs/utils/result.h"
 #include "common/libs/utils/subprocess.h"
+#include "host/libs/vm_manager/pci.h"
 
 namespace cuttlefish {
 
@@ -48,8 +50,9 @@
   void AddSerial(const std::string& output, const std::string& input);
 
 #ifdef __linux__
-  SharedFD AddTap(const std::string& tap_name);
-  SharedFD AddTap(const std::string& tap_name, const std::string& mac);
+  SharedFD AddTap(const std::string& tap_name,
+                  std::optional<std::string_view> mac = std::nullopt,
+                  const std::optional<pci::Address>& pci = std::nullopt);
 #endif
 
   int HvcNum();
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index 899e396..529a660 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -582,10 +582,16 @@
   // having to pass arguments to crosvm via a wrapper script.
 #ifdef __linux__
   if (!gpu_capture_enabled) {
-    // The ordering of tap devices is important. Make sure any change here
-    // is reflected in ethprime u-boot variable
-    crosvm_cmd.AddTap(instance.mobile_tap_name(), instance.mobile_mac());
-    crosvm_cmd.AddTap(instance.ethernet_tap_name(), instance.ethernet_mac());
+    // The PCI ordering of tap devices is important. Make sure any change here
+    // is reflected in ethprime u-boot variable.
+    // TODO(b/218364216, b/322862402): Crosvm occupies 32 PCI devices first and only then uses PCI
+    // functions which may break order. The final solution is going to be a PCI allocation strategy
+    // that will guarantee the ordering. For now, hardcode PCI network devices to unoccupied
+    // functions.
+    const pci::Address mobile_pci = pci::Address(0, VmManager::kNetPciDeviceNum, 1);
+    const pci::Address ethernet_pci = pci::Address(0, VmManager::kNetPciDeviceNum, 2);
+    crosvm_cmd.AddTap(instance.mobile_tap_name(), instance.mobile_mac(), mobile_pci);
+    crosvm_cmd.AddTap(instance.ethernet_tap_name(), instance.ethernet_mac(), ethernet_pci);
 
     if (!config.virtio_mac80211_hwsim() && environment.enable_wifi()) {
       wifi_tap = crosvm_cmd.AddTap(instance.wifi_tap_name());
diff --git a/host/libs/vm_manager/pci.cpp b/host/libs/vm_manager/pci.cpp
new file mode 100644
index 0000000..9b57fcf
--- /dev/null
+++ b/host/libs/vm_manager/pci.cpp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2024 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/pci.h"
+
+#include <android-base/logging.h>
+
+namespace cuttlefish {
+namespace pci {
+namespace {
+constexpr unsigned int kMaxBus = 255;
+constexpr unsigned int kMaxDevice = 31;
+constexpr unsigned int kMaxFunction = 7;
+}  // namespace
+
+Address::Address(unsigned int bus, unsigned int device, unsigned int function)
+    : bus_(bus), device_(device), function_(function) {
+  if (bus_ > kMaxBus || device_ > kMaxDevice || function_ > kMaxFunction) {
+    LOG(FATAL) << "Failed to create PCI address instance with bus: " << bus_
+               << " device: " << device_ << " function: " << function_;
+  }
+}
+
+}  // namespace pci
+}  // namespace cuttlefish
\ No newline at end of file
diff --git a/host/libs/vm_manager/pci.h b/host/libs/vm_manager/pci.h
new file mode 100644
index 0000000..47ca076
--- /dev/null
+++ b/host/libs/vm_manager/pci.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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 <fmt/core.h>
+
+namespace cuttlefish {
+namespace pci {
+
+class Address {
+ public:
+  Address(unsigned int bus, unsigned int device, unsigned int function);
+
+  unsigned int Bus() const { return bus_; };
+  unsigned int Device() const { return device_; }
+  unsigned int Function() const { return function_; }
+  std::string Id() const {
+    return fmt::format("{:02x}:{:02x}.{:01x}", bus_, device_, function_);
+  }
+
+ private:
+  unsigned int bus_;
+  unsigned int device_;
+  unsigned int function_;
+};
+
+}  // namespace pci
+}  // namespace cuttlefish
diff --git a/host/libs/vm_manager/vm_manager.cpp b/host/libs/vm_manager/vm_manager.cpp
index c916b13..7860d3c 100644
--- a/host/libs/vm_manager/vm_manager.cpp
+++ b/host/libs/vm_manager/vm_manager.cpp
@@ -37,21 +37,22 @@
 namespace cuttlefish {
 namespace vm_manager {
 
-std::unique_ptr<VmManager> GetVmManager(const std::string& name, Arch arch) {
+std::unique_ptr<VmManager> GetVmManager(VmmMode vmm_mode, Arch arch) {
   std::unique_ptr<VmManager> vmm;
-  if (name == QemuManager::name()) {
+  if (vmm_mode == VmmMode::kQemu) {
     vmm.reset(new QemuManager(arch));
-  } else if (name == Gem5Manager::name()) {
+  } else if (vmm_mode == VmmMode::kGem5) {
     vmm.reset(new Gem5Manager(arch));
-  } else if (name == CrosvmManager::name()) {
+  } else if (vmm_mode == VmmMode::kCrosvm) {
     vmm.reset(new CrosvmManager());
   }
   if (!vmm) {
-    LOG(ERROR) << "Invalid VM manager: " << name;
+    LOG(ERROR) << "Invalid VM manager: " << vmm_mode;
     return {};
   }
   if (!vmm->IsSupported()) {
-    LOG(ERROR) << "VM manager " << name << " is not supported on this machine.";
+    LOG(ERROR) << "VM manager " << vmm_mode
+               << " is not supported on this machine.";
     return {};
   }
   return vmm;
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
index 29fb68a..9b70477 100644
--- a/host/libs/vm_manager/vm_manager.h
+++ b/host/libs/vm_manager/vm_manager.h
@@ -80,6 +80,8 @@
   // the persistent disk
   static const int kDefaultNumBootDevices = 2;
 
+  static constexpr const int kNetPciDeviceNum = 1;
+
   // LINT.IfChange(virtio_gpu_pci_address)
   static constexpr const int kGpuPciSlotNum = 2;
   // LINT.ThenChange(../../../shared/sepolicy/vendor/genfs_contexts:virtio_gpu_pci_address)
@@ -116,7 +118,7 @@
                  VmManager>
 VmManagerComponent();
 
-std::unique_ptr<VmManager> GetVmManager(const std::string&, Arch arch);
+std::unique_ptr<VmManager> GetVmManager(VmmMode vmm, Arch arch);
 
 Result<std::unordered_map<std::string, std::string>>
 ConfigureMultipleBootDevices(const std::string& pci_path, int pci_offset,
diff --git a/shared/auto/device_vendor.mk b/shared/auto/device_vendor.mk
index 07c2f05..8b4cdec 100644
--- a/shared/auto/device_vendor.mk
+++ b/shared/auto/device_vendor.mk
@@ -24,6 +24,7 @@
 $(call inherit-product, device/google/cuttlefish/shared/gnss/device_vendor.mk)
 $(call inherit-product, device/google/cuttlefish/shared/graphics/device_vendor.mk)
 $(call inherit-product, device/google/cuttlefish/shared/secure_element/device_vendor.mk)
+$(call inherit-product, device/google/cuttlefish/shared/swiftshader/device_vendor.mk)
 $(call inherit-product, device/google/cuttlefish/shared/telephony/device_vendor.mk)
 $(call inherit-product, device/google/cuttlefish/shared/sensors/device_vendor.mk)
 $(call inherit-product, device/google/cuttlefish/shared/device.mk)
diff --git a/shared/config/config_auto.json b/shared/config/config_auto.json
index 22282d7..67dd3bc 100644
--- a/shared/config/config_auto.json
+++ b/shared/config/config_auto.json
@@ -1,6 +1,5 @@
 {
 	"display0": "width=1080,height=600,dpi=120",
 	"display1": "width=400,height=600,dpi=120",
-	"gpu_mode": "gfxstream_guest_angle_host_swiftshader",
 	"memory_mb" : 4096
 }
diff --git a/shared/config/config_auto_md.json b/shared/config/config_auto_md.json
index af23661..7eb22c3 100644
--- a/shared/config/config_auto_md.json
+++ b/shared/config/config_auto_md.json
@@ -3,6 +3,5 @@
 	"display1": "width=400,height=600,dpi=120",
 	"display2": "width=800,height=600,dpi=120",
 	"display3": "width=800,height=600,dpi=120",
-	"gpu_mode": "gfxstream_guest_angle_host_swiftshader",
 	"memory_mb" : 4096
 }
diff --git a/shared/config/config_auto_portrait.json b/shared/config/config_auto_portrait.json
index 96a19cb..565f3dc 100644
--- a/shared/config/config_auto_portrait.json
+++ b/shared/config/config_auto_portrait.json
@@ -1,5 +1,4 @@
 {
 	"display0": "width=1224,height=2175,dpi=140",
-	"gpu_mode": "gfxstream_guest_angle_host_swiftshader",
 	"memory_mb" : 4096
 }
diff --git a/system_image/Android.bp b/system_image/Android.bp
index c4282f3..c8347dd 100644
--- a/system_image/Android.bp
+++ b/system_image/Android.bp
@@ -62,430 +62,15 @@
     },
 ]
 
-android_system_image {
-    name: "aosp_cf_system_x86_64",
-    use_avb: true,
-    avb_private_key: ":microdroid_sign_key",
-    avb_algorithm: "SHA256_RSA4096",
-    avb_hash_algorithm: "sha256",
-    partition_name: "system",
-    deps: [
-        "abb",
-        "abx",
-        "abx2xml",
-        "aconfigd",
-        "adb_debug.prop",
-        "adevice_fingerprint",
-        "aflags",
-        "aidl_lazy_cb_test_server",
-        "aidl_lazy_test_server",
-        "am",
-        "android_build_prop",
-        "android_vintf_manifest",
-        "android.hardware.audio.common-util",
-        "android.hardware.audio.common-V3-ndk",
-        "android.hardware.audio.common@5.0-util",
-        "android.hardware.audio.common@5.0",
-        "android.hardware.audio.common@6.0-util",
-        "android.hardware.audio.common@6.0",
-        "android.hardware.audio.core-V2-ndk",
-        "android.hardware.audio.core.sounddose-V2-ndk",
-        "android.hardware.audio.effect-V2-ndk",
-        "android.hardware.audio.effect.service-aidl.example",
-        "android.hardware.audio.effect@5.0-util",
-        "android.hardware.audio.effect@5.0",
-        "android.hardware.audio.effect@6.0-util",
-        "android.hardware.audio.effect@6.0",
-        "android.hardware.audio.effect@7.0-impl",
-        "android.hardware.audio.service-aidl.example",
-        "android.hardware.audio@5.0-util",
-        "android.hardware.audio@5.0",
-        "android.hardware.audio@6.0-util",
-        "android.hardware.audio@6.0",
-        "android.hardware.biometrics.fingerprint@2.1",
-        "android.hardware.bluetooth.audio-impl",
-        "android.hardware.boot-service.default_recovery",
-        "android.hardware.common.fmq-V1-ndk",
-        "android.hardware.fastboot@1.1-impl-mock",
-        "android.hardware.graphics.allocator-service.minigbm",
-        "android.hardware.graphics.mapper@4.0-impl.minigbm",
-        "android.hardware.health-service.cuttlefish_recovery",
-        "android.hardware.health-V2-ndk",
-        "android.hardware.health-V3-ndk",
-        "android.hardware.power.stats-V1-cpp",
-        "android.hardware.radio.config@1.0",
-        "android.hardware.radio.deprecated@1.0",
-        "android.hardware.radio@1.0",
-        "android.hardware.radio@1.1",
-        "android.hardware.radio@1.2",
-        "android.hardware.radio@1.3",
-        "android.hardware.radio@1.4",
-        "android.hardware.secure_element@1.0",
-        "android.hardware.security.secretkeeper-service.nonsecure",
-        "android.hidl.allocator@1.0-service",
-        "android.hidl.memory.token@1.0",
-        "android.hidl.memory@1.0",
-        "android.hidl.safe_union@1.0",
-        "android.media.audio.common.types-V3-ndk",
-        "android.system.suspend-service",
+phony {
+    name: "fonts",
+    required: [
         "AndroidClock.ttf",
-        "apexd",
-        "app_process",
-        "appops",
-        "appwidget",
-        "arping",
-        "atrace",
-        "audio.primary.default",
-        "audioserver",
-        "auditctl",
-        "av-audio-types-aidl-V1-ndk",
-        "avbctl",
-        "awk",
-        "bc",
-        "bcc",
-        "blank_screen",
-        "blkid",
-        "bmgr",
-        "bootanimation",
-        "bootctl",
-        "bootstat",
-        "boringssl_self_test",
-        "bpfloader",
-        "bu",
-        "bugreport_procdump",
-        "bugreport",
-        "bugreportz",
-        "bzip2",
-        "cacerts",
-        "cameraserver",
         "CarroisGothicSC-Regular.ttf",
-        "cgroups.json",
-        "cgroups.recovery.json",
-        "checkpoint_gc",
-        "cmd",
         "ComingSoon.ttf",
-        "content",
-        "cppreopts.sh",
-        "credstore",
         "CutiveMono.ttf",
-        "cuttlefish_sensor_injection",
         "DancingScript-Regular.ttf",
-        "debuggerd",
-        "device_config",
-        "dirty-image-objects",
-        "dlkm_loader",
-        "dmabuf_dump",
-        "dmctl",
-        "dmesgd",
-        "dmuserd",
-        "dnsmasq",
-        "dpm",
         "DroidSansMono.ttf",
-        "dump.erofs",
-        "dumpstate",
-        "dumpsys_vendor",
-        "dumpsys",
-        "e2freefrag",
-        "e2fsck",
-        "e2fsdroid",
-        "etc_hosts",
-        "evemu-record",
-        "extra_free_kbytes",
-        "fastbootd",
-        "flags_health_check",
-        "font_fallback.xml",
-        "fonts.xml",
-        "framework-audio_effects.xml",
-        "framework-sysconfig.xml",
-        "fsck_msdos",
-        "fsck.erofs",
-        "fsck.exfat",
-        "fsck.f2fs",
-        "fstab.cf.ext4.cts",
-        "fstab.cf.ext4.hctr2",
-        "fstab.cf.f2fs.cts",
-        "fstab.cf.f2fs.hctr2",
-        "fstab.postinstall",
-        "fsverity_init",
-        "fsverity-release-cert-der",
-        "gatekeeperd",
-        "gpu_counter_producer",
-        "gpuservice",
-        "group_odm",
-        "group_product",
-        "group_system_ext",
-        "group_system",
-        "group_vendor",
-        "gsi_tool",
-        "gsid",
-        "heapprofd_client",
-        "heapprofd",
-        "hid",
-        "hiddenapi-package-whitelist.xml",
-        "hidl_lazy_cb_test_server",
-        "hidl_lazy_test_server",
-        "hwservicemanager",
-        "idlcli",
-        "idmap2",
-        "idmap2d",
-        "ime",
-        "incident_helper",
-        "incident-helper-cmd",
-        "incident",
-        "incidentd",
-        "init-debug.rc",
-        "init_first_stage",
-        "init.boringssl.zygote64_32.rc",
-        "init.boringssl.zygote64.rc",
-        "init.rc",
-        "init.usb.configfs.rc",
-        "init.usb.rc",
-        "init.zygote32.rc",
-        "init.zygote64_32.rc",
-        "init.zygote64.rc",
-        "initial-package-stopped-states-aosp.xml",
-        "initial-package-stopped-states.xml",
-        "input",
-        "installd",
-        "iotop",
-        "ip",
-        "iperf3",
-        "iptables",
-        "iw",
-        "kcmdlinectrl",
-        "keystore_cli_v2",
-        "keystore2",
-        "layertracegenerator",
-        "ld.config.recovery.txt",
-        "ld.mc",
-        "ldd",
-        "lib_renderControl_enc",
-        "libaaudio",
-        "libadbd_auth",
-        "libadbd_fs",
-        "libalarm_jni",
-        "libamidi",
-        "libandroid_runtime",
-        "libandroid_servers",
-        "libandroid",
-        "libandroidemu",
-        "libandroidfw",
-        "libartpalette-system",
-        "libasyncio",
-        "libasyncio",
-        "libaudio_aidl_conversion_common_ndk_cpp",
-        "libaudio_aidl_conversion_common_ndk",
-        "libaudio_aidl_conversion_common_ndk",
-        "libaudio_aidl_conversion_core_ndk",
-        "libaudio_aidl_conversion_effect_ndk",
-        "libaudio-resampler",
-        "libaudioaidlcommon",
-        "libaudioaidlcommon",
-        "libaudioeffect_jni",
-        "libaudiohal_deathhandler",
-        "libaudiohal",
-        "libaudiohal@5.0",
-        "libaudiohal@6.0",
-        "libaudiohal@7.0",
-        "libaudiohal@7.1",
-        "libaudiohal@aidl",
-        "libaudiopolicyengineconfigurable",
-        "libaudiopreprocessing",
-        "libaudioutils",
-        "libaudioutils",
-        "libbinder_ndk",
-        "libbinder_rpc_unstable",
-        "libbinder",
-        "libblas",
-        "libbootloader_message",
-        "libbundlewrapper",
-        "libcamera2ndk",
-        "libclang_rt.asan",
-        "libclcore_debug_g.bc",
-        "libclcore_debug.bc",
-        "libclcore_g.bc",
-        "libclcore.bc",
-        "libclearkeycasplugin",
-        "libcompiler_rt",
-        "libcrypto_utils",
-        "libcups",
-        "libcutils",
-        "libdmabufheap",
-        "libdmabufheap",
-        "libdownmix",
-        "libdrm",
-        "libdrm",
-        "libdrm",
-        "libdrmclearkeyplugin",
-        "libdrmframework_jni",
-        "libdrmframework",
-        "libdynproc",
-        "libeffectproxy",
-        "libeffects",
-        "libeffectsconfig",
-        "libeffectsconfig",
-        "libEGL_angle",
-        "libEGL_emulation",
-        "libEGL",
-        "libepoxy",
-        "libETC1",
-        "libext4_utils",
-        "libfdtrack",
-        "libfec",
-        "libFFTEm",
-        "libfilterfw",
-        "libfilterpack_imageproc",
-        "libfmq",
-        "libfs_mgr",
-        "libfwdlockengine",
-        "libgatekeeper",
-        "libgbm",
-        "libGLESv1_CM_angle",
-        "libGLESv1_CM_emulation",
-        "libGLESv1_CM",
-        "libGLESv1_enc",
-        "libGLESv2_angle",
-        "libGLESv2_emulation",
-        "libGLESv2_enc",
-        "libGLESv2",
-        "libGLESv3",
-        "libgralloctypes",
-        "libgsi",
-        "libgui",
-        "libhapticgenerator",
-        "libhardware_legacy",
-        "libhardware",
-        "libhidcommand_jni",
-        "libhidlmemory",
-        "libhidlmemory",
-        "libhidltransport",
-        "libhwbinder",
-        "libincident",
-        "libinput",
-        "libinputflinger",
-        "libiprouteutil",
-        "libjni_deviceAsWebcam",
-        "libjnigraphics",
-        "libjpeg",
-        "libldnhncr",
-        "liblockagent",
-        "liblog",
-        "liblogwrap",
-        "liblp",
-        "liblz4",
-        "libmedia_helper",
-        "libmedia_helper",
-        "libmedia_jni",
-        "libmedia",
-        "libmediandk",
-        "libmediaplayerservice",
-        "libmediautils_delayed",
-        "libminui",
-        "libmtp",
-        "libnativewindow",
-        "libnetd_client",
-        "libnetlink",
-        "libnetutils",
-        "libneuralnetworks_packageinfo",
-        "libnfc_nci_jni",
-        "libnl",
-        "libOpenglCodecCommon",
-        "libOpenglSystemCommon",
-        "libOpenMAXAL",
-        "libOpenSLES",
-        "libpdfium",
-        "libperfetto_android_internal",
-        "libpolicy-subsystem",
-        "libpower",
-        "libpowermanager",
-        "libprintspooler_jni",
-        "libprocessgroup_setup",
-        "libprotobuf-cpp-full",
-        "libradio_metadata",
-        "librank",
-        "libresourcemanagerservice",
-        "libreverbwrapper",
-        "libRS_internal",
-        "librs_jni",
-        "libRSCacheDir",
-        "libRSCpuRef",
-        "libRSDriver",
-        "librtp_jni",
-        "libsensorservice",
-        "libsfplugin_ccodec",
-        "libskia",
-        "libsonic",
-        "libsonivox",
-        "libsoundpool",
-        "libspeexresampler",
-        "libspeexresampler",
-        "libspeexresampler",
-        "libsqlite",
-        "libsquashfs_utils",
-        "libssl",
-        "libstagefright_foundation",
-        "libstagefright_foundation",
-        "libstagefright_foundation",
-        "libstagefright_httplive",
-        "libstagefright_omx",
-        "libstagefright",
-        "libstdc++",
-        "libsync",
-        "libsysutils",
-        "libtinyxml2",
-        "libtombstoned_client",
-        "libtracingproxy",
-        "libui",
-        "libuinputcommand_jni",
-        "libukey2_jni_shared",
-        "libusbhost",
-        "libutils",
-        "libvendorsupport",
-        "libvintf_jni",
-        "libvirglrenderer",
-        "libvisualizer",
-        "libvulkan",
-        "libwebviewchromium_loader",
-        "libwebviewchromium_plat_support",
-        "libwfds",
-        "libwilhelm",
-        "libxml2",
-        "libxml2",
-        "linker",
-        "llkd",
-        "lmkd",
-        "local_time.default",
-        "lockagent_crasher",
-        "locksettings",
-        "logcat",
-        "logcatd",
-        "logd",
-        "logpersist.start",
-        "logtagd.rc",
-        "logwrapper",
-        "lpdump",
-        "lpdumpd",
-        "lshal",
-        "make_f2fs",
-        "mapper.minigbm",
-        "mdnsd",
-        "media_profiles_V1_0.dtd",
-        "mediacodec.policy",
-        "mediaextractor",
-        "mediametrics",
-        "migrate_legacy_obb_data",
-        "mini-keyctl",
-        "misctrl",
-        "mke2fs",
-        "mkfs.erofs",
-        "mkfs.exfat",
-        "mm_events",
-        "monkey",
-        "mtectrl",
-        "ndc",
-        "netd",
-        "netutils-wrapper-1.0",
-        "newfs_msdos",
         "NotoColorEmoji.ttf",
         "NotoColorEmojiFlags.ttf",
         "NotoColorEmojiLegacy.ttf",
@@ -508,13 +93,13 @@
         "NotoSansBrahmi-Regular.ttf",
         "NotoSansBuginese-Regular.ttf",
         "NotoSansBuhid-Regular.ttf",
+        "NotoSansCJK-Regular.ttc",
         "NotoSansCanadianAboriginal-Regular.ttf",
         "NotoSansCarian-Regular.ttf",
         "NotoSansChakma-Regular.otf",
         "NotoSansCham-Bold.ttf",
         "NotoSansCham-Regular.ttf",
         "NotoSansCherokee-Regular.ttf",
-        "NotoSansCJK-Regular.ttc",
         "NotoSansCoptic-Regular.ttf",
         "NotoSansCuneiform-Regular.ttf",
         "NotoSansCypriot-Regular.ttf",
@@ -584,10 +169,10 @@
         "NotoSansMyanmarUI-Bold.otf",
         "NotoSansMyanmarUI-Medium.otf",
         "NotoSansMyanmarUI-Regular.otf",
-        "NotoSansNabataean-Regular.otf",
-        "NotoSansNewa-Regular.otf",
-        "NotoSansNewTaiLue-Regular.ttf",
         "NotoSansNKo-Regular.ttf",
+        "NotoSansNabataean-Regular.otf",
+        "NotoSansNewTaiLue-Regular.ttf",
+        "NotoSansNewa-Regular.otf",
         "NotoSansOgham-Regular.ttf",
         "NotoSansOlChiki-Regular.ttf",
         "NotoSansOldItalic-Regular.ttf",
@@ -677,18 +262,336 @@
         "NotoSerifThai-Regular.ttf",
         "NotoSerifTibetan-VF.ttf",
         "NotoSerifYezidi-VF.ttf",
+        "Roboto-Regular.ttf",
+        "RobotoFlex-Regular.ttf",
+        "RobotoStatic-Regular.ttf",
+        "SourceSansPro-Bold.ttf",
+        "SourceSansPro-BoldItalic.ttf",
+        "SourceSansPro-Italic.ttf",
+        "SourceSansPro-Regular.ttf",
+        "SourceSansPro-SemiBold.ttf",
+        "SourceSansPro-SemiBoldItalic.ttf",
+        "font_fallback.xml",
+        "fonts.xml",
+    ],
+}
+
+android_system_image {
+    name: "aosp_cf_system_x86_64",
+
+    partition_name: "system",
+    base_dir: "system",
+    dirs: android_rootdirs,
+    symlinks: android_symlinks,
+    file_contexts: ":plat_file_contexts",
+    linker_config_src: "linker.config.json",
+    fsverity: {
+        inputs: [
+            "etc/boot-image.prof",
+            "etc/dirty-image-objects",
+            "etc/preloaded-classes",
+            "etc/classpaths/*.pb",
+            "framework/*",
+            "framework/*/*", // framework/{arch}
+            "framework/oat/*/*", // framework/oat/{arch}
+        ],
+        libs: [":framework-res{.export-package.apk}"],
+    },
+    build_logtags: true,
+
+    use_avb: true,
+    avb_private_key: ":microdroid_sign_key",
+    avb_algorithm: "SHA256_RSA4096",
+    avb_hash_algorithm: "sha256",
+
+    deps: [
+        "abx",
+        "aconfigd",
+        "aflags",
+        "am",
+        "android_build_prop",
+        "android_vintf_manifest",
+        "android.system.suspend-service",
+        "apexd",
+        "app_process",
+        "appops",
+        "appwidget",
+        "atrace",
+        "audioserver",
+        "bcc",
+        "blank_screen",
+        "blkid",
+        "bmgr",
+        "bootanimation",
+        "bootstat",
+        "boringssl_self_test",
+        "bpfloader",
+        "bu",
+        "bugreport",
+        "bugreportz",
+        "cameraserver",
+        "cgroups.json",
+        "cmd",
+        "content",
+        "cppreopts.sh",
+        "credstore",
+        "debuggerd",
+        "device_config",
+        "dirty-image-objects",
+        "dlkm_loader",
+        "dmabuf_dump",
+        "dmctl",
+        "dmesgd",
+        "dnsmasq",
+        "dpm",
+        "dump.erofs",
+        "dumpstate",
+        "dumpsys",
+        "e2freefrag",
+        "e2fsck",
+        "e2fsdroid",
+        "etc_hosts",
+        "extra_free_kbytes",
+        "fastbootd",
+        "flags_health_check",
+        "framework-audio_effects.xml",
+        "framework-sysconfig.xml",
+        "fsck_msdos",
+        "fsck.erofs",
+        "fsck.f2fs",
+        "fstab.postinstall",
+        "fsverity_init",
+        "fsverity-release-cert-der",
+        "gatekeeperd",
+        "gpu_counter_producer",
+        "gpuservice",
+        "group_system",
+        "gsi_tool",
+        "gsid",
+        "heapprofd_client",
+        "heapprofd",
+        "hid",
+        "hiddenapi-package-whitelist.xml",
+        "hidl_lazy_cb_test_server",
+        "hidl_lazy_test_server",
+        "idc_data",
+        "idmap2",
+        "idmap2d",
+        "ime",
+        "incident_helper",
+        "incident-helper-cmd",
+        "incident",
+        "incidentd",
+        "init_first_stage",
+        "init.boringssl.zygote64_32.rc",
+        "init.boringssl.zygote64.rc",
+        "init.rc",
+        "init.usb.configfs.rc",
+        "init.usb.rc",
+        "init.zygote32.rc",
+        "init.zygote64_32.rc",
+        "init.zygote64.rc",
+        "initial-package-stopped-states-aosp.xml",
+        "initial-package-stopped-states.xml",
+        "input",
+        "installd",
+        "ip",
+        "iptables",
+        "kcmdlinectrl",
+        "keychars_data",
+        "keylayout_data",
+        "keystore_cli_v2",
+        "keystore2",
+        "ld.mc",
+        "libaaudio",
+        "libalarm_jni",
+        "libamidi",
+        "libandroid_runtime",
+        "libandroid_servers",
+        "libandroid",
+        "libandroidfw",
+        "libartpalette-system",
+        "libasyncio",
+        "libaudio-resampler",
+        "libaudioeffect_jni",
+        "libaudiohal_deathhandler",
+        "libaudiohal",
+        "libaudiopolicyengineconfigurable",
+        "libaudiopreprocessing",
+        "libaudioutils",
+        "libbinder_ndk",
+        "libbinder_rpc_unstable",
+        "libbinder",
+        "libblas",
+        "libbootloader_message",
+        "libbundlewrapper",
+        "libcamera2ndk",
+        "libclang_rt.asan",
+        "libclcore_debug_g.bc",
+        "libclcore_debug.bc",
+        "libclcore_g.bc",
+        "libclcore.bc",
+        "libclearkeycasplugin",
+        "libcompiler_rt",
+        "libcrypto_utils",
+        "libcups",
+        "libcutils",
+        "libdmabufheap",
+        "libdownmix",
+        "libdrm",
+        "libdrmclearkeyplugin",
+        "libdrmframework_jni",
+        "libdrmframework",
+        "libdynproc",
+        "libeffectproxy",
+        "libeffects",
+        "libeffectsconfig",
+        "libEGL_angle",
+        "libEGL",
+        "libepoxy",
+        "libETC1",
+        "libext4_utils",
+        "libfdtrack",
+        "libfec",
+        "libFFTEm",
+        "libfilterfw",
+        "libfilterpack_imageproc",
+        "libfmq",
+        "libfs_mgr",
+        "libfwdlockengine",
+        "libgatekeeper",
+        "libgbm",
+        "libGLESv1_CM_angle",
+        "libGLESv1_CM",
+        "libGLESv2_angle",
+        "libGLESv2",
+        "libGLESv3",
+        "libgralloctypes",
+        "libgsi",
+        "libgui",
+        "libhapticgenerator",
+        "libhardware_legacy",
+        "libhardware",
+        "libhidcommand_jni",
+        "libhidlmemory",
+        "libhidltransport",
+        "libhwbinder",
+        "libincident",
+        "libinput",
+        "libinputflinger",
+        "libiprouteutil",
+        "libjni_deviceAsWebcam",
+        "libjnigraphics",
+        "libjpeg",
+        "libldnhncr",
+        "liblockagent",
+        "liblog",
+        "liblogwrap",
+        "liblp",
+        "liblz4",
+        "libmedia_helper",
+        "libmedia_jni",
+        "libmedia",
+        "libmediandk",
+        "libmediaplayerservice",
+        "libmediautils_delayed",
+        "libminui",
+        "libmtp",
+        "libnativewindow",
+        "libnetd_client",
+        "libnetlink",
+        "libnetutils",
+        "libneuralnetworks_packageinfo",
+        "libnl",
+        "libOpenMAXAL",
+        "libOpenSLES",
+        "libpdfium",
+        "libperfetto_android_internal",
+        "libpolicy-subsystem",
+        "libpower",
+        "libpowermanager",
+        "libprocessgroup_setup",
+        "libprotobuf-cpp-full",
+        "libradio_metadata",
+        "librank",
+        "libresourcemanagerservice",
+        "libreverbwrapper",
+        "libRS_internal",
+        "librs_jni",
+        "libRSCacheDir",
+        "libRSCpuRef",
+        "libRSDriver",
+        "librtp_jni",
+        "libsensorservice",
+        "libsfplugin_ccodec",
+        "libskia",
+        "libsonic",
+        "libsonivox",
+        "libsoundpool",
+        "libspeexresampler",
+        "libsqlite",
+        "libsquashfs_utils",
+        "libssl",
+        "libstagefright_foundation",
+        "libstagefright_httplive",
+        "libstagefright_omx",
+        "libstagefright",
+        "libstdc++",
+        "libsync",
+        "libsysutils",
+        "libtinyxml2",
+        "libtombstoned_client",
+        "libtracingproxy",
+        "libui",
+        "libuinputcommand_jni",
+        "libukey2_jni_shared",
+        "libusbhost",
+        "libutils",
+        "libvendorsupport",
+        "libvintf_jni",
+        "libvirglrenderer",
+        "libvisualizer",
+        "libvulkan",
+        "libwebviewchromium_loader",
+        "libwebviewchromium_plat_support",
+        "libwfds",
+        "libwilhelm",
+        "libxml2",
+        "libxml2",
+        "linker",
+        "llkd",
+        "lmkd",
+        "local_time.default",
+        "lockagent_crasher",
+        "locksettings",
+        "logcat",
+        "logcatd",
+        "logd",
+        "lpdump",
+        "lpdumpd",
+        "lshal",
+        "make_f2fs",
+        "mdnsd",
+        "media_profiles_V1_0.dtd",
+        "mediacodec.policy",
+        "mediaextractor",
+        "mediametrics",
+        "migrate_legacy_obb_data",
+        "misctrl",
+        "mke2fs",
+        "mkfs.erofs",
+        "mm_events",
+        "monkey",
+        "mtectrl",
+        "ndc",
+        "netd",
+        "netutils-wrapper-1.0",
         "odsign",
-        "ot-cli-ftd",
-        "ot-ctl",
         "otapreopt_chroot",
         "otapreopt_script",
         "otapreopt_slot",
         "otapreopt",
-        "passwd_odm",
-        "passwd_product",
-        "passwd_system_ext",
         "passwd_system",
-        "passwd_vendor",
         "perfetto",
         "ping",
         "ping6",
@@ -697,11 +600,8 @@
         "pm",
         "power.default",
         "preinstalled-packages-asl-files.xml",
-        "preinstalled-packages-platform-aosp-product.xml",
         "preinstalled-packages-platform-generic-system.xml",
-        "preinstalled-packages-platform-handheld-product.xml",
         "preinstalled-packages-platform-handheld-system.xml",
-        "preinstalled-packages-platform-telephony-product.xml",
         "preinstalled-packages-platform.xml",
         "preinstalled-packages-strict-signature.xml",
         "preloaded-classes",
@@ -712,24 +612,13 @@
         "printflags",
         "privapp-permissions-platform.xml",
         "prng_seeder",
-        "procrank",
-        "profcollectctl",
-        "profcollectd",
-        "reboot",
-        "record_binder",
         "recovery-persist",
         "recovery-refresh",
-        "recovery",
-        "remount",
         "requestsync",
         "resize2fs",
-        "Roboto-Regular.ttf",
-        "RobotoFlex-Regular.ttf",
-        "RobotoStatic-Regular.ttf",
         "rss_hwm_reset",
         "run-as",
         "sample_camera_extensions.xml",
-        "sanitizer-status",
         "schedtest",
         "screencap",
         "screenrecord",
@@ -738,68 +627,36 @@
         "sensorservice",
         "server_configurable_flags",
         "service",
-        "servicedispatcher",
-        "servicemanager.recovery",
         "servicemanager",
-        "settaskprofile",
         "settings",
         "sfdo",
         "sgdisk",
-        "sh",
-        "showmap",
-        "simpleperf_app_runner",
-        "simpleperf",
         "sload_f2fs",
         "sm",
         "snapshotctl",
         "snapuserd_ramdisk",
         "snapuserd",
         "socket_vsock_proxy",
-        "SourceSansPro-Bold.ttf",
-        "SourceSansPro-BoldItalic.ttf",
-        "SourceSansPro-Italic.ttf",
-        "SourceSansPro-Regular.ttf",
-        "SourceSansPro-SemiBold.ttf",
-        "SourceSansPro-SemiBoldItalic.ttf",
-        "sqlite3",
-        "ss",
-        "start_with_lockagent",
         "storaged",
-        "strace",
         "surfaceflinger",
         "suspend_blocker",
         "svc",
         "task_profiles.json",
         "tc",
-        "tcpdump",
         "telecom",
-        "tinycap",
-        "tinyhostless",
-        "tinymix",
-        "tinypcminfo",
-        "tinyplay",
         "tombstone_producer",
         "tombstone_transmit",
         "tombstoned",
         "toolbox",
-        "toybox",
-        "toybox",
         "traced_perf",
         "traced_probes",
         "traced",
-        "tracepath",
-        "tracepath6",
-        "traceroute6",
         "trigger_perfetto",
         "tune2fs",
         "ueventd.rc",
         "uiautomator",
         "uinput",
         "uncrypt",
-        "unwind_info",
-        "unwind_reg_info",
-        "unwind_symbols",
-        "update_engine_client",
         "update_engine_sideload",
         "update_engine",
         "update_verifier",
@@ -814,9 +671,7 @@
         "wifi.rc",
         "wificond",
         "wm",
-        "xml2abx",
         "xtables.lock",
-        "ziptool",
     ],
     multilib: {
         common: {
@@ -859,57 +714,12 @@
                 "com.android.cellbroadcast",
                 "com.android.compos",
                 "com.android.future.usb.accessory",
-                "com.android.hardware.authsecret",
-                "com.android.hardware.biometrics.face.virtual",
-                "com.android.hardware.biometrics.fingerprint.virtual",
-                "com.android.hardware.boot",
-                "com.android.hardware.cas",
-                "com.android.hardware.contexthub",
-                "com.android.hardware.dumpstate",
-                "com.android.hardware.gnss",
-                "com.android.hardware.input.processor",
-                "com.android.hardware.keymint.rust_cf_remote",
-                "com.android.hardware.keymint.rust_nonsecure",
-                "com.android.hardware.memtrack",
-                "com.android.hardware.net.nlinterceptor",
-                "com.android.hardware.neuralnetworks",
-                "com.android.hardware.power",
-                "com.android.hardware.rebootescrow",
-                "com.android.hardware.secure_element",
-                "com.android.hardware.security.authgraph",
-                "com.android.hardware.sensors",
-                "com.android.hardware.tetheroffload",
-                "com.android.hardware.thermal",
-                "com.android.hardware.threadnetwork",
-                "com.android.hardware.usb",
-                "com.android.hardware.uwb",
-                "com.android.hardware.vibrator",
-                "com.android.hardware.wifi",
-                "com.android.i18n",
-                "com.android.ipsec",
-                "com.android.hardware.gatekeeper.cf_remote",
-                "com.android.hardware.gatekeeper.nonsecure",
-                "com.android.hardware.keymint.rust_cf_remote",
-                "com.android.hardware.keymint.rust_nonsecure",
                 "com.android.location.provider",
                 "com.android.media.remotedisplay.xml",
                 "com.android.media.remotedisplay",
                 "com.android.mediadrm.signer",
                 "com.android.nfc_extras",
                 "com.android.runtime",
-                "com.google.cf.bt",
-                "com.google.cf.confirmationui",
-                "com.google.cf.health.storage",
-                "com.google.cf.health",
-                "com.google.cf.identity",
-                "com.google.cf.input.config",
-                "com.google.cf.ir",
-                "com.google.cf.light",
-                "com.google.cf.nfc",
-                "com.google.cf.oemlock",
-                "com.google.cf.rild",
-                "com.google.cf.vulkan",
-                "com.google.cf.wifi",
                 "CompanionDeviceManager",
                 "Contacts",
                 "ContactsProvider",
@@ -928,6 +738,7 @@
                 "ext",
                 "ExternalStorageProvider",
                 "ExtShared",
+                "fonts",
                 "framework-graphics",
                 "framework-location",
                 "framework-minus-apex-install-dependencies",
@@ -984,9 +795,7 @@
                 "SettingsIntelligence",
                 "SettingsProvider",
                 "SharedStorageBackup",
-                "shell_and_utilities_recovery",
                 "shell_and_utilities_system",
-                "shell_and_utilities_vendor",
                 "Shell",
                 "SimAppDialog",
                 "SoundPicker",
@@ -1024,52 +833,52 @@
             ],
         },
     },
-    base_dir: "system",
-    dirs: android_rootdirs,
-    symlinks: android_symlinks,
-    file_contexts: ":plat_file_contexts",
-    linker_config_src: "linker.config.json",
-    arch: {
-        x86: {
-            multilib: {
-                common: {
-                    deps: [
-                        "com.android.wifi",
-                    ],
-                },
-            },
-        },
-        x86_64: {
-            deps: [
-                "libgfxstream_backend",
-            ],
-            multilib: {
-                common: {
-                    deps: [
-                        "com.android.wifi",
-                    ],
-                },
-            },
-        },
-    },
     product_variables: {
         debuggable: {
-            deps: ["su"],
+            deps: [
+                "adevice_fingerprint",
+                "arping",
+                "avbctl",
+                "bootctl",
+                "dmuserd",
+                "evemu-record",
+                "idlcli",
+                "init-debug.rc",
+                "iotop",
+                "iperf3",
+                "iw",
+                "layertracegenerator",
+                "logpersist.start",
+                "logtagd.rc",
+                "ot-cli-ftd",
+                "ot-ctl",
+                "procrank",
+                "profcollectctl",
+                "profcollectd",
+                "record_binder",
+                "sanitizer-status",
+                "servicedispatcher",
+                "showmap",
+                "sqlite3",
+                "ss",
+                "start_with_lockagent",
+                "strace",
+                "su",
+                "tinycap",
+                "tinyhostless",
+                "tinymix",
+                "tinypcminfo",
+                "tinyplay",
+                "tracepath",
+                "tracepath6",
+                "traceroute6",
+                "unwind_info",
+                "unwind_reg_info",
+                "unwind_symbols",
+                "update_engine_client",
+            ],
         },
     },
-    fsverity: {
-        inputs: [
-            "etc/boot-image.prof",
-            "etc/dirty-image-objects",
-            "etc/preloaded-classes",
-            "etc/classpaths/*.pb",
-            "framework/*",
-            "framework/*/*", // framework/{arch}
-            "framework/oat/*/*", // framework/oat/{arch}
-        ],
-        libs: [":framework-res{.export-package.apk}"],
-    },
-    build_logtags: true,
 }
 
 prebuilt_etc {
diff --git a/vsoc_arm64/BoardConfig.mk b/vsoc_arm64/BoardConfig.mk
index c3708bd..1c07a21 100644
--- a/vsoc_arm64/BoardConfig.mk
+++ b/vsoc_arm64/BoardConfig.mk
@@ -42,10 +42,10 @@
 -include device/google/cuttlefish/shared/identity/BoardConfig.mk
 -include device/google/cuttlefish/shared/reboot_escrow/BoardConfig.mk
 -include device/google/cuttlefish/shared/sensors/BoardConfig.mk
+-include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/telephony/BoardConfig.mk
 -include device/google/cuttlefish/shared/vibrator/BoardConfig.mk
 
 ifneq ($(BOARD_IS_AUTOMOTIVE), true)
--include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/virgl/BoardConfig.mk
 endif
diff --git a/vsoc_arm64_only/BoardConfig.mk b/vsoc_arm64_only/BoardConfig.mk
index 173571c..4f363ed 100644
--- a/vsoc_arm64_only/BoardConfig.mk
+++ b/vsoc_arm64_only/BoardConfig.mk
@@ -38,10 +38,10 @@
 -include device/google/cuttlefish/shared/identity/BoardConfig.mk
 -include device/google/cuttlefish/shared/reboot_escrow/BoardConfig.mk
 -include device/google/cuttlefish/shared/sensors/BoardConfig.mk
+-include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/telephony/BoardConfig.mk
 -include device/google/cuttlefish/shared/vibrator/BoardConfig.mk
 
 ifneq ($(BOARD_IS_AUTOMOTIVE), true)
--include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/virgl/BoardConfig.mk
 endif
diff --git a/vsoc_x86_64/BoardConfig.mk b/vsoc_x86_64/BoardConfig.mk
index 2f7c363..cb3eab2 100644
--- a/vsoc_x86_64/BoardConfig.mk
+++ b/vsoc_x86_64/BoardConfig.mk
@@ -45,10 +45,10 @@
 -include device/google/cuttlefish/shared/identity/BoardConfig.mk
 -include device/google/cuttlefish/shared/reboot_escrow/BoardConfig.mk
 -include device/google/cuttlefish/shared/sensors/BoardConfig.mk
+-include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/telephony/BoardConfig.mk
 -include device/google/cuttlefish/shared/vibrator/BoardConfig.mk
 
 ifneq ($(BOARD_IS_AUTOMOTIVE), true)
--include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/virgl/BoardConfig.mk
 endif
diff --git a/vsoc_x86_64_only/BoardConfig.mk b/vsoc_x86_64_only/BoardConfig.mk
index 71d6b43..7b2de1e 100644
--- a/vsoc_x86_64_only/BoardConfig.mk
+++ b/vsoc_x86_64_only/BoardConfig.mk
@@ -38,10 +38,10 @@
 -include device/google/cuttlefish/shared/identity/BoardConfig.mk
 -include device/google/cuttlefish/shared/reboot_escrow/BoardConfig.mk
 -include device/google/cuttlefish/shared/sensors/BoardConfig.mk
+-include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/telephony/BoardConfig.mk
 -include device/google/cuttlefish/shared/vibrator/BoardConfig.mk
 
 ifneq ($(BOARD_IS_AUTOMOTIVE), true)
--include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/virgl/BoardConfig.mk
 endif
diff --git a/vsoc_x86_64_pgagnostic/BoardConfig.mk b/vsoc_x86_64_pgagnostic/BoardConfig.mk
index e2bdcfd..c0def09 100644
--- a/vsoc_x86_64_pgagnostic/BoardConfig.mk
+++ b/vsoc_x86_64_pgagnostic/BoardConfig.mk
@@ -52,10 +52,10 @@
 -include device/google/cuttlefish/shared/identity/BoardConfig.mk
 -include device/google/cuttlefish/shared/reboot_escrow/BoardConfig.mk
 -include device/google/cuttlefish/shared/sensors/BoardConfig.mk
+-include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/telephony/BoardConfig.mk
 -include device/google/cuttlefish/shared/vibrator/BoardConfig.mk
 
 ifneq ($(BOARD_IS_AUTOMOTIVE), true)
--include device/google/cuttlefish/shared/swiftshader/BoardConfig.mk
 -include device/google/cuttlefish/shared/virgl/BoardConfig.mk
 endif