Merge "Slightly increase testRilConnets timeout"
diff --git a/common/frontend/socket_vsock_proxy/main.cpp b/common/frontend/socket_vsock_proxy/main.cpp
index 5d08790..783ad06 100644
--- a/common/frontend/socket_vsock_proxy/main.cpp
+++ b/common/frontend/socket_vsock_proxy/main.cpp
@@ -19,6 +19,7 @@
 #include <gflags/gflags.h>
 
 #include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/contains.h"
 #include "common/libs/utils/socket2socket_proxy.h"
 #include "host/commands/kernel_log_monitor/utils.h"
 
@@ -112,7 +113,7 @@
 
 bool socketErrorIsRecoverable(int error) {
   std::set<int> unrecoverable{EACCES, EAFNOSUPPORT, EINVAL, EPROTONOSUPPORT};
-  return unrecoverable.find(error) == unrecoverable.end();
+  return !cuttlefish::Contains(unrecoverable, error);
 }
 
 [[noreturn]] static void SleepForever() {
diff --git a/common/libs/confui/protocol_types.cpp b/common/libs/confui/protocol_types.cpp
index 8c293ea..e5b68e2 100644
--- a/common/libs/confui/protocol_types.cpp
+++ b/common/libs/confui/protocol_types.cpp
@@ -21,6 +21,7 @@
 
 #include "common/libs/confui/packet.h"
 #include "common/libs/confui/utils.h"
+#include "common/libs/utils/contains.h"
 
 namespace cuttlefish {
 namespace confui {
@@ -40,7 +41,7 @@
       {ConfUiCmd::kAbort, "kAbort"},
       {ConfUiCmd::kUserInputEvent, "kUserInputEvent"},
       {ConfUiCmd::kUserInputEvent, "kUserTouchEvent"}};
-  if (look_up_tab.find(cmd) != look_up_tab.end()) {
+  if (Contains(look_up_tab, cmd)) {
     return look_up_tab[cmd] + suffix;
   }
   return "kUnknown" + suffix;
@@ -73,7 +74,7 @@
       {"kUserInputEvent", ConfUiCmd::kUserInputEvent},
       {"kUserTouchEvent", ConfUiCmd::kUserTouchEvent},
   };
-  if (cmds.find(cmd_str) != cmds.end()) {
+  if (Contains(cmds, cmd_str)) {
     return cmds[cmd_str];
   }
   return ConfUiCmd::kUnknown;
diff --git a/common/libs/utils/contains.h b/common/libs/utils/contains.h
index 9dea1fc..70923f5 100644
--- a/common/libs/utils/contains.h
+++ b/common/libs/utils/contains.h
@@ -25,37 +25,42 @@
  *
  * The function returns true if container has the key, or false.
  *
- * If the container has a find(key) method (e.g. set, unordered_set, etc), the
- * find method is used. Otherwise, the std::find function from algorithm is
- * used, which may result in a linear search.
+ * If the container has a find(key) method (e.g. set, unordered_set, std::map,
+ * etc), the find method is used. Otherwise, the std::find function from
+ * algorithm is used, which may result in a linear search.
+ *
  */
 namespace cuttlefish {
 namespace contains_internal_impl {
 
-template <typename Container>
-using ElemType = decltype(*(std::cbegin(std::declval<Container&>())));
+template <typename Container, typename Key>
+using CheckFindMethodType =
+    decltype(void(std::declval<Container&>().find(std::declval<Key&>())));
 
-template <typename Container>
-using CheckFindMethodType = decltype(void(
-    std::declval<Container&>().find(std::declval<ElemType<Container&>>())));
-
-template <typename Container, typename = void>
+template <typename Container, typename T, typename = void>
 struct HasFindImpl : std::false_type {};
 
-template <typename Container>
-struct HasFindImpl<Container, CheckFindMethodType<Container>> : std::true_type {
-};
+template <typename Container, typename T>
+struct HasFindImpl<Container, T, CheckFindMethodType<Container, T>>
+    : std::true_type {};
 
 }  // namespace contains_internal_impl
 
 // TODO(kwstephenkim): Replace these when C++20 starts to be used.
-template <typename Container, typename U>
-bool Contains(Container&& container, U&& u) {
-  if constexpr (contains_internal_impl::HasFindImpl<Container>::value) {
-    // using O(1) or O(lgN) find()
-    return container.find(std::forward<U>(u)) != container.end();
-  }
-  // using std::find, which is likely O(n)
+template <typename Container, typename U,
+          typename = std::enable_if_t<
+              contains_internal_impl::HasFindImpl<Container, U>::value, void>>
+constexpr bool Contains(Container&& container, U&& u) {
+  // using O(1) or O(lgN) find()
+  return container.find(std::forward<U>(u)) != container.end();
+}
+
+template <
+    typename Container, typename U,
+    std::enable_if_t<!contains_internal_impl::HasFindImpl<Container, U>::value,
+                     int> = 0>
+constexpr bool Contains(Container&& container, U&& u) {
+  // falls back to a generic, likely linear search
   const auto itr =
       std::find(std::begin(container), std::end(container), std::forward<U>(u));
   return itr != std::end(container);
diff --git a/common/libs/utils/users.cpp b/common/libs/utils/users.cpp
index 0e38f6c..dc434ce 100644
--- a/common/libs/utils/users.cpp
+++ b/common/libs/utils/users.cpp
@@ -33,6 +33,8 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 
+#include "common/libs/utils/contains.h"
+
 namespace cuttlefish {
 namespace {
 gid_t GroupIdFromName(const std::string& group_name) {
@@ -91,11 +93,7 @@
   }
 
   auto groups = GetSuplementaryGroups();
-
-  if (std::find(groups.cbegin(), groups.cend(), gid) != groups.cend()) {
-    return true;
-  }
-  return false;
+  return Contains(groups, gid);
 }
 
 Result<std::string> SystemWideUserHome(const uid_t uid) {
diff --git a/guest/hals/keymint/remote/Android.bp b/guest/hals/keymint/remote/Android.bp
index 1f7a16e..748a0ec 100644
--- a/guest/hals/keymint/remote/Android.bp
+++ b/guest/hals/keymint/remote/Android.bp
@@ -32,6 +32,7 @@
         "-Wextra",
     ],
     shared_libs: [
+        "android.hardware.security.rkp-V3-ndk",
         "android.hardware.security.secureclock-V1-ndk",
         "android.hardware.security.sharedsecret-V1-ndk",
         "lib_android_keymaster_keymint_utils",
diff --git a/guest/hals/keymint/remote/remote_keymaster.cpp b/guest/hals/keymint/remote/remote_keymaster.cpp
index 2387df6..a725c45 100644
--- a/guest/hals/keymint/remote/remote_keymaster.cpp
+++ b/guest/hals/keymint/remote/remote_keymaster.cpp
@@ -111,6 +111,48 @@
     }
   }
 
+  // Pass attestation IDs to the remote KM implementation.
+  // Skip IMEI and MEID as those aren't present on emulators.
+  SetAttestationIdsRequest request(message_version());
+
+  static constexpr char brand_prop_name[] = "ro.product.brand";
+  static constexpr char device_prop_name[] = "ro.product.device";
+  static constexpr char product_prop_name[] = "ro.product.name";
+  static constexpr char serial_prop_name[] = "ro.serialno";
+  static constexpr char manufacturer_prop_name[] = "ro.product.manufacturer";
+  static constexpr char model_prop_name[] = "ro.product.model";
+
+  std::string brand_prop_value =
+      android::base::GetProperty(brand_prop_name, "");
+  std::string device_prop_value =
+      android::base::GetProperty(device_prop_name, "");
+  std::string product_prop_value =
+      android::base::GetProperty(product_prop_name, "");
+  std::string serial_prop_value =
+      android::base::GetProperty(serial_prop_name, "");
+  std::string manufacturer_prop_value =
+      android::base::GetProperty(manufacturer_prop_name, "");
+  std::string model_prop_value =
+      android::base::GetProperty(model_prop_name, "");
+
+  request.brand.Reinitialize(brand_prop_value.data(), brand_prop_value.size());
+  request.device.Reinitialize(device_prop_value.data(),
+                              device_prop_value.size());
+  request.product.Reinitialize(product_prop_value.data(),
+                               product_prop_value.size());
+  request.serial.Reinitialize(serial_prop_value.data(),
+                              serial_prop_value.size());
+  request.manufacturer.Reinitialize(manufacturer_prop_value.data(),
+                                    manufacturer_prop_value.size());
+  request.model.Reinitialize(model_prop_value.data(), model_prop_value.size());
+
+  SetAttestationIdsResponse response = SetAttestationIds(request);
+  if (response.error != KM_ERROR_OK) {
+    LOG(ERROR) << "Failed to configure keymaster attestation IDs: "
+               << response.error;
+    return false;
+  }
+
   return true;
 }
 
@@ -332,4 +374,11 @@
   return response;
 }
 
+SetAttestationIdsResponse RemoteKeymaster::SetAttestationIds(
+    const SetAttestationIdsRequest& request) {
+  SetAttestationIdsResponse response(message_version());
+  ForwardCommand(SET_ATTESTATION_IDS, request, &response);
+  return response;
+}
+
 }  // namespace keymaster
diff --git a/guest/hals/keymint/remote/remote_keymaster.h b/guest/hals/keymint/remote/remote_keymaster.h
index 85d38e4..1e24672 100644
--- a/guest/hals/keymint/remote/remote_keymaster.h
+++ b/guest/hals/keymint/remote/remote_keymaster.h
@@ -98,6 +98,8 @@
                               GenerateTimestampTokenResponse* response);
   GetRootOfTrustResponse GetRootOfTrust(const GetRootOfTrustRequest& request);
   GetHwInfoResponse GetHwInfo();
+  SetAttestationIdsResponse SetAttestationIds(
+      const SetAttestationIdsRequest& request);
 
   // CF HAL and remote sides are always compiled together, so will never
   // disagree about message versions.
diff --git a/guest/hals/keymint/remote/remote_keymint_operation.cpp b/guest/hals/keymint/remote/remote_keymint_operation.cpp
index aa05035..59624a5 100644
--- a/guest/hals/keymint/remote/remote_keymint_operation.cpp
+++ b/guest/hals/keymint/remote/remote_keymint_operation.cpp
@@ -102,7 +102,7 @@
     const optional<vector<uint8_t>>& signature,  //
     const optional<HardwareAuthToken>& authToken,
     const optional<TimeStampToken>& /* timestampToken */,
-    const optional<vector<uint8_t>>& /* confirmationToken */,
+    const optional<vector<uint8_t>>& confirmationToken,
     vector<uint8_t>* output) {
   if (!output) {
     return ScopedAStatus(AStatus_fromServiceSpecificError(
@@ -120,6 +120,11 @@
     request.additional_params.push_back(keymaster::TAG_AUTH_TOKEN,
                                         tokenAsVec.data(), tokenAsVec.size());
   }
+  if (confirmationToken) {
+    request.additional_params.push_back(keymaster::TAG_CONFIRMATION_TOKEN,
+                                        confirmationToken->data(),
+                                        confirmationToken->size());
+  }
 
   FinishOperationResponse response(impl_.message_version());
   impl_.FinishOperation(request, &response);
diff --git a/host/commands/assemble_cvd/assemble_cvd.cc b/host/commands/assemble_cvd/assemble_cvd.cc
index f8b2702..75af31c 100644
--- a/host/commands/assemble_cvd/assemble_cvd.cc
+++ b/host/commands/assemble_cvd/assemble_cvd.cc
@@ -51,8 +51,6 @@
             "will be reset to the state it was initially launched "
             "in. This flag is ignored if the underlying partition "
             "images have been updated since the first launch.");
-DEFINE_int32(modem_simulator_count, CF_DEFAULTS_MODEM_SIMULATOR_COUNT,
-             "Modem simulator count corresponding to maximum sim number");
 
 DECLARE_bool(use_overlay);
 
@@ -168,10 +166,15 @@
     // SaveConfig line below. Don't launch cuttlefish subprocesses between these
     // two operations, as those will assume they can read the config object from
     // disk.
-    auto config = CF_EXPECT(InitializeCuttlefishConfiguration(
-                                FLAGS_instance_dir, FLAGS_modem_simulator_count,
-                                kernel_configs, injector, fetcher_config),
-                            "cuttlefish configuration initialization failed");
+    auto config = CF_EXPECT(
+        InitializeCuttlefishConfiguration(FLAGS_instance_dir, kernel_configs,
+                                          injector, fetcher_config),
+        "cuttlefish configuration initialization failed");
+
+    // take the max value of modem_simulator_instance_number in each instance
+    // which is used for preserving/deleting iccprofile_for_simX.xml files
+    int modem_simulator_count = 0;
+
     std::set<std::string> preserving;
     bool creating_os_disk = false;
     // if any device needs to rebuild its composite disk,
@@ -179,6 +182,9 @@
     for (const auto& instance : config.Instances()) {
       auto os_builder = OsCompositeDiskBuilder(config, instance);
       creating_os_disk |= CF_EXPECT(os_builder.WillRebuildCompositeDisk());
+      if (instance.modem_simulator_instance_number() > modem_simulator_count) {
+        modem_simulator_count = instance.modem_simulator_instance_number();
+      }
     }
     // TODO(schuffelen): Add smarter decision for when to delete runtime files.
     // Files like NVChip are tightly bound to Android keymint and should be
@@ -214,7 +220,7 @@
       preserving.insert("uboot_env.img");
       preserving.insert("factory_reset_protected.img");
       std::stringstream ss;
-      for (int i = 0; i < FLAGS_modem_simulator_count; i++) {
+      for (int i = 0; i < modem_simulator_count; i++) {
         ss.clear();
         ss << "iccprofile_for_sim" << i << ".xml";
         preserving.insert(ss.str());
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 30e1fe6..56b3ca2 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -126,10 +126,11 @@
 
 DEFINE_string(use_allocd, CF_DEFAULTS_USE_ALLOCD?"true":"false",
             "Acquire static resources from the resource allocator daemon.");
-DEFINE_bool(enable_minimal_mode, CF_DEFAULTS_ENABLE_MINIMAL_MODE,
-            "Only enable the minimum features to boot a cuttlefish device and "
-            "support minimal UI interactions.\nNote: Currently only supports "
-            "handheld/phone targets");
+DEFINE_string(
+    enable_minimal_mode, CF_DEFAULTS_ENABLE_MINIMAL_MODE ? "true" : "false",
+    "Only enable the minimum features to boot a cuttlefish device and "
+    "support minimal UI interactions.\nNote: Currently only supports "
+    "handheld/phone targets");
 DEFINE_string(
     pause_in_bootloader, CF_DEFAULTS_PAUSE_IN_BOOTLOADER?"true":"false",
     "Stop the bootflow in u-boot. You can continue the boot by connecting "
@@ -250,7 +251,7 @@
 
 DEFINE_string(uuid, CF_DEFAULTS_UUID,
               "UUID to use for the device. Random if not specified");
-DEFINE_bool(daemon, CF_DEFAULTS_DAEMON,
+DEFINE_string(daemon, CF_DEFAULTS_DAEMON?"true":"false",
             "Run cuttlefish in background, the launcher exits on boot "
             "completed/failed");
 
@@ -310,11 +311,13 @@
               "Local fixed location file path for the gnss proxy");
 
 // by default, this modem-simulator is disabled
-DEFINE_bool(enable_modem_simulator, CF_DEFAULTS_ENABLE_MODEM_SIMULATOR,
-            "Enable the modem simulator to process RILD AT commands");
+DEFINE_string(enable_modem_simulator,
+              CF_DEFAULTS_ENABLE_MODEM_SIMULATOR ? "true" : "false",
+              "Enable the modem simulator to process RILD AT commands");
 // modem_simulator_sim_type=2 for test CtsCarrierApiTestCases
-DEFINE_int32(modem_simulator_sim_type, CF_DEFAULTS_MODEM_SIMULATOR_SIM_TYPE,
-             "Sim type: 1 for normal, 2 for CtsCarrierApiTestCases");
+DEFINE_string(modem_simulator_sim_type,
+              std::to_string(CF_DEFAULTS_MODEM_SIMULATOR_SIM_TYPE),
+              "Sim type: 1 for normal, 2 for CtsCarrierApiTestCases");
 
 DEFINE_bool(console, CF_DEFAULTS_CONSOLE, "Enable the serial console");
 
@@ -390,6 +393,10 @@
             "Capture disk writes an overlay. This is a "
             "prerequisite for powerwash_cvd or multiple instances.");
 
+DEFINE_string(modem_simulator_count,
+              std::to_string(CF_DEFAULTS_MODEM_SIMULATOR_COUNT),
+              "Modem simulator count corresponding to maximum sim number");
+
 DECLARE_string(assembly_dir);
 DECLARE_string(boot_image);
 DECLARE_string(system_image_dir);
@@ -587,7 +594,7 @@
 } // namespace
 
 Result<CuttlefishConfig> InitializeCuttlefishConfiguration(
-    const std::string& root_dir, int modem_simulator_count,
+    const std::string& root_dir,
     const std::vector<KernelConfig>& kernel_configs,
     fruit::Injector<>& injector, const FetcherConfig& fetcher_config) {
   CuttlefishConfig tmp_config_obj;
@@ -605,8 +612,9 @@
   std::vector<std::string> vm_manager_vec =
       android::base::Split(FLAGS_vm_manager, ",");
   for (int i=1; i<vm_manager_vec.size(); i++) {
-    CHECK(vm_manager_vec[0]==vm_manager_vec[i])
-      << "All instances should have same vm_manager, " << FLAGS_vm_manager;
+    CF_EXPECT(
+        vm_manager_vec[0] == vm_manager_vec[i],
+        "All instances should have same vm_manager, " << FLAGS_vm_manager);
   }
 
   // TODO(weihsu), b/250988697: these should move to instance,
@@ -746,16 +754,9 @@
   auto udp_range  = ParsePortRange(FLAGS_udp_port_range);
   tmp_config_obj.set_webrtc_udp_port_range(udp_range);
 
-  tmp_config_obj.set_enable_modem_simulator(FLAGS_enable_modem_simulator &&
-                                            !FLAGS_enable_minimal_mode);
-  tmp_config_obj.set_modem_simulator_instance_number(modem_simulator_count);
-  tmp_config_obj.set_modem_simulator_sim_type(FLAGS_modem_simulator_sim_type);
-
   tmp_config_obj.set_webrtc_enable_adb_websocket(
           FLAGS_webrtc_enable_adb_websocket);
 
-  tmp_config_obj.set_run_as_daemon(FLAGS_daemon);
-
   tmp_config_obj.set_enable_gnss_grpc_proxy(FLAGS_start_gnss_proxy);
 
   tmp_config_obj.set_enable_vehicle_hal_grpc_server(
@@ -771,8 +772,6 @@
 
   tmp_config_obj.set_ril_dns(FLAGS_ril_dns);
 
-  tmp_config_obj.set_enable_minimal_mode(FLAGS_enable_minimal_mode);
-
   tmp_config_obj.set_vhost_net(FLAGS_vhost_net);
 
   tmp_config_obj.set_vhost_user_mac80211_hwsim(FLAGS_vhost_user_mac80211_hwsim);
@@ -850,6 +849,16 @@
       android::base::Split(FLAGS_use_sdcard, ",");
   std::vector<std::string> pause_in_bootloader_vec =
       android::base::Split(FLAGS_pause_in_bootloader, ",");
+  std::vector<std::string> daemon_vec =
+      android::base::Split(FLAGS_daemon, ",");
+  std::vector<std::string> enable_minimal_mode_vec =
+      android::base::Split(FLAGS_enable_minimal_mode, ",");
+  std::vector<std::string> enable_modem_simulator_vec =
+      android::base::Split(FLAGS_enable_modem_simulator, ",");
+  std::vector<std::string> modem_simulator_count_vec =
+      android::base::Split(FLAGS_modem_simulator_count, ",");
+  std::vector<std::string> modem_simulator_sim_type_vec =
+      android::base::Split(FLAGS_modem_simulator_sim_type, ",");
 
   // new instance specific flags (moved from common flags)
   std::vector<std::string> gem5_binary_dirs =
@@ -923,15 +932,16 @@
 
     int vsock_guest_cid_int;
     if (instance_index < vsock_guest_cid_vec.size()) {
-      CHECK(android::base::ParseInt(vsock_guest_cid_vec[instance_index].c_str(),
-                                    &vsock_guest_cid_int))
-        << "Failed to parse value \"" << vsock_guest_cid_vec[instance_index]
-        << "\" for vsock_guest_cid";
+      CF_EXPECT(
+          android::base::ParseInt(vsock_guest_cid_vec[instance_index].c_str(),
+                                  &vsock_guest_cid_int),
+          "Failed to parse value \"" << vsock_guest_cid_vec[instance_index]
+                                     << "\" for vsock_guest_cid");
     } else {
-      CHECK(android::base::ParseInt(vsock_guest_cid_vec[0].c_str(),
-                                    &vsock_guest_cid_int))
-        << "Failed to parse value \"" << vsock_guest_cid_vec[0]
-        << "\" for vsock_guest_cid";
+      CF_EXPECT(android::base::ParseInt(vsock_guest_cid_vec[0].c_str(),
+                                        &vsock_guest_cid_int),
+                "Failed to parse value \"" << vsock_guest_cid_vec[0]
+                                           << "\" for vsock_guest_cid");
     }
 
     // call this before all stuff that has vsock server: e.g. touchpad, keyboard, etc
@@ -945,53 +955,55 @@
 
     int cpus_int;
     if (instance_index < cpus_vec.size()) {
-      CHECK(android::base::ParseInt(cpus_vec[instance_index].c_str(),&cpus_int))
-        << "Failed to parse value \"" << cpus_vec[instance_index]
-        << "\" for cpus";
+      CF_EXPECT(
+          android::base::ParseInt(cpus_vec[instance_index].c_str(), &cpus_int),
+          "Failed to parse value \"" << cpus_vec[instance_index]
+                                     << "\" for cpus");
     } else {
-      CHECK(android::base::ParseInt(cpus_vec[0].c_str(),&cpus_int))
-        << "Failed to parse value \"" << cpus_vec[0]
-        << "\" for cpus";
+      CF_EXPECT(android::base::ParseInt(cpus_vec[0].c_str(), &cpus_int),
+                "Failed to parse value \"" << cpus_vec[0] << "\" for cpus");
     }
     instance.set_cpus(cpus_int);
     // TODO(weihsu): before vectorizing smt flag,
     // make sure all instances have multiple of 2 then SMT mode
     // if any of instance doesn't have multiple of 2 then NOT SMT
-    CHECK(!FLAGS_smt || cpus_int % 2 == 0)
-        << "CPUs must be a multiple of 2 in SMT mode";
+    CF_EXPECT(!FLAGS_smt || cpus_int % 2 == 0,
+              "CPUs must be a multiple of 2 in SMT mode");
 
     // new instance specific flags (moved from common flags)
-    CHECK(instance_index<kernel_configs.size())
-      << "instance_index " << instance_index << " out of boundary " << kernel_configs.size();
+    CF_EXPECT(instance_index < kernel_configs.size(),
+              "instance_index " << instance_index << " out of boundary "
+                                << kernel_configs.size());
     instance.set_target_arch(kernel_configs[instance_index].target_arch);
     instance.set_console(FLAGS_console);
     instance.set_kgdb(FLAGS_console && FLAGS_kgdb);
 
     int blank_data_image_mb_int;
     if (instance_index < blank_data_image_mb_vec.size()) {
-      CHECK(android::base::ParseInt(blank_data_image_mb_vec[instance_index].c_str(),
-                                    &blank_data_image_mb_int))
-        << "Failed to parse value \"" << blank_data_image_mb_vec[instance_index]
-        << "\" for blank_data_image_mb";
+      CF_EXPECT(android::base::ParseInt(
+                    blank_data_image_mb_vec[instance_index].c_str(),
+                    &blank_data_image_mb_int),
+                "Failed to parse value \""
+                    << blank_data_image_mb_vec[instance_index]
+                    << "\" for blank_data_image_mb");
     } else {
-      CHECK(android::base::ParseInt(blank_data_image_mb_vec[0].c_str(),
-                                    &blank_data_image_mb_int))
-        << "Failed to parse value \"" << blank_data_image_mb_vec[0]
-        << "\" for blank_data_image_mb";
+      CF_EXPECT(android::base::ParseInt(blank_data_image_mb_vec[0].c_str(),
+                                        &blank_data_image_mb_int),
+                "Failed to parse value \"" << blank_data_image_mb_vec[0]
+                                           << "\" for blank_data_image_mb");
     }
     instance.set_blank_data_image_mb(blank_data_image_mb_int);
 
     int gdb_port_int;
     if (instance_index < gdb_port_vec.size()) {
-      CHECK(android::base::ParseInt(gdb_port_vec[instance_index].c_str(),
-                                    &gdb_port_int))
-        << "Failed to parse value \"" << gdb_port_vec[instance_index]
-        << "\" for gdb_port";
+      CF_EXPECT(android::base::ParseInt(gdb_port_vec[instance_index].c_str(),
+                                        &gdb_port_int),
+                "Failed to parse value \"" << gdb_port_vec[instance_index]
+                                           << "\" for gdb_port");
     } else {
-      CHECK(android::base::ParseInt(gdb_port_vec[0].c_str(),
-                                    &gdb_port_int))
-        << "Failed to parse value \"" << gdb_port_vec[0]
-        << "\" for gdb_port";
+      CF_EXPECT(
+          android::base::ParseInt(gdb_port_vec[0].c_str(), &gdb_port_int),
+          "Failed to parse value \"" << gdb_port_vec[0] << "\" for gdb_port");
     }
     instance.set_gdb_port(gdb_port_int);
 
@@ -1015,43 +1027,45 @@
 
     int x_res = 0;
     if (instance_index < x_res_vec.size()) {
-      CHECK(android::base::ParseInt(x_res_vec[instance_index].c_str(), &x_res))
-        << "Failed to parse value \"" << x_res_vec[instance_index]
-        << "\" for x_res";
+      CF_EXPECT(
+          android::base::ParseInt(x_res_vec[instance_index].c_str(), &x_res),
+          "Failed to parse value \"" << x_res_vec[instance_index]
+                                     << "\" for x_res");
     } else if (x_res_vec.size() == 1) {
-      CHECK(android::base::ParseInt(x_res_vec[0].c_str(), &x_res))
-        << "Failed to parse value \"" << x_res_vec[0]
-        << "\" for x_res";
+      CF_EXPECT(android::base::ParseInt(x_res_vec[0].c_str(), &x_res),
+                "Failed to parse value \"" << x_res_vec[0] << "\" for x_res");
     }
     int y_res = 0;
     if (instance_index < y_res_vec.size()) {
-      CHECK(android::base::ParseInt(y_res_vec[instance_index].c_str(), &y_res))
-        << "Failed to parse value \"" << y_res_vec[instance_index]
-        << "\" for y_res";
+      CF_EXPECT(
+          android::base::ParseInt(y_res_vec[instance_index].c_str(), &y_res),
+          "Failed to parse value \"" << y_res_vec[instance_index]
+                                     << "\" for y_res");
     } else if (y_res_vec.size() == 1) {
-      CHECK(android::base::ParseInt(y_res_vec[0].c_str(), &y_res))
-        << "Failed to parse value \"" << y_res_vec[0]
-        << "\" for y_res";
+      CF_EXPECT(android::base::ParseInt(y_res_vec[0].c_str(), &y_res),
+                "Failed to parse value \"" << y_res_vec[0] << "\" for y_res");
     }
     int dpi = 0;
     if (instance_index < dpi_vec.size()) {
-      CHECK(android::base::ParseInt(dpi_vec[instance_index].c_str(), &dpi))
-        << "Failed to parse value \"" << dpi_vec[instance_index]
-        << "\" for dpi";
+      CF_EXPECT(android::base::ParseInt(dpi_vec[instance_index].c_str(), &dpi),
+                "Failed to parse value \"" << dpi_vec[instance_index]
+                                           << "\" for dpi");
     } else if (dpi_vec.size() == 1) {
-      CHECK(android::base::ParseInt(dpi_vec[0].c_str(), &dpi))
-        << "Failed to parse value \"" << dpi_vec[0]
-        << "\" for dpi";
+      CF_EXPECT(android::base::ParseInt(dpi_vec[0].c_str(), &dpi),
+                "Failed to parse value \"" << dpi_vec[0] << "\" for dpi");
     }
     int refresh_rate_hz = 0;
     if (instance_index < refresh_rate_hz_vec.size()) {
-      CHECK(android::base::ParseInt(refresh_rate_hz_vec[instance_index].c_str(), &refresh_rate_hz))
-        << "Failed to parse value \"" << refresh_rate_hz_vec[instance_index]
-        << "\" for refresh_rate_hz";
+      CF_EXPECT(
+          android::base::ParseInt(refresh_rate_hz_vec[instance_index].c_str(),
+                                  &refresh_rate_hz),
+          "Failed to parse value \"" << refresh_rate_hz_vec[instance_index]
+                                     << "\" for refresh_rate_hz");
     } else if (refresh_rate_hz_vec.size() == 1) {
-      CHECK(android::base::ParseInt(refresh_rate_hz_vec[0].c_str(), &refresh_rate_hz))
-        << "Failed to parse value \"" << refresh_rate_hz_vec[0]
-        << "\" for refresh_rate_hz";
+      CF_EXPECT(android::base::ParseInt(refresh_rate_hz_vec[0].c_str(),
+                                        &refresh_rate_hz),
+                "Failed to parse value \"" << refresh_rate_hz_vec[0]
+                                           << "\" for refresh_rate_hz");
     }
     if (x_res > 0 && y_res > 0) {
       if (display_configs.empty()) {
@@ -1069,13 +1083,14 @@
 
     int memory_mb;
     if (instance_index >= memory_mb_vec.size()) {
-      CHECK(android::base::ParseInt(memory_mb_vec[0].c_str(), &memory_mb))
-        << "Failed to parse value \"" << memory_mb_vec[0]
-        << "\" for memory_mb";
+      CF_EXPECT(
+          android::base::ParseInt(memory_mb_vec[0].c_str(), &memory_mb),
+          "Failed to parse value \"" << memory_mb_vec[0] << "\" for memory_mb");
     } else {
-      CHECK(android::base::ParseInt(memory_mb_vec[instance_index].c_str(), &memory_mb))
-        << "Failed to parse value \"" << memory_mb_vec[instance_index]
-        << "\" for memory_mb";
+      CF_EXPECT(android::base::ParseInt(memory_mb_vec[instance_index].c_str(),
+                                        &memory_mb),
+                "Failed to parse value \"" << memory_mb_vec[instance_index]
+                                           << "\" for memory_mb");
     }
     instance.set_memory_mb(memory_mb);
     instance.set_ddr_mem_mb(memory_mb * 2);
@@ -1097,8 +1112,8 @@
 
     bool guest_enforce_security;
     if (instance_index >= guest_enforce_security_vec.size()) {
-      guest_enforce_security = CF_EXPECT(ParseBool(guest_enforce_security_vec[0],
-                                    "guest_enforce_security"));
+      guest_enforce_security = CF_EXPECT(
+          ParseBool(guest_enforce_security_vec[0], "guest_enforce_security"));
     } else {
       guest_enforce_security = CF_EXPECT(ParseBool(
           guest_enforce_security_vec[instance_index], "guest_enforce_security"));
@@ -1115,17 +1130,80 @@
     }
     instance.set_pause_in_bootloader(pause_in_bootloader);
 
+    bool daemon;
+    if (instance_index >= daemon_vec.size()) {
+      daemon = CF_EXPECT(ParseBool(daemon_vec[0], "daemon"));
+    } else {
+      daemon = CF_EXPECT(ParseBool(daemon_vec[instance_index], "daemon"));
+    }
+    instance.set_run_as_daemon(daemon);
+
+    bool enable_minimal_mode;
+    if (instance_index >= enable_minimal_mode_vec.size()) {
+      enable_minimal_mode = CF_EXPECT(
+          ParseBool(enable_minimal_mode_vec[0], "enable_minimal_mode"));
+    } else {
+      enable_minimal_mode = CF_EXPECT(ParseBool(
+          enable_minimal_mode_vec[instance_index], "enable_minimal_mode"));
+    }
+    bool enable_modem_simulator;
+    if (instance_index >= enable_modem_simulator_vec.size()) {
+      enable_modem_simulator = CF_EXPECT(
+          ParseBool(enable_modem_simulator_vec[0], "enable_modem_simulator"));
+    } else {
+      enable_modem_simulator =
+          CF_EXPECT(ParseBool(enable_modem_simulator_vec[instance_index],
+                              "enable_modem_simulator"));
+    }
+    int modem_simulator_count;
+    if (instance_index >= modem_simulator_count_vec.size()) {
+      CF_EXPECT(android::base::ParseInt(modem_simulator_count_vec[0].c_str(),
+                                        &modem_simulator_count),
+                "Failed to parse value \"" << modem_simulator_count_vec[0]
+                                           << "\" for modem_simulator_count");
+    } else {
+      CF_EXPECT(android::base::ParseInt(
+                    modem_simulator_count_vec[instance_index].c_str(),
+                    &modem_simulator_count),
+                "Failed to parse value \""
+                    << modem_simulator_count_vec[instance_index]
+                    << "\" for modem_simulator_count");
+    }
+    int modem_simulator_sim_type;
+    if (instance_index >= modem_simulator_sim_type_vec.size()) {
+      CF_EXPECT(android::base::ParseInt(modem_simulator_sim_type_vec[0].c_str(),
+                                        &modem_simulator_sim_type),
+                "Failed to parse value \""
+                    << modem_simulator_sim_type_vec[0]
+                    << "\" for modem_simulator_sim_type");
+    } else {
+      CF_EXPECT(android::base::ParseInt(
+                    modem_simulator_sim_type_vec[instance_index].c_str(),
+                    &modem_simulator_sim_type),
+                "Failed to parse value \""
+                    << modem_simulator_sim_type_vec[instance_index]
+                    << "\" for modem_simulator_sim_type");
+    }
+    instance.set_enable_modem_simulator(enable_modem_simulator &&
+                                        !enable_minimal_mode);
+    instance.set_modem_simulator_instance_number(modem_simulator_count);
+    instance.set_modem_simulator_sim_type(modem_simulator_sim_type);
+
+    instance.set_enable_minimal_mode(enable_minimal_mode);
+
     int camera_server_port;
     if (instance_index < camera_server_port_vec.size()) {
-      CHECK(android::base::ParseInt(camera_server_port_vec[instance_index].c_str(),
-                                    &camera_server_port))
-        << "Failed to parse value \"" << camera_server_port_vec[instance_index]
-        << "\" for camera_server_port";
+      CF_EXPECT(android::base::ParseInt(
+                    camera_server_port_vec[instance_index].c_str(),
+                    &camera_server_port),
+                "Failed to parse value \""
+                    << camera_server_port_vec[instance_index]
+                    << "\" for camera_server_port");
     } else {
-      CHECK(android::base::ParseInt(camera_server_port_vec[0].c_str(),
-                                    &camera_server_port))
-        << "Failed to parse value \"" << camera_server_port_vec[0]
-        << "\" for camera_server_port";
+      CF_EXPECT(android::base::ParseInt(camera_server_port_vec[0].c_str(),
+                                        &camera_server_port),
+                "Failed to parse value \"" << camera_server_port_vec[0]
+                                           << "\" for camera_server_port");
     }
     instance.set_camera_server_port(camera_server_port);
 
diff --git a/host/commands/assemble_cvd/flags.h b/host/commands/assemble_cvd/flags.h
index 4278f35..34b3c4c 100644
--- a/host/commands/assemble_cvd/flags.h
+++ b/host/commands/assemble_cvd/flags.h
@@ -22,7 +22,7 @@
 Result<std::vector<KernelConfig>> GetKernelConfigAndSetDefaults();
 // Must be called after ParseCommandLineFlags.
 Result<CuttlefishConfig> InitializeCuttlefishConfiguration(
-    const std::string& root_dir, int modem_simulator_count,
+    const std::string& root_dir,
     const std::vector<KernelConfig>& kernel_configs,
     fruit::Injector<>& injector, const FetcherConfig& fetcher_config);
 
diff --git a/host/commands/console_forwarder/README.md b/host/commands/console_forwarder/README.md
index bc29757..45b662f 100644
--- a/host/commands/console_forwarder/README.md
+++ b/host/commands/console_forwarder/README.md
@@ -1,11 +1,7 @@
-_This page is best viewed on [codesearch]._
-
 Console pass-through to the serial console on a device to access a root shell.
 
 If a user invokes `launch_cvd --console` or `cvd start --console`, this
 executable runs to forward data from a serial console to a virtual terminal
 that can be accessed with `screen` from the host.
 
-![linkage](./linkage.svg)
-
-[codesearch]: https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/console_forwarder/README.md
+[![linkage](./doc/linkage.png)](https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/console_forwarder/doc/linkage.svg)
diff --git a/host/commands/console_forwarder/linkage.dot b/host/commands/console_forwarder/doc/linkage.dot
similarity index 100%
rename from host/commands/console_forwarder/linkage.dot
rename to host/commands/console_forwarder/doc/linkage.dot
diff --git a/host/commands/console_forwarder/doc/linkage.png b/host/commands/console_forwarder/doc/linkage.png
new file mode 100644
index 0000000..ddf7a84
--- /dev/null
+++ b/host/commands/console_forwarder/doc/linkage.png
Binary files differ
diff --git a/host/commands/console_forwarder/linkage.svg b/host/commands/console_forwarder/doc/linkage.svg
similarity index 100%
rename from host/commands/console_forwarder/linkage.svg
rename to host/commands/console_forwarder/doc/linkage.svg
diff --git a/host/commands/cvd/Android.bp b/host/commands/cvd/Android.bp
index 4f0e0d2..2dfa152 100644
--- a/host/commands/cvd/Android.bp
+++ b/host/commands/cvd/Android.bp
@@ -122,9 +122,8 @@
 cc_defaults {
     name: "cvd_and_fetch_cvd_defaults",
     static_libs: [
-        "libcvd_instance_db",
-        "libcvd_selector_frontend",
         "libcvd_parser",
+        "libcvd_selector",
         "libcvd_server_client",
         "libfetch_cvd",
     ],
diff --git a/host/commands/cvd/main.cc b/host/commands/cvd/main.cc
index 6c5fb40..1ae9fce 100644
--- a/host/commands/cvd/main.cc
+++ b/host/commands/cvd/main.cc
@@ -86,10 +86,8 @@
     return {};
   }
 
-  auto separated_args = CF_EXPECT(selector::SeparateArguments(all_args));
-  auto [pre, selector_args, post] = std::move(separated_args);
-  std::vector<std::string> args = std::move(pre);
-  std::move(post.begin(), post.end(), std::back_inserter(args));
+  auto [args, selector_args] =
+      CF_EXPECT(selector::GetCommandAndSelectorArguments(all_args));
 
   std::vector<std::string> env;
   for (char** e = envp; *e != 0; e++) {
diff --git a/host/commands/cvd/parser/Android.bp b/host/commands/cvd/parser/Android.bp
index af41e57..d7bade1 100644
--- a/host/commands/cvd/parser/Android.bp
+++ b/host/commands/cvd/parser/Android.bp
@@ -22,6 +22,7 @@
     srcs: [
         "instance/cf_vm_configs.cpp",
         "instance/cf_boot_configs.cpp",
+        "instance/cf_security_configs.cpp",
         "cf_configs_common.cpp",
         "cf_configs_instances.cpp",
         "load_configs_parser.cpp",
diff --git a/host/commands/cvd/parser/cf_configs_instances.cpp b/host/commands/cvd/parser/cf_configs_instances.cpp
index ad19d5f..7b2c2f5 100644
--- a/host/commands/cvd/parser/cf_configs_instances.cpp
+++ b/host/commands/cvd/parser/cf_configs_instances.cpp
@@ -22,6 +22,7 @@
 #include "common/libs/utils/flags_validator.h"
 #include "host/commands/cvd/parser/cf_configs_common.h"
 #include "host/commands/cvd/parser/instance/cf_boot_configs.h"
+#include "host/commands/cvd/parser/instance/cf_security_configs.h"
 #include "host/commands/cvd/parser/instance/cf_vm_configs.h"
 
 namespace cuttlefish {
@@ -29,6 +30,7 @@
 static std::map<std::string, Json::ValueType> kInstanceKeyMap = {
     {"vm", Json::ValueType::objectValue},
     {"boot", Json::ValueType::objectValue},
+    {"security", Json::ValueType::objectValue},
     {"disk", Json::ValueType::objectValue},
     {"graphics", Json::ValueType::objectValue},
     {"camera", Json::ValueType::objectValue},
@@ -52,6 +54,10 @@
     if (root[i].isMember("boot")) {
       CF_EXPECT(ValidateBootConfigs(root[i]["boot"]), "ValidateBootConfigs fail");
     }
+    if (root[i].isMember("security")) {
+      CF_EXPECT(ValidateSecurityConfigs(root[i]["security"]),
+                "ValidateSecurityConfigs fail");
+    }
   }
   CF_EXPECT(ValidateStringConfig(root, "vm", "setupwizard_mode",
                                  ValidateStupWizardMode),
@@ -63,11 +69,13 @@
 void InitInstancesConfigs(Json::Value& root) {
   InitVmConfigs(root);
   InitBootConfigs(root);
+  InitSecurityConfigs(root);
 }
 
 std::vector<std::string> GenerateInstancesConfigs(const Json::Value& root) {
   std::vector<std::string> result = GenerateVmConfigs(root);
   result = MergeResults(result, GenerateBootConfigs(root));
+  result = MergeResults(result, GenerateSecurityFlags(root));
   return result;
 }
 
diff --git a/host/commands/cvd/parser/instance/cf_boot_configs.cpp b/host/commands/cvd/parser/instance/cf_boot_configs.cpp
index f507ea1..5796bee 100644
--- a/host/commands/cvd/parser/instance/cf_boot_configs.cpp
+++ b/host/commands/cvd/parser/instance/cf_boot_configs.cpp
@@ -23,25 +23,16 @@
 
 namespace cuttlefish {
 
-static std::map<std::string, Json::ValueType> securitykeyMap = {
-    {"serial_number", Json::ValueType::stringValue}};
-
 static std::map<std::string, Json::ValueType> kernelkeyMap = {
     {"extra_kernel_cmdline", Json::ValueType::stringValue},
 };
 
 static std::map<std::string, Json::ValueType> kBootKeyMap = {
     {"extra_bootconfig_args", Json::ValueType::stringValue},
-    {"security", Json::ValueType::objectValue},
     {"kernel", Json::ValueType::objectValue},
     {"enable_bootanimation", Json::ValueType::booleanValue},
 };
 
-Result<void> ValidateSecurityConfigs(const Json::Value& root) {
-  CF_EXPECT(ValidateTypo(root, securitykeyMap), "ValidateSecurityConfigs ValidateTypo fail");
-  return {};
-}
-
 Result<void> ValidateKernelConfigs(const Json::Value& root) {
   CF_EXPECT(ValidateTypo(root, kernelkeyMap), "ValidateKernelConfigs ValidateTypo fail");
   return {};
@@ -50,10 +41,6 @@
 Result<void> ValidateBootConfigs(const Json::Value& root) {
   CF_EXPECT(ValidateTypo(root, kBootKeyMap), "ValidateBootConfigs ValidateTypo fail");
 
-  if (root.isMember("security")) {
-    CF_EXPECT(ValidateSecurityConfigs(root["security"]), "ValidateSecurityConfigs fail");
-  }
-
    if (root.isMember("kernel")) {
     CF_EXPECT(ValidateKernelConfigs(root["kernel"]), "ValidateKernelConfigs fail");
   }
@@ -66,8 +53,6 @@
                    CF_DEFAULTS_EXTRA_BOOTCONFIG_ARGS);
   InitBoolConfig(instances, "boot", "enable_bootanimation",
                  CF_DEFAULTS_ENABLE_BOOTANIMATION);
-  InitStringConfigSubGroup(instances, "boot", "security", "serial_number",
-                           CF_DEFAULTS_SERIAL_NUMBER);
   InitStringConfigSubGroup(instances, "boot", "kernel", "extra_kernel_cmdline",
                            CF_DEFAULTS_EXTRA_KERNEL_CMDLINE);
 }
@@ -78,8 +63,6 @@
                                     "extra_bootconfig_args"));
   result.emplace_back(GenerateStrGflag(instances, "enable_bootanimation",
                                        "boot", "enable_bootanimation"));
-  result.emplace_back(GenerateStrGflagSubGroup(instances, "serial_number", "boot",
-                                            "security", "serial_number"));
   result.emplace_back(GenerateStrGflagSubGroup(instances, "extra_kernel_cmdline",
                                             "boot", "kernel",
                                             "extra_kernel_cmdline"));
diff --git a/host/commands/cvd/parser/instance/cf_security_configs.cpp b/host/commands/cvd/parser/instance/cf_security_configs.cpp
new file mode 100644
index 0000000..3d58085
--- /dev/null
+++ b/host/commands/cvd/parser/instance/cf_security_configs.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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/commands/cvd/parser/instance/cf_security_configs.h"
+
+#include <android-base/logging.h>
+
+#include "host/commands/assemble_cvd/flags_defaults.h"
+#include "host/commands/cvd/parser/cf_configs_common.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+namespace cuttlefish {
+
+static std::map<std::string, Json::ValueType> kSecurityKeyMap = {
+    {"serial_number", Json::ValueType::stringValue}};
+
+Result<void> ValidateSecurityConfigs(const Json::Value& root) {
+  CF_EXPECT(ValidateTypo(root, kSecurityKeyMap),
+            "ValidateSecurityConfigs ValidateTypo fail");
+  return {};
+}
+
+/*This function is created to cover the initiation use_random_serial flag
+when the json value of serial_number equal "@random"
+*/
+void InitRandomSerialNumber(Json::Value& instances) {
+  int size = instances.size();
+  for (int i = 0; i < size; i++) {
+    std::string serial_number_str =
+        instances[i]["security"]["serial_number"].asString();
+    if (serial_number_str == "@random") {
+      instances[i]["security"]["use_random_serial"] = true;
+    } else {
+      instances[i]["security"]["use_random_serial"] = false;
+    }
+  }
+}
+
+void InitSecurityConfigs(Json::Value& instances) {
+  InitStringConfig(instances, "security", "serial_number",
+                   CF_DEFAULTS_SERIAL_NUMBER);
+  // This init should be called after the InitSecurityConfigs call, since it
+  // depends on  serial_number flag
+  InitRandomSerialNumber(instances);
+}
+
+std::vector<std::string> GenerateSecurityFlags(const Json::Value& instances) {
+  std::vector<std::string> result;
+  result.emplace_back(GenerateStrGflag(instances, "serial_number", "security",
+                                       "serial_number"));
+  result.emplace_back(GenerateBoolGflag(instances, "use_random_serial",
+                                        "security", "use_random_serial"));
+  return result;
+}
+
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/parser/instance/cf_security_configs.h b/host/commands/cvd/parser/instance/cf_security_configs.h
new file mode 100644
index 0000000..0d5c3b3
--- /dev/null
+++ b/host/commands/cvd/parser/instance/cf_security_configs.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ std::string * 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
+ std::string * 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 <string>
+#include <vector>
+
+#include <json/json.h>
+
+#include "common/libs/utils/result.h"
+
+namespace cuttlefish {
+Result<void> ValidateSecurityConfigs(const Json::Value& root);
+void InitSecurityConfigs(Json::Value& root);
+std::vector<std::string> GenerateSecurityFlags(const Json::Value& root);
+};  // namespace cuttlefish
diff --git a/host/commands/cvd/selector/Android.bp b/host/commands/cvd/selector/Android.bp
index 292badc..b08c648 100644
--- a/host/commands/cvd/selector/Android.bp
+++ b/host/commands/cvd/selector/Android.bp
@@ -18,23 +18,17 @@
 }
 
 cc_library_host_static {
-    name: "libcvd_selector_frontend",
+    name: "libcvd_selector",
     srcs: [
-        "selector_cmdline_parser.cpp",
-        "selector_flags_parser.cpp",
-        "selector_option_parser_utils.cpp",
-    ],
-    defaults: ["cvd_lib_defaults"],
-}
-
-cc_library_host_static {
-    name: "libcvd_instance_db",
-    srcs: [
+        "creation_analyzer.cpp",
         "instance_database.cpp",
         "instance_database_impl.cpp",
         "instance_database_utils.cpp",
         "instance_group_record.cpp",
         "instance_record.cpp",
+        "selector_cmdline_parser.cpp",
+        "selector_flags_parser.cpp",
+        "selector_option_parser_utils.cpp",
     ],
     defaults: ["cvd_lib_defaults"],
 }
diff --git a/host/commands/cvd/selector/creation_analyzer.cpp b/host/commands/cvd/selector/creation_analyzer.cpp
new file mode 100644
index 0000000..b8ea927
--- /dev/null
+++ b/host/commands/cvd/selector/creation_analyzer.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 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/commands/cvd/selector/creation_analyzer.h"
+
+#include <sys/types.h>
+
+#include <algorithm>
+#include <regex>
+#include <set>
+#include <string>
+
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "common/libs/utils/contains.h"
+#include "common/libs/utils/flag_parser.h"
+#include "common/libs/utils/users.h"
+#include "host/commands/cvd/selector/instance_database_utils.h"
+#include "host/commands/cvd/selector/selector_cmdline_parser.h"
+#include "host/commands/cvd/selector/selector_constants.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+namespace cuttlefish {
+namespace selector {
+
+Result<GroupCreationInfo> CreationAnalyzer::Analyze(
+    const CreationAnalyzerParam& param, const std::optional<ucred>& credential,
+    InstanceLockFileManager& instance_lock_file_manager) {
+  auto selector_options_parser =
+      CF_EXPECT(SelectorFlagsParser::ConductSelectFlagsParser(
+          param.selector_args, param.cmd_args, param.envs));
+  CreationAnalyzer analyzer(param, credential,
+                            std::move(selector_options_parser),
+                            instance_lock_file_manager);
+  auto result = CF_EXPECT(analyzer.Analyze());
+  return result;
+}
+
+CreationAnalyzer::CreationAnalyzer(
+    const CreationAnalyzerParam& param, const std::optional<ucred>& credential,
+    SelectorFlagsParser&& selector_options_parser,
+    InstanceLockFileManager& instance_file_lock_manager)
+    : cmd_args_(param.cmd_args),
+      envs_(param.envs),
+      selector_args_(param.selector_args),
+      credential_(credential),
+      selector_options_parser_{std::move(selector_options_parser)},
+      instance_file_lock_manager_{instance_file_lock_manager} {}
+
+static void PlaceHolder(InstanceLockFileManager&) {}
+
+Result<std::vector<PerInstanceInfo>>
+CreationAnalyzer::AnalyzeInstanceIdsWithLock() {
+  // TODO(kwstephenkim): implement AnalyzeInstanceIdsWithLock()
+  PlaceHolder(instance_file_lock_manager_);
+  return std::vector<PerInstanceInfo>{};
+}
+
+Result<GroupCreationInfo> CreationAnalyzer::Analyze() {
+  // TODO(kwstephenkim): check if the command is "start"
+  auto instance_info = CF_EXPECT(AnalyzeInstanceIdsWithLock());
+  group_name_ = AnalyzeGroupName(instance_info);
+  home_ = CF_EXPECT(AnalyzeHome());
+  // TODO(kwstephenkim): implement host_artifacts_path_
+  host_artifacts_path_ = "";
+
+  GroupCreationInfo report = {.home = home_,
+                              .host_artifacts_path = host_artifacts_path_,
+                              .group_name = group_name_,
+                              .instances = std::move(instance_info),
+                              .args = cmd_args_,
+                              .envs = envs_};
+  return report;
+}
+
+std::string CreationAnalyzer::AnalyzeGroupName(
+    const std::vector<PerInstanceInfo>&) const {
+  // TODO(kwstephenkim): implement AnalyzeGroupName()
+  return "";
+}
+
+Result<std::string> CreationAnalyzer::AnalyzeHome() const {
+  // TODO(kwstephenkim): implement AnalyzeHome()
+  return "";
+}
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/selector/creation_analyzer.h b/host/commands/cvd/selector/creation_analyzer.h
new file mode 100644
index 0000000..a5334c4
--- /dev/null
+++ b/host/commands/cvd/selector/creation_analyzer.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 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 <sys/socket.h>  // for ucred
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "common/libs/utils/result.h"
+
+#include "host/commands/cvd/instance_lock.h"
+#include "host/commands/cvd/selector/instance_database.h"
+#include "host/commands/cvd/selector/selector_cmdline_parser.h"
+#include "host/commands/cvd/selector/unique_resource_allocator.h"
+
+namespace cuttlefish {
+namespace selector {
+
+struct PerInstanceInfo {
+  // for the sake of std::vector::emplace_back
+  PerInstanceInfo(const unsigned id, const std::string& per_instance_name,
+                  InstanceLockFile&& instance_file_lock)
+      : instance_id_(id),
+        per_instance_name_(per_instance_name),
+        instance_file_lock_(std::move(instance_file_lock)) {}
+  const unsigned instance_id_;
+  const std::string per_instance_name_;
+  InstanceLockFile instance_file_lock_;
+};
+
+/**
+ * Creation is currently group by group
+ *
+ * If you want one instance, you should create a group with one instance.
+ */
+struct GroupCreationInfo {
+  std::string home;
+  std::string host_artifacts_path;  ///< e.g. out/host/linux-x86
+  std::string group_name;
+  std::vector<PerInstanceInfo> instances;
+  std::vector<std::string> args;
+  std::unordered_map<std::string, std::string> envs;
+};
+
+/**
+ * Instance IDs:
+ *  Use the InstanceNumCalculator's logic
+ *
+ * HOME directory:
+ *  If given in envs and is different from the system-wide home, use it
+ *  If not, try kParentOfDefaultHomeDirectories/.${group_name}
+ *
+ * host_artifacts_path:
+ *  ANDROID_HOST_OUT must be given.
+ *
+ * Group name:
+ *  if --group_name or --device_name is given, find the group name there
+ *  if --name is given and when it is a group name (i.e. --name=<one token>
+ *  and that one token is an eligible group name, and the operation is for
+ *  a group -- e.g. start), use the "name" as a group name
+ *  if a group name is not given, automatically generate:
+ *   default_prefix + "_" + android::base::Join(instance_ids, "_")
+ *
+ * Per-instance name:
+ *  When not given, use std::string(id) as the per instance name of each
+ *
+ * Number of instances:
+ *  Controlled by --instance_nums, --num_instances, etc.
+ *  Also controlled by --device_name or equivalent options
+ *
+ * p.s.
+ *  dependency: (a-->b means b depends on a)
+ *    group_name --> HOME
+ *    instance ids --> per_instance_name
+ *
+ */
+class CreationAnalyzer {
+ public:
+  struct CreationAnalyzerParam {
+    const std::vector<std::string>& cmd_args;
+    const std::unordered_map<std::string, std::string>& envs;
+    const std::vector<std::string>& selector_args;
+  };
+
+  static Result<GroupCreationInfo> Analyze(
+      const CreationAnalyzerParam& param,
+      const std::optional<ucred>& credential,
+      InstanceLockFileManager& instance_lock_file_manager);
+
+ private:
+  using IdAllocator = UniqueResourceAllocator<unsigned>;
+
+  CreationAnalyzer(const CreationAnalyzerParam& param,
+                   const std::optional<ucred>& credential,
+                   SelectorFlagsParser&& selector_options_parser_,
+                   InstanceLockFileManager& instance_lock_file_manager);
+
+  Result<GroupCreationInfo> Analyze();
+
+  /**
+   * calculate n_instances_ and instance_ids_
+   */
+  Result<std::vector<PerInstanceInfo>> AnalyzeInstanceIdsWithLock();
+
+  /*
+   * When group name is nil, it is auto-generated using instance ids
+   *
+   * if the given ids are {l, m, n}, the auto-generated group name will be
+   * GenDefaultGroupName() + "_l_m_n." If the ids set is equal to {1}, the
+   * auto-generated group name will be just GenDefaultGroupName()
+   *
+   */
+  std::string AnalyzeGroupName(const std::vector<PerInstanceInfo>&) const;
+
+  /**
+   * Figures out the HOME directory
+   *
+   *  If given in envs and is different from the system-wide home, use it
+   *  If not, try $(SYSTEM_WIDE_HOME)/.cuttlefish_home/group_name
+   *
+   * The issue here is, mostly, HOME is given anyway. How would we tell
+   * if the HOME is given explicitly or not?
+   * e.g. HOME=/any/path cvd start vs. cvd start
+   *
+   */
+  Result<std::string> AnalyzeHome() const;
+
+  // inputs
+  std::vector<std::string> cmd_args_;
+  std::unordered_map<std::string, std::string> envs_;
+  std::vector<std::string> selector_args_;
+  const std::optional<ucred> credential_;
+
+  // information to return later
+  std::string home_;
+  std::string host_artifacts_path_;  ///< e.g. out/host/linux-x86
+  std::string group_name_;
+  std::optional<std::vector<std::string>> per_instance_names_;
+
+  // internal, temporary
+  SelectorFlagsParser selector_options_parser_;
+  InstanceLockFileManager& instance_file_lock_manager_;
+};
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/selector/selector_cmdline_parser.cpp b/host/commands/cvd/selector/selector_cmdline_parser.cpp
index cdd2e1f..381495e 100644
--- a/host/commands/cvd/selector/selector_cmdline_parser.cpp
+++ b/host/commands/cvd/selector/selector_cmdline_parser.cpp
@@ -19,12 +19,19 @@
 #include <algorithm>
 #include <cctype>
 #include <deque>
+#include <iterator>
 #include <stack>
 
 namespace cuttlefish {
 namespace selector {
 namespace {
 
+struct SeparatedArguments {
+  std::vector<std::string> before_selector_opts;
+  std::vector<std::string> selector_specific;
+  std::vector<std::string> after_selector_opts;
+};
+
 enum class ParseState {
   kInit = 0,
   kSelector = 1,
@@ -32,8 +39,6 @@
   kParseError = 3
 };
 
-}  // namespace
-
 /*
  * Basically, cmd with the arguments would look this:
  *  $ cvd [ <selector options> ] <cvd options>
@@ -125,5 +130,16 @@
                              .after_selector_opts = after_selector_opts}};
 }
 
+}  // namespace
+
+Result<CommandAndSelectorArguments> GetCommandAndSelectorArguments(
+    const std::vector<std::string>& args) {
+  auto [pre, selector_specific, post] = CF_EXPECT(SeparateArguments(args));
+  std::vector<std::string> cmd_args{pre};
+  std::copy(post.begin(), post.end(), std::back_inserter(cmd_args));
+  return CommandAndSelectorArguments{.cmd_args = cmd_args,
+                                     .selector_args = selector_specific};
+}
+
 }  // namespace selector
 }  // namespace cuttlefish
diff --git a/host/commands/cvd/selector/selector_cmdline_parser.h b/host/commands/cvd/selector/selector_cmdline_parser.h
index 50f9c6f..a917f96 100644
--- a/host/commands/cvd/selector/selector_cmdline_parser.h
+++ b/host/commands/cvd/selector/selector_cmdline_parser.h
@@ -18,6 +18,7 @@
 
 #include <optional>
 #include <string>
+#include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
@@ -27,16 +28,12 @@
 namespace cuttlefish {
 namespace selector {
 
-struct SeparatedArguments {
-  std::vector<std::string> before_selector_opts;
-  std::vector<std::string> selector_specific;
-  std::vector<std::string> after_selector_opts;
+struct CommandAndSelectorArguments {
+  std::vector<std::string> cmd_args;
+  std::vector<std::string> selector_args;
 };
 
-/**
- * takes cmdline arguments, and separate them into 3 pieces above
- */
-Result<SeparatedArguments> SeparateArguments(
+Result<CommandAndSelectorArguments> GetCommandAndSelectorArguments(
     const std::vector<std::string>& args);
 
 /**
@@ -53,13 +50,21 @@
 class SelectorFlagsParser {
  public:
   static Result<SelectorFlagsParser> ConductSelectFlagsParser(
-      const std::vector<std::string>& args);
+      const std::vector<std::string>& selector_args,
+      const std::vector<std::string>& cmd_args,
+      const std::unordered_map<std::string, std::string>& envs);
   std::optional<std::string> GroupName() const;
   std::optional<std::vector<std::string>> PerInstanceNames() const;
   const auto& SubstringQueries() const { return substring_queries_; }
+  const std::optional<std::unordered_set<unsigned>>& InstanceIds() const {
+    return instance_ids_;
+  }
+  unsigned RequestedNumInstances() const { return requested_num_instances_; }
 
  private:
-  SelectorFlagsParser(const std::vector<std::string>& args);
+  SelectorFlagsParser(const std::vector<std::string>& selector_args,
+                      const std::vector<std::string>& cmd_args,
+                      const std::unordered_map<std::string, std::string>& envs);
 
   /*
    * Note: name may or may not be valid. A name could be a
@@ -102,13 +107,86 @@
       const std::optional<std::string>& per_instance_names) const;
   Result<std::string> HandleGroupName(
       const std::optional<std::string>& group_name) const;
+  struct InstanceIdsParams {
+    std::optional<std::string> num_instances;
+    std::optional<std::string> instance_nums;
+    std::optional<std::string> base_instance_num;
+    std::optional<unsigned> cuttlefish_instance_env;
+    std::optional<unsigned> vsoc_suffix;
+  };
 
+  class ParsedInstanceIdsOpt {
+    friend class SelectorFlagsParser;
+
+   private:
+    ParsedInstanceIdsOpt(const std::unordered_set<unsigned>& instance_ids)
+        : instance_ids_{instance_ids},
+          n_instances_{static_cast<unsigned>(instance_ids.size())} {}
+    ParsedInstanceIdsOpt(const unsigned n_instances)
+        : instance_ids_{std::nullopt}, n_instances_{n_instances} {}
+    auto GetInstanceIds() { return std::move(instance_ids_); }
+    unsigned GetNumOfInstances() const { return n_instances_; }
+    std::optional<std::unordered_set<unsigned>> instance_ids_;
+    const unsigned n_instances_;
+  };
+
+  /*
+   * CF_ERR is meant to be an error:
+   *  For example, --num_instances != |--instance_nums|.
+   *
+   * On the contrary, std::nullopt inside Result is not necessary one.
+   * std::nullopt inside Result means that with the given information,
+   * the instance_ids_ cannot be yet figured out, so the task is deferred
+   * to CreationAnaylizer or so, which has more contexts. For example,
+   * if no option at all is given, it is not an error; however, the
+   * SelectorFlagsParser alone cannot figure out the list of instance ids. The
+   * InstanceDatabase, UniqueResourceAllocator, InstanceLockFileManager will be
+   * involved to automatically generate the valid, numeric instance ids.
+   * If that's the case, Result{std::nullopt} could be returned.
+   *
+   */
+  Result<ParsedInstanceIdsOpt> HandleInstanceIds(
+      const InstanceIdsParams& instance_id_params);
+
+  struct InstanceFromEnvParam {
+    std::optional<unsigned> cuttlefish_instance_env;
+    std::optional<unsigned> vsoc_suffix;
+    std::optional<unsigned> num_instances;
+  };
+  std::optional<std::unordered_set<unsigned>> InstanceFromEnvironment(
+      const InstanceFromEnvParam& params);
+
+  struct VerifyNumOfInstancesParam {
+    std::optional<std::string> num_instances_flag;
+    std::optional<std::vector<std::string>> instance_names;
+    std::optional<std::string> instance_nums_flag;
+  };
+  Result<unsigned> VerifyNumOfInstances(
+      const VerifyNumOfInstancesParam& params,
+      const unsigned default_n_instances = 1) const;
   std::optional<std::string> group_name_;
   std::optional<std::vector<std::string>> instance_names_;
   std::unordered_set<std::string> substring_queries_;
+  /**
+   * The following are considered, and left empty if can't be figured out.
+   *
+   * --base_instance_num, --instance_nums, --num_instances,
+   * instance_names_.size(), CUTTLEFISH_INSTANCE, and vsoc-suffix if
+   * it is the user name.
+   *
+   * instance_names_.size() is effectively another --num_instances.
+   * CUTTLEFISH_INSTANCE and the suffix in order are considered as
+   * --base_instance_num if --base_instance_num is not given and
+   * --instance_nums is not given.
+   *
+   */
+  std::optional<std::unordered_set<unsigned>> instance_ids_;
+  unsigned requested_num_instances_;
 
-  // temporarily keeps the leftover of the input args
-  std::vector<std::string> args_;
+  // temporarily keeps the leftover of the input cmd_args
+  std::vector<std::string> selector_args_;
+  std::vector<std::string> cmd_args_;
+  std::unordered_map<std::string, std::string> envs_;
 };
 
 }  // namespace selector
diff --git a/host/commands/cvd/selector/selector_flags_parser.cpp b/host/commands/cvd/selector/selector_flags_parser.cpp
index 1525198..a2b7ca5 100644
--- a/host/commands/cvd/selector/selector_flags_parser.cpp
+++ b/host/commands/cvd/selector/selector_flags_parser.cpp
@@ -16,24 +16,42 @@
 
 #include "host/commands/cvd/selector/selector_cmdline_parser.h"
 
+#include <sstream>
+#include <string_view>
+
+#include <android-base/parseint.h>
 #include <android-base/strings.h>
 
 #include "host/commands/cvd/selector/instance_database_utils.h"
 #include "host/commands/cvd/selector/selector_constants.h"
 #include "host/commands/cvd/selector/selector_option_parser_utils.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/instance_nums.h"
 
 namespace cuttlefish {
 namespace selector {
 
+static Result<unsigned> ParseNaturalNumber(const std::string& token) {
+  std::int32_t value;
+  CF_EXPECT(android::base::ParseInt(token, &value));
+  CF_EXPECT(value > 0);
+  return static_cast<unsigned>(value);
+}
+
 Result<SelectorFlagsParser> SelectorFlagsParser::ConductSelectFlagsParser(
-    const std::vector<std::string>& args) {
-  SelectorFlagsParser parser(args);
+    const std::vector<std::string>& selector_args,
+    const std::vector<std::string>& cmd_args,
+    const std::unordered_map<std::string, std::string>& envs) {
+  SelectorFlagsParser parser(selector_args, cmd_args, envs);
   CF_EXPECT(parser.ParseOptions(), "selector option flag parsing failed.");
   return {std::move(parser)};
 }
 
-SelectorFlagsParser::SelectorFlagsParser(const std::vector<std::string>& args)
-    : args_(args) {}
+SelectorFlagsParser::SelectorFlagsParser(
+    const std::vector<std::string>& selector_args,
+    const std::vector<std::string>& cmd_args,
+    const std::unordered_map<std::string, std::string>& envs)
+    : selector_args_(selector_args), cmd_args_(cmd_args), envs_(envs) {}
 
 std::optional<std::string> SelectorFlagsParser::GroupName() const {
   return group_name_;
@@ -170,6 +188,153 @@
                           .instance_names = std::move(instance_names_output)}};
 }
 
+namespace {
+
+using Envs = std::unordered_map<std::string, std::string>;
+
+std::optional<unsigned> TryFromCuttlefishInstance(const Envs& envs) {
+  if (envs.find(kCuttlefishInstanceEnvVarName) == envs.end()) {
+    return std::nullopt;
+  }
+  const auto cuttlefish_instance = envs.at(kCuttlefishInstanceEnvVarName);
+  if (cuttlefish_instance.empty()) {
+    return std::nullopt;
+  }
+  auto parsed = ParseNaturalNumber(cuttlefish_instance);
+  return parsed.ok() ? std::optional(*parsed) : std::nullopt;
+}
+
+std::optional<unsigned> TryFromUser(const Envs& envs) {
+  if (envs.find("USER") == envs.end()) {
+    return std::nullopt;
+  }
+  std::string_view user{envs.at("USER")};
+  if (user.empty() || !android::base::ConsumePrefix(&user, kVsocUserPrefix)) {
+    return std::nullopt;
+  }
+  const auto& vsoc_num = user;
+  auto vsoc_id = ParseNaturalNumber(vsoc_num.data());
+  return vsoc_id.ok() ? std::optional(*vsoc_id) : std::nullopt;
+}
+
+}  // namespace
+
+std::optional<std::unordered_set<unsigned>>
+SelectorFlagsParser::InstanceFromEnvironment(
+    const InstanceFromEnvParam& params) {
+  const auto& cuttlefish_instance_env = params.cuttlefish_instance_env;
+  const auto& vsoc_suffix = params.vsoc_suffix;
+  const auto& num_instances = params.num_instances;
+
+  // see the logic in cuttlefish::InstanceFromEnvironment()
+  // defined in host/libs/config/cuttlefish_config.cpp
+  std::unordered_set<unsigned> nums;
+  std::optional<unsigned> base;
+  if (cuttlefish_instance_env) {
+    base = *cuttlefish_instance_env;
+  }
+  if (!base && vsoc_suffix) {
+    base = *vsoc_suffix;
+  }
+  if (!base) {
+    return std::nullopt;
+  }
+  // this is guaranteed by the caller
+  // assert(num_instances != std::nullopt);
+  for (unsigned i = 0; i != *num_instances; i++) {
+    nums.insert(base.value() + i);
+  }
+  return nums;
+}
+
+Result<unsigned> SelectorFlagsParser::VerifyNumOfInstances(
+    const VerifyNumOfInstancesParam& params,
+    const unsigned default_n_instances) const {
+  const auto& num_instances_flag = params.num_instances_flag;
+  const auto& instance_names = params.instance_names;
+  const auto& instance_nums_flag = params.instance_nums_flag;
+
+  std::optional<unsigned> num_instances;
+  if (num_instances_flag) {
+    num_instances = CF_EXPECT(ParseNaturalNumber(*num_instances_flag));
+  }
+  if (instance_names && !instance_names->empty()) {
+    auto implied_n_instances = instance_names->size();
+    if (num_instances) {
+      CF_EXPECT_EQ(*num_instances, static_cast<unsigned>(implied_n_instances),
+                   "The number of instances requested by --num_instances "
+                       << " are not the same as what is implied by "
+                       << " --name/device_name/instance_name.");
+    }
+    num_instances = implied_n_instances;
+  }
+  if (instance_nums_flag) {
+    std::vector<std::string> tokens =
+        android::base::Split(*instance_nums_flag, ",");
+    for (const auto& t : tokens) {
+      CF_EXPECT(ParseNaturalNumber(t), t << " must be a natural number");
+    }
+    if (!num_instances) {
+      num_instances = tokens.size();
+    }
+    CF_EXPECT_EQ(*num_instances, tokens.size(),
+                 "All information for the number of instances must match.");
+  }
+  return num_instances.value_or(default_n_instances);
+}
+
+Result<SelectorFlagsParser::ParsedInstanceIdsOpt>
+SelectorFlagsParser::HandleInstanceIds(
+    const InstanceIdsParams& instance_id_params) {
+  const auto& instance_nums = instance_id_params.instance_nums;
+  const auto& base_instance_num = instance_id_params.base_instance_num;
+  const auto& cuttlefish_instance_env =
+      instance_id_params.cuttlefish_instance_env;
+  const auto& vsoc_suffix = instance_id_params.vsoc_suffix;
+
+  // calculate and/or verify the number of instances
+  unsigned num_instances =
+      CF_EXPECT(VerifyNumOfInstances(VerifyNumOfInstancesParam{
+          .num_instances_flag = instance_id_params.num_instances,
+          .instance_names = instance_names_,
+          .instance_nums_flag = instance_nums}));
+
+  if (!instance_nums && !base_instance_num) {
+    // num_instances is given. if non-std::nullopt is returned,
+    // the base is also figured out. If base can't be figured out,
+    // std::nullopt is returned.
+    auto instance_ids = InstanceFromEnvironment(
+        {.cuttlefish_instance_env = cuttlefish_instance_env,
+         .vsoc_suffix = vsoc_suffix,
+         .num_instances = num_instances});
+    if (instance_ids) {
+      return ParsedInstanceIdsOpt(*instance_ids);
+    }
+    // the return value, n_instances is the "desired/requested" instances
+    // When instance_ids set isn't figured out, n_instances is not meant to
+    // be always zero; it could be any natural number.
+    return ParsedInstanceIdsOpt(num_instances);
+  }
+
+  InstanceNumsCalculator calculator;
+  calculator.NumInstances(static_cast<std::int32_t>(num_instances));
+  if (instance_nums) {
+    calculator.InstanceNums(*instance_nums);
+  }
+  if (base_instance_num) {
+    unsigned base = CF_EXPECT(ParseNaturalNumber(*base_instance_num));
+    calculator.BaseInstanceNum(static_cast<std::int32_t>(base));
+  }
+  auto instance_ids = std::move(CF_EXPECT(calculator.CalculateFromFlags()));
+  CF_EXPECT(!instance_ids.empty(),
+            "CalculateFromFlags() must be called when --num_instances or "
+                << "--base_instance_num is given, and must not return an "
+                << "empty set");
+  auto instance_ids_hash_set =
+      std::unordered_set<unsigned>{instance_ids.begin(), instance_ids.end()};
+  return ParsedInstanceIdsOpt{instance_ids_hash_set};
+}
+
 Result<void> SelectorFlagsParser::ParseOptions() {
   // Handling name-related options
   std::optional<std::string> names;
@@ -188,7 +353,7 @@
   for (auto& [flag_name, value] : key_optional_map) {
     // value is set to std::nullopt if parsing failed or no flag_name flag is
     // given.
-    CF_EXPECT(FilterSelectorFlag(args_, flag_name, value));
+    CF_EXPECT(FilterSelectorFlag(selector_args_, flag_name, value));
   }
 
   NameFlagsParam name_flags_param{
@@ -200,7 +365,25 @@
   group_name_ = parsed_name_flags.group_name;
   instance_names_ = parsed_name_flags.instance_names;
 
-  if (args_.empty()) {
+  std::optional<std::string> num_instances;
+  std::optional<std::string> instance_nums;
+  std::optional<std::string> base_instance_num;
+  // set num_instances as std::nullptr or the value of --num_instances
+  FilterSelectorFlag(cmd_args_, "num_instances", num_instances);
+  FilterSelectorFlag(cmd_args_, "instance_nums", instance_nums);
+  FilterSelectorFlag(cmd_args_, "base_instance_num", base_instance_num);
+
+  InstanceIdsParams instance_nums_param{
+      .num_instances = std::move(num_instances),
+      .instance_nums = std::move(instance_nums),
+      .base_instance_num = std::move(base_instance_num),
+      .cuttlefish_instance_env = TryFromCuttlefishInstance(envs_),
+      .vsoc_suffix = TryFromUser(envs_)};
+  auto parsed_ids = CF_EXPECT(HandleInstanceIds(instance_nums_param));
+  requested_num_instances_ = parsed_ids.GetNumOfInstances();
+  instance_ids_ = std::move(parsed_ids.GetInstanceIds());
+
+  if (selector_args_.empty()) {
     return {};
   }
   substring_queries_ = CF_EXPECT(FindSubstringsToMatch());
@@ -214,20 +397,20 @@
 Result<std::unordered_set<std::string>>
 SelectorFlagsParser::FindSubstringsToMatch() {
   std::unordered_set<std::string> substring_queries;
-  const auto args_size = args_.size();
-  for (int i = 0; i < args_size; i++) {
+  const auto selector_args_size = selector_args_.size();
+  for (int i = 0; i < selector_args_size; i++) {
     /*
      * Logically, the order does not matter. The reason why we start from
      * behind is that pop_back() of a vector is much cheaper than pop_front()
      */
-    const auto& substring = args_.back();
+    const auto& substring = selector_args_.back();
     auto tokens = android::base::Split(substring, ",");
     for (const auto& t : tokens) {
       CF_EXPECT(!t.empty(),
                 "Empty keyword for substring search is not allowed.");
       substring_queries_.insert(t);
     }
-    args_.pop_back();
+    selector_args_.pop_back();
   }
   return {substring_queries};
 }
diff --git a/host/commands/cvd/unittests/parser/instance/boot_configs_test.cc b/host/commands/cvd/unittests/parser/instance/boot_configs_test.cc
index e853774..17bf24d 100644
--- a/host/commands/cvd/unittests/parser/instance/boot_configs_test.cc
+++ b/host/commands/cvd/unittests/parser/instance/boot_configs_test.cc
@@ -222,16 +222,12 @@
     "instances" :
     [
         {
-            "boot": {
-                "security": {
-                }
+            "security": {
             }
         },
         {
-            "boot": {
-                "security": {
-                    "serial_number": "CUTTLEFISHCVD101"
-                }
+            "security": {
+                "serial_number": "CUTTLEFISHCVD101"
             }
         }
     ]
@@ -256,17 +252,13 @@
     "instances" :
     [
         {
-            "boot": {
-                "security": {
-                    "serial_number": "CUTTLEFISHCVD101"
-                }
+            "security": {
+                "serial_number": "CUTTLEFISHCVD101"
             }
         },
         {
-            "boot": {
-                "security": {
-                    "serial_number": "CUTTLEFISHCVD102"
-                }
+            "security": {
+                "serial_number": "CUTTLEFISHCVD102"
             }
         }
     ]
@@ -285,6 +277,91 @@
       << "serial_number flag is missing or wrongly formatted";
 }
 
+TEST(BootFlagsParserTest, ParseTwoInstancesRandomSerialFlagEmptyJson) {
+  const char* test_string = R""""(
+{
+    "instances" :
+    [
+        {
+        },
+        {
+        }
+    ]
+}
+  )"""";
+
+  Json::Value json_configs;
+  std::string json_text(test_string);
+
+  EXPECT_TRUE(ParseJsonString(json_text, json_configs))
+      << "Invalid Json string";
+  auto serialized_data = ParseCvdConfigs(json_configs);
+  EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace();
+  EXPECT_TRUE(
+      FindConfig(*serialized_data, R"(--use_random_serial=false,false)"))
+      << "use_random_serial flag is missing or wrongly formatted";
+}
+
+TEST(BootFlagsParserTest, ParseTwoInstancesRandomSerialFlagPartialJson) {
+  const char* test_string = R""""(
+{
+    "instances" :
+    [
+        {
+            "security": {
+                "serial_number": "CUTTLEFISHCVD101"
+            }
+        },
+        {
+            "security": {
+                "serial_number": "@random"
+            }
+        }
+    ]
+}
+  )"""";
+
+  Json::Value json_configs;
+  std::string json_text(test_string);
+
+  EXPECT_TRUE(ParseJsonString(json_text, json_configs))
+      << "Invalid Json string";
+  auto serialized_data = ParseCvdConfigs(json_configs);
+  EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace();
+  EXPECT_TRUE(FindConfig(*serialized_data, R"(--use_random_serial=false,true)"))
+      << "use_random_serial flag is missing or wrongly formatted";
+}
+
+TEST(BootFlagsParserTest, ParseTwoInstancesRandomSerialFlagFullJson) {
+  const char* test_string = R""""(
+{
+    "instances" :
+    [
+        {
+            "security": {
+                "serial_number": "@random"
+            }
+        },
+        {
+            "security": {
+                "serial_number": "@random"
+            }
+        }
+    ]
+}
+  )"""";
+
+  Json::Value json_configs;
+  std::string json_text(test_string);
+
+  EXPECT_TRUE(ParseJsonString(json_text, json_configs))
+      << "Invalid Json string";
+  auto serialized_data = ParseCvdConfigs(json_configs);
+  EXPECT_TRUE(serialized_data.ok()) << serialized_data.error().Trace();
+  EXPECT_TRUE(FindConfig(*serialized_data, R"(--use_random_serial=true,true)"))
+      << "use_random_serial flag is missing or wrongly formatted";
+}
+
 TEST(BootFlagsParserTest, ParseTwoInstancesKernelCmdFlagEmptyJson) {
   const char* test_string = R""""(
 {
diff --git a/host/commands/cvd/unittests/selector/Android.bp b/host/commands/cvd/unittests/selector/Android.bp
index ad41ae1..ac1b247 100644
--- a/host/commands/cvd/unittests/selector/Android.bp
+++ b/host/commands/cvd/unittests/selector/Android.bp
@@ -20,7 +20,6 @@
 cc_test_host {
     name: "cvd_db_instance_test",
     srcs: [
-        "test_group_record.cpp",
         "test_instance_record.cpp",
     ],
     test_options: {
@@ -30,10 +29,20 @@
 }
 
 cc_test_host {
+    name: "cvd_db_group_test",
+    srcs: [
+        "test_group_record.cpp",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    defaults: ["cvd_and_fetch_cvd_defaults"],
+}
+
+cc_test_host {
     name: "cvd_db_test",
     srcs: [
         "instance_database_test_helper.cpp",
-        "test_id_allocator.cpp",
         "test_instance_database.cpp",
     ],
     test_options: {
@@ -43,9 +52,25 @@
 }
 
 cc_test_host {
+    name: "cvd_id_allocator_test",
+    srcs: [
+        "test_id_allocator.cpp",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    defaults: ["cvd_and_fetch_cvd_defaults"],
+}
+
+cc_test_host {
     name: "cvd_selector_parser_test",
     srcs: [
-        "test_cvd_selector_parser.cpp",
+        "selector_parser_ids_test_helper.cpp",
+        "selector_parser_names_test_helper.cpp",
+        "selector_parser_substring_test_helper.cpp",
+        "test_cvd_selector_parser_ids.cpp",
+        "test_cvd_selector_parser_names.cpp",
+        "test_cvd_selector_parser_substring.cpp",
     ],
     test_options: {
         unit_test: true,
diff --git a/host/commands/cvd/unittests/selector/id_allocator_test_helper.h b/host/commands/cvd/unittests/selector/id_allocator_test_helper.h
new file mode 100644
index 0000000..7719897
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/id_allocator_test_helper.h
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2022 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 <gtest/gtest.h>
+
+#include <vector>
+
+namespace cuttlefish {
+namespace selector {
+
+// Get one unique item at a time
+class OneEachTest : public testing::TestWithParam<std::vector<unsigned>> {};
+
+/*
+ * ClaimAll, StrideBeyond1, Consecutive, Take, TakeAll, TakeRange,
+ * ReclaimAll, ReclaimEmptyPool
+ *
+ */
+class CvdIdAllocatorTest : public testing::Test {};
+
+/*
+ * Reclaim and Take tests
+ */
+class ReclaimTest : public testing::TestWithParam<std::vector<unsigned>> {};
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/selector_parser_ids_test_helper.cpp b/host/commands/cvd/unittests/selector/selector_parser_ids_test_helper.cpp
new file mode 100644
index 0000000..493b9cb
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/selector_parser_ids_test_helper.cpp
@@ -0,0 +1,48 @@
+//
+// Copyright (C) 2022 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/commands/cvd/unittests/selector/selector_parser_ids_test_helper.h"
+
+#include <android-base/strings.h>
+
+#include "host/commands/cvd/selector/selector_cmdline_parser.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+namespace cuttlefish {
+namespace selector {
+
+InstanceIdTest::InstanceIdTest() {
+  auto [input, cuttlefish_instance, ids, num_instances, result] = GetParam();
+  auto args = android::base::Tokenize(input, " ");
+  flag_separation_result_ = GetCommandAndSelectorArguments(args);
+  if (!flag_separation_result_.ok()) {
+    return;
+  }
+  auto [cmd_args, selector_args] = *flag_separation_result_;
+  if (cuttlefish_instance) {
+    envs_[kCuttlefishInstanceEnvVarName] = cuttlefish_instance.value();
+  }
+  auto parse_result = SelectorFlagsParser::ConductSelectFlagsParser(
+      selector_args, cmd_args, envs_);
+  if (parse_result.ok()) {
+    parser_ = std::move(*parse_result);
+  }
+  expected_ids_ = std::move(ids);
+  expected_result_ = result;
+  requested_num_instances_ = num_instances;
+}
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/selector_parser_ids_test_helper.h b/host/commands/cvd/unittests/selector/selector_parser_ids_test_helper.h
new file mode 100644
index 0000000..a637643
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/selector_parser_ids_test_helper.h
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2022 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 <optional>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "host/commands/cvd/selector/selector_cmdline_parser.h"
+
+namespace cuttlefish {
+namespace selector {
+
+using Envs = std::unordered_map<std::string, std::string>;
+using Args = std::vector<std::string>;
+
+struct InstanceIdTestInput {
+  std::string input_args;
+  std::optional<std::string> cuttlefish_instance;
+  std::optional<std::unordered_set<unsigned>> expected_ids;
+  unsigned requested_num_instances;
+  bool expected_result;
+};
+
+class InstanceIdTest : public testing::TestWithParam<InstanceIdTestInput> {
+ protected:
+  InstanceIdTest();
+
+  bool expected_result_;
+  unsigned requested_num_instances_;
+  std::optional<std::unordered_set<unsigned>> expected_ids_;
+  std::unordered_map<std::string, std::string> envs_;
+  std::optional<SelectorFlagsParser> parser_;
+  Result<CommandAndSelectorArguments> flag_separation_result_;
+};
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/selector_parser_names_test_helper.cpp b/host/commands/cvd/unittests/selector/selector_parser_names_test_helper.cpp
new file mode 100644
index 0000000..29d9c24
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/selector_parser_names_test_helper.cpp
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2022 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/commands/cvd/unittests/selector/selector_parser_names_test_helper.h"
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "host/commands/cvd/selector/selector_cmdline_parser.h"
+#include "host/commands/cvd/selector/selector_option_parser_utils.h"
+
+namespace cuttlefish {
+namespace selector {
+
+ValidNamesTest::ValidNamesTest() { Init(); }
+
+void ValidNamesTest::Init() {
+  auto [input, expected_output] = GetParam();
+  selector_args_ = android::base::Tokenize(input, " ");
+  expected_output_ = std::move(expected_output);
+  auto parse_result = SelectorFlagsParser::ConductSelectFlagsParser(
+      selector_args_, Args{}, Envs{});
+  if (parse_result.ok()) {
+    parser_ = std::move(*parse_result);
+  }
+}
+
+InvalidNamesTest::InvalidNamesTest() {
+  auto input = GetParam();
+  auto selector_args = android::base::Tokenize(input, " ");
+  auto parse_result = SelectorFlagsParser::ConductSelectFlagsParser(
+      selector_args, Args{}, Envs{});
+  if (parse_result.ok()) {
+    parser_ = std::move(*parse_result);
+  }
+}
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/selector_parser_names_test_helper.h b/host/commands/cvd/unittests/selector/selector_parser_names_test_helper.h
new file mode 100644
index 0000000..ea164b9
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/selector_parser_names_test_helper.h
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2022 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 <optional>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "host/commands/cvd/selector/selector_cmdline_parser.h"
+
+namespace cuttlefish {
+namespace selector {
+
+using Envs = std::unordered_map<std::string, std::string>;
+using Args = std::vector<std::string>;
+
+struct ExpectedOutput {
+  std::optional<std::vector<std::string>> names;
+  std::optional<std::string> group_name;
+  std::optional<std::vector<std::string>> per_instance_names;
+};
+
+struct InputOutput {
+  std::string input;
+  ExpectedOutput expected;
+};
+
+class ValidNamesTest : public testing::TestWithParam<InputOutput> {
+ protected:
+  ValidNamesTest();
+  void Init();
+
+  std::vector<std::string> selector_args_;
+  ExpectedOutput expected_output_;
+  std::optional<SelectorFlagsParser> parser_;
+};
+
+class InvalidNamesTest : public testing::TestWithParam<std::string> {
+ protected:
+  InvalidNamesTest();
+
+  std::optional<SelectorFlagsParser> parser_;
+};
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/selector_parser_substring_test_helper.cpp b/host/commands/cvd/unittests/selector/selector_parser_substring_test_helper.cpp
new file mode 100644
index 0000000..6d25197
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/selector_parser_substring_test_helper.cpp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2022 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/commands/cvd/unittests/selector/selector_parser_substring_test_helper.h"
+
+#include <android-base/strings.h>
+
+namespace cuttlefish {
+namespace selector {
+
+SubstringTest::SubstringTest() {
+  auto [input, expected] = GetParam();
+  auto selector_args = android::base::Tokenize(input, " ");
+  auto parse_result = SelectorFlagsParser::ConductSelectFlagsParser(
+      selector_args, Args{}, Envs{});
+  if (parse_result.ok()) {
+    parser_ = std::move(*parse_result);
+  }
+  expected_result_ = expected;
+}
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/selector_parser_substring_test_helper.h b/host/commands/cvd/unittests/selector/selector_parser_substring_test_helper.h
new file mode 100644
index 0000000..f1cd32f
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/selector_parser_substring_test_helper.h
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2022 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 <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "host/commands/cvd/selector/selector_cmdline_parser.h"
+
+namespace cuttlefish {
+namespace selector {
+
+using Envs = std::unordered_map<std::string, std::string>;
+using Args = std::vector<std::string>;
+
+struct SubstringTestInput {
+  std::string input_args;
+  bool expected;
+};
+
+class SubstringTest : public testing::TestWithParam<SubstringTestInput> {
+ protected:
+  SubstringTest();
+
+  bool expected_result_;
+  std::optional<SelectorFlagsParser> parser_;
+};
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/test_cvd_selector_parser.cpp b/host/commands/cvd/unittests/selector/test_cvd_selector_parser.cpp
deleted file mode 100644
index c64eb74..0000000
--- a/host/commands/cvd/unittests/selector/test_cvd_selector_parser.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-//
-// Copyright (C) 2022 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 <optional>
-#include <string>
-#include <vector>
-
-#include <android-base/strings.h>
-#include <gtest/gtest.h>
-
-#include "host/commands/cvd/selector/selector_cmdline_parser.h"
-#include "host/commands/cvd/selector/selector_option_parser_utils.h"
-
-namespace cuttlefish {
-namespace selector {
-namespace {
-
-struct ExpectedOutput {
-  std::optional<std::vector<std::string>> names;
-  std::optional<std::string> group_name;
-  std::optional<std::vector<std::string>> per_instance_names;
-};
-
-struct InputOutput {
-  std::string input;
-  ExpectedOutput expected;
-};
-
-}  // namespace
-
-class CvdSelectorParserNamesTest : public testing::TestWithParam<InputOutput> {
- protected:
-  CvdSelectorParserNamesTest() { Init(); }
-  void Init() {
-    auto [input, expected_output] = GetParam();
-    selector_args_ = android::base::Tokenize(input, " ");
-    expected_output_ = std::move(expected_output);
-    auto parse_result =
-        SelectorFlagsParser::ConductSelectFlagsParser(selector_args_);
-    if (parse_result.ok()) {
-      parser_ = std::move(*parse_result);
-    }
-  }
-
-  std::vector<std::string> selector_args_;
-  ExpectedOutput expected_output_;
-  std::optional<SelectorFlagsParser> parser_;
-};
-
-TEST_P(CvdSelectorParserNamesTest, ValidInputs) {
-  /*
-   * if ParseOptions() is successful, parser_ is not nullopt
-   */
-  ASSERT_TRUE(parser_);
-}
-
-/**
- * Note that invalid inputs must be tested at the InstanceDatabase level
- */
-TEST_P(CvdSelectorParserNamesTest, FieldsNoSubstring) {
-  if (!parser_) {
-    /*
-     * We aren't testing whether or not parsing is working.
-     * That's tested in ValidInputs tests. We test fields.
-     */
-    GTEST_SKIP() << "Parsing failed, which must be tested in ValidInputs Test";
-  }
-
-  ASSERT_EQ(parser_->GroupName(), expected_output_.group_name);
-  ASSERT_EQ(parser_->PerInstanceNames(), expected_output_.per_instance_names);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    CvdParser, CvdSelectorParserNamesTest,
-    testing::Values(
-        InputOutput{.input = "--name=cf",
-                    .expected = ExpectedOutput{.group_name = "cf"}},
-        InputOutput{.input = "--name=cvd,cf",
-                    .expected = ExpectedOutput{.per_instance_names =
-                                                   std::vector<std::string>{
-                                                       "cvd", "cf"}}},
-        InputOutput{.input = "--name=cf-09,cf-tv",
-                    .expected = ExpectedOutput{.group_name = "cf",
-                                               .per_instance_names =
-                                                   std::vector<std::string>{
-                                                       "09", "tv"}}},
-        InputOutput{
-            .input = "--device_name cf-09",
-            .expected = ExpectedOutput{.group_name = "cf",
-                                       .per_instance_names =
-                                           std::vector<std::string>{"09"}}},
-        InputOutput{.input = "--device_name my_cool-phone,my_cool-tv",
-                    .expected = ExpectedOutput{.group_name = "my_cool",
-                                               .per_instance_names =
-                                                   std::vector<std::string>{
-                                                       "phone", "tv"}}},
-        InputOutput{
-            .input = "--group_name=my_cool --instance_name=phone",
-            .expected = ExpectedOutput{.group_name = "my_cool",
-                                       .per_instance_names =
-                                           std::vector<std::string>{"phone"}}},
-        InputOutput{.input = "--group_name=my_cool --instance_name=phone,tv",
-                    .expected = ExpectedOutput{.group_name = "my_cool",
-                                               .per_instance_names =
-                                                   std::vector<std::string>{
-                                                       "phone", "tv"}}},
-        InputOutput{
-            .input = "--group_name=my_cool",
-            .expected =
-                ExpectedOutput{
-                    .group_name = "my_cool",
-                }},
-        InputOutput{
-            .input = "--instance_name=my_cool",
-            .expected = ExpectedOutput{
-                .per_instance_names = std::vector<std::string>{"my_cool"}}}));
-
-class CvdSelectorParserInvalidNamesTest
-    : public testing::TestWithParam<std::string> {
- protected:
-  CvdSelectorParserInvalidNamesTest() {
-    input_ = GetParam();
-    selector_args_ = android::base::Tokenize(input_, " ");
-  }
-  std::string input_;
-  std::vector<std::string> selector_args_;
-};
-
-TEST_P(CvdSelectorParserInvalidNamesTest, InvalidInputs) {
-  auto parse_result =
-      SelectorFlagsParser::ConductSelectFlagsParser(selector_args_);
-
-  ASSERT_FALSE(parse_result.ok()) << "Failed with " << input_;
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    CvdParser2, CvdSelectorParserInvalidNamesTest,
-    testing::Values("--name", "--name=?34", "--device_name=abcd",
-                    "--group_name=3ab", "--name=x --device_name=y",
-                    "--name=x --group_name=cf",
-                    "--device_name=z --instance_name=p", "--instance_name=*79a",
-                    "--device_name=abcd-e,xyz-f", "--device_name=xyz-e,xyz-e"));
-
-struct SubstringTestInput {
-  std::string input_args;
-  bool expected;
-};
-
-class CvdSelectorParserSubstringTest
-    : public testing::TestWithParam<SubstringTestInput> {
- protected:
-  CvdSelectorParserSubstringTest() {
-    auto [input, expected] = GetParam();
-    auto selector_args = android::base::Tokenize(input, " ");
-    auto parse_result =
-        SelectorFlagsParser::ConductSelectFlagsParser(selector_args);
-    if (parse_result.ok()) {
-      parser_ = std::move(*parse_result);
-    }
-    expected_result_ = expected;
-  }
-  bool expected_result_;
-  std::optional<SelectorFlagsParser> parser_;
-};
-
-TEST_P(CvdSelectorParserSubstringTest, Substring) {
-  ASSERT_EQ(parser_ != std::nullopt, expected_result_);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    CvdParser3, CvdSelectorParserSubstringTest,
-    testing::Values(SubstringTestInput{"--name cvd", true},
-                    SubstringTestInput{"c v --name cvd d", true},
-                    SubstringTestInput{"--name cvd c", true},
-                    SubstringTestInput{"--name cvd c v", true},
-                    SubstringTestInput{"c --name cvd v", true},
-                    SubstringTestInput{"--name cvd c,v,d", true},
-                    SubstringTestInput{"--name cvd c v,d", true},
-                    SubstringTestInput{"--name cvd c,", false},
-                    SubstringTestInput{"--name cvd c v,,d", false}));
-
-}  // namespace selector
-}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/test_cvd_selector_parser_ids.cpp b/host/commands/cvd/unittests/selector/test_cvd_selector_parser_ids.cpp
new file mode 100644
index 0000000..e2f4068
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/test_cvd_selector_parser_ids.cpp
@@ -0,0 +1,135 @@
+//
+// Copyright (C) 2022 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 <optional>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "host/commands/cvd/selector/selector_cmdline_parser.h"
+#include "host/commands/cvd/unittests/selector/selector_parser_ids_test_helper.h"
+
+namespace cuttlefish {
+namespace selector {
+
+TEST_P(InstanceIdTest, InstanceIdCalculation) {
+  if (!flag_separation_result_.ok()) {
+    GTEST_SKIP()
+        << "Selector and Command Args separation failed. "
+        << "Developers must make sure that this does not happen. "
+        << "This is not what InstanceIdCalculation test intended to test.";
+  }
+
+  ASSERT_EQ(parser_ != std::nullopt, expected_result_);
+  if (!expected_result_) {
+    return;
+  }
+  ASSERT_EQ(parser_->InstanceIds(), expected_ids_);
+  ASSERT_EQ(parser_->RequestedNumInstances(), requested_num_instances_);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    CvdParser, InstanceIdTest,
+    testing::Values(
+        InstanceIdTestInput{.input_args = "",
+                            .cuttlefish_instance = std::nullopt,
+                            .expected_ids = std::nullopt,
+                            .requested_num_instances = 1,
+                            .expected_result = true},
+        InstanceIdTestInput{.input_args = "",
+                            .cuttlefish_instance = "8",
+                            .expected_ids = std::unordered_set<unsigned>{8},
+                            .requested_num_instances = 1,
+                            .expected_result = true},
+        InstanceIdTestInput{.input_args = "--num_instances=2",
+                            .expected_ids = std::nullopt,
+                            .requested_num_instances = 2,
+                            .expected_result = true},
+        InstanceIdTestInput{.input_args = "--num_instances=2",
+                            .cuttlefish_instance = "8",
+                            .expected_ids = std::unordered_set<unsigned>{8, 9},
+                            .requested_num_instances = 2,
+                            .expected_result = true},
+        InstanceIdTestInput{
+            .input_args = "--base_instance_num=10 --num_instances=2",
+            .cuttlefish_instance = "8",
+            .expected_ids = std::unordered_set<unsigned>{10, 11},
+            .requested_num_instances = 2,
+            .expected_result = true},
+        InstanceIdTestInput{.input_args = "--instance_nums 2",
+                            .cuttlefish_instance = std::nullopt,
+                            .expected_ids = std::unordered_set<unsigned>{2},
+                            .requested_num_instances = 1,
+                            .expected_result = true},
+        InstanceIdTestInput{
+            .input_args = "--instance_nums 2,5,6",
+            .cuttlefish_instance = std::nullopt,
+            .expected_ids = std::unordered_set<unsigned>{2, 5, 6},
+            .requested_num_instances = 3,
+            .expected_result = true},
+        InstanceIdTestInput{
+            .input_args = "--instance_nums 2,5,6 --num_instances=3",
+            .cuttlefish_instance = std::nullopt,
+            .expected_ids = std::unordered_set<unsigned>{2, 5, 6},
+            .requested_num_instances = 3,
+            .expected_result = true},
+        InstanceIdTestInput{
+            .input_args = "[--device_name=c-1,c-3,c-5] "
+                          "--instance_nums 2,5,6 --num_instances=3",
+            .cuttlefish_instance = std::nullopt,
+            .expected_ids = std::unordered_set<unsigned>{2, 5, 6},
+            .requested_num_instances = 3,
+            .expected_result = true},
+        InstanceIdTestInput{.input_args = "[--device_name=c-1,c-3,c-5]",
+                            .cuttlefish_instance = std::nullopt,
+                            .expected_ids = std::nullopt,
+                            .requested_num_instances = 3,
+                            .expected_result = true},
+        // CUTTLEFISH_INSTANCE should be ignored
+        InstanceIdTestInput{
+            .input_args = "--instance_nums 2,5,6 --num_instances=3",
+            .cuttlefish_instance = "8",
+            .expected_ids = std::unordered_set<unsigned>{2, 5, 6},
+            .requested_num_instances = 3,
+            .expected_result = true},
+        // instance_nums and num_instances mismatch
+        InstanceIdTestInput{
+            .input_args = "--instance_nums 2,5,6 --num_instances=7",
+            .cuttlefish_instance = std::nullopt,
+            .expected_ids = std::unordered_set<unsigned>{2, 5, 6},
+            .requested_num_instances = 3,
+            .expected_result = false},
+        // device_name requested 2 instances while instance_nums 3.
+        InstanceIdTestInput{
+            .input_args = "[--device_name=c-1,c-3] --instance_nums 2,5,6 "
+                          "--num_instances=3",
+            .cuttlefish_instance = std::nullopt,
+            .expected_ids = std::unordered_set<unsigned>{2, 5, 6},
+            .requested_num_instances = 3,
+            .expected_result = false},
+        // --base_instance_num is not allowed with --instance_nums
+        InstanceIdTestInput{
+            .input_args = "--instance_nums 2,5,6 --base_instance_num=7",
+            .cuttlefish_instance = std::nullopt,
+            .expected_ids = std::unordered_set<unsigned>{2, 5, 6},
+            .requested_num_instances = 3,
+            .expected_result = false}));
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/test_cvd_selector_parser_names.cpp b/host/commands/cvd/unittests/selector/test_cvd_selector_parser_names.cpp
new file mode 100644
index 0000000..2119c33
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/test_cvd_selector_parser_names.cpp
@@ -0,0 +1,95 @@
+//
+// Copyright (C) 2022 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/commands/cvd/unittests/selector/selector_parser_names_test_helper.h"
+
+namespace cuttlefish {
+namespace selector {
+
+TEST_P(ValidNamesTest, ValidInputs) { ASSERT_TRUE(parser_); }
+
+/**
+ * Note that invalid inputs must be tested at the InstanceDatabase level
+ */
+TEST_P(ValidNamesTest, FieldsNoSubstring) {
+  if (!parser_) {
+    /*
+     * We aren't testing whether or not parsing is working.
+     * That's tested in ValidInputs tests. We test fields.
+     */
+    GTEST_SKIP() << "Parsing failed, which must be tested in ValidInputs Test";
+  }
+
+  ASSERT_EQ(parser_->GroupName(), expected_output_.group_name);
+  ASSERT_EQ(parser_->PerInstanceNames(), expected_output_.per_instance_names);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    CvdParser, ValidNamesTest,
+    testing::Values(
+        InputOutput{.input = "--name=cf",
+                    .expected = ExpectedOutput{.group_name = "cf"}},
+        InputOutput{.input = "--name=cvd,cf",
+                    .expected = ExpectedOutput{.per_instance_names =
+                                                   std::vector<std::string>{
+                                                       "cvd", "cf"}}},
+        InputOutput{.input = "--name=cf-09,cf-tv",
+                    .expected = ExpectedOutput{.group_name = "cf",
+                                               .per_instance_names =
+                                                   std::vector<std::string>{
+                                                       "09", "tv"}}},
+        InputOutput{
+            .input = "--device_name cf-09",
+            .expected = ExpectedOutput{.group_name = "cf",
+                                       .per_instance_names =
+                                           std::vector<std::string>{"09"}}},
+        InputOutput{.input = "--device_name my_cool-phone,my_cool-tv",
+                    .expected = ExpectedOutput{.group_name = "my_cool",
+                                               .per_instance_names =
+                                                   std::vector<std::string>{
+                                                       "phone", "tv"}}},
+        InputOutput{
+            .input = "--group_name=my_cool --instance_name=phone",
+            .expected = ExpectedOutput{.group_name = "my_cool",
+                                       .per_instance_names =
+                                           std::vector<std::string>{"phone"}}},
+        InputOutput{.input = "--group_name=my_cool --instance_name=phone,tv",
+                    .expected = ExpectedOutput{.group_name = "my_cool",
+                                               .per_instance_names =
+                                                   std::vector<std::string>{
+                                                       "phone", "tv"}}},
+        InputOutput{
+            .input = "--group_name=my_cool",
+            .expected =
+                ExpectedOutput{
+                    .group_name = "my_cool",
+                }},
+        InputOutput{
+            .input = "--instance_name=my_cool",
+            .expected = ExpectedOutput{
+                .per_instance_names = std::vector<std::string>{"my_cool"}}}));
+
+TEST_P(InvalidNamesTest, InvalidInputs) { ASSERT_FALSE(parser_); }
+
+INSTANTIATE_TEST_SUITE_P(
+    CvdParser, InvalidNamesTest,
+    testing::Values("--name", "--name=?34", "--device_name=abcd",
+                    "--group_name=3ab", "--name=x --device_name=y",
+                    "--name=x --group_name=cf",
+                    "--device_name=z --instance_name=p", "--instance_name=*79a",
+                    "--device_name=abcd-e,xyz-f", "--device_name=xyz-e,xyz-e"));
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/test_cvd_selector_parser_substring.cpp b/host/commands/cvd/unittests/selector/test_cvd_selector_parser_substring.cpp
new file mode 100644
index 0000000..6175643
--- /dev/null
+++ b/host/commands/cvd/unittests/selector/test_cvd_selector_parser_substring.cpp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2022 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/commands/cvd/unittests/selector/selector_parser_substring_test_helper.h"
+
+namespace cuttlefish {
+namespace selector {
+
+TEST_P(SubstringTest, Substring) {
+  ASSERT_EQ((parser_ != std::nullopt), expected_result_);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    CvdParser, SubstringTest,
+    testing::Values(SubstringTestInput{"--name cvd", true},
+                    SubstringTestInput{"--name cvd cv", true},
+                    SubstringTestInput{"--name cvd c v", true},
+                    SubstringTestInput{"--name cvd c,v,d", true},
+                    SubstringTestInput{"--name cvd c v,d", true},
+                    SubstringTestInput{"--name cvd c", true},
+                    SubstringTestInput{"c v --name cvd d", true},
+                    SubstringTestInput{"c --name cvd v", true},
+                    SubstringTestInput{"--name cvd c,", false},
+                    SubstringTestInput{"--name cvd c v,,d", false}));
+
+}  // namespace selector
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/test_id_allocator.cpp b/host/commands/cvd/unittests/selector/test_id_allocator.cpp
index e8e6603..ed8beec 100644
--- a/host/commands/cvd/unittests/selector/test_id_allocator.cpp
+++ b/host/commands/cvd/unittests/selector/test_id_allocator.cpp
@@ -13,20 +13,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "host/commands/cvd/unittests/selector/id_allocator_test_helper.h"
+
 #include <memory>
 #include <unordered_set>
-#include <vector>
-
-#include <gtest/gtest.h>
 
 #include "host/commands/cvd/selector/unique_resource_allocator.h"
 
 namespace cuttlefish::selector {
 
-class CvdIdAllocatorOneEachTest
-    : public testing::TestWithParam<std::vector<unsigned>> {};
-
-TEST_P(CvdIdAllocatorOneEachTest, GetAnyAvailableOne) {
+TEST_P(OneEachTest, GetAnyAvailableOne) {
   const auto resources = GetParam();
   auto allocator = UniqueResourceAllocator<unsigned>::New(resources);
   std::unordered_set<unsigned> expected_ids{resources.cbegin(),
@@ -41,12 +37,10 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    AllocatorGetterTest, CvdIdAllocatorOneEachTest,
+    CvdIdAllocator, OneEachTest,
     testing::Values(std::vector<unsigned>{}, std::vector<unsigned>{1},
                     std::vector<unsigned>{1, 22, 3, 43, 5}));
 
-class CvdIdAllocatorTest : public testing::Test {};
-
 TEST_F(CvdIdAllocatorTest, ClaimAll) {
   std::vector<unsigned> inputs{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
   auto allocator = UniqueResourceAllocator<unsigned>::New(inputs);
@@ -117,26 +111,6 @@
   ASSERT_FALSE(allocator.TakeRange(2, 4));
 }
 
-class CvdIdAllocatorReclaimTest
-    : public testing::TestWithParam<std::vector<unsigned>> {};
-
-TEST_P(CvdIdAllocatorReclaimTest, Reclaim) {
-  const auto inputs{GetParam()};
-  auto allocator = UniqueResourceAllocator<unsigned>::New(inputs);
-  if (!allocator.TakeAll(inputs)) {
-    GTEST_SKIP() << "In set up for Reclaim, TakeAll failed.";
-  }
-
-  ASSERT_TRUE(allocator.Reclaim(7));
-  ASSERT_FALSE(allocator.Reclaim(100));
-  ASSERT_TRUE(allocator.Take(7));
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    AllocatorReclaimTest, CvdIdAllocatorReclaimTest,
-    testing::Values(std::vector<unsigned>{7}, std::vector<unsigned>{7, 3},
-                    std::vector<unsigned>{1, 22, 3, 43, 7}));
-
 TEST_F(CvdIdAllocatorTest, ReclaimAll) {
   std::vector<unsigned> inputs{1, 2, 4, 5, 6, 7, 8, 9, 10, 11};
   auto allocator = UniqueResourceAllocator<unsigned>::New(inputs);
@@ -163,4 +137,21 @@
   ASSERT_TRUE(allocator.ReclaimAll(std::vector<unsigned>{}));
 }
 
+TEST_P(ReclaimTest, Reclaim) {
+  const auto inputs{GetParam()};
+  auto allocator = UniqueResourceAllocator<unsigned>::New(inputs);
+  if (!allocator.TakeAll(inputs)) {
+    GTEST_SKIP() << "In set up for Reclaim, TakeAll failed.";
+  }
+
+  ASSERT_TRUE(allocator.Reclaim(7));
+  ASSERT_FALSE(allocator.Reclaim(100));
+  ASSERT_TRUE(allocator.Take(7));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    CvdIdAllocator, ReclaimTest,
+    testing::Values(std::vector<unsigned>{7}, std::vector<unsigned>{7, 3},
+                    std::vector<unsigned>{1, 22, 3, 43, 7}));
+
 }  // namespace cuttlefish::selector
diff --git a/host/commands/gnss_grpc_proxy/doc/linkage.dot b/host/commands/gnss_grpc_proxy/doc/linkage.dot
index 4c4ba96..ec3acbc 100644
--- a/host/commands/gnss_grpc_proxy/doc/linkage.dot
+++ b/host/commands/gnss_grpc_proxy/doc/linkage.dot
@@ -1,37 +1,45 @@
 digraph {
+  browser [label = "Browser"]
+  cli [label = "User CLI"]
   cvd_import_locations [URL = "https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/cvd_import_locations/"]
   cvd_update_location [URL = "https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/cvd_update_location/"]
   gnss_grpc_proxy [label = < <B>gnss_grpc_proxy</B> >]
   gnss_grpc_server [label = "TCP gRPC", shape = "rectangle"]
   run_cvd [URL = "https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/run_cvd/"]
-  host_gnss_console_in [color = "green", label = "internal/gnsshvc_fifo_vm.in", shape = "rectangle"]
-  host_gnss_console_out [color = "green", label = "internal/gnsshvc_fifo_vm.out", shape = "rectangle"]
-  host_fixed_location_console_in [color = "blue", label = "internal/locationhvc_fifo_vm.in", shape = "rectangle"]
-  host_fixed_location_console_out [color = "blue", label = "internal/locationhvc_fifo_vm.out", shape = "rectangle"]
   vmm [label = "crosvm / qemu"]
+  webrtc
+  subgraph fifos {
+    rank = same;
+    host_gnss_console_in [color = "green", label = "internal/gnsshvc_fifo_vm.in", shape = "rectangle"]
+    host_gnss_console_out [color = "green", label = "internal/gnsshvc_fifo_vm.out", shape = "rectangle"]
+    host_fixed_location_console_in [color = "blue", label = "internal/locationhvc_fifo_vm.in", shape = "rectangle"]
+    host_fixed_location_console_out [color = "blue", label = "internal/locationhvc_fifo_vm.out", shape = "rectangle"]
+  }
   subgraph cluster_android {
     label = "Android"
 
     gnss_hal [label = "vendor.gnss-default", URL = "https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/gnss/aidl/default/"]
-    fixed_location_console [color = "blue", label = "/dev/hvc6 | /dev/gnss1", shape = "rectangle"]
-    gnss_console [color = "green", label = "/dev/hvc5 | /dev/gnss0", shape = "rectangle"]
+    subgraph consoles {
+      rank = same;
+      fixed_location_console [color = "blue", label = "/dev/hvc6 | /dev/gnss1", shape = "rectangle"]
+      gnss_console [color = "green", label = "/dev/hvc5 | /dev/gnss0", shape = "rectangle"]
+    }
   }
 
+  cli -> cvd_import_locations
+  cli -> cvd_update_location
+  browser -> webrtc
+
   run_cvd -> gnss_grpc_proxy
 
-  fixed_location_console -> gnss_hal [color = "blue"]
-  gnss_hal -> fixed_location_console [color = "blue"]
-  gnss_console -> gnss_hal [color = "green"]
-  gnss_hal -> gnss_console [color = "green"]
+  fixed_location_console -> gnss_hal [color = "blue", dir = "both"]
+  gnss_console -> gnss_hal [color = "green", dir = "both"]
 
-  cvd_import_locations -> gnss_grpc_server
-  gnss_grpc_server -> cvd_import_locations
+  cvd_import_locations -> gnss_grpc_server [dir = "both"]
+  cvd_update_location -> gnss_grpc_server [dir = "both"]
+  webrtc -> gnss_grpc_server [dir = "both"]
 
-  cvd_update_location -> gnss_grpc_server
-  gnss_grpc_server -> cvd_update_location
-
-  gnss_grpc_server -> gnss_grpc_proxy
-  gnss_grpc_proxy -> gnss_grpc_server
+  gnss_grpc_server -> gnss_grpc_proxy [dir = "both"]
 
   gnss_grpc_proxy -> host_gnss_console_in [color = "green"]
   host_gnss_console_out -> gnss_grpc_proxy [color = "green"]
@@ -39,15 +47,12 @@
   vmm -> host_gnss_console_out [color = "green"]
   host_gnss_console_in -> vmm [color = "green"]
 
-  gnss_grpc_proxy -> host_fixed_location_console_in [color = "blue"]
+  host_fixed_location_console_in -> gnss_grpc_proxy [color = "blue", dir = "back"]
   host_fixed_location_console_out -> gnss_grpc_proxy [color = "blue"]
 
-  vmm -> host_fixed_location_console_out [color = "blue"]
-  host_fixed_location_console_in -> vmm [color = "blue"]
+  host_fixed_location_console_out -> vmm [color = "blue"]
+  host_fixed_location_console_in -> vmm [color = "blue", dir = "back"]
 
-  vmm -> fixed_location_console [color = "blue"]
-  fixed_location_console -> vmm [color = "blue"]
-
-  gnss_console -> vmm [color = "green"]
-  vmm -> gnss_console [color = "green"]
+  vmm -> fixed_location_console [color = "blue", dir = "both"]
+  vmm -> gnss_console [color = "green", dir = "both"]
 }
diff --git a/host/commands/gnss_grpc_proxy/doc/linkage.png b/host/commands/gnss_grpc_proxy/doc/linkage.png
index b07d4ae..11f5541 100644
--- a/host/commands/gnss_grpc_proxy/doc/linkage.png
+++ b/host/commands/gnss_grpc_proxy/doc/linkage.png
Binary files differ
diff --git a/host/commands/gnss_grpc_proxy/doc/linkage.svg b/host/commands/gnss_grpc_proxy/doc/linkage.svg
index 748c8cc..3e2466e 100644
--- a/host/commands/gnss_grpc_proxy/doc/linkage.svg
+++ b/host/commands/gnss_grpc_proxy/doc/linkage.svg
@@ -4,245 +4,253 @@
 <!-- Generated by graphviz version 2.43.0 (0)
  -->
 <!-- Title: %3 Pages: 1 -->
-<svg width="986pt" height="407pt"
- viewBox="0.00 0.00 986.34 407.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 403)">
+<svg width="798pt" height="567pt"
+ viewBox="0.00 0.00 797.50 567.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 563)">
 <title>%3</title>
-<polygon fill="white" stroke="transparent" points="-4,4 -4,-403 982.34,-403 982.34,4 -4,4"/>
-<g id="clust1" class="cluster">
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-563 793.5,-563 793.5,4 -4,4"/>
+<g id="clust2" class="cluster">
 <title>cluster_android</title>
-<polygon fill="none" stroke="black" points="175.84,-136 175.84,-283 485.84,-283 485.84,-136 175.84,-136"/>
-<text text-anchor="middle" x="330.84" y="-267.8" font-family="Times,serif" font-size="14.00">Android</text>
+<polygon fill="none" stroke="black" points="223,-8 223,-155 533,-155 533,-8 223,-8"/>
+<text text-anchor="middle" x="378" y="-139.8" font-family="Times,serif" font-size="14.00">Android</text>
+</g>
+<!-- browser -->
+<g id="node1" class="node">
+<title>browser</title>
+<ellipse fill="none" stroke="black" cx="190" cy="-541" rx="40.09" ry="18"/>
+<text text-anchor="middle" x="190" y="-537.3" font-family="Times,serif" font-size="14.00">Browser</text>
+</g>
+<!-- webrtc -->
+<g id="node9" class="node">
+<title>webrtc</title>
+<ellipse fill="none" stroke="black" cx="190" cy="-469" rx="34.39" ry="18"/>
+<text text-anchor="middle" x="190" y="-465.3" font-family="Times,serif" font-size="14.00">webrtc</text>
+</g>
+<!-- browser&#45;&gt;webrtc -->
+<g id="edge3" class="edge">
+<title>browser&#45;&gt;webrtc</title>
+<path fill="none" stroke="black" d="M190,-522.7C190,-514.98 190,-505.71 190,-497.11"/>
+<polygon fill="black" stroke="black" points="193.5,-497.1 190,-487.1 186.5,-497.1 193.5,-497.1"/>
+</g>
+<!-- cli -->
+<g id="node2" class="node">
+<title>cli</title>
+<ellipse fill="none" stroke="black" cx="425" cy="-541" rx="44.39" ry="18"/>
+<text text-anchor="middle" x="425" y="-537.3" font-family="Times,serif" font-size="14.00">User CLI</text>
 </g>
 <!-- cvd_import_locations -->
-<g id="node1" class="node">
+<g id="node3" class="node">
 <title>cvd_import_locations</title>
-<g id="a_node1"><a xlink:href="https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/cvd_import_locations/" xlink:title="cvd_import_locations">
-<ellipse fill="none" stroke="black" cx="383.84" cy="-381" rx="87.99" ry="18"/>
-<text text-anchor="middle" x="383.84" y="-377.3" font-family="Times,serif" font-size="14.00">cvd_import_locations</text>
+<g id="a_node3"><a xlink:href="https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/cvd_import_locations/" xlink:title="cvd_import_locations">
+<ellipse fill="none" stroke="black" cx="330" cy="-469" rx="87.99" ry="18"/>
+<text text-anchor="middle" x="330" y="-465.3" font-family="Times,serif" font-size="14.00">cvd_import_locations</text>
 </a>
 </g>
 </g>
-<!-- gnss_grpc_server -->
-<g id="node4" class="node">
-<title>gnss_grpc_server</title>
-<polygon fill="none" stroke="black" points="422.34,-327 345.34,-327 345.34,-291 422.34,-291 422.34,-327"/>
-<text text-anchor="middle" x="383.84" y="-305.3" font-family="Times,serif" font-size="14.00">TCP gRPC</text>
-</g>
-<!-- cvd_import_locations&#45;&gt;gnss_grpc_server -->
-<g id="edge6" class="edge">
-<title>cvd_import_locations&#45;&gt;gnss_grpc_server</title>
-<path fill="none" stroke="black" d="M377.93,-362.7C377.13,-354.98 376.9,-345.71 377.25,-337.11"/>
-<polygon fill="black" stroke="black" points="380.74,-337.32 377.95,-327.1 373.76,-336.84 380.74,-337.32"/>
+<!-- cli&#45;&gt;cvd_import_locations -->
+<g id="edge1" class="edge">
+<title>cli&#45;&gt;cvd_import_locations</title>
+<path fill="none" stroke="black" d="M404.39,-524.81C391.61,-515.4 374.97,-503.14 360.67,-492.6"/>
+<polygon fill="black" stroke="black" points="362.69,-489.74 352.57,-486.63 358.54,-495.38 362.69,-489.74"/>
 </g>
 <!-- cvd_update_location -->
-<g id="node2" class="node">
+<g id="node4" class="node">
 <title>cvd_update_location</title>
-<g id="a_node2"><a xlink:href="https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/cvd_update_location/" xlink:title="cvd_update_location">
-<ellipse fill="none" stroke="black" cx="83.84" cy="-234" rx="83.69" ry="18"/>
-<text text-anchor="middle" x="83.84" y="-230.3" font-family="Times,serif" font-size="14.00">cvd_update_location</text>
+<g id="a_node4"><a xlink:href="https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/cvd_update_location/" xlink:title="cvd_update_location">
+<ellipse fill="none" stroke="black" cx="520" cy="-469" rx="83.69" ry="18"/>
+<text text-anchor="middle" x="520" y="-465.3" font-family="Times,serif" font-size="14.00">cvd_update_location</text>
 </a>
 </g>
 </g>
+<!-- cli&#45;&gt;cvd_update_location -->
+<g id="edge2" class="edge">
+<title>cli&#45;&gt;cvd_update_location</title>
+<path fill="none" stroke="black" d="M445.61,-524.81C458.48,-515.33 475.27,-502.96 489.64,-492.37"/>
+<polygon fill="black" stroke="black" points="491.8,-495.13 497.77,-486.38 487.64,-489.49 491.8,-495.13"/>
+</g>
+<!-- gnss_grpc_server -->
+<g id="node6" class="node">
+<title>gnss_grpc_server</title>
+<polygon fill="none" stroke="black" points="368.5,-415 291.5,-415 291.5,-379 368.5,-379 368.5,-415"/>
+<text text-anchor="middle" x="330" y="-393.3" font-family="Times,serif" font-size="14.00">TCP gRPC</text>
+</g>
+<!-- cvd_import_locations&#45;&gt;gnss_grpc_server -->
+<g id="edge7" class="edge">
+<title>cvd_import_locations&#45;&gt;gnss_grpc_server</title>
+<path fill="none" stroke="black" d="M330,-440.67C330,-435.69 330,-430.49 330,-425.51"/>
+<polygon fill="black" stroke="black" points="326.5,-440.7 330,-450.7 333.5,-440.7 326.5,-440.7"/>
+<polygon fill="black" stroke="black" points="333.5,-425.1 330,-415.1 326.5,-425.1 333.5,-425.1"/>
+</g>
 <!-- cvd_update_location&#45;&gt;gnss_grpc_server -->
 <g id="edge8" class="edge">
 <title>cvd_update_location&#45;&gt;gnss_grpc_server</title>
-<path fill="none" stroke="black" d="M114.5,-250.85C133.14,-261.75 157.46,-275.55 180.84,-283 231.95,-299.28 293.03,-305.14 334.85,-307.16"/>
-<polygon fill="black" stroke="black" points="334.91,-310.67 345.05,-307.6 335.21,-303.68 334.91,-310.67"/>
+<path fill="none" stroke="black" d="M470.16,-449.64C441.76,-439.18 406.36,-426.13 378.23,-415.77"/>
+<polygon fill="black" stroke="black" points="469.11,-452.98 479.71,-453.15 471.53,-446.41 469.11,-452.98"/>
+<polygon fill="black" stroke="black" points="379.21,-412.4 368.61,-412.23 376.79,-418.97 379.21,-412.4"/>
 </g>
 <!-- gnss_grpc_proxy -->
-<g id="node3" class="node">
+<g id="node5" class="node">
 <title>gnss_grpc_proxy</title>
-<ellipse fill="none" stroke="black" cx="679.84" cy="-234" rx="79.89" ry="18"/>
-<text text-anchor="start" x="626.34" y="-231.3" font-family="Times,serif" font-size="14.00"> </text>
-<text text-anchor="start" x="630.34" y="-231.3" font-family="Times,serif" font-weight="bold" font-size="14.00">gnss_grpc_proxy</text>
-<text text-anchor="start" x="729.34" y="-231.3" font-family="Times,serif" font-size="14.00"> </text>
-</g>
-<!-- gnss_grpc_proxy&#45;&gt;gnss_grpc_server -->
-<g id="edge11" class="edge">
-<title>gnss_grpc_proxy&#45;&gt;gnss_grpc_server</title>
-<path fill="none" stroke="black" d="M633.8,-248.78C597.23,-258.5 544.95,-271.74 498.84,-283 477.22,-288.28 453.06,-293.99 432.4,-298.62"/>
-<polygon fill="black" stroke="black" points="431.58,-295.22 422.57,-300.8 433.09,-302.05 431.58,-295.22"/>
+<ellipse fill="none" stroke="black" cx="378" cy="-325" rx="79.89" ry="18"/>
+<text text-anchor="start" x="324.5" y="-322.3" font-family="Times,serif" font-size="14.00"> </text>
+<text text-anchor="start" x="328.5" y="-322.3" font-family="Times,serif" font-weight="bold" font-size="14.00">gnss_grpc_proxy</text>
+<text text-anchor="start" x="427.5" y="-322.3" font-family="Times,serif" font-size="14.00"> </text>
 </g>
 <!-- host_gnss_console_in -->
-<g id="node6" class="node">
+<g id="node10" class="node">
 <title>host_gnss_console_in</title>
-<polygon fill="none" stroke="green" points="665.84,-180 495.84,-180 495.84,-144 665.84,-144 665.84,-180"/>
-<text text-anchor="middle" x="580.84" y="-158.3" font-family="Times,serif" font-size="14.00">internal/gnsshvc_fifo_vm.in</text>
+<polygon fill="none" stroke="green" points="170,-271 0,-271 0,-235 170,-235 170,-271"/>
+<text text-anchor="middle" x="85" y="-249.3" font-family="Times,serif" font-size="14.00">internal/gnsshvc_fifo_vm.in</text>
 </g>
 <!-- gnss_grpc_proxy&#45;&gt;host_gnss_console_in -->
-<g id="edge12" class="edge">
+<g id="edge11" class="edge">
 <title>gnss_grpc_proxy&#45;&gt;host_gnss_console_in</title>
-<path fill="none" stroke="green" d="M656.63,-216.59C643.76,-207.49 627.54,-196.02 613.42,-186.03"/>
-<polygon fill="green" stroke="green" points="615.13,-182.96 604.94,-180.04 611.09,-188.67 615.13,-182.96"/>
-</g>
-<!-- host_fixed_location_console_in -->
-<g id="node8" class="node">
-<title>host_fixed_location_console_in</title>
-<polygon fill="none" stroke="blue" points="873.84,-180 683.84,-180 683.84,-144 873.84,-144 873.84,-180"/>
-<text text-anchor="middle" x="778.84" y="-158.3" font-family="Times,serif" font-size="14.00">internal/locationhvc_fifo_vm.in</text>
-</g>
-<!-- gnss_grpc_proxy&#45;&gt;host_fixed_location_console_in -->
-<g id="edge16" class="edge">
-<title>gnss_grpc_proxy&#45;&gt;host_fixed_location_console_in</title>
-<path fill="none" stroke="blue" d="M703.05,-216.59C715.93,-207.49 732.14,-196.02 746.27,-186.03"/>
-<polygon fill="blue" stroke="blue" points="748.6,-188.67 754.74,-180.04 744.56,-182.96 748.6,-188.67"/>
-</g>
-<!-- gnss_grpc_server&#45;&gt;cvd_import_locations -->
-<g id="edge7" class="edge">
-<title>gnss_grpc_server&#45;&gt;cvd_import_locations</title>
-<path fill="none" stroke="black" d="M389.74,-327.1C390.54,-334.79 390.78,-344.05 390.45,-352.67"/>
-<polygon fill="black" stroke="black" points="386.95,-352.48 389.76,-362.7 393.93,-352.96 386.95,-352.48"/>
-</g>
-<!-- gnss_grpc_server&#45;&gt;cvd_update_location -->
-<g id="edge9" class="edge">
-<title>gnss_grpc_server&#45;&gt;cvd_update_location</title>
-<path fill="none" stroke="black" d="M345.26,-307.97C300.14,-307.09 224.29,-302.57 162.84,-283 144.03,-277.01 124.61,-266.9 109.6,-257.5"/>
-<polygon fill="black" stroke="black" points="111.15,-254.33 100.86,-251.78 107.31,-260.19 111.15,-254.33"/>
+<path fill="none" stroke="green" d="M325.28,-311.4C281.03,-300.83 217.08,-285.55 166.28,-273.42"/>
+<polygon fill="green" stroke="green" points="166.84,-269.95 156.3,-271.03 165.21,-276.76 166.84,-269.95"/>
 </g>
 <!-- gnss_grpc_server&#45;&gt;gnss_grpc_proxy -->
 <g id="edge10" class="edge">
 <title>gnss_grpc_server&#45;&gt;gnss_grpc_proxy</title>
-<path fill="none" stroke="black" d="M422.47,-297.08C440.26,-292.76 461.56,-287.71 480.84,-283 525.44,-272.11 575.82,-259.36 614.8,-249.74"/>
-<polygon fill="black" stroke="black" points="616.09,-253.03 624.97,-247.24 614.42,-246.23 616.09,-253.03"/>
+<path fill="none" stroke="black" d="M347.58,-370.36C351.76,-364.26 356.23,-357.75 360.41,-351.65"/>
+<polygon fill="black" stroke="black" points="344.63,-368.47 341.87,-378.7 350.41,-372.43 344.63,-368.47"/>
+<polygon fill="black" stroke="black" points="363.5,-353.33 366.27,-343.1 357.73,-349.37 363.5,-353.33"/>
 </g>
 <!-- run_cvd -->
-<g id="node5" class="node">
+<g id="node7" class="node">
 <title>run_cvd</title>
-<g id="a_node5"><a xlink:href="https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/run_cvd/" xlink:title="run_cvd">
-<ellipse fill="none" stroke="black" cx="679.84" cy="-309" rx="39.79" ry="18"/>
-<text text-anchor="middle" x="679.84" y="-305.3" font-family="Times,serif" font-size="14.00">run_cvd</text>
+<g id="a_node7"><a xlink:href="https://cs.android.com/android/platform/superproject/+/master:device/google/cuttlefish/host/commands/run_cvd/" xlink:title="run_cvd">
+<ellipse fill="none" stroke="black" cx="426" cy="-397" rx="39.79" ry="18"/>
+<text text-anchor="middle" x="426" y="-393.3" font-family="Times,serif" font-size="14.00">run_cvd</text>
 </a>
 </g>
 </g>
 <!-- run_cvd&#45;&gt;gnss_grpc_proxy -->
-<g id="edge1" class="edge">
+<g id="edge4" class="edge">
 <title>run_cvd&#45;&gt;gnss_grpc_proxy</title>
-<path fill="none" stroke="black" d="M679.84,-290.7C679.84,-282.25 679.84,-271.87 679.84,-262.37"/>
-<polygon fill="black" stroke="black" points="683.34,-262.18 679.84,-252.18 676.34,-262.18 683.34,-262.18"/>
+<path fill="none" stroke="black" d="M414.62,-379.41C408.91,-371.08 401.86,-360.8 395.48,-351.49"/>
+<polygon fill="black" stroke="black" points="398.17,-349.22 389.63,-342.96 392.4,-353.18 398.17,-349.22"/>
 </g>
 <!-- vmm -->
-<g id="node10" class="node">
+<g id="node8" class="node">
 <title>vmm</title>
-<ellipse fill="none" stroke="black" cx="494.84" cy="-90" rx="64.19" ry="18"/>
-<text text-anchor="middle" x="494.84" y="-86.3" font-family="Times,serif" font-size="14.00">crosvm / qemu</text>
-</g>
-<!-- host_gnss_console_in&#45;&gt;vmm -->
-<g id="edge15" class="edge">
-<title>host_gnss_console_in&#45;&gt;vmm</title>
-<path fill="none" stroke="green" d="M559.58,-143.7C548.43,-134.62 534.63,-123.38 522.64,-113.62"/>
-<polygon fill="green" stroke="green" points="524.64,-110.74 514.68,-107.14 520.22,-116.17 524.64,-110.74"/>
+<ellipse fill="none" stroke="black" cx="378" cy="-181" rx="64.19" ry="18"/>
+<text text-anchor="middle" x="378" y="-177.3" font-family="Times,serif" font-size="14.00">crosvm / qemu</text>
 </g>
 <!-- host_gnss_console_out -->
-<g id="node7" class="node">
+<g id="node11" class="node">
 <title>host_gnss_console_out</title>
-<polygon fill="none" stroke="green" points="643.34,-36 466.34,-36 466.34,0 643.34,0 643.34,-36"/>
-<text text-anchor="middle" x="554.84" y="-14.3" font-family="Times,serif" font-size="14.00">internal/gnsshvc_fifo_vm.out</text>
-</g>
-<!-- host_gnss_console_out&#45;&gt;gnss_grpc_proxy -->
-<g id="edge13" class="edge">
-<title>host_gnss_console_out&#45;&gt;gnss_grpc_proxy</title>
-<path fill="none" stroke="green" d="M643.63,-23.63C721.05,-32.93 829.11,-59.78 882.84,-136 894.11,-151.98 894.8,-164.52 882.84,-180 867.24,-200.2 807.87,-214.17 757.34,-222.67"/>
-<polygon fill="green" stroke="green" points="756.67,-219.24 747.37,-224.3 757.8,-226.14 756.67,-219.24"/>
-</g>
-<!-- host_fixed_location_console_in&#45;&gt;vmm -->
-<g id="edge19" class="edge">
-<title>host_fixed_location_console_in&#45;&gt;vmm</title>
-<path fill="none" stroke="blue" d="M709.73,-143.97C661.09,-131.98 596.79,-116.13 551.24,-104.9"/>
-<polygon fill="blue" stroke="blue" points="552.08,-101.5 541.53,-102.51 550.4,-108.3 552.08,-101.5"/>
-</g>
-<!-- host_fixed_location_console_out -->
-<g id="node9" class="node">
-<title>host_fixed_location_console_out</title>
-<polygon fill="none" stroke="blue" points="978.34,-36 781.34,-36 781.34,0 978.34,0 978.34,-36"/>
-<text text-anchor="middle" x="879.84" y="-14.3" font-family="Times,serif" font-size="14.00">internal/locationhvc_fifo_vm.out</text>
-</g>
-<!-- host_fixed_location_console_out&#45;&gt;gnss_grpc_proxy -->
-<g id="edge17" class="edge">
-<title>host_fixed_location_console_out&#45;&gt;gnss_grpc_proxy</title>
-<path fill="none" stroke="blue" d="M889.41,-36.19C905.79,-68.19 934.35,-137.5 900.84,-180 883.33,-202.21 816.1,-216.17 760.34,-224.15"/>
-<polygon fill="blue" stroke="blue" points="759.86,-220.68 750.44,-225.52 760.82,-227.62 759.86,-220.68"/>
+<polygon fill="none" stroke="green" points="365.5,-271 188.5,-271 188.5,-235 365.5,-235 365.5,-271"/>
+<text text-anchor="middle" x="277" y="-249.3" font-family="Times,serif" font-size="14.00">internal/gnsshvc_fifo_vm.out</text>
 </g>
 <!-- vmm&#45;&gt;host_gnss_console_out -->
-<g id="edge14" class="edge">
+<g id="edge13" class="edge">
 <title>vmm&#45;&gt;host_gnss_console_out</title>
-<path fill="none" stroke="green" d="M509.06,-72.41C516.32,-63.95 525.29,-53.47 533.37,-44.05"/>
-<polygon fill="green" stroke="green" points="536.04,-46.32 539.89,-36.45 530.72,-41.76 536.04,-46.32"/>
-</g>
-<!-- vmm&#45;&gt;host_fixed_location_console_out -->
-<g id="edge18" class="edge">
-<title>vmm&#45;&gt;host_fixed_location_console_out</title>
-<path fill="none" stroke="blue" d="M547.17,-79.49C605.68,-68.85 701.94,-51.35 775.9,-37.9"/>
-<polygon fill="blue" stroke="blue" points="776.97,-41.26 786.18,-36.03 775.72,-34.37 776.97,-41.26"/>
+<path fill="none" stroke="green" d="M354.93,-197.99C341.65,-207.2 324.73,-218.92 310.05,-229.1"/>
+<polygon fill="green" stroke="green" points="307.93,-226.3 301.71,-234.88 311.92,-232.06 307.93,-226.3"/>
 </g>
 <!-- fixed_location_console -->
-<g id="node12" class="node">
+<g id="node15" class="node">
 <title>fixed_location_console</title>
-<polygon fill="none" stroke="blue" points="321.84,-180 183.84,-180 183.84,-144 321.84,-144 321.84,-180"/>
-<text text-anchor="middle" x="252.84" y="-158.3" font-family="Times,serif" font-size="14.00">/dev/hvc6 | /dev/gnss1</text>
+<polygon fill="none" stroke="blue" points="525,-124 387,-124 387,-88 525,-88 525,-124"/>
+<text text-anchor="middle" x="456" y="-102.3" font-family="Times,serif" font-size="14.00">/dev/hvc6 | /dev/gnss1</text>
 </g>
 <!-- vmm&#45;&gt;fixed_location_console -->
-<g id="edge20" class="edge">
+<g id="edge19" class="edge">
 <title>vmm&#45;&gt;fixed_location_console</title>
-<path fill="none" stroke="blue" d="M453.54,-103.85C422.21,-112.57 378.33,-124.56 339.84,-136 334.65,-137.54 329.27,-139.18 323.85,-140.86"/>
-<polygon fill="blue" stroke="blue" points="322.59,-137.58 314.07,-143.88 324.66,-144.27 322.59,-137.58"/>
+<path fill="none" stroke="blue" d="M403.17,-156.44C411.9,-148.27 421.69,-139.11 430.45,-130.91"/>
+<polygon fill="blue" stroke="blue" points="400.61,-154.05 395.7,-163.44 405.39,-159.16 400.61,-154.05"/>
+<polygon fill="blue" stroke="blue" points="432.92,-133.39 437.83,-124.01 428.14,-128.28 432.92,-133.39"/>
 </g>
 <!-- gnss_console -->
-<g id="node13" class="node">
+<g id="node16" class="node">
 <title>gnss_console</title>
-<polygon fill="none" stroke="green" points="477.84,-180 339.84,-180 339.84,-144 477.84,-144 477.84,-180"/>
-<text text-anchor="middle" x="408.84" y="-158.3" font-family="Times,serif" font-size="14.00">/dev/hvc5 | /dev/gnss0</text>
+<polygon fill="none" stroke="green" points="369,-124 231,-124 231,-88 369,-88 369,-124"/>
+<text text-anchor="middle" x="300" y="-102.3" font-family="Times,serif" font-size="14.00">/dev/hvc5 | /dev/gnss0</text>
 </g>
 <!-- vmm&#45;&gt;gnss_console -->
-<g id="edge23" class="edge">
+<g id="edge20" class="edge">
 <title>vmm&#45;&gt;gnss_console</title>
-<path fill="none" stroke="green" d="M480.26,-107.62C470.27,-116.55 456.77,-127.69 444.14,-137.49"/>
-<polygon fill="green" stroke="green" points="441.84,-134.84 436.02,-143.7 446.09,-140.41 441.84,-134.84"/>
+<path fill="none" stroke="green" d="M352.83,-156.44C344.1,-148.27 334.31,-139.11 325.55,-130.91"/>
+<polygon fill="green" stroke="green" points="350.61,-159.16 360.3,-163.44 355.39,-154.05 350.61,-159.16"/>
+<polygon fill="green" stroke="green" points="327.86,-128.28 318.17,-124.01 323.08,-133.39 327.86,-128.28"/>
+</g>
+<!-- webrtc&#45;&gt;gnss_grpc_server -->
+<g id="edge9" class="edge">
+<title>webrtc&#45;&gt;gnss_grpc_server</title>
+<path fill="none" stroke="black" d="M223.3,-451.35C242.27,-441.87 266.25,-429.87 286.67,-419.66"/>
+<polygon fill="black" stroke="black" points="221.45,-448.36 214.07,-455.97 224.58,-454.62 221.45,-448.36"/>
+<polygon fill="black" stroke="black" points="288.4,-422.72 295.77,-415.11 285.26,-416.45 288.4,-422.72"/>
+</g>
+<!-- host_gnss_console_in&#45;&gt;vmm -->
+<g id="edge14" class="edge">
+<title>host_gnss_console_in&#45;&gt;vmm</title>
+<path fill="none" stroke="green" d="M156.3,-234.97C206.8,-222.9 273.66,-206.93 320.7,-195.69"/>
+<polygon fill="green" stroke="green" points="321.81,-199.02 330.72,-193.3 320.18,-192.21 321.81,-199.02"/>
+</g>
+<!-- host_gnss_console_out&#45;&gt;gnss_grpc_proxy -->
+<g id="edge12" class="edge">
+<title>host_gnss_console_out&#45;&gt;gnss_grpc_proxy</title>
+<path fill="none" stroke="green" d="M301.71,-271.12C315.05,-280.37 331.68,-291.9 346,-301.82"/>
+<polygon fill="green" stroke="green" points="344.36,-304.94 354.57,-307.76 348.35,-299.19 344.36,-304.94"/>
+</g>
+<!-- host_fixed_location_console_in -->
+<g id="node12" class="node">
+<title>host_fixed_location_console_in</title>
+<polygon fill="none" stroke="blue" points="574,-271 384,-271 384,-235 574,-235 574,-271"/>
+<text text-anchor="middle" x="479" y="-249.3" font-family="Times,serif" font-size="14.00">internal/locationhvc_fifo_vm.in</text>
+</g>
+<!-- host_fixed_location_console_in&#45;&gt;gnss_grpc_proxy -->
+<g id="edge15" class="edge">
+<title>host_fixed_location_console_in&#45;&gt;gnss_grpc_proxy</title>
+<path fill="none" stroke="blue" d="M446.02,-276.86C431.46,-286.95 414.68,-298.58 401.43,-307.76"/>
+<polygon fill="blue" stroke="blue" points="448.07,-279.7 454.29,-271.12 444.08,-273.94 448.07,-279.7"/>
+</g>
+<!-- host_fixed_location_console_in&#45;&gt;vmm -->
+<g id="edge18" class="edge">
+<title>host_fixed_location_console_in&#45;&gt;vmm</title>
+<path fill="none" stroke="blue" d="M445.95,-229.1C431.27,-218.92 414.35,-207.2 401.07,-197.99"/>
+<polygon fill="blue" stroke="blue" points="444.08,-232.06 454.29,-234.88 448.07,-226.3 444.08,-232.06"/>
+</g>
+<!-- host_fixed_location_console_out -->
+<g id="node13" class="node">
+<title>host_fixed_location_console_out</title>
+<polygon fill="none" stroke="blue" points="789.5,-271 592.5,-271 592.5,-235 789.5,-235 789.5,-271"/>
+<text text-anchor="middle" x="691" y="-249.3" font-family="Times,serif" font-size="14.00">internal/locationhvc_fifo_vm.out</text>
+</g>
+<!-- host_fixed_location_console_out&#45;&gt;gnss_grpc_proxy -->
+<g id="edge16" class="edge">
+<title>host_fixed_location_console_out&#45;&gt;gnss_grpc_proxy</title>
+<path fill="none" stroke="blue" d="M614.83,-271.03C562.08,-282.83 492.61,-298.37 442.57,-309.56"/>
+<polygon fill="blue" stroke="blue" points="441.52,-306.21 432.52,-311.81 443.04,-313.04 441.52,-306.21"/>
+</g>
+<!-- host_fixed_location_console_out&#45;&gt;vmm -->
+<g id="edge17" class="edge">
+<title>host_fixed_location_console_out&#45;&gt;vmm</title>
+<path fill="none" stroke="blue" d="M614.83,-234.97C559.87,-222.67 486.76,-206.32 436.39,-195.06"/>
+<polygon fill="blue" stroke="blue" points="437.15,-191.64 426.63,-192.88 435.62,-198.47 437.15,-191.64"/>
 </g>
 <!-- gnss_hal -->
-<g id="node11" class="node">
+<g id="node14" class="node">
 <title>gnss_hal</title>
-<g id="a_node11"><a xlink:href="https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/gnss/aidl/default/" xlink:title="vendor.gnss&#45;default">
-<ellipse fill="none" stroke="black" cx="331.84" cy="-234" rx="79.89" ry="18"/>
-<text text-anchor="middle" x="331.84" y="-230.3" font-family="Times,serif" font-size="14.00">vendor.gnss&#45;default</text>
+<g id="a_node14"><a xlink:href="https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/gnss/aidl/default/" xlink:title="vendor.gnss&#45;default">
+<ellipse fill="none" stroke="black" cx="378" cy="-34" rx="79.89" ry="18"/>
+<text text-anchor="middle" x="378" y="-30.3" font-family="Times,serif" font-size="14.00">vendor.gnss&#45;default</text>
 </a>
 </g>
 </g>
-<!-- gnss_hal&#45;&gt;fixed_location_console -->
-<g id="edge3" class="edge">
-<title>gnss_hal&#45;&gt;fixed_location_console</title>
-<path fill="none" stroke="blue" d="M307.72,-216.76C296.86,-208.01 284.25,-197.03 273.79,-187.28"/>
-<polygon fill="blue" stroke="blue" points="275.93,-184.49 266.27,-180.12 271.1,-189.56 275.93,-184.49"/>
-</g>
-<!-- gnss_hal&#45;&gt;gnss_console -->
-<g id="edge5" class="edge">
-<title>gnss_hal&#45;&gt;gnss_console</title>
-<path fill="none" stroke="green" d="M344.6,-216.05C353.34,-207.26 365.13,-196.38 376.27,-186.76"/>
-<polygon fill="green" stroke="green" points="378.85,-189.17 384.22,-180.03 374.33,-183.82 378.85,-189.17"/>
-</g>
-<!-- fixed_location_console&#45;&gt;vmm -->
-<g id="edge21" class="edge">
-<title>fixed_location_console&#45;&gt;vmm</title>
-<path fill="none" stroke="blue" d="M296.32,-143.88C304.75,-141.19 313.53,-138.47 321.84,-136 359.58,-124.78 402.49,-113.04 436.23,-104.37"/>
-<polygon fill="blue" stroke="blue" points="437.11,-107.75 445.94,-101.89 435.38,-100.97 437.11,-107.75"/>
-</g>
 <!-- fixed_location_console&#45;&gt;gnss_hal -->
-<g id="edge2" class="edge">
+<g id="edge5" class="edge">
 <title>fixed_location_console&#45;&gt;gnss_hal</title>
-<path fill="none" stroke="blue" d="M277.95,-180.03C288.71,-188.75 301.04,-199.51 311.26,-209.05"/>
-<polygon fill="blue" stroke="blue" points="308.95,-211.68 318.6,-216.05 313.78,-206.62 308.95,-211.68"/>
-</g>
-<!-- gnss_console&#45;&gt;vmm -->
-<g id="edge22" class="edge">
-<title>gnss_console&#45;&gt;vmm</title>
-<path fill="none" stroke="green" d="M424.19,-143.7C434.65,-134.4 448.72,-122.84 461.61,-112.91"/>
-<polygon fill="green" stroke="green" points="463.99,-115.5 469.85,-106.67 459.77,-109.92 463.99,-115.5"/>
+<path fill="none" stroke="blue" d="M429.18,-80.93C421.11,-73.68 412.26,-65.75 404.23,-58.54"/>
+<polygon fill="blue" stroke="blue" points="426.94,-83.62 436.72,-87.7 431.61,-78.41 426.94,-83.62"/>
+<polygon fill="blue" stroke="blue" points="406.3,-55.7 396.52,-51.62 401.63,-60.91 406.3,-55.7"/>
 </g>
 <!-- gnss_console&#45;&gt;gnss_hal -->
-<g id="edge4" class="edge">
+<g id="edge6" class="edge">
 <title>gnss_console&#45;&gt;gnss_hal</title>
-<path fill="none" stroke="green" d="M395.9,-180.12C386.87,-189.2 374.64,-200.46 363.22,-210.26"/>
-<polygon fill="green" stroke="green" points="360.9,-207.65 355.5,-216.76 365.41,-213 360.9,-207.65"/>
+<path fill="none" stroke="green" d="M326.82,-80.93C334.89,-73.68 343.74,-65.75 351.77,-58.54"/>
+<polygon fill="green" stroke="green" points="324.39,-78.41 319.28,-87.7 329.06,-83.62 324.39,-78.41"/>
+<polygon fill="green" stroke="green" points="354.37,-60.91 359.48,-51.62 349.7,-55.7 354.37,-60.91"/>
 </g>
 </g>
 </svg>
diff --git a/host/commands/log_tee/log_tee.cpp b/host/commands/log_tee/log_tee.cpp
index e147d00..863f983 100644
--- a/host/commands/log_tee/log_tee.cpp
+++ b/host/commands/log_tee/log_tee.cpp
@@ -36,7 +36,7 @@
 
   auto instance = config->ForDefaultInstance();
 
-  if (config->run_as_daemon()) {
+  if (instance.run_as_daemon()) {
     android::base::SetLogger(
         cuttlefish::LogToFiles({instance.launcher_log_path()}));
   } else {
diff --git a/host/commands/metrics/metrics.cc b/host/commands/metrics/metrics.cc
index a2d23f1..70500d9 100644
--- a/host/commands/metrics/metrics.cc
+++ b/host/commands/metrics/metrics.cc
@@ -52,7 +52,7 @@
   CHECK(config) << "Could not open cuttlefish config";
   auto instance = config->ForDefaultInstance();
   auto metrics_log_path = instance.PerInstanceLogPath("metrics.log");
-  if (config->run_as_daemon()) {
+  if (instance.run_as_daemon()) {
     android::base::SetLogger(
         cuttlefish::LogToFiles({metrics_log_path, instance.launcher_log_path()}));
   } else {
diff --git a/host/commands/modem_simulator/network_service.cpp b/host/commands/modem_simulator/network_service.cpp
index 4d102fa..2334eb3 100644
--- a/host/commands/modem_simulator/network_service.cpp
+++ b/host/commands/modem_simulator/network_service.cpp
@@ -319,13 +319,13 @@
 
 bool NetworkService::WakeupFromSleep() {
   // It has not called once yet
-  if (android_last_signal_time_ == 0) {
+  if (android_last_signal_time_.load() == 0) {
       return false;
   }
   // Heuristics: if guest has not asked for signal strength
   // for 2 minutes, we assume it is caused by host sleep
   time_t now = time(0);
-  const bool wakeup_from_sleep = (now > android_last_signal_time_ + 120);
+  const bool wakeup_from_sleep = (now > android_last_signal_time_.load() + 120);
   return wakeup_from_sleep;
 }
 
@@ -416,11 +416,10 @@
 void NetworkService::HandleSignalStrength(const Client& client) {
   std::vector<std::string> responses;
   std::stringstream ss;
-
+  bool expected = true;
   if (WakeupFromSleep()) {
     misc_service_->TimeUpdate();
-  } else if (first_signal_strength_request_) {
-    first_signal_strength_request_ = false;
+  } else if (first_signal_strength_request_.compare_exchange_strong(expected, false)) {
     misc_service_->TimeUpdate();
   }
 
diff --git a/host/commands/modem_simulator/network_service.h b/host/commands/modem_simulator/network_service.h
index 37808fb..171cc11 100644
--- a/host/commands/modem_simulator/network_service.h
+++ b/host/commands/modem_simulator/network_service.h
@@ -15,6 +15,7 @@
 
 #pragma once
 
+#include <atomic>
 #include <ctime>
 
 #include "host/commands/modem_simulator/data_service.h"
@@ -290,8 +291,8 @@
   int getModemTechFromPrefer(int preferred_mask);
   ModemTechnology getTechFromNetworkType(NetworkRegistrationStatus::AccessTechnoloy act);
 
-  bool first_signal_strength_request_;  // For time update
-  time_t android_last_signal_time_;
+  std::atomic<bool> first_signal_strength_request_;  // For time update
+  std::atomic<time_t> android_last_signal_time_;
 
   class KeepSignalStrengthChangingLoop {
    public:
diff --git a/host/commands/run_cvd/boot_state_machine.cc b/host/commands/run_cvd/boot_state_machine.cc
index d0cfe18..27a5591 100644
--- a/host/commands/run_cvd/boot_state_machine.cc
+++ b/host/commands/run_cvd/boot_state_machine.cc
@@ -112,7 +112,9 @@
 
 class ProcessLeader : public SetupFeature {
  public:
-  INJECT(ProcessLeader(const CuttlefishConfig& config)) : config_(config) {}
+  INJECT(ProcessLeader(const CuttlefishConfig& config,
+                       const CuttlefishConfig::InstanceSpecific& instance))
+      : config_(config), instance_(instance) {}
 
   SharedFD ForegroundLauncherPipe() { return foreground_launcher_pipe_; }
 
@@ -126,7 +128,7 @@
     /* These two paths result in pretty different process state, but both
      * achieve the same goal of making the current process the leader of a
      * process group, and are therefore grouped together. */
-    if (config_.run_as_daemon()) {
+    if (instance_.run_as_daemon()) {
       foreground_launcher_pipe_ = DaemonizeLauncher(config_);
       if (!foreground_launcher_pipe_->IsOpen()) {
         return false;
@@ -146,6 +148,7 @@
   }
 
   const CuttlefishConfig& config_;
+  const CuttlefishConfig::InstanceSpecific& instance_;
   SharedFD foreground_launcher_pipe_;
 };
 
@@ -301,7 +304,8 @@
 
 }  // namespace
 
-fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider>>
+fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
+                     const CuttlefishConfig::InstanceSpecific>>
 bootStateMachineComponent() {
   return fruit::createComponent()
       .addMultibinding<KernelLogPipeConsumer, CvdBootStateMachine>()
diff --git a/host/commands/run_cvd/boot_state_machine.h b/host/commands/run_cvd/boot_state_machine.h
index 0cb92d5..1a2ebfd 100644
--- a/host/commands/run_cvd/boot_state_machine.h
+++ b/host/commands/run_cvd/boot_state_machine.h
@@ -21,7 +21,8 @@
 
 namespace cuttlefish {
 
-fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider>>
+fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
+                     const CuttlefishConfig::InstanceSpecific>>
 bootStateMachineComponent();
 
 }  // namespace cuttlefish
diff --git a/host/commands/run_cvd/launch/modem.cpp b/host/commands/run_cvd/launch/modem.cpp
index 8aa6adb..b1c5c64 100644
--- a/host/commands/run_cvd/launch/modem.cpp
+++ b/host/commands/run_cvd/launch/modem.cpp
@@ -61,9 +61,8 @@
 
 class ModemSimulator : public CommandSource {
  public:
-  INJECT(ModemSimulator(const CuttlefishConfig& config,
-                        const CuttlefishConfig::InstanceSpecific& instance))
-      : config_(config), instance_(instance) {}
+  INJECT(ModemSimulator(const CuttlefishConfig::InstanceSpecific& instance))
+      : instance_(instance) {}
 
   // CommandSource
   Result<std::vector<Command>> Commands() override {
@@ -79,7 +78,7 @@
                  : StopperResult::kStopFailure;
     });
 
-    auto sim_type = config_.modem_simulator_sim_type();
+    auto sim_type = instance_.modem_simulator_sim_type();
     cmd.AddParameter(std::string{"-sim_type="} + std::to_string(sim_type));
     cmd.AddParameter("-server_fds=");
     bool first_socket = true;
@@ -99,16 +98,16 @@
   // SetupFeature
   std::string Name() const override { return "ModemSimulator"; }
   bool Enabled() const override {
-    if (!config_.enable_modem_simulator()) {
+    if (!instance_.enable_modem_simulator()) {
       LOG(DEBUG) << "Modem simulator not enabled";
     }
-    return config_.enable_modem_simulator();
+    return instance_.enable_modem_simulator();
   }
 
  private:
   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
   Result<void> ResultSetup() override {
-    int instance_number = config_.modem_simulator_instance_number();
+    int instance_number = instance_.modem_simulator_instance_number();
     CF_EXPECT(instance_number >= 0 && instance_number < 4,
               "Modem simulator instance number should range between 0 and 3");
     auto ports = instance_.modem_simulator_ports();
@@ -125,7 +124,6 @@
     return {};
   }
 
-  const CuttlefishConfig& config_;
   const CuttlefishConfig::InstanceSpecific& instance_;
   std::vector<SharedFD> sockets_;
 };
diff --git a/host/commands/secure_env/confui_sign_server.cpp b/host/commands/secure_env/confui_sign_server.cpp
index 082f48c..c6d3227 100644
--- a/host/commands/secure_env/confui_sign_server.cpp
+++ b/host/commands/secure_env/confui_sign_server.cpp
@@ -22,6 +22,14 @@
 #include "host/libs/config/cuttlefish_config.h"
 #include "tpm_keymaster_context.h"
 
+namespace {
+
+// Defined in
+// hardware/interfaces/confirmationui/1.0/IConfirmationResultCallback.hal
+constexpr const char kConfirmationTokenMessageTag[] = "confirmation token";
+
+}  // namespace
+
 namespace cuttlefish {
 ConfUiSignServer::ConfUiSignServer(TpmResourceManager& tpm_resource_manager,
                                    SharedFD server_fd)
@@ -63,10 +71,14 @@
       continue;
     }
 
-    // hmac
+    // hmac over (prefix || data)
+    std::vector<std::uint8_t> data{std::begin(kConfirmationTokenMessageTag),
+                                   std::end(kConfirmationTokenMessageTag) - 1};
+
+    data.insert(data.end(), request.payload_.data(),
+                request.payload_.data() + request.payload_.size());
     auto hmac = TpmHmac(tpm_resource_manager_, signing_key->get(),
-                        TpmAuth(ESYS_TR_PASSWORD), request.payload_.data(),
-                        request.payload_.size());
+                        TpmAuth(ESYS_TR_PASSWORD), data.data(), data.size());
     if (!hmac) {
       LOG(ERROR) << "Could not calculate confirmation token hmac";
       sign_sender.Send(confui::SignMessageError::kUnknownError, {});
diff --git a/host/commands/secure_env/doc/linkage.dot b/host/commands/secure_env/doc/linkage.dot
index c33d43c..069ff82 100644
--- a/host/commands/secure_env/doc/linkage.dot
+++ b/host/commands/secure_env/doc/linkage.dot
@@ -1,70 +1,52 @@
 digraph {
   browser [label = "Browser"]
-  confirmationui_sign [label = "internal/confui_sign.sock", shape = "rectangle"]
-  host_confirmationui_in [color = "red", label = "internal/confui_fifo_vm.in", shape = "rectangle"]
-  host_confirmationui_out [color = "red", label = "internal/confui_fifo_vm.out", shape = "rectangle"]
-  host_gatekeeper_in [color = "green", label = "internal/gatekeeper_fifo_vm.in", shape = "rectangle"]
-  host_gatekeeper_out [color = "green", label = "internal/gatekeeper_fifo_vm.out", shape = "rectangle"]
-  host_keymint_in [color = "blue", label = "internal/keymaster_fifo_vm.in", shape = "rectangle"]
-  host_keymint_out [color = "blue", label = "internal/keymaster_fifo_vm.out", shape = "rectangle"]
+  confirmationui_sign [color = "red", label = "internal/confui_sign.sock", shape = "rectangle"]
   run_cvd
   secure_env [label = < <B>secure_env</B> >, penwidth=2]
   vmm [label = "crosvm / qemu"]
   webRTC
+
+  subgraph fifos {
+    rank = same;
+
+    host_keymint_in [color = "blue", label = "internal/keymaster_fifo_vm.in", shape = "rectangle"]
+    host_keymint_out [color = "blue", label = "internal/keymaster_fifo_vm.out", shape = "rectangle"]
+    host_gatekeeper_in [color = "green", label = "internal/gatekeeper_fifo_vm.in", shape = "rectangle"]
+    host_gatekeeper_out [color = "green", label = "internal/gatekeeper_fifo_vm.out", shape = "rectangle"]
+    host_confirmationui_in [color = "red", label = "internal/confui_fifo_vm.in", shape = "rectangle"]
+    host_confirmationui_out [color = "red", label = "internal/confui_fifo_vm.out", shape = "rectangle"]
+  }
+
   subgraph cluster_android {
     label = "Android VM"
     u_boot [label = "u-boot"]
     confirmationui [color = "red", label = "ConfirmationUI HAL"]
-    confirmationui_console [color = "red", label = "/dev/hvc8", shape = "rectangle"]
     gatekeeper [color = "green", label = "Gatekeeper HAL"]
-    gatekeeper_console [color = "green", label = "/dev/hvc4", shape = "rectangle"]
     keymint [color = "blue", label = "Keymint HAL"]
-    keymint_console [color = "blue", label = "/dev/hvc3", shape = "rectangle"]
+
+    subgraph consoles {
+      	rank = same;
+        confirmationui_console [color = "red", label = "/dev/hvc8", shape = "rectangle"]
+        gatekeeper_console [color = "green", label = "/dev/hvc4", shape = "rectangle"]
+        keymint_console [color = "blue", label = "/dev/hvc3", shape = "rectangle"]
+    }
   }
 
   browser -> webRTC
   run_cvd -> secure_env
-  host_confirmationui_out -> webRTC [color = "red"]
-  webRTC -> host_confirmationui_in [color = "red"]
-  host_gatekeeper_out -> secure_env [color = "green"]
-  secure_env -> host_gatekeeper_in [color = "green"]
-  host_keymint_out -> secure_env [color = "blue"]
-  secure_env -> host_keymint_in [color = "blue"]
 
-  vmm -> host_confirmationui_out [color = "red"]
-  host_confirmationui_in -> vmm [color = "red"]
+  webRTC -> host_confirmationui_out -> vmm [dir = "back", color = "red"]
+  webRTC -> host_confirmationui_in -> vmm [color = "red"]
+  vmm -> confirmationui_console -> confirmationui [dir = "both", color = "red"]
+  { rank = same; secure_env -> confirmationui_sign -> webRTC [color = "red", dir = "both"] }
 
-  vmm -> host_gatekeeper_out [color = "green"]
-  host_gatekeeper_in -> vmm [color = "green"]
+  secure_env -> host_gatekeeper_out -> vmm [color = "green", dir = "back"]
+  secure_env -> host_gatekeeper_in -> vmm [color = "green"]
+  vmm -> gatekeeper_console -> gatekeeper [color = "green", dir = "both"]
 
-  vmm -> host_keymint_out [color = "blue"]
-  host_keymint_in -> vmm [color = "blue"]
+  secure_env -> host_keymint_out -> vmm [color = "blue", dir = "back"]
+  secure_env -> host_keymint_in -> vmm [color = "blue"]
+  vmm -> keymint_console -> keymint [color = "blue", dir = "both"]
+  keymint_console -> u_boot [color = "blue", dir = "both"]
 
-  vmm -> confirmationui_console [color = "red"]
-  confirmationui_console -> vmm [color = "red"]
-
-  vmm -> gatekeeper_console [color = "green"]
-  gatekeeper_console -> vmm [color = "green"]
-
-  vmm -> keymint_console [color = "blue"]
-  keymint_console -> vmm [color = "blue"]
-
-  confirmationui_console -> confirmationui [color = "red"]
-  confirmationui -> confirmationui_console [color = "red"]
-
-  gatekeeper_console -> gatekeeper [color = "green"]
-  gatekeeper -> gatekeeper_console [color = "green"]
-
-  keymint -> keymint_console [color = "blue"]
-  keymint_console -> keymint [color = "blue"]
-
-  u_boot -> keymint_console [color = "blue"]
-  keymint_console -> u_boot [color = "blue"]
-
-  webRTC -> confirmationui_sign
-  confirmationui_sign -> webRTC
-
-  secure_env -> confirmationui_sign
-  confirmationui_sign -> secure_env
 }
-
diff --git a/host/commands/secure_env/doc/linkage.png b/host/commands/secure_env/doc/linkage.png
index 481aa6a..2549569 100644
--- a/host/commands/secure_env/doc/linkage.png
+++ b/host/commands/secure_env/doc/linkage.png
Binary files differ
diff --git a/host/commands/secure_env/doc/linkage.svg b/host/commands/secure_env/doc/linkage.svg
index a464ca3..6bb643f 100644
--- a/host/commands/secure_env/doc/linkage.svg
+++ b/host/commands/secure_env/doc/linkage.svg
@@ -4,323 +4,278 @@
 <!-- Generated by graphviz version 2.43.0 (0)
  -->
 <!-- Title: %3 Pages: 1 -->
-<svg width="1009pt" height="479pt"
- viewBox="0.00 0.00 1008.52 479.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 475)">
+<svg width="1180pt" height="423pt"
+ viewBox="0.00 0.00 1179.50 423.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 419)">
 <title>%3</title>
-<polygon fill="white" stroke="transparent" points="-4,4 -4,-475 1004.52,-475 1004.52,4 -4,4"/>
-<g id="clust1" class="cluster">
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-419 1175.5,-419 1175.5,4 -4,4"/>
+<g id="clust2" class="cluster">
 <title>cluster_android</title>
-<polygon fill="none" stroke="black" points="538,-64 538,-283 977,-283 977,-64 538,-64"/>
-<text text-anchor="middle" x="757.5" y="-267.8" font-family="Times,serif" font-size="14.00">Android VM</text>
+<polygon fill="none" stroke="black" points="297.5,-8 297.5,-155 879.5,-155 879.5,-8 297.5,-8"/>
+<text text-anchor="middle" x="588.5" y="-139.8" font-family="Times,serif" font-size="14.00">Android VM</text>
 </g>
 <!-- browser -->
 <g id="node1" class="node">
 <title>browser</title>
-<ellipse fill="none" stroke="black" cx="346" cy="-453" rx="40.09" ry="18"/>
-<text text-anchor="middle" x="346" y="-449.3" font-family="Times,serif" font-size="14.00">Browser</text>
+<ellipse fill="none" stroke="black" cx="904.5" cy="-397" rx="40.09" ry="18"/>
+<text text-anchor="middle" x="904.5" y="-393.3" font-family="Times,serif" font-size="14.00">Browser</text>
 </g>
 <!-- webRTC -->
-<g id="node12" class="node">
+<g id="node6" class="node">
 <title>webRTC</title>
-<ellipse fill="none" stroke="black" cx="346" cy="-381" rx="42.49" ry="18"/>
-<text text-anchor="middle" x="346" y="-377.3" font-family="Times,serif" font-size="14.00">webRTC</text>
+<ellipse fill="none" stroke="black" cx="904.5" cy="-325" rx="42.49" ry="18"/>
+<text text-anchor="middle" x="904.5" y="-321.3" font-family="Times,serif" font-size="14.00">webRTC</text>
 </g>
 <!-- browser&#45;&gt;webRTC -->
 <g id="edge1" class="edge">
 <title>browser&#45;&gt;webRTC</title>
-<path fill="none" stroke="black" d="M346,-434.7C346,-426.98 346,-417.71 346,-409.11"/>
-<polygon fill="black" stroke="black" points="349.5,-409.1 346,-399.1 342.5,-409.1 349.5,-409.1"/>
+<path fill="none" stroke="black" d="M904.5,-378.7C904.5,-370.98 904.5,-361.71 904.5,-353.11"/>
+<polygon fill="black" stroke="black" points="908,-353.1 904.5,-343.1 901,-353.1 908,-353.1"/>
 </g>
 <!-- confirmationui_sign -->
 <g id="node2" class="node">
 <title>confirmationui_sign</title>
-<polygon fill="none" stroke="black" points="287,-327 133,-327 133,-291 287,-291 287,-327"/>
-<text text-anchor="middle" x="210" y="-305.3" font-family="Times,serif" font-size="14.00">internal/confui_sign.sock</text>
-</g>
-<!-- secure_env -->
-<g id="node10" class="node">
-<title>secure_env</title>
-<ellipse fill="none" stroke="black" stroke-width="2" cx="210" cy="-234" rx="57.39" ry="18"/>
-<text text-anchor="start" x="174" y="-231.3" font-family="Times,serif" font-size="14.00"> </text>
-<text text-anchor="start" x="178" y="-231.3" font-family="Times,serif" font-weight="bold" font-size="14.00">secure_env</text>
-<text text-anchor="start" x="242" y="-231.3" font-family="Times,serif" font-size="14.00"> </text>
-</g>
-<!-- confirmationui_sign&#45;&gt;secure_env -->
-<g id="edge32" class="edge">
-<title>confirmationui_sign&#45;&gt;secure_env</title>
-<path fill="none" stroke="black" d="M204.16,-290.7C203.27,-282.18 203.03,-271.72 203.45,-262.17"/>
-<polygon fill="black" stroke="black" points="206.96,-262.15 204.2,-251.92 199.98,-261.64 206.96,-262.15"/>
+<polygon fill="none" stroke="red" points="788.5,-343 634.5,-343 634.5,-307 788.5,-307 788.5,-343"/>
+<text text-anchor="middle" x="711.5" y="-321.3" font-family="Times,serif" font-size="14.00">internal/confui_sign.sock</text>
 </g>
 <!-- confirmationui_sign&#45;&gt;webRTC -->
-<g id="edge30" class="edge">
-<title>confirmationui_sign&#45;&gt;webRTC</title>
-<path fill="none" stroke="black" d="M248.98,-327.03C269.91,-337.25 295.27,-350.27 314.88,-360.85"/>
-<polygon fill="black" stroke="black" points="313.27,-363.95 323.72,-365.67 316.62,-357.81 313.27,-363.95"/>
-</g>
-<!-- host_confirmationui_in -->
-<g id="node3" class="node">
-<title>host_confirmationui_in</title>
-<polygon fill="none" stroke="red" points="586.5,-327 425.5,-327 425.5,-291 586.5,-291 586.5,-327"/>
-<text text-anchor="middle" x="506" y="-305.3" font-family="Times,serif" font-size="14.00">internal/confui_fifo_vm.in</text>
-</g>
-<!-- vmm -->
-<g id="node11" class="node">
-<title>vmm</title>
-<ellipse fill="none" stroke="black" cx="466" cy="-90" rx="64.19" ry="18"/>
-<text text-anchor="middle" x="466" y="-86.3" font-family="Times,serif" font-size="14.00">crosvm / qemu</text>
-</g>
-<!-- host_confirmationui_in&#45;&gt;vmm -->
 <g id="edge10" class="edge">
-<title>host_confirmationui_in&#45;&gt;vmm</title>
-<path fill="none" stroke="red" d="M508.5,-290.73C512.24,-260.25 517.27,-195.32 501,-144 497.88,-134.17 492.37,-124.45 486.63,-116.07"/>
-<polygon fill="red" stroke="red" points="489.38,-113.9 480.66,-107.88 483.72,-118.02 489.38,-113.9"/>
-</g>
-<!-- host_confirmationui_out -->
-<g id="node4" class="node">
-<title>host_confirmationui_out</title>
-<polygon fill="none" stroke="red" points="860,-36 692,-36 692,0 860,0 860,-36"/>
-<text text-anchor="middle" x="776" y="-14.3" font-family="Times,serif" font-size="14.00">internal/confui_fifo_vm.out</text>
-</g>
-<!-- host_confirmationui_out&#45;&gt;webRTC -->
-<g id="edge3" class="edge">
-<title>host_confirmationui_out&#45;&gt;webRTC</title>
-<path fill="none" stroke="red" d="M860.12,-27.89C909.02,-34.83 964.23,-46.32 981,-64 1011.07,-95.7 997,-117.31 997,-161 997,-235 997,-235 997,-235 997,-257.49 998.19,-268.51 981,-283 893.01,-357.16 534.22,-374.79 398.42,-378.84"/>
-<polygon fill="red" stroke="red" points="398.16,-375.34 388.27,-379.13 398.36,-382.34 398.16,-375.34"/>
-</g>
-<!-- host_gatekeeper_in -->
-<g id="node5" class="node">
-<title>host_gatekeeper_in</title>
-<polygon fill="none" stroke="green" points="492.5,-180 307.5,-180 307.5,-144 492.5,-144 492.5,-180"/>
-<text text-anchor="middle" x="400" y="-158.3" font-family="Times,serif" font-size="14.00">internal/gatekeeper_fifo_vm.in</text>
-</g>
-<!-- host_gatekeeper_in&#45;&gt;vmm -->
-<g id="edge12" class="edge">
-<title>host_gatekeeper_in&#45;&gt;vmm</title>
-<path fill="none" stroke="green" d="M416.31,-143.7C424.43,-135.09 434.36,-124.55 443.21,-115.17"/>
-<polygon fill="green" stroke="green" points="446.01,-117.3 450.33,-107.62 440.92,-112.5 446.01,-117.3"/>
-</g>
-<!-- host_gatekeeper_out -->
-<g id="node6" class="node">
-<title>host_gatekeeper_out</title>
-<polygon fill="none" stroke="green" points="508,-36 316,-36 316,0 508,0 508,-36"/>
-<text text-anchor="middle" x="412" y="-14.3" font-family="Times,serif" font-size="14.00">internal/gatekeeper_fifo_vm.out</text>
-</g>
-<!-- host_gatekeeper_out&#45;&gt;secure_env -->
-<g id="edge5" class="edge">
-<title>host_gatekeeper_out&#45;&gt;secure_env</title>
-<path fill="none" stroke="green" d="M315.74,-28.77C242.24,-41.85 145.59,-72.45 97,-144 88.01,-157.24 88.42,-166.5 97,-180 109.71,-199.99 132.17,-212.58 153.73,-220.43"/>
-<polygon fill="green" stroke="green" points="152.62,-223.75 163.22,-223.62 154.85,-217.11 152.62,-223.75"/>
-</g>
-<!-- host_keymint_in -->
-<g id="node7" class="node">
-<title>host_keymint_in</title>
-<polygon fill="none" stroke="blue" points="289.5,-180 106.5,-180 106.5,-144 289.5,-144 289.5,-180"/>
-<text text-anchor="middle" x="198" y="-158.3" font-family="Times,serif" font-size="14.00">internal/keymaster_fifo_vm.in</text>
-</g>
-<!-- host_keymint_in&#45;&gt;vmm -->
-<g id="edge14" class="edge">
-<title>host_keymint_in&#45;&gt;vmm</title>
-<path fill="none" stroke="blue" d="M263.22,-143.97C308.5,-132.14 368.15,-116.56 411.02,-105.36"/>
-<polygon fill="blue" stroke="blue" points="411.92,-108.74 420.72,-102.83 410.16,-101.97 411.92,-108.74"/>
-</g>
-<!-- host_keymint_out -->
-<g id="node8" class="node">
-<title>host_keymint_out</title>
-<polygon fill="none" stroke="blue" points="190,-36 0,-36 0,0 190,0 190,-36"/>
-<text text-anchor="middle" x="95" y="-14.3" font-family="Times,serif" font-size="14.00">internal/keymaster_fifo_vm.out</text>
-</g>
-<!-- host_keymint_out&#45;&gt;secure_env -->
-<g id="edge7" class="edge">
-<title>host_keymint_out&#45;&gt;secure_env</title>
-<path fill="none" stroke="blue" d="M79.6,-36.07C73.48,-43.97 67.16,-53.85 64,-64 48.68,-113.23 34.41,-137.78 64,-180 82.49,-206.39 115.53,-219.69 145.51,-226.37"/>
-<polygon fill="blue" stroke="blue" points="145.04,-229.85 155.53,-228.39 146.42,-222.98 145.04,-229.85"/>
+<title>confirmationui_sign&#45;&gt;webRTC</title>
+<path fill="none" stroke="red" d="M798.79,-325C816.4,-325 834.02,-325 851.64,-325"/>
+<polygon fill="red" stroke="red" points="798.78,-321.5 788.78,-325 798.78,-328.5 798.78,-321.5"/>
+<polygon fill="red" stroke="red" points="852.01,-328.5 862.01,-325 852.01,-321.5 852.01,-328.5"/>
 </g>
 <!-- run_cvd -->
-<g id="node9" class="node">
+<g id="node3" class="node">
 <title>run_cvd</title>
-<ellipse fill="none" stroke="black" cx="345" cy="-309" rx="39.79" ry="18"/>
-<text text-anchor="middle" x="345" y="-305.3" font-family="Times,serif" font-size="14.00">run_cvd</text>
+<ellipse fill="none" stroke="black" cx="502.5" cy="-397" rx="39.79" ry="18"/>
+<text text-anchor="middle" x="502.5" y="-393.3" font-family="Times,serif" font-size="14.00">run_cvd</text>
+</g>
+<!-- secure_env -->
+<g id="node4" class="node">
+<title>secure_env</title>
+<ellipse fill="none" stroke="black" stroke-width="2" cx="502.5" cy="-325" rx="57.39" ry="18"/>
+<text text-anchor="start" x="466.5" y="-322.3" font-family="Times,serif" font-size="14.00"> </text>
+<text text-anchor="start" x="470.5" y="-322.3" font-family="Times,serif" font-weight="bold" font-size="14.00">secure_env</text>
+<text text-anchor="start" x="534.5" y="-322.3" font-family="Times,serif" font-size="14.00"> </text>
 </g>
 <!-- run_cvd&#45;&gt;secure_env -->
 <g id="edge2" class="edge">
 <title>run_cvd&#45;&gt;secure_env</title>
-<path fill="none" stroke="black" d="M320.55,-294.78C299.89,-283.61 270,-267.44 246.5,-254.74"/>
-<polygon fill="black" stroke="black" points="247.98,-251.56 237.52,-249.88 244.65,-257.72 247.98,-251.56"/>
+<path fill="none" stroke="black" d="M502.5,-378.7C502.5,-370.98 502.5,-361.71 502.5,-353.11"/>
+<polygon fill="black" stroke="black" points="506,-353.1 502.5,-343.1 499,-353.1 506,-353.1"/>
 </g>
 <!-- secure_env&#45;&gt;confirmationui_sign -->
-<g id="edge31" class="edge">
+<g id="edge9" class="edge">
 <title>secure_env&#45;&gt;confirmationui_sign</title>
-<path fill="none" stroke="black" d="M215.8,-251.92C216.72,-260.47 216.97,-271.06 216.55,-280.72"/>
-<polygon fill="black" stroke="black" points="213.06,-280.48 215.84,-290.7 220.05,-280.98 213.06,-280.48"/>
+<path fill="none" stroke="red" d="M570.21,-325C588.23,-325 606.26,-325 624.28,-325"/>
+<polygon fill="red" stroke="red" points="570.06,-321.5 560.06,-325 570.06,-328.5 570.06,-321.5"/>
+<polygon fill="red" stroke="red" points="624.3,-328.5 634.3,-325 624.3,-321.5 624.3,-328.5"/>
 </g>
-<!-- secure_env&#45;&gt;host_gatekeeper_in -->
-<g id="edge6" class="edge">
-<title>secure_env&#45;&gt;host_gatekeeper_in</title>
-<path fill="none" stroke="green" d="M245.74,-219.83C273.4,-209.64 312.24,-195.33 344.02,-183.62"/>
-<polygon fill="green" stroke="green" points="345.46,-186.82 353.63,-180.08 343.04,-180.25 345.46,-186.82"/>
+<!-- host_keymint_in -->
+<g id="node7" class="node">
+<title>host_keymint_in</title>
+<polygon fill="none" stroke="blue" points="183,-271 0,-271 0,-235 183,-235 183,-271"/>
+<text text-anchor="middle" x="91.5" y="-249.3" font-family="Times,serif" font-size="14.00">internal/keymaster_fifo_vm.in</text>
 </g>
 <!-- secure_env&#45;&gt;host_keymint_in -->
-<g id="edge8" class="edge">
+<g id="edge19" class="edge">
 <title>secure_env&#45;&gt;host_keymint_in</title>
-<path fill="none" stroke="blue" d="M207.03,-215.7C205.71,-207.98 204.12,-198.71 202.65,-190.11"/>
-<polygon fill="blue" stroke="blue" points="206.07,-189.37 200.93,-180.1 199.17,-190.55 206.07,-189.37"/>
+<path fill="none" stroke="blue" d="M453.33,-315.63C389.35,-304.73 275.95,-285.41 193.11,-271.31"/>
+<polygon fill="blue" stroke="blue" points="193.61,-267.84 183.17,-269.61 192.44,-274.74 193.61,-267.84"/>
 </g>
-<!-- vmm&#45;&gt;host_confirmationui_out -->
-<g id="edge9" class="edge">
-<title>vmm&#45;&gt;host_confirmationui_out</title>
-<path fill="none" stroke="red" d="M500.93,-74.88C511.44,-70.98 523.08,-67 534,-64 582.08,-50.79 636.6,-40.28 681.71,-32.74"/>
-<polygon fill="red" stroke="red" points="682.47,-36.16 691.77,-31.08 681.34,-29.26 682.47,-36.16"/>
+<!-- host_keymint_out -->
+<g id="node8" class="node">
+<title>host_keymint_out</title>
+<polygon fill="none" stroke="blue" points="391.5,-271 201.5,-271 201.5,-235 391.5,-235 391.5,-271"/>
+<text text-anchor="middle" x="296.5" y="-249.3" font-family="Times,serif" font-size="14.00">internal/keymaster_fifo_vm.out</text>
 </g>
-<!-- vmm&#45;&gt;host_gatekeeper_out -->
-<g id="edge11" class="edge">
-<title>vmm&#45;&gt;host_gatekeeper_out</title>
-<path fill="none" stroke="green" d="M452.93,-72.05C446.52,-63.75 438.68,-53.58 431.58,-44.38"/>
-<polygon fill="green" stroke="green" points="434.21,-42.06 425.33,-36.28 428.66,-46.33 434.21,-42.06"/>
+<!-- secure_env&#45;&gt;host_keymint_out -->
+<g id="edge17" class="edge">
+<title>secure_env&#45;&gt;host_keymint_out</title>
+<path fill="none" stroke="blue" d="M455.48,-308.02C423,-296.99 379.85,-282.32 346.71,-271.06"/>
+<polygon fill="blue" stroke="blue" points="454.6,-311.42 465.19,-311.32 456.85,-304.79 454.6,-311.42"/>
 </g>
-<!-- vmm&#45;&gt;host_keymint_out -->
+<!-- host_gatekeeper_in -->
+<g id="node9" class="node">
+<title>host_gatekeeper_in</title>
+<polygon fill="none" stroke="green" points="595,-271 410,-271 410,-235 595,-235 595,-271"/>
+<text text-anchor="middle" x="502.5" y="-249.3" font-family="Times,serif" font-size="14.00">internal/gatekeeper_fifo_vm.in</text>
+</g>
+<!-- secure_env&#45;&gt;host_gatekeeper_in -->
 <g id="edge13" class="edge">
-<title>vmm&#45;&gt;host_keymint_out</title>
-<path fill="none" stroke="blue" d="M414.02,-79.19C357.57,-68.54 266.02,-51.27 195.44,-37.95"/>
-<polygon fill="blue" stroke="blue" points="195.66,-34.43 185.19,-36.02 194.36,-41.31 195.66,-34.43"/>
+<title>secure_env&#45;&gt;host_gatekeeper_in</title>
+<path fill="none" stroke="green" d="M502.5,-306.7C502.5,-298.98 502.5,-289.71 502.5,-281.11"/>
+<polygon fill="green" stroke="green" points="506,-281.1 502.5,-271.1 499,-281.1 506,-281.1"/>
+</g>
+<!-- host_gatekeeper_out -->
+<g id="node10" class="node">
+<title>host_gatekeeper_out</title>
+<polygon fill="none" stroke="green" points="805.5,-271 613.5,-271 613.5,-235 805.5,-235 805.5,-271"/>
+<text text-anchor="middle" x="709.5" y="-249.3" font-family="Times,serif" font-size="14.00">internal/gatekeeper_fifo_vm.out</text>
+</g>
+<!-- secure_env&#45;&gt;host_gatekeeper_out -->
+<g id="edge11" class="edge">
+<title>secure_env&#45;&gt;host_gatekeeper_out</title>
+<path fill="none" stroke="green" d="M549.75,-308.02C582.38,-296.99 625.74,-282.32 659.05,-271.06"/>
+<polygon fill="green" stroke="green" points="548.34,-304.8 539.99,-311.32 550.58,-311.44 548.34,-304.8"/>
+</g>
+<!-- vmm -->
+<g id="node5" class="node">
+<title>vmm</title>
+<ellipse fill="none" stroke="black" cx="605.5" cy="-181" rx="64.19" ry="18"/>
+<text text-anchor="middle" x="605.5" y="-177.3" font-family="Times,serif" font-size="14.00">crosvm / qemu</text>
 </g>
 <!-- confirmationui_console -->
-<g id="node15" class="node">
+<g id="node17" class="node">
 <title>confirmationui_console</title>
-<polygon fill="none" stroke="red" points="776,-180 706,-180 706,-144 776,-144 776,-180"/>
-<text text-anchor="middle" x="741" y="-158.3" font-family="Times,serif" font-size="14.00">/dev/hvc8</text>
+<polygon fill="none" stroke="red" points="773.5,-124 703.5,-124 703.5,-88 773.5,-88 773.5,-124"/>
+<text text-anchor="middle" x="738.5" y="-102.3" font-family="Times,serif" font-size="14.00">/dev/hvc8</text>
 </g>
 <!-- vmm&#45;&gt;confirmationui_console -->
-<g id="edge15" class="edge">
+<g id="edge7" class="edge">
 <title>vmm&#45;&gt;confirmationui_console</title>
-<path fill="none" stroke="red" d="M514.28,-102C565.63,-114.52 646.01,-134.96 696.05,-148.19"/>
-<polygon fill="red" stroke="red" points="695.38,-151.64 705.94,-150.82 697.17,-144.87 695.38,-151.64"/>
+<path fill="none" stroke="red" d="M642.4,-159.74C659.81,-150.19 680.61,-138.77 698.4,-129.01"/>
+<polygon fill="red" stroke="red" points="640.46,-156.82 633.38,-164.7 643.83,-162.95 640.46,-156.82"/>
+<polygon fill="red" stroke="red" points="700.32,-131.95 707.4,-124.07 696.95,-125.81 700.32,-131.95"/>
 </g>
 <!-- gatekeeper_console -->
-<g id="node17" class="node">
+<g id="node18" class="node">
 <title>gatekeeper_console</title>
-<polygon fill="none" stroke="green" points="635,-180 565,-180 565,-144 635,-144 635,-180"/>
-<text text-anchor="middle" x="600" y="-158.3" font-family="Times,serif" font-size="14.00">/dev/hvc4</text>
+<polygon fill="none" stroke="green" points="640.5,-124 570.5,-124 570.5,-88 640.5,-88 640.5,-124"/>
+<text text-anchor="middle" x="605.5" y="-102.3" font-family="Times,serif" font-size="14.00">/dev/hvc4</text>
 </g>
 <!-- vmm&#45;&gt;gatekeeper_console -->
-<g id="edge17" class="edge">
+<g id="edge15" class="edge">
 <title>vmm&#45;&gt;gatekeeper_console</title>
-<path fill="none" stroke="green" d="M499.52,-105.58C519.26,-115.25 544.14,-128.12 564.25,-139.02"/>
-<polygon fill="green" stroke="green" points="562.66,-142.15 573.12,-143.88 566.02,-136.01 562.66,-142.15"/>
+<path fill="none" stroke="green" d="M605.5,-152.49C605.5,-146.55 605.5,-140.27 605.5,-134.33"/>
+<polygon fill="green" stroke="green" points="602,-152.7 605.5,-162.7 609,-152.7 602,-152.7"/>
+<polygon fill="green" stroke="green" points="609,-134.18 605.5,-124.18 602,-134.18 609,-134.18"/>
 </g>
 <!-- keymint_console -->
 <g id="node19" class="node">
 <title>keymint_console</title>
-<polygon fill="none" stroke="blue" points="903,-180 833,-180 833,-144 903,-144 903,-180"/>
-<text text-anchor="middle" x="868" y="-158.3" font-family="Times,serif" font-size="14.00">/dev/hvc3</text>
+<polygon fill="none" stroke="blue" points="488.5,-124 418.5,-124 418.5,-88 488.5,-88 488.5,-124"/>
+<text text-anchor="middle" x="453.5" y="-102.3" font-family="Times,serif" font-size="14.00">/dev/hvc3</text>
 </g>
 <!-- vmm&#45;&gt;keymint_console -->
-<g id="edge19" class="edge">
+<g id="edge21" class="edge">
 <title>vmm&#45;&gt;keymint_console</title>
-<path fill="none" stroke="blue" d="M514.88,-101.75C524.24,-104 533.92,-106.22 543,-108 653.59,-129.7 682.97,-124.68 794,-144 803.36,-145.63 813.29,-147.52 822.75,-149.43"/>
-<polygon fill="blue" stroke="blue" points="822.29,-152.91 832.79,-151.51 823.71,-146.06 822.29,-152.91"/>
+<path fill="none" stroke="blue" d="M565.23,-160.66C544.45,-150.68 519.06,-138.49 497.74,-128.25"/>
+<polygon fill="blue" stroke="blue" points="563.84,-163.88 574.37,-165.05 566.87,-157.57 563.84,-163.88"/>
+<polygon fill="blue" stroke="blue" points="499.1,-125.02 488.57,-123.84 496.07,-131.33 499.1,-125.02"/>
 </g>
-<!-- webRTC&#45;&gt;confirmationui_sign -->
-<g id="edge29" class="edge">
-<title>webRTC&#45;&gt;confirmationui_sign</title>
-<path fill="none" stroke="black" d="M317.04,-367.81C296.25,-357.89 268.24,-343.65 246.02,-331.79"/>
-<polygon fill="black" stroke="black" points="247.67,-328.7 237.21,-327.03 244.35,-334.86 247.67,-328.7"/>
+<!-- host_confirmationui_in -->
+<g id="node11" class="node">
+<title>host_confirmationui_in</title>
+<polygon fill="none" stroke="red" points="985,-271 824,-271 824,-235 985,-235 985,-271"/>
+<text text-anchor="middle" x="904.5" y="-249.3" font-family="Times,serif" font-size="14.00">internal/confui_fifo_vm.in</text>
 </g>
 <!-- webRTC&#45;&gt;host_confirmationui_in -->
-<g id="edge4" class="edge">
+<g id="edge5" class="edge">
 <title>webRTC&#45;&gt;host_confirmationui_in</title>
-<path fill="none" stroke="red" d="M374.6,-367.49C397.68,-357.39 430.65,-342.96 457.79,-331.09"/>
-<polygon fill="red" stroke="red" points="459.36,-334.22 467.12,-327.01 456.56,-327.81 459.36,-334.22"/>
+<path fill="none" stroke="red" d="M904.5,-306.7C904.5,-298.98 904.5,-289.71 904.5,-281.11"/>
+<polygon fill="red" stroke="red" points="908,-281.1 904.5,-271.1 901,-281.1 908,-281.1"/>
+</g>
+<!-- host_confirmationui_out -->
+<g id="node12" class="node">
+<title>host_confirmationui_out</title>
+<polygon fill="none" stroke="red" points="1171.5,-271 1003.5,-271 1003.5,-235 1171.5,-235 1171.5,-271"/>
+<text text-anchor="middle" x="1087.5" y="-249.3" font-family="Times,serif" font-size="14.00">internal/confui_fifo_vm.out</text>
+</g>
+<!-- webRTC&#45;&gt;host_confirmationui_out -->
+<g id="edge3" class="edge">
+<title>webRTC&#45;&gt;host_confirmationui_out</title>
+<path fill="none" stroke="red" d="M944.57,-308.67C973.65,-297.55 1013,-282.5 1043.05,-271"/>
+<polygon fill="red" stroke="red" points="943.22,-305.44 935.13,-312.28 945.72,-311.98 943.22,-305.44"/>
+</g>
+<!-- host_keymint_in&#45;&gt;vmm -->
+<g id="edge20" class="edge">
+<title>host_keymint_in&#45;&gt;vmm</title>
+<path fill="none" stroke="blue" d="M183.13,-236.42C186.29,-235.93 189.42,-235.46 192.5,-235 313.66,-217.04 455.35,-199.63 537.32,-189.92"/>
+<polygon fill="blue" stroke="blue" points="537.74,-193.4 547.26,-188.75 536.92,-186.44 537.74,-193.4"/>
+</g>
+<!-- host_keymint_out&#45;&gt;vmm -->
+<g id="edge18" class="edge">
+<title>host_keymint_out&#45;&gt;vmm</title>
+<path fill="none" stroke="blue" d="M381.69,-232.7C438.14,-219.91 510.16,-203.6 557.03,-192.98"/>
+<polygon fill="blue" stroke="blue" points="380.67,-229.34 371.69,-234.97 382.22,-236.17 380.67,-229.34"/>
+</g>
+<!-- host_gatekeeper_in&#45;&gt;vmm -->
+<g id="edge14" class="edge">
+<title>host_gatekeeper_in&#45;&gt;vmm</title>
+<path fill="none" stroke="green" d="M527.7,-234.88C541.54,-225.47 558.84,-213.71 573.62,-203.67"/>
+<polygon fill="green" stroke="green" points="575.67,-206.5 581.98,-197.99 571.74,-200.71 575.67,-206.5"/>
+</g>
+<!-- host_gatekeeper_out&#45;&gt;vmm -->
+<g id="edge12" class="edge">
+<title>host_gatekeeper_out&#45;&gt;vmm</title>
+<path fill="none" stroke="green" d="M675.47,-229.1C660.36,-218.92 642.93,-207.2 629.25,-197.99"/>
+<polygon fill="green" stroke="green" points="673.81,-232.2 684.06,-234.88 677.72,-226.39 673.81,-232.2"/>
+</g>
+<!-- host_confirmationui_in&#45;&gt;vmm -->
+<g id="edge6" class="edge">
+<title>host_confirmationui_in&#45;&gt;vmm</title>
+<path fill="none" stroke="red" d="M831.74,-234.97C779.94,-222.84 711.27,-206.76 663.22,-195.51"/>
+<polygon fill="red" stroke="red" points="663.83,-192.06 653.3,-193.19 662.24,-198.88 663.83,-192.06"/>
+</g>
+<!-- host_confirmationui_out&#45;&gt;vmm -->
+<g id="edge4" class="edge">
+<title>host_confirmationui_out&#45;&gt;vmm</title>
+<path fill="none" stroke="red" d="M993.38,-234.86C869.99,-215.37 737.36,-198.25 663.29,-189.04"/>
+<polygon fill="red" stroke="red" points="992.95,-238.33 1003.38,-236.45 994.05,-231.42 992.95,-238.33"/>
 </g>
 <!-- u_boot -->
 <g id="node13" class="node">
 <title>u_boot</title>
-<ellipse fill="none" stroke="black" cx="935" cy="-234" rx="33.6" ry="18"/>
-<text text-anchor="middle" x="935" y="-230.3" font-family="Times,serif" font-size="14.00">u&#45;boot</text>
-</g>
-<!-- u_boot&#45;&gt;keymint_console -->
-<g id="edge27" class="edge">
-<title>u_boot&#45;&gt;keymint_console</title>
-<path fill="none" stroke="blue" d="M915.89,-218.83C906.18,-209.92 894.63,-198.17 885.21,-187.75"/>
-<polygon fill="blue" stroke="blue" points="887.72,-185.31 878.48,-180.11 882.46,-189.93 887.72,-185.31"/>
+<ellipse fill="none" stroke="black" cx="339.5" cy="-34" rx="33.6" ry="18"/>
+<text text-anchor="middle" x="339.5" y="-30.3" font-family="Times,serif" font-size="14.00">u&#45;boot</text>
 </g>
 <!-- confirmationui -->
 <g id="node14" class="node">
 <title>confirmationui</title>
-<ellipse fill="none" stroke="red" cx="795" cy="-234" rx="88.28" ry="18"/>
-<text text-anchor="middle" x="795" y="-230.3" font-family="Times,serif" font-size="14.00">ConfirmationUI HAL</text>
-</g>
-<!-- confirmationui&#45;&gt;confirmationui_console -->
-<g id="edge22" class="edge">
-<title>confirmationui&#45;&gt;confirmationui_console</title>
-<path fill="none" stroke="red" d="M776.05,-216.05C768.84,-207.84 760.87,-197.79 754.26,-188.66"/>
-<polygon fill="red" stroke="red" points="757.01,-186.47 748.41,-180.28 751.26,-190.48 757.01,-186.47"/>
-</g>
-<!-- confirmationui_console&#45;&gt;vmm -->
-<g id="edge16" class="edge">
-<title>confirmationui_console&#45;&gt;vmm</title>
-<path fill="none" stroke="red" d="M705.81,-153.17C658.48,-141.79 574.22,-120.44 519.17,-105.97"/>
-<polygon fill="red" stroke="red" points="519.81,-102.52 509.25,-103.36 518.03,-109.29 519.81,-102.52"/>
-</g>
-<!-- confirmationui_console&#45;&gt;confirmationui -->
-<g id="edge21" class="edge">
-<title>confirmationui_console&#45;&gt;confirmationui</title>
-<path fill="none" stroke="red" d="M760.24,-180.28C767.47,-188.54 775.43,-198.6 782,-207.71"/>
-<polygon fill="red" stroke="red" points="779.22,-209.84 787.81,-216.05 784.97,-205.85 779.22,-209.84"/>
+<ellipse fill="none" stroke="red" cx="783.5" cy="-34" rx="88.28" ry="18"/>
+<text text-anchor="middle" x="783.5" y="-30.3" font-family="Times,serif" font-size="14.00">ConfirmationUI HAL</text>
 </g>
 <!-- gatekeeper -->
-<g id="node16" class="node">
+<g id="node15" class="node">
 <title>gatekeeper</title>
-<ellipse fill="none" stroke="green" cx="617" cy="-234" rx="71.49" ry="18"/>
-<text text-anchor="middle" x="617" y="-230.3" font-family="Times,serif" font-size="14.00">Gatekeeper HAL</text>
-</g>
-<!-- gatekeeper&#45;&gt;gatekeeper_console -->
-<g id="edge24" class="edge">
-<title>gatekeeper&#45;&gt;gatekeeper_console</title>
-<path fill="none" stroke="green" d="M607.01,-216.05C604.32,-208.35 601.81,-199.03 600.04,-190.36"/>
-<polygon fill="green" stroke="green" points="603.45,-189.53 598.28,-180.28 596.55,-190.73 603.45,-189.53"/>
-</g>
-<!-- gatekeeper_console&#45;&gt;vmm -->
-<g id="edge18" class="edge">
-<title>gatekeeper_console&#45;&gt;vmm</title>
-<path fill="none" stroke="green" d="M564.89,-145.64C544.59,-135.66 519.28,-122.52 499.29,-111.64"/>
-<polygon fill="green" stroke="green" points="500.96,-108.56 490.51,-106.81 497.59,-114.7 500.96,-108.56"/>
-</g>
-<!-- gatekeeper_console&#45;&gt;gatekeeper -->
-<g id="edge23" class="edge">
-<title>gatekeeper_console&#45;&gt;gatekeeper</title>
-<path fill="none" stroke="green" d="M610.05,-180.1C612.72,-187.79 615.21,-197.05 616.97,-205.67"/>
-<polygon fill="green" stroke="green" points="613.55,-206.45 618.71,-215.7 620.44,-205.24 613.55,-206.45"/>
+<ellipse fill="none" stroke="green" cx="605.5" cy="-34" rx="71.49" ry="18"/>
+<text text-anchor="middle" x="605.5" y="-30.3" font-family="Times,serif" font-size="14.00">Gatekeeper HAL</text>
 </g>
 <!-- keymint -->
-<g id="node18" class="node">
+<g id="node16" class="node">
 <title>keymint</title>
-<ellipse fill="none" stroke="blue" cx="868" cy="-90" rx="62.29" ry="18"/>
-<text text-anchor="middle" x="868" y="-86.3" font-family="Times,serif" font-size="14.00">Keymint HAL</text>
+<ellipse fill="none" stroke="blue" cx="453.5" cy="-34" rx="62.29" ry="18"/>
+<text text-anchor="middle" x="453.5" y="-30.3" font-family="Times,serif" font-size="14.00">Keymint HAL</text>
 </g>
-<!-- keymint&#45;&gt;keymint_console -->
-<g id="edge25" class="edge">
-<title>keymint&#45;&gt;keymint_console</title>
-<path fill="none" stroke="blue" d="M873.9,-108.1C874.7,-115.79 874.94,-125.05 874.6,-133.67"/>
-<polygon fill="blue" stroke="blue" points="871.11,-133.48 873.92,-143.7 878.09,-133.96 871.11,-133.48"/>
+<!-- confirmationui_console&#45;&gt;confirmationui -->
+<g id="edge8" class="edge">
+<title>confirmationui_console&#45;&gt;confirmationui</title>
+<path fill="none" stroke="red" d="M755.16,-79.08C759.05,-73.03 763.19,-66.59 767.07,-60.56"/>
+<polygon fill="red" stroke="red" points="752.09,-77.39 749.62,-87.7 757.98,-81.18 752.09,-77.39"/>
+<polygon fill="red" stroke="red" points="770.04,-62.41 772.5,-52.1 764.15,-58.62 770.04,-62.41"/>
 </g>
-<!-- keymint_console&#45;&gt;vmm -->
-<g id="edge20" class="edge">
-<title>keymint_console&#45;&gt;vmm</title>
-<path fill="none" stroke="blue" d="M832.94,-155C815.62,-151.54 794.69,-147.25 776,-144 664.97,-124.68 635.59,-129.7 525,-108 522.2,-107.45 519.35,-106.86 516.47,-106.24"/>
-<polygon fill="blue" stroke="blue" points="517.23,-102.82 506.7,-104.03 515.68,-109.65 517.23,-102.82"/>
+<!-- gatekeeper_console&#45;&gt;gatekeeper -->
+<g id="edge16" class="edge">
+<title>gatekeeper_console&#45;&gt;gatekeeper</title>
+<path fill="none" stroke="green" d="M605.5,-77.67C605.5,-72.69 605.5,-67.49 605.5,-62.51"/>
+<polygon fill="green" stroke="green" points="602,-77.7 605.5,-87.7 609,-77.7 602,-77.7"/>
+<polygon fill="green" stroke="green" points="609,-62.1 605.5,-52.1 602,-62.1 609,-62.1"/>
 </g>
 <!-- keymint_console&#45;&gt;u_boot -->
-<g id="edge28" class="edge">
+<g id="edge23" class="edge">
 <title>keymint_console&#45;&gt;u_boot</title>
-<path fill="none" stroke="blue" d="M890.37,-180.2C899.52,-188.82 909.84,-199.41 918.33,-208.84"/>
-<polygon fill="blue" stroke="blue" points="915.74,-211.2 924.96,-216.41 921,-206.58 915.74,-211.2"/>
+<path fill="none" stroke="blue" d="M416.87,-82.51C401.61,-73.14 384.11,-62.39 369.62,-53.49"/>
+<polygon fill="blue" stroke="blue" points="415.26,-85.63 425.61,-87.88 418.92,-79.66 415.26,-85.63"/>
+<polygon fill="blue" stroke="blue" points="371.38,-50.47 361.03,-48.22 367.72,-56.44 371.38,-50.47"/>
 </g>
 <!-- keymint_console&#45;&gt;keymint -->
-<g id="edge26" class="edge">
+<g id="edge22" class="edge">
 <title>keymint_console&#45;&gt;keymint</title>
-<path fill="none" stroke="blue" d="M862.08,-143.7C861.29,-135.98 861.06,-126.71 861.4,-118.11"/>
-<polygon fill="blue" stroke="blue" points="864.9,-118.32 862.1,-108.1 857.92,-117.84 864.9,-118.32"/>
+<path fill="none" stroke="blue" d="M453.5,-77.67C453.5,-72.69 453.5,-67.49 453.5,-62.51"/>
+<polygon fill="blue" stroke="blue" points="450,-77.7 453.5,-87.7 457,-77.7 450,-77.7"/>
+<polygon fill="blue" stroke="blue" points="457,-62.1 453.5,-52.1 450,-62.1 457,-62.1"/>
 </g>
 </g>
 </svg>
diff --git a/host/commands/secure_env/keymaster_responder.cpp b/host/commands/secure_env/keymaster_responder.cpp
index 7289d48..2504e4d 100644
--- a/host/commands/secure_env/keymaster_responder.cpp
+++ b/host/commands/secure_env/keymaster_responder.cpp
@@ -91,6 +91,7 @@
     HANDLE_MESSAGE_W_RETURN(CONFIGURE_VERIFIED_BOOT_INFO,
                             ConfigureVerifiedBootInfo)
     HANDLE_MESSAGE_W_RETURN(GET_ROOT_OF_TRUST, GetRootOfTrust)
+    HANDLE_MESSAGE_W_RETURN(SET_ATTESTATION_IDS, SetAttestationIds)
 #undef HANDLE_MESSAGE_W_RETURN
 #define HANDLE_MESSAGE_W_RETURN_NO_ARG(ENUM_NAME, METHOD_NAME) \
   case ENUM_NAME: {                                            \
diff --git a/host/commands/secure_env/proxy_keymaster_context.h b/host/commands/secure_env/proxy_keymaster_context.h
index 2be72b0..a296074 100644
--- a/host/commands/secure_env/proxy_keymaster_context.h
+++ b/host/commands/secure_env/proxy_keymaster_context.h
@@ -163,6 +163,11 @@
     return wrapped_.GetBootPatchlevel();
   }
 
+  keymaster_error_t SetAttestationIds(
+      const keymaster::SetAttestationIdsRequest& request) override {
+    return wrapped_.SetAttestationIds(request);
+  }
+
  private:
   KeymasterContext& wrapped_;
 };
diff --git a/host/commands/secure_env/tpm_attestation_record.cpp b/host/commands/secure_env/tpm_attestation_record.cpp
index 674570b..70620ae 100644
--- a/host/commands/secure_env/tpm_attestation_record.cpp
+++ b/host/commands/secure_env/tpm_attestation_record.cpp
@@ -54,11 +54,76 @@
   return KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
 }
 
+// Return true if entries match, false otherwise.
+bool matchAttestationId(keymaster_blob_t blob, const std::vector<uint8_t>& id) {
+  if (blob.data_length != id.size()) {
+    return false;
+  }
+  if (memcmp(blob.data, id.data(), id.size())) {
+    return false;
+  }
+  return true;
+}
+
 keymaster_error_t TpmAttestationRecordContext::VerifyAndCopyDeviceIds(
-    const AuthorizationSet& /*attestation_params*/,
-    AuthorizationSet* /*attestation*/) const {
-  LOG(DEBUG) << "TODO(schuffelen): Implement VerifyAndCopyDeviceIds";
-  return KM_ERROR_UNIMPLEMENTED;
+    const AuthorizationSet& attestation_params,
+    AuthorizationSet* attestation) const {
+  const AttestationIds& ids = attestation_ids_;
+  bool found_mismatch = false;
+  for (auto& entry : attestation_params) {
+    switch (entry.tag) {
+      case KM_TAG_ATTESTATION_ID_BRAND:
+        found_mismatch |= !matchAttestationId(entry.blob, ids.brand);
+        attestation->push_back(entry);
+        break;
+
+      case KM_TAG_ATTESTATION_ID_DEVICE:
+        found_mismatch |= !matchAttestationId(entry.blob, ids.device);
+        attestation->push_back(entry);
+        break;
+
+      case KM_TAG_ATTESTATION_ID_PRODUCT:
+        found_mismatch |= !matchAttestationId(entry.blob, ids.product);
+        attestation->push_back(entry);
+        break;
+
+      case KM_TAG_ATTESTATION_ID_SERIAL:
+        found_mismatch |= !matchAttestationId(entry.blob, ids.serial);
+        attestation->push_back(entry);
+        break;
+
+      case KM_TAG_ATTESTATION_ID_IMEI:
+        found_mismatch |= !matchAttestationId(entry.blob, ids.imei);
+        attestation->push_back(entry);
+        break;
+
+      case KM_TAG_ATTESTATION_ID_MEID:
+        found_mismatch |= !matchAttestationId(entry.blob, ids.meid);
+        attestation->push_back(entry);
+        break;
+
+      case KM_TAG_ATTESTATION_ID_MANUFACTURER:
+        found_mismatch |= !matchAttestationId(entry.blob, ids.manufacturer);
+        attestation->push_back(entry);
+        break;
+
+      case KM_TAG_ATTESTATION_ID_MODEL:
+        found_mismatch |= !matchAttestationId(entry.blob, ids.model);
+        attestation->push_back(entry);
+        break;
+
+      default:
+        // Ignore non-ID tags.
+        break;
+    }
+  }
+
+  if (found_mismatch) {
+    attestation->Clear();
+    return KM_ERROR_CANNOT_ATTEST_IDS;
+  }
+
+  return KM_ERROR_OK;
 }
 
 keymaster::Buffer TpmAttestationRecordContext::GenerateUniqueId(
@@ -107,4 +172,20 @@
   vb_params_.device_locked = bootloader_state == "locked";
 }
 
+keymaster_error_t TpmAttestationRecordContext::SetAttestationIds(
+    const keymaster::SetAttestationIdsRequest& request) {
+  attestation_ids_.brand.assign(request.brand.begin(), request.brand.end());
+  attestation_ids_.device.assign(request.device.begin(), request.device.end());
+  attestation_ids_.product.assign(request.product.begin(),
+                                  request.product.end());
+  attestation_ids_.serial.assign(request.serial.begin(), request.serial.end());
+  attestation_ids_.imei.assign(request.imei.begin(), request.imei.end());
+  attestation_ids_.meid.assign(request.meid.begin(), request.meid.end());
+  attestation_ids_.manufacturer.assign(request.manufacturer.begin(),
+                                       request.manufacturer.end());
+  attestation_ids_.model.assign(request.model.begin(), request.model.end());
+
+  return KM_ERROR_OK;
+}
+
 }  // namespace cuttlefish
diff --git a/host/commands/secure_env/tpm_attestation_record.h b/host/commands/secure_env/tpm_attestation_record.h
index dba0e91..3a48fdd 100644
--- a/host/commands/secure_env/tpm_attestation_record.h
+++ b/host/commands/secure_env/tpm_attestation_record.h
@@ -22,10 +22,22 @@
 #include <string_view>
 #include <vector>
 
+#include <keymaster/android_keymaster_messages.h>
 #include <keymaster/attestation_context.h>
 
 namespace cuttlefish {
 
+struct AttestationIds {
+  std::vector<uint8_t> brand;
+  std::vector<uint8_t> device;
+  std::vector<uint8_t> product;
+  std::vector<uint8_t> serial;
+  std::vector<uint8_t> imei;
+  std::vector<uint8_t> meid;
+  std::vector<uint8_t> manufacturer;
+  std::vector<uint8_t> model;
+};
+
 class TpmAttestationRecordContext : public keymaster::AttestationContext {
 public:
  TpmAttestationRecordContext();
@@ -46,11 +58,14 @@
  void SetVerifiedBootInfo(std::string_view verified_boot_state,
                           std::string_view bootloader_state,
                           const std::vector<uint8_t>& vbmeta_digest);
+ keymaster_error_t SetAttestationIds(
+     const keymaster::SetAttestationIdsRequest& request);
 
 private:
  std::vector<uint8_t> vbmeta_digest_;
  VerifiedBootParams vb_params_;
  std::vector<uint8_t> unique_id_hbk_;
+ AttestationIds attestation_ids_;
 };
 
 }  // namespace cuttlefish
diff --git a/host/commands/secure_env/tpm_keymaster_context.h b/host/commands/secure_env/tpm_keymaster_context.h
index 2271019..df0a007 100644
--- a/host/commands/secure_env/tpm_keymaster_context.h
+++ b/host/commands/secure_env/tpm_keymaster_context.h
@@ -132,6 +132,11 @@
   keymaster_error_t SetBootPatchlevel(uint32_t boot_patchlevel) override;
   std::optional<uint32_t> GetVendorPatchlevel() const override;
   std::optional<uint32_t> GetBootPatchlevel() const override;
+
+  keymaster_error_t SetAttestationIds(
+      const keymaster::SetAttestationIdsRequest& request) override {
+    return attestation_context_->SetAttestationIds(request);
+  }
 };
 
 }  // namespace cuttlefish
diff --git a/host/commands/secure_env/tpm_remote_provisioning_context.cpp b/host/commands/secure_env/tpm_remote_provisioning_context.cpp
index 48bb545..b63d713 100644
--- a/host/commands/secure_env/tpm_remote_provisioning_context.cpp
+++ b/host/commands/secure_env/tpm_remote_provisioning_context.cpp
@@ -234,17 +234,18 @@
 cppcose::ErrMsgOr<cppbor::Array> TpmRemoteProvisioningContext::BuildCsr(
     const std::vector<uint8_t>& challenge, cppbor::Array keysToSign) const {
   auto deviceInfo = std::move(*CreateDeviceInfo());
-  auto signedDataPayload = cppbor::Array()
-                               .add(1 /* version */)
-                               .add("keymint" /* CertificateType */)
-                               .add(std::move(deviceInfo))
-                               .add(challenge)
-                               .add(std::move(keysToSign));
+  auto csrPayload = cppbor::Array()
+                        .add(3 /* version */)
+                        .add("keymint" /* CertificateType */)
+                        .add(std::move(deviceInfo))
+                        .add(std::move(keysToSign));
+  auto signedDataPayload =
+      cppbor::Array().add(challenge).add(cppbor::Bstr(csrPayload.encode()));
   auto signedData = constructCoseSign1(
       devicePrivKey_, signedDataPayload.encode(), {} /* aad */);
 
   return cppbor::Array()
-      .add(3 /* version */)
+      .add(1 /* version */)
       .add(cppbor::Map() /* UdsCerts */)
       .add(std::move(*bcc_.clone()->asArray()) /* DiceCertChain */)
       .add(std::move(*signedData) /* SignedData */);
diff --git a/host/commands/start/main.cc b/host/commands/start/main.cc
index 45101a1..0bdd794 100644
--- a/host/commands/start/main.cc
+++ b/host/commands/start/main.cc
@@ -189,7 +189,13 @@
 // Used to find bool flag and convert "flag"/"noflag" to "--flag=value"
 // This is the solution for vectorize bool flags in gFlags
 std::unordered_set<std::string> kBoolFlags = {"guest_enforce_security",
-  "use_random_serial", "use_allocd", "use_sdcard", "pause_in_bootloader"};
+                                              "use_random_serial",
+                                              "use_allocd",
+                                              "use_sdcard",
+                                              "pause_in_bootloader",
+                                              "daemon",
+                                              "enable_minimal_mode",
+                                              "enable_modem_simulator"};
 
 struct BooleanFlag {
   bool is_bool_flag;
diff --git a/host/libs/config/Android.bp b/host/libs/config/Android.bp
index 668b745..7d3029a 100644
--- a/host/libs/config/Android.bp
+++ b/host/libs/config/Android.bp
@@ -26,6 +26,7 @@
         "cuttlefish_config.cpp",
         "cuttlefish_config_instance.cpp",
         "data_image.cpp",
+        "esp.cpp",
         "feature.cpp",
         "fetcher_config.cpp",
         "host_tools_version.cpp",
diff --git a/host/libs/config/bootconfig_args.cpp b/host/libs/config/bootconfig_args.cpp
index 5ba0112..a751021 100644
--- a/host/libs/config/bootconfig_args.cpp
+++ b/host/libs/config/bootconfig_args.cpp
@@ -161,7 +161,7 @@
         concat("androidboot.vsock_camera_cid=", instance.vsock_guest_cid()));
   }
 
-  if (config.enable_modem_simulator() &&
+  if (instance.enable_modem_simulator() &&
       instance.modem_simulator_ports() != "") {
     bootconfig_args.push_back(concat("androidboot.modem_simulator_ports=",
                                      instance.modem_simulator_ports()));
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 1bda946..d625c29 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -339,14 +339,6 @@
   (*dictionary_)[kRestartSubprocesses] = restart_subprocesses;
 }
 
-static constexpr char kRunAsDaemon[] = "run_as_daemon";
-bool CuttlefishConfig::run_as_daemon() const {
-  return (*dictionary_)[kRunAsDaemon].asBool();
-}
-void CuttlefishConfig::set_run_as_daemon(bool run_as_daemon) {
-  (*dictionary_)[kRunAsDaemon] = run_as_daemon;
-}
-
 static constexpr char kBootSlot[] = "boot_slot";
 void CuttlefishConfig::set_boot_slot(const std::string& boot_slot) {
   (*dictionary_)[kBootSlot] = boot_slot;
@@ -443,32 +435,6 @@
   return (*dictionary_)[kSigServerHeadersPath].asString();
 }
 
-static constexpr char kRunModemSimulator[] = "enable_modem_simulator";
-bool CuttlefishConfig::enable_modem_simulator() const {
-  return (*dictionary_)[kRunModemSimulator].asBool();
-}
-void CuttlefishConfig::set_enable_modem_simulator(bool enable_modem_simulator) {
-  (*dictionary_)[kRunModemSimulator] = enable_modem_simulator;
-}
-
-static constexpr char kModemSimulatorInstanceNumber[] =
-    "modem_simulator_instance_number";
-void CuttlefishConfig::set_modem_simulator_instance_number(
-    int instance_number) {
-  (*dictionary_)[kModemSimulatorInstanceNumber] = instance_number;
-}
-int CuttlefishConfig::modem_simulator_instance_number() const {
-  return (*dictionary_)[kModemSimulatorInstanceNumber].asInt();
-}
-
-static constexpr char kModemSimulatorSimType[] = "modem_simulator_sim_type";
-void CuttlefishConfig::set_modem_simulator_sim_type(int sim_type) {
-  (*dictionary_)[kModemSimulatorSimType] = sim_type;
-}
-int CuttlefishConfig::modem_simulator_sim_type() const {
-  return (*dictionary_)[kModemSimulatorSimType].asInt();
-}
-
 static constexpr char kHostToolsVersion[] = "host_tools_version";
 void CuttlefishConfig::set_host_tools_version(
     const std::map<std::string, uint32_t>& versions) {
@@ -591,14 +557,6 @@
   return (*dictionary_)[kRilDns].asString();
 }
 
-static constexpr char kEnableMinimalMode[] = "enable_minimal_mode";
-bool CuttlefishConfig::enable_minimal_mode() const {
-  return (*dictionary_)[kEnableMinimalMode].asBool();
-}
-void CuttlefishConfig::set_enable_minimal_mode(bool enable_minimal_mode) {
-  (*dictionary_)[kEnableMinimalMode] = enable_minimal_mode;
-}
-
 static constexpr char kEnableKernelLog[] = "enable_kernel_log";
 void CuttlefishConfig::set_enable_kernel_log(bool enable_kernel_log) {
   (*dictionary_)[kEnableKernelLog] = enable_kernel_log;
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 278ad5e..debbb2b 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -169,9 +169,6 @@
   void set_enable_gnss_grpc_proxy(const bool enable_gnss_grpc_proxy);
   bool enable_gnss_grpc_proxy() const;
 
-  void set_run_as_daemon(bool run_as_daemon);
-  bool run_as_daemon() const;
-
   void set_boot_slot(const std::string& boot_slot);
   std::string boot_slot() const;
 
@@ -259,19 +256,6 @@
   void set_enable_kernel_log(bool enable_kernel_log);
   bool enable_kernel_log() const;
 
-  // Configuration flags for a minimal device
-  bool enable_minimal_mode() const;
-  void set_enable_minimal_mode(bool enable_minimal_mode);
-
-  void set_enable_modem_simulator(bool enable_modem_simulator);
-  bool enable_modem_simulator() const;
-
-  void set_modem_simulator_instance_number(int instance_numbers);
-  int modem_simulator_instance_number() const;
-
-  void set_modem_simulator_sim_type(int sim_type);
-  int modem_simulator_sim_type() const;
-
   void set_host_tools_version(const std::map<std::string, uint32_t>&);
   std::map<std::string, uint32_t> host_tools_version() const;
 
@@ -537,6 +521,13 @@
     bool guest_enforce_security() const;
     bool use_sdcard() const;
     bool pause_in_bootloader() const;
+    bool run_as_daemon() const;
+
+    // Configuration flags for a minimal device
+    bool enable_minimal_mode() const;
+    bool enable_modem_simulator() const;
+    int modem_simulator_instance_number() const;
+    int modem_simulator_sim_type() const;
 
     // android artifacts
     std::string boot_image() const;
@@ -643,6 +634,13 @@
     void set_guest_enforce_security(bool guest_enforce_security);
     void set_use_sdcard(bool use_sdcard);
     void set_pause_in_bootloader(bool pause_in_bootloader);
+    void set_run_as_daemon(bool run_as_daemon);
+
+    // Configuration flags for a minimal device
+    void set_enable_minimal_mode(bool enable_minimal_mode);
+    void set_enable_modem_simulator(bool enable_modem_simulator);
+    void set_modem_simulator_instance_number(int instance_numbers);
+    void set_modem_simulator_sim_type(int sim_type);
 
     // system image files
     void set_boot_image(const std::string& boot_image);
diff --git a/host/libs/config/cuttlefish_config_instance.cpp b/host/libs/config/cuttlefish_config_instance.cpp
index eb2cc3e..c3d7e91 100644
--- a/host/libs/config/cuttlefish_config_instance.cpp
+++ b/host/libs/config/cuttlefish_config_instance.cpp
@@ -479,6 +479,52 @@
   return (*Dictionary())[kPauseInBootloader].asBool();
 }
 
+static constexpr char kRunAsDaemon[] = "run_as_daemon";
+bool CuttlefishConfig::InstanceSpecific::run_as_daemon() const {
+  return (*Dictionary())[kRunAsDaemon].asBool();
+}
+void CuttlefishConfig::MutableInstanceSpecific::set_run_as_daemon(bool run_as_daemon) {
+  (*Dictionary())[kRunAsDaemon] = run_as_daemon;
+}
+
+static constexpr char kEnableMinimalMode[] = "enable_minimal_mode";
+bool CuttlefishConfig::InstanceSpecific::enable_minimal_mode() const {
+  return (*Dictionary())[kEnableMinimalMode].asBool();
+}
+void CuttlefishConfig::MutableInstanceSpecific::set_enable_minimal_mode(
+    bool enable_minimal_mode) {
+  (*Dictionary())[kEnableMinimalMode] = enable_minimal_mode;
+}
+
+static constexpr char kRunModemSimulator[] = "enable_modem_simulator";
+bool CuttlefishConfig::InstanceSpecific::enable_modem_simulator() const {
+  return (*Dictionary())[kRunModemSimulator].asBool();
+}
+void CuttlefishConfig::MutableInstanceSpecific::set_enable_modem_simulator(
+    bool enable_modem_simulator) {
+  (*Dictionary())[kRunModemSimulator] = enable_modem_simulator;
+}
+
+static constexpr char kModemSimulatorInstanceNumber[] =
+    "modem_simulator_instance_number";
+void CuttlefishConfig::MutableInstanceSpecific::
+    set_modem_simulator_instance_number(int instance_number) {
+  (*Dictionary())[kModemSimulatorInstanceNumber] = instance_number;
+}
+int CuttlefishConfig::InstanceSpecific::modem_simulator_instance_number()
+    const {
+  return (*Dictionary())[kModemSimulatorInstanceNumber].asInt();
+}
+
+static constexpr char kModemSimulatorSimType[] = "modem_simulator_sim_type";
+void CuttlefishConfig::MutableInstanceSpecific::set_modem_simulator_sim_type(
+    int sim_type) {
+  (*Dictionary())[kModemSimulatorSimType] = sim_type;
+}
+int CuttlefishConfig::InstanceSpecific::modem_simulator_sim_type() const {
+  return (*Dictionary())[kModemSimulatorSimType].asInt();
+}
+
 static constexpr char kDisplayConfigs[] = "display_configs";
 static constexpr char kXRes[] = "x_res";
 static constexpr char kYRes[] = "y_res";
diff --git a/host/libs/config/data_image.cpp b/host/libs/config/data_image.cpp
index 86b6362..e0bdb8d 100644
--- a/host/libs/config/data_image.cpp
+++ b/host/libs/config/data_image.cpp
@@ -10,6 +10,7 @@
 #include "common/libs/utils/result.h"
 #include "common/libs/utils/subprocess.h"
 #include "host/libs/config/mbr.h"
+#include "host/libs/config/esp.h"
 #include "host/libs/vm_manager/gem5_manager.h"
 
 namespace cuttlefish {
@@ -28,24 +29,20 @@
 // these search paths. Install all bootloaders to one of these paths.
 // NOTE: For now, just ignore the 32-bit ARM version, as Debian doesn't
 //       build an EFI monolith for this architecture.
-const std::string kBootPathIA32 = "EFI/BOOT/BOOTIA32.EFI";
-const std::string kBootPathAA64 = "EFI/BOOT/BOOTAA64.EFI";
-const std::string kModulesPath = "EFI/modules";
-const std::string kMultibootModulePath = kModulesPath + "/multiboot.mod";
-const std::string kM5 = "";
-
 // These are the paths Debian installs the monoliths to. If another distro
 // uses an alternative monolith path, add it to this table
-const std::pair<std::string, std::string> kGrubBlobTable[] = {
-    {"/usr/lib/grub/i386-efi/multiboot.mod", kMultibootModulePath},
-    {"/usr/lib/grub/i386-efi/monolithic/grubia32.efi", kBootPathIA32},
-    {"/usr/lib/grub/arm64-efi/monolithic/grubaa64.efi", kBootPathAA64},
-};
+const std::string kBootSrcPathIA32 = "/usr/lib/grub/i386-efi/monolithic/grubia32.efi";
+const std::string kBootDestPathIA32 = "EFI/BOOT/BOOTIA32.EFI";
 
-// M5 checkpoint required binary file
-const std::pair<std::string, std::string> kM5BlobTable[] = {
-    {"/tmp/m5", kM5},
-};
+const std::string kBootSrcPathAA64 = "/usr/lib/grub/arm64-efi/monolithic/grubaa64.efi";
+const std::string kBootDestPathAA64 = "EFI/BOOT/BOOTAA64.EFI";
+
+const std::string kModulesDestPath = "EFI/modules";
+const std::string kMultibootModuleSrcPathIA32 = "/usr/lib/grub/i386-efi/multiboot.mod";
+const std::string kMultibootModuleDestPathIA32 = kModulesDestPath + "/multiboot.mod";
+
+const std::string kMultibootModuleSrcPathAA64 = "/usr/lib/grub/arm64-efi/multiboot.mod";
+const std::string kMultibootModuleDestPathAA64 = kModulesDestPath + "/multiboot.mod";
 
 bool ForceFsckImage(const std::string& data_image,
                     const CuttlefishConfig::InstanceSpecific& instance) {
@@ -64,48 +61,6 @@
   return true;
 }
 
-bool NewfsMsdos(const std::string& data_image, int data_image_mb,
-                int offset_num_mb) {
-  off_t image_size_bytes = static_cast<off_t>(data_image_mb) << 20;
-  off_t offset_size_bytes = static_cast<off_t>(offset_num_mb) << 20;
-  image_size_bytes -= offset_size_bytes;
-  off_t image_size_sectors = image_size_bytes / 512;
-  auto newfs_msdos_path = HostBinaryPath("newfs_msdos");
-  return execute({newfs_msdos_path,
-                         "-F",
-                         "32",
-                         "-m",
-                         "0xf8",
-                         "-o",
-                         "0",
-                         "-c",
-                         "8",
-                         "-h",
-                         "255",
-                         "-u",
-                         "63",
-                         "-S",
-                         "512",
-                         "-s",
-                         std::to_string(image_size_sectors),
-                         "-C",
-                         std::to_string(data_image_mb) + "M",
-                         "-@",
-                         std::to_string(offset_size_bytes),
-                         data_image}) == 0;
-}
-
-bool CopyToMsdos(const std::string& image, const std::string& path,
-                 const std::string& destination) {
-  const auto mcopy = HostBinaryPath("mcopy");
-  const auto success = execute({mcopy, "-o", "-i", image, "-s", path, destination});
-  if (success != 0) {
-    LOG(ERROR) << "Failed to copy " << path << " to " << image;
-    return false;
-  }
-  return true;
-}
-
 bool ResizeImage(const std::string& data_image, int data_image_mb,
                  const CuttlefishConfig::InstanceSpecific& instance) {
   auto file_mb = FileSize(data_image) >> 20;
@@ -395,22 +350,20 @@
   std::string Name() const override { return "InitializeEspImageImpl"; }
   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
   bool Enabled() const override {
-    auto flow = instance_.boot_flow();
-    return flow == CuttlefishConfig::InstanceSpecific::BootFlow::Linux ||
-      flow == CuttlefishConfig::InstanceSpecific::BootFlow::Fuchsia;
+    const auto flow = instance_.boot_flow();
+    const auto vm = config_.vm_manager();
+    const auto not_gem5 = vm != vm_manager::Gem5Manager::name();
+    const auto boot_flow_required_esp =
+        flow == CuttlefishConfig::InstanceSpecific::BootFlow::Linux ||
+        flow == CuttlefishConfig::InstanceSpecific::BootFlow::Fuchsia;
+
+    return not_gem5 && boot_flow_required_esp;
   }
 
  protected:
   bool Setup() override {
     LOG(DEBUG) << "esp partition image: creating default";
-
-    // newfs_msdos won't make a partition smaller than 257 mb
-    // this should be enough for anybody..
-    auto tmp_esp_image = instance_.otheros_esp_image() + ".tmp";
-    if (!NewfsMsdos(tmp_esp_image, 257 /* mb */, 0 /* mb (offset) */)) {
-      LOG(ERROR) << "Failed to create filesystem for " << tmp_esp_image;
-      return false;
-    }
+    auto builder = EspBuilder(instance_.otheros_esp_image());
 
     // For licensing and build reproducibility reasons, pick up the bootloaders
     // from the host Linux distribution (if present) and pack them into the
@@ -418,91 +371,54 @@
     // they can use -esp_image=/path/to/esp.img to override, so we don't need
     // to accommodate customizations of this packing process.
 
-    int success;
-    const std::pair<std::string, std::string> *kBlobTable;
-    std::size_t size;
-    // Skip GRUB on Gem5
-    if (config_.vm_manager() != vm_manager::Gem5Manager::name()) {
-      // Currently we only support Debian based distributions, and GRUB is built
-      // for those distros to always load grub.cfg from EFI/debian/grub.cfg, and
-      // nowhere else. If you want to add support for other distros, make the
-      // extra directories below and copy the initial grub.cfg there as well
-      auto mmd = HostBinaryPath("mmd");
-      success =
-          execute({mmd, "-i", tmp_esp_image, "EFI", "EFI/BOOT", "EFI/debian", "EFI/modules"});
-      if (success != 0) {
-        LOG(ERROR) << "Failed to create directories in " << tmp_esp_image;
-        return false;
-      }
-      size = sizeof(kGrubBlobTable)/sizeof(const std::pair<std::string, std::string>);
-      kBlobTable = kGrubBlobTable;
-    } else {
-      size = sizeof(kM5BlobTable)/sizeof(const std::pair<std::string, std::string>);
-      kBlobTable = kM5BlobTable;
-    }
+    // Currently we only support Debian based distributions, and GRUB is built
+    // for those distros to always load grub.cfg from EFI/debian/grub.cfg, and
+    // nowhere else. If you want to add support for other distros, make the
+    // extra directories below and copy the initial grub.cfg there as well
+    builder.Directory("EFI")
+        .Directory("EFI/BOOT")
+        .Directory("EFI/debian")
+        .Directory("EFI/modules");
 
-    // The grub binaries are small, so just copy all the architecture blobs
-    // we can find, which minimizes complexity. If the user removed the grub bin
-    // package from their system, the ESP will be empty and Other OS will not be
-    // supported
-    bool copied = false;
-    for (int i=0; i<size; i++) {
-      auto grub = kBlobTable[i];
-      if (!FileExists(grub.first)) {
-        continue;
-      }
-      if (!CopyToMsdos(tmp_esp_image, grub.first, "::" + grub.second)) {
-        return false;
-      }
-      copied = true;
-    }
-
-    if (!copied) {
-      LOG(ERROR) << "Binary dependencies were not found on this system; Other OS "
-                    "support will be broken";
-      return false;
-    }
-
-    // Skip Gem5 case. Gem5 will never be able to use bootloaders like grub.
-    if (config_.vm_manager() != vm_manager::Gem5Manager::name()) {
+    const auto flow = instance_.boot_flow();
+    if (flow == CuttlefishConfig::InstanceSpecific::BootFlow::Linux ||
+        flow == CuttlefishConfig::InstanceSpecific::BootFlow::Fuchsia) {
       auto grub_cfg = DefaultHostArtifactsPath("etc/grub/grub.cfg");
-      CHECK(FileExists(grub_cfg)) << "Missing file " << grub_cfg << "!";
-      if (!CopyToMsdos(tmp_esp_image, grub_cfg, "::EFI/debian/")) {
-        return false;
+      builder.File(grub_cfg, "EFI/debian/grub.cfg", /* required */ true);
+      switch (instance_.target_arch()) {
+        case Arch::Arm:
+        case Arch::Arm64:
+          builder.File(kBootSrcPathAA64, kBootDestPathAA64, /* required */ true);
+          builder.File(kMultibootModuleSrcPathAA64, kMultibootModuleDestPathAA64,
+                        /* required */ true);
+          break;
+        case Arch::X86:
+        case Arch::X86_64:
+          builder.File(kBootSrcPathIA32, kBootDestPathIA32, /* required */ true);
+          builder.File(kMultibootModuleSrcPathIA32, kMultibootModuleDestPathIA32,
+                        /* required */ true);
+          break;
       }
     }
 
-    switch (instance_.boot_flow()) {
+    switch (flow) {
       case CuttlefishConfig::InstanceSpecific::BootFlow::Linux:
-        if (!CopyToMsdos(tmp_esp_image, instance_.linux_kernel_path(), "::vmlinuz")) {
-          return false;
-        }
-
+        builder.File(instance_.linux_kernel_path(), "vmlinuz", /* required */ true);
         if (!instance_.linux_initramfs_path().empty()) {
-          if (!CopyToMsdos(tmp_esp_image, instance_.linux_initramfs_path(), "::initrd.img")) {
-            return false;
-          }
+          builder.File(instance_.linux_initramfs_path(), "initrd.img", /* required */ true);
         }
         break;
       case CuttlefishConfig::InstanceSpecific::BootFlow::Fuchsia:
-        if (!CopyToMsdos(tmp_esp_image, instance_.fuchsia_zedboot_path(), "::zedboot.zbi")) {
-          return false;
-        }
-        if (!CopyToMsdos(tmp_esp_image,
-                         instance_.fuchsia_multiboot_bin_path(), "::multiboot.bin")) {
-          return false;
-        }
+        builder.File(instance_.fuchsia_zedboot_path(), "zedboot.zbi",
+                     /* required */ true);
+        builder.File(instance_.fuchsia_multiboot_bin_path(), "multiboot.bin",
+                     /* required */ true);
         break;
       default:
         break;
     }
 
-    if (!cuttlefish::RenameFile(tmp_esp_image, instance_.otheros_esp_image())) {
-      LOG(ERROR) << "Renaming " << tmp_esp_image << " to "
-                 << instance_.otheros_esp_image() << " failed";
-      return false;
-    }
-    return true;
+    return builder.Build();
   }
 
  private:
diff --git a/host/libs/config/esp.cpp b/host/libs/config/esp.cpp
new file mode 100644
index 0000000..f0bcf55
--- /dev/null
+++ b/host/libs/config/esp.cpp
@@ -0,0 +1,145 @@
+//
+// Copyright (C) 2022 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/config/esp.h"
+#include "common/libs/utils/subprocess.h"
+#include "common/libs/utils/files.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+namespace cuttlefish {
+
+bool NewfsMsdos(const std::string& data_image, int data_image_mb,
+                int offset_num_mb) {
+  off_t image_size_bytes = static_cast<off_t>(data_image_mb) << 20;
+  off_t offset_size_bytes = static_cast<off_t>(offset_num_mb) << 20;
+  image_size_bytes -= offset_size_bytes;
+  off_t image_size_sectors = image_size_bytes / 512;
+  auto newfs_msdos_path = HostBinaryPath("newfs_msdos");
+  return execute({newfs_msdos_path,
+                  "-F",
+                  "32",
+                  "-m",
+                  "0xf8",
+                  "-o",
+                  "0",
+                  "-c",
+                  "8",
+                  "-h",
+                  "255",
+                  "-u",
+                  "63",
+                  "-S",
+                  "512",
+                  "-s",
+                  std::to_string(image_size_sectors),
+                  "-C",
+                  std::to_string(data_image_mb) + "M",
+                  "-@",
+                  std::to_string(offset_size_bytes),
+                  data_image}) == 0;
+}
+
+bool MsdosMakeDirectories(const std::string& image_path,
+                          const std::vector<std::string>& directories) {
+  auto mmd = HostBinaryPath("mmd");
+  std::vector<std::string> command {mmd, "-i", image_path};
+  command.insert(command.end(), directories.begin(), directories.end());
+
+  const auto success = execute(command);
+  if (success != 0) {
+    return false;
+  }
+  return true;
+}
+
+bool CopyToMsdos(const std::string& image, const std::string& path,
+                 const std::string& destination) {
+  const auto mcopy = HostBinaryPath("mcopy");
+  const auto success = execute({mcopy, "-o", "-i", image, "-s", path, destination});
+  if (success != 0) {
+    return false;
+  }
+  return true;
+}
+
+EspBuilder& EspBuilder::File(std::string from, std::string to, bool required) & {
+  files_.push_back(FileToAdd {std::move(from), std::move(to), required});
+  return *this;
+}
+
+EspBuilder EspBuilder::File(std::string from, std::string to, bool required) && {
+  files_.push_back(FileToAdd {std::move(from), std::move(to), required});
+  return *this;
+}
+
+EspBuilder& EspBuilder::File(std::string from, std::string to) & {
+  return File(std::move(from), std::move(to), false);
+}
+
+EspBuilder EspBuilder::File(std::string from, std::string to) && {
+  return File(std::move(from), std::move(to), false);
+}
+
+EspBuilder& EspBuilder::Directory(std::string path) & {
+  directories_.push_back(std::move(path));
+  return *this;
+}
+
+EspBuilder EspBuilder::Directory(std::string path) && {
+  directories_.push_back(std::move(path));
+  return *this;
+}
+
+bool EspBuilder::Build() const {
+  // newfs_msdos won't make a partition smaller than 257 mb
+  // this should be enough for anybody..
+  const auto tmp_esp_image = image_path_ + ".tmp";
+  if (!NewfsMsdos(tmp_esp_image, 257 /* mb */, 0 /* mb (offset) */)) {
+    LOG(ERROR) << "Failed to create filesystem for " << tmp_esp_image;
+    return false;
+  }
+
+  if (!MsdosMakeDirectories(tmp_esp_image, directories_)) {
+    LOG(ERROR) << "Failed to create directories in " << tmp_esp_image;
+    return false;
+  }
+
+  for (const auto file : files_) {
+    if (!FileExists(file.from)) {
+      if (file.required) {
+        LOG(ERROR) << "Failed to copy " << file.from << " to " << tmp_esp_image
+                   << ": File does not exist";
+        return false;
+      }
+      continue;
+    }
+
+    if (!CopyToMsdos(tmp_esp_image, file.from, "::" + file.to)) {
+      LOG(ERROR) << "Failed to copy " << file.from << " to " << tmp_esp_image
+                 << ": mcopy execution failed";
+      return false;
+    }
+  }
+
+  if (!RenameFile(tmp_esp_image, image_path_)) {
+    LOG(ERROR) << "Renaming " << tmp_esp_image << " to "
+                << image_path_ << " failed";
+    return false;
+  }
+
+  return true;
+}
+
+} // namespace cuttlefish
diff --git a/host/libs/config/esp.h b/host/libs/config/esp.h
new file mode 100644
index 0000000..f5a0f19
--- /dev/null
+++ b/host/libs/config/esp.h
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2022 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 <string>
+#include <vector>
+
+namespace cuttlefish {
+
+class EspBuilder final {
+ public:
+  EspBuilder() = delete;
+  EspBuilder(std::string image_path): image_path_(std::move(image_path)) {}
+
+  EspBuilder& File(std::string from, std::string to, bool required) &;
+  EspBuilder File(std::string from, std::string to, bool required) &&;
+
+  EspBuilder& File(std::string from, std::string to) &;
+  EspBuilder File(std::string from, std::string to) &&;
+
+  EspBuilder& Directory(std::string path) &;
+  EspBuilder Directory(std::string path) &&;
+
+  bool Build() const;
+
+ private:
+  std::string image_path_;
+
+  struct FileToAdd {
+    std::string from;
+    std::string to;
+    bool required;
+  };
+  std::vector<std::string> directories_;
+  std::vector<FileToAdd> files_;
+};
+
+bool NewfsMsdos(const std::string& data_image, int data_image_mb,
+                int offset_num_mb);
+
+} // namespace cuttlefish
diff --git a/host/libs/config/logging.cpp b/host/libs/config/logging.cpp
index 9cf1705..f0a17bc 100644
--- a/host/libs/config/logging.cpp
+++ b/host/libs/config/logging.cpp
@@ -38,7 +38,7 @@
     prefix = instance.instance_name() + ": ";
   }
 
-  if (config->run_as_daemon()) {
+  if (instance.run_as_daemon()) {
     SetLogger(LogToFiles({instance.launcher_log_path()}));
   } else {
     SetLogger(LogToStderrAndFiles({instance.launcher_log_path()}, prefix));
diff --git a/host/libs/confui/host_utils.cc b/host/libs/confui/host_utils.cc
index 6ff67b0..bc5d23e 100644
--- a/host/libs/confui/host_utils.cc
+++ b/host/libs/confui/host_utils.cc
@@ -21,7 +21,7 @@
 namespace thread {
 std::string ThreadTracer::Get(const std::thread::id tid) {
   std::lock_guard<std::mutex> lock(mtx_);
-  if (id2name_.find(tid) != id2name_.end()) {
+  if (Contains(id2name_, tid)) {
     return id2name_[tid];
   }
   std::stringstream ss;
@@ -31,7 +31,7 @@
 
 void ThreadTracer::Set(const std::string& name, const std::thread::id tid) {
   std::lock_guard<std::mutex> lock(mtx_);
-  if (name2id_.find(name) != name2id_.end()) {
+  if (Contains(name2id_, name)) {
     // has the name already
     if (name2id_[name] != tid) {  // used for another thread
       ConfUiLog(FATAL) << "Thread name is duplicated.";
@@ -39,7 +39,7 @@
     // name and id are already set correctly
     return;
   }
-  if (id2name_.find(tid) != id2name_.end()) {
+  if (Contains(id2name_, tid)) {
     // tid exists but has a different name
     name2id_.erase(id2name_[tid]);  // delete old_name -> tid map
   }
@@ -50,10 +50,10 @@
 
 std::optional<std::thread::id> ThreadTracer::Get(const std::string& name) {
   std::lock_guard<std::mutex> lock(mtx_);
-  if (name2id_.find(name) != name2id_.end()) {
+  if (Contains(name2id_, name)) {
     return {name2id_[name]};
   }
-  return std::nullopt;  // unknown
+  return std::nullopt;
 }
 
 ThreadTracer& GetThreadTracer() {
diff --git a/host/libs/confui/host_utils.h b/host/libs/confui/host_utils.h
index bd4004b..4037419 100644
--- a/host/libs/confui/host_utils.h
+++ b/host/libs/confui/host_utils.h
@@ -27,6 +27,7 @@
 #include <android-base/logging.h>
 
 #include "common/libs/confui/confui.h"
+#include "common/libs/utils/contains.h"
 #include "host/commands/kernel_log_monitor/utils.h"
 #include "host/libs/config/logging.h"
 
@@ -68,7 +69,7 @@
   template <typename F, typename... Args>
   std::thread RunThread(const std::string& name, F&& f, Args&&... args) {
     auto th = std::thread(std::forward<F>(f), std::forward<Args>(args)...);
-    if (name2id_.find(name) != name2id_.end()) {
+    if (Contains(name2id_, name)) {
       ConfUiLog(FATAL) << "Thread name is duplicated";
     }
     name2id_[name] = th.get_id();
diff --git a/host/libs/confui/session.cc b/host/libs/confui/session.cc
index 2b7069b..66ffdb6 100644
--- a/host/libs/confui/session.cc
+++ b/host/libs/confui/session.cc
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 
+#include "common/libs/utils/contains.h"
 #include "host/libs/confui/secure_input.h"
 
 namespace cuttlefish {
@@ -65,12 +66,6 @@
   return false;
 }
 
-template <typename C, typename T>
-static bool Contains(const C& c, T&& item) {
-  auto itr = std::find(c.begin(), c.end(), std::forward<T>(item));
-  return itr != c.end();
-}
-
 bool Session::IsInverted() const {
   return Contains(ui_options_, teeui::UIOption::AccessibilityInverted);
 }
diff --git a/shared/device.mk b/shared/device.mk
index 4c1b8ea..eb2349c 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -291,6 +291,7 @@
     device/google/cuttlefish/shared/config/media_profiles.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_profiles_V1_0.xml \
     device/google/cuttlefish/shared/permissions/privapp-permissions-cuttlefish.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/privapp-permissions-cuttlefish.xml \
     frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \
+    hardware/interfaces/audio/aidl/default/audio_effects_config.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects_config.xml \
     frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_audio.xml \
     frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_telephony.xml \
     frameworks/av/services/audiopolicy/config/a2dp_in_audio_policy_configuration_7_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/a2dp_in_audio_policy_configuration_7_0.xml \
@@ -440,7 +441,8 @@
     frameworks/av/services/audiopolicy/config/r_submix_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/r_submix_audio_policy_configuration.xml \
     frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_volumes.xml \
     frameworks/av/services/audiopolicy/config/default_volume_tables.xml:$(TARGET_COPY_OUT_VENDOR)/etc/default_volume_tables.xml \
-    frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml
+    frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \
+    hardware/interfaces/audio/aidl/default/audio_effects_config.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects_config.xml
 endif
 
 PRODUCT_PACKAGES += $(LOCAL_AUDIO_PRODUCT_PACKAGE)
diff --git a/shared/sepolicy/vendor/hal_keymint_remote.te b/shared/sepolicy/vendor/hal_keymint_remote.te
index 7d5f6d5..c1baebb 100644
--- a/shared/sepolicy/vendor/hal_keymint_remote.te
+++ b/shared/sepolicy/vendor/hal_keymint_remote.te
@@ -13,3 +13,4 @@
 
 get_prop(hal_keymint_remote, vendor_security_patch_level_prop)
 get_prop(hal_keymint_remote, vendor_boot_security_patch_level_prop)
+get_prop(hal_keymint_remote, serialno_prop)
diff --git a/tests/hal/hal_implementation_test.cpp b/tests/hal/hal_implementation_test.cpp
index 480b25f..0057d38 100644
--- a/tests/hal/hal_implementation_test.cpp
+++ b/tests/hal/hal_implementation_test.cpp
@@ -166,7 +166,7 @@
     // Cuttlefish Identity Credential HAL implementation is currently
     // stuck at version 3 while RKP support is being added. Will be
     // updated soon.
-    {"android.hardware.identity.", 5},
+    {"android.hardware.identity.", 4},
 
     // The interface is in development (b/205884982)
     {"android.hardware.audio.core.", 1},