Merge "Workaround collection to get to VIRTUAL_DEVICE_BOOT_STARTED on mac OS" into main
diff --git a/common/libs/utils/flag_parser.cpp b/common/libs/utils/flag_parser.cpp
index b1d1483..6cb65c9 100644
--- a/common/libs/utils/flag_parser.cpp
+++ b/common/libs/utils/flag_parser.cpp
@@ -432,15 +432,9 @@
 Flag VerbosityFlag(android::base::LogSeverity& value) {
   return GflagsCompatFlag("verbosity")
       .Getter([&value]() { return FromSeverity(value); })
-      .Setter([&value](const FlagMatch& match) {
-        Result<android::base::LogSeverity> result = ToSeverity(match.value);
-        if (!result.ok()) {
-          LOG(ERROR) << "Unable to convert \"" << match.value
-                     << "\" to a LogSeverity";
-          return false;
-        }
-        value = result.value();
-        return true;
+      .Setter([&value](const FlagMatch& match) -> Result<void> {
+        value = CF_EXPECT(ToSeverity(match.value));
+        return {};
       })
       .Help("Used to set the verbosity level for logging.");
 }
@@ -461,31 +455,29 @@
       .Setter(setter);
 }
 
-static bool GflagsCompatBoolFlagSetter(const std::string& name, bool& value,
-                                       const FlagMatch& match) {
+static Result<void> GflagsCompatBoolFlagSetter(const std::string& name,
+                                               bool& value,
+                                               const FlagMatch& match) {
   const auto& key = match.key;
   if (key == "-" + name || key == "--" + name) {
     value = true;
-    return true;
+    return {};
   } else if (key == "-no" + name || key == "--no" + name) {
     value = false;
-    return true;
+    return {};
   } else if (key == "-" + name + "=" || key == "--" + name + "=") {
     if (match.value == "true") {
       value = true;
-      return true;
+      return {};
     } else if (match.value == "false") {
       value = false;
-      return true;
+      return {};
     } else {
-      LOG(ERROR) << "Unexpected boolean value \"" << match.value << "\""
-                 << " for \"" << name << "\"";
-      return false;
+      return CF_ERRF("Unexpected boolean value \"{}\" for \{}\"", match.value,
+                     name);
     }
   }
-  LOG(ERROR) << "Unexpected key \"" << match.key << "\""
-             << " for \"" << name << "\"";
-  return false;
+  return CF_ERRF("Unexpected key \"{}\" for \"{}\"", match.key, name);
 }
 
 static Flag GflagsCompatBoolFlagBase(const std::string& name) {
@@ -501,14 +493,12 @@
 Flag HelpXmlFlag(const std::vector<Flag>& flags, std::ostream& out, bool& value,
                  const std::string& text) {
   const std::string name = "helpxml";
-  auto setter = [name, &out, &value, &text, &flags](const FlagMatch& match) {
+  auto setter = [name, &out, &value, &text,
+                 &flags](const FlagMatch& match) -> Result<void> {
     bool print_xml = false;
-    auto parse_success = GflagsCompatBoolFlagSetter(name, print_xml, match);
-    if (!parse_success) {
-      return false;
-    }
+    CF_EXPECT(GflagsCompatBoolFlagSetter(name, print_xml, match));
     if (!print_xml) {
-      return true;
+      return {};
     }
     if (!text.empty()) {
       out << text << std::endl;
@@ -517,7 +507,7 @@
     out << "<?xml version=\"1.0\"?>" << std::endl << "<AllFlags>" << std::endl;
     WriteGflagsCompatXml(flags, out);
     out << "</AllFlags>" << std::flush;
-    return false;
+    return CF_ERR("Requested early exit");
   };
   return GflagsCompatBoolFlagBase(name).Setter(setter);
 }
@@ -585,16 +575,10 @@
 static Flag GflagsCompatNumericFlagGeneric(const std::string& name, T& value) {
   return GflagsCompatFlag(name)
       .Getter([&value]() { return std::to_string(value); })
-      .Setter([&value](const FlagMatch& match) {
-        auto parsed = ParseInteger<T>(match.value);
-        if (parsed) {
-          value = *parsed;
-          return true;
-        } else {
-          LOG(ERROR) << "Failed to parse \"" << match.value
-                     << "\" as an integer";
-          return false;
-        }
+      .Setter([&value](const FlagMatch& match) -> Result<void> {
+        value = CF_EXPECTF(ParseInteger<T>(match.value),
+                           "Failed to parse \"{}\" as an integer", match.value);
+        return {};
       });
 }
 
@@ -614,45 +598,37 @@
                       std::vector<std::string>& value) {
   return GflagsCompatFlag(name)
       .Getter([&value]() { return android::base::Join(value, ','); })
-      .Setter([&name, &value](const FlagMatch& match) {
-        if (match.value.empty()) {
-          LOG(ERROR) << "No values given for flag \"" << name << "\"";
-          return false;
-        }
+      .Setter([&name, &value](const FlagMatch& match) -> Result<void> {
+        CF_EXPECTF(!match.value.empty(), "No values given for flag \"{}\"",
+                   name);
         std::vector<std::string> str_vals =
             android::base::Split(match.value, ",");
         value = std::move(str_vals);
-        return true;
+        return {};
       });
 }
 
 Flag GflagsCompatFlag(const std::string& name, std::vector<bool>& value,
-                      const bool default_value) {
+                      const bool def_val) {
   return GflagsCompatFlag(name)
       .Getter([&value]() { return fmt::format("{}", fmt::join(value, ",")); })
-      .Setter([&name, &value, default_value](const FlagMatch& match) {
-        if (match.value.empty()) {
-          LOG(ERROR) << "No values given for flag \"" << name << "\"";
-          return false;
-        }
+      .Setter([&name, &value, def_val](const FlagMatch& match) -> Result<void> {
+        CF_EXPECTF(!match.value.empty(), "No values given for flag \"{}\"",
+                   name);
         std::vector<std::string> str_vals =
             android::base::Split(match.value, ",");
         value.clear();
-        value.reserve(str_vals.size());
+        std::vector<bool> output_vals;
+        output_vals.reserve(str_vals.size());
         for (const auto& str_val : str_vals) {
           if (str_val.empty()) {
-            value.push_back(default_value);
+            output_vals.push_back(def_val);
           } else {
-            Result<bool> result = ParseBool(str_val, name);
-            if (!result.ok()) {
-              value.clear();
-              LOG(ERROR) << result.error().Trace();
-              return false;
-            }
-            value.push_back(result.value());
+            output_vals.push_back(CF_EXPECT(ParseBool(str_val, name)));
           }
         }
-        return true;
+        value = output_vals;
+        return {};
       });
 }
 
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 0b5c29c..03f3f0c 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -246,6 +246,8 @@
             "trusted signing authority (Disallow self signed certificates). "
             "This is ignored if an insecure server is configured.");
 
+DEFINE_string(group_id, "", "The group name of instance");
+
 DEFINE_vec(
     webrtc_device_id, CF_DEFAULTS_WEBRTC_DEVICE_ID,
     "The for the device to register with the signaling server. Every "
@@ -1441,6 +1443,8 @@
               "Error in looking up num to webrtc_device_id_flag_map");
     instance.set_webrtc_device_id(num_to_webrtc_device_id_flag_map[num]);
 
+    instance.set_group_id(FLAGS_group_id);
+
     if (!is_first_instance || !start_webrtc_vec[instance_index]) {
       // Only the first instance starts the signaling server or proxy
       instance.set_start_webrtc_signaling_server(false);
diff --git a/host/commands/cvd/server_command/start.cpp b/host/commands/cvd/server_command/start.cpp
index 9adf728..1296b4d 100644
--- a/host/commands/cvd/server_command/start.cpp
+++ b/host/commands/cvd/server_command/start.cpp
@@ -380,6 +380,7 @@
   // take --webrtc_device_id flag away
   new_args.emplace_back("--webrtc_device_id=" +
                         android::base::Join(device_name_list, ","));
+  new_args.emplace_back("--group_id=" + group_name);
   return new_args;
 }
 
diff --git a/host/commands/openwrt_control_server/Android.bp b/host/commands/openwrt_control_server/Android.bp
index d4bc02b..8f32cf9 100644
--- a/host/commands/openwrt_control_server/Android.bp
+++ b/host/commands/openwrt_control_server/Android.bp
@@ -73,6 +73,7 @@
     name: "OpenwrtControlServerProto",
     srcs: [
         "openwrt_control.proto",
+        ":libprotobuf-internal-protos",
     ],
 }
 
diff --git a/host/commands/run_cvd/launch/streamer.cpp b/host/commands/run_cvd/launch/streamer.cpp
index 8063734..36fdccf 100644
--- a/host/commands/run_cvd/launch/streamer.cpp
+++ b/host/commands/run_cvd/launch/streamer.cpp
@@ -255,6 +255,9 @@
     };
 
     Command webrtc(WebRtcBinary(), stopper);
+
+    webrtc.AddParameter("-group_id=", instance_.group_id());
+
     webrtc.UnsetFromEnvironment("http_proxy");
     sockets_.AppendCommandArguments(webrtc);
     if (config_.vm_manager() == vm_manager::CrosvmManager::name()) {
diff --git a/host/commands/secure_env/encrypted_serializable.cpp b/host/commands/secure_env/encrypted_serializable.cpp
index 75891fc..6c1c200 100644
--- a/host/commands/secure_env/encrypted_serializable.cpp
+++ b/host/commands/secure_env/encrypted_serializable.cpp
@@ -42,7 +42,7 @@
     TPM2B_PRIVATE* key_private_out, // out
     TpmObjectSlot* key_slot_out) { // out
   TPM2B_AUTH authValue = {};
-  auto rc = Esys_TR_SetAuth(resource_manager.Esys(), parent_key, &authValue);
+  auto rc = Esys_TR_SetAuth(*resource_manager.Esys(), parent_key, &authValue);
   if (rc != TSS2_RC_SUCCESS) {
     LOG(ERROR) << "Esys_TR_SetAuth failed with return code " << rc
                << " (" << Tss2_RC_Decode(rc) << ")";
@@ -90,16 +90,16 @@
   TPM2B_PRIVATE* key_private = nullptr;
   // TODO(schuffelen): Use Esys_Create when key_slot is NULL
   rc = Esys_CreateLoaded(
-    /* esysContext */ resource_manager.Esys(),
-    /* primaryHandle */ parent_key,
-    /* shandle1 */ ESYS_TR_PASSWORD,
-    /* shandle2 */ ESYS_TR_NONE,
-    /* shandle3 */ ESYS_TR_NONE,
-    /* inSensitive */ &in_sensitive,
-    /* inPublic */ &public_template,
-    /* objectHandle */ &raw_handle,
-    /* outPrivate */ &key_private,
-    /* outPublic */ &key_public);
+      /* esysContext */ *resource_manager.Esys(),
+      /* primaryHandle */ parent_key,
+      /* shandle1 */ ESYS_TR_PASSWORD,
+      /* shandle2 */ ESYS_TR_NONE,
+      /* shandle3 */ ESYS_TR_NONE,
+      /* inSensitive */ &in_sensitive,
+      /* inPublic */ &public_template,
+      /* objectHandle */ &raw_handle,
+      /* outPrivate */ &key_private,
+      /* outPublic */ &key_public);
   if (rc != TSS2_RC_SUCCESS) {
     LOG(ERROR) << "Esys_CreateLoaded failed with return code " << rc
                << " (" << Tss2_RC_Decode(rc) << ")";
@@ -113,7 +113,7 @@
   Esys_Free(key_public);
   Esys_Free(key_private);
   if (key_slot_out) {
-    rc = Esys_TR_SetAuth(resource_manager.Esys(), raw_handle, &authValue);
+    rc = Esys_TR_SetAuth(*resource_manager.Esys(), raw_handle, &authValue);
     if (rc != TSS2_RC_SUCCESS) {
       LOG(ERROR) << "Esys_TR_SetAuth failed with return code " << rc
                 << " (" << Tss2_RC_Decode(rc) << ")";
@@ -138,15 +138,9 @@
     LOG(ERROR) << "No slots available";
     return {};
   }
-  auto rc = Esys_Load(
-      resource_manager.Esys(),
-      parent_key,
-      ESYS_TR_PASSWORD,
-      ESYS_TR_NONE,
-      ESYS_TR_NONE,
-      key_private,
-      key_public,
-      &raw_handle);
+  auto rc = Esys_Load(*resource_manager.Esys(), parent_key, ESYS_TR_PASSWORD,
+                      ESYS_TR_NONE, ESYS_TR_NONE, key_private, key_public,
+                      &raw_handle);
   if (rc != TSS2_RC_SUCCESS) {
     LOG(ERROR) << "Esys_Load failed with return code " << rc
                << " (" << Tss2_RC_Decode(rc) << ")";
@@ -203,7 +197,7 @@
 
   TPM2B_IV iv;
   iv.size = sizeof(iv.buffer);
-  auto rc = TpmRandomSource(resource_manager_.Esys())
+  auto rc = TpmRandomSource(resource_manager_)
                 .GenerateRandom(iv.buffer, sizeof(iv.buffer));
   if (rc != KM_ERROR_OK) {
     LOG(ERROR) << "Failed to get random data";
@@ -222,7 +216,7 @@
   }
   std::vector<uint8_t> encrypted(encrypted_size, 0);
   if (!TpmEncrypt(  //
-          resource_manager_.Esys(), key_slot->get(), TpmAuth(ESYS_TR_PASSWORD),
+          *resource_manager_.Esys(), key_slot->get(), TpmAuth(ESYS_TR_PASSWORD),
           iv, unencrypted.data(), encrypted.data(), encrypted_size)) {
     LOG(ERROR) << "Encryption failed";
     return buf;
@@ -305,7 +299,7 @@
   }
   std::vector<uint8_t> decrypted_data(encrypted_size, 0);
   if (!TpmDecrypt(  //
-          resource_manager_.Esys(), key_slot->get(), TpmAuth(ESYS_TR_PASSWORD),
+          *resource_manager_.Esys(), key_slot->get(), TpmAuth(ESYS_TR_PASSWORD),
           iv, encrypted_data.data(), decrypted_data.data(), encrypted_size)) {
     LOG(ERROR) << "Failed to decrypt encrypted data";
     return false;
diff --git a/host/commands/secure_env/primary_key_builder.cpp b/host/commands/secure_env/primary_key_builder.cpp
index 173f31e..ff0d2d7 100644
--- a/host/commands/secure_env/primary_key_builder.cpp
+++ b/host/commands/secure_env/primary_key_builder.cpp
@@ -67,7 +67,7 @@
     TpmResourceManager& resource_manager) {
   TPM2B_AUTH authValue = {};
   auto rc =
-      Esys_TR_SetAuth(resource_manager.Esys(), ESYS_TR_RH_OWNER, &authValue);
+      Esys_TR_SetAuth(*resource_manager.Esys(), ESYS_TR_RH_OWNER, &authValue);
   if (rc != TSS2_RC_SUCCESS) {
     LOG(ERROR) << "Esys_TR_SetAuth failed with return code " << rc
                << " (" << Tss2_RC_Decode(rc) << ")";
@@ -97,16 +97,16 @@
   // Since this is a primary key, it's generated deterministically. It would
   // also be possible to generate this once and hold it in storage.
   rc = Esys_CreateLoaded(
-    /* esysContext */ resource_manager.Esys(),
-    /* primaryHandle */ ESYS_TR_RH_OWNER,
-    /* shandle1 */ ESYS_TR_PASSWORD,
-    /* shandle2 */ ESYS_TR_NONE,
-    /* shandle3 */ ESYS_TR_NONE,
-    /* inSensitive */ &in_sensitive,
-    /* inPublic */ &public_template,
-    /* objectHandle */ &raw_handle,
-    /* outPrivate */ nullptr,
-    /* outPublic */ nullptr);
+      /* esysContext */ *resource_manager.Esys(),
+      /* primaryHandle */ ESYS_TR_RH_OWNER,
+      /* shandle1 */ ESYS_TR_PASSWORD,
+      /* shandle2 */ ESYS_TR_NONE,
+      /* shandle3 */ ESYS_TR_NONE,
+      /* inSensitive */ &in_sensitive,
+      /* inPublic */ &public_template,
+      /* objectHandle */ &raw_handle,
+      /* outPrivate */ nullptr,
+      /* outPublic */ nullptr);
   if (rc != TSS2_RC_SUCCESS) {
     LOG(ERROR) << "Esys_CreateLoaded failed with return code " << rc
                << " (" << Tss2_RC_Decode(rc) << ")";
diff --git a/host/commands/secure_env/rust/lib.rs b/host/commands/secure_env/rust/lib.rs
index 500f5fa..7fb614d 100644
--- a/host/commands/secure_env/rust/lib.rs
+++ b/host/commands/secure_env/rust/lib.rs
@@ -73,7 +73,7 @@
         supported_num_of_keys_in_csr: MINIMUM_SUPPORTED_KEYS_IN_CSR,
     };
 
-    let mut rng = BoringRng::default();
+    let mut rng = BoringRng;
     let sdd_mgr: Option<Box<dyn kmr_common::keyblob::SecureDeletionSecretManager>> =
         match sdd::HostSddManager::new(&mut rng) {
             Ok(v) => Some(Box::new(v)),
@@ -82,7 +82,7 @@
                 None
             }
         };
-    let clock = clock::StdClock::default();
+    let clock = clock::StdClock;
     let rsa = BoringRsa::default();
     let ec = BoringEc::default();
     let hkdf: Box<dyn kmr_common::crypto::Hkdf> =
diff --git a/host/commands/secure_env/rust/rpc.rs b/host/commands/secure_env/rust/rpc.rs
index 2779e0d..1e5c3ee 100644
--- a/host/commands/secure_env/rust/rpc.rs
+++ b/host/commands/secure_env/rust/rpc.rs
@@ -140,10 +140,10 @@
             }
             // TODO: generate the *same* key after reboot, by use of the TPM.
             CsrSigningAlgorithm::ES256 => {
-                ec.generate_nist_key(&mut BoringRng::default(), ec::NistCurve::P256, &[])
+                ec.generate_nist_key(&mut BoringRng, ec::NistCurve::P256, &[])
             }
             CsrSigningAlgorithm::ES384 => {
-                ec.generate_nist_key(&mut BoringRng::default(), ec::NistCurve::P384, &[])
+                ec.generate_nist_key(&mut BoringRng, ec::NistCurve::P384, &[])
             }
         }?;
         let (pub_cose_key, private_key) = match key_material {
diff --git a/host/commands/secure_env/rust/soft.rs b/host/commands/secure_env/rust/soft.rs
index 2f7aa38..3f031bb 100644
--- a/host/commands/secure_env/rust/soft.rs
+++ b/host/commands/secure_env/rust/soft.rs
@@ -50,7 +50,7 @@
     fn default() -> Self {
         // Use random data as an emulation of a hardware-backed key.
         let mut hbk = vec![0; 32];
-        let mut rng = BoringRng::default();
+        let mut rng = BoringRng;
         rng.fill_bytes(&mut hbk);
         Self { hbk }
     }
diff --git a/host/commands/secure_env/storage/tpm_storage.cpp b/host/commands/secure_env/storage/tpm_storage.cpp
index f1d451f..4bdb54e 100644
--- a/host/commands/secure_env/storage/tpm_storage.cpp
+++ b/host/commands/secure_env/storage/tpm_storage.cpp
@@ -58,26 +58,26 @@
   auto handle_optional = CF_EXPECT(GetHandle(key));
   auto handle = CF_EXPECT(handle_optional.value());
   auto close_tr = [this](ESYS_TR* handle) {
-    Esys_TR_Close(resource_manager_.Esys(), handle);
+    Esys_TR_Close(*resource_manager_.Esys(), handle);
     delete handle;
   };
   std::unique_ptr<ESYS_TR, decltype(close_tr)> nv_handle(new ESYS_TR, close_tr);
   auto rc = Esys_TR_FromTPMPublic(
-    /* esysContext */ resource_manager_.Esys(),
-    /* tpm_handle */ handle,
-    /* optionalSession1 */ ESYS_TR_NONE,
-    /* optionalSession2 */ ESYS_TR_NONE,
-    /* optionalSession3 */ ESYS_TR_NONE,
-    /* object */ nv_handle.get());
+      /* esysContext */ *resource_manager_.Esys(),
+      /* tpm_handle */ handle,
+      /* optionalSession1 */ ESYS_TR_NONE,
+      /* optionalSession2 */ ESYS_TR_NONE,
+      /* optionalSession3 */ ESYS_TR_NONE,
+      /* object */ nv_handle.get());
   CF_EXPECTF(rc == TPM2_RC_SUCCESS, "Esys_TR_FromTPMPublic failed: {}: {}",
              rc, Tss2_RC_Decode(rc));
 
   TPM2B_AUTH auth = { .size = 0, .buffer = {} };
-  Esys_TR_SetAuth(resource_manager_.Esys(), *nv_handle, &auth);
+  Esys_TR_SetAuth(*resource_manager_.Esys(), *nv_handle, &auth);
 
   TPM2B_NV_PUBLIC* public_area;
   rc = Esys_NV_ReadPublic(
-      /* esysContext */ resource_manager_.Esys(),
+      /* esysContext */ *resource_manager_.Esys(),
       /* nvIndex */ *nv_handle,
       /* shandle1 */ ESYS_TR_NONE,
       /* shandle2 */ ESYS_TR_NONE,
@@ -90,7 +90,7 @@
   std::unique_ptr<TPM2B_NV_PUBLIC, decltype(Esys_Free)*> public_deleter(public_area, Esys_Free);
   TPM2B_MAX_NV_BUFFER* buffer = nullptr;
   rc = Esys_NV_Read(
-      /* esysContext */ resource_manager_.Esys(),
+      /* esysContext */ *resource_manager_.Esys(),
       /* authHandle */ *nv_handle,
       /* nvIndex */ *nv_handle,
       /* shandle1 */ ESYS_TR_PASSWORD,
@@ -116,7 +116,7 @@
   auto handle = CF_EXPECT(handle_optional.value());
   ESYS_TR nv_handle;
   auto rc = Esys_TR_FromTPMPublic(
-      /* esysContext */ resource_manager_.Esys(),
+      /* esysContext */ *resource_manager_.Esys(),
       /* tpm_handle */ handle,
       /* optionalSession1 */ ESYS_TR_NONE,
       /* optionalSession2 */ ESYS_TR_NONE,
@@ -126,14 +126,14 @@
              rc, Tss2_RC_Decode(rc));
 
   TPM2B_AUTH auth = { .size = 0, .buffer = {} };
-  Esys_TR_SetAuth(resource_manager_.Esys(), nv_handle, &auth);
+  Esys_TR_SetAuth(*resource_manager_.Esys(), nv_handle, &auth);
 
   TPM2B_MAX_NV_BUFFER buffer;
   buffer.size = data.size;
   std::memcpy(buffer.buffer, data.payload, data.size);
 
   rc = Esys_NV_Write(
-      /* esysContext */ resource_manager_.Esys(),
+      /* esysContext */ *resource_manager_.Esys(),
       /* authHandle */ nv_handle,
       /* nvIndex */ nv_handle,
       /* shandle1 */ ESYS_TR_PASSWORD,
@@ -141,7 +141,7 @@
       /* shandle3 */ ESYS_TR_NONE,
       /* data */ &buffer,
       /* offset */ 0);
-  Esys_TR_Close(resource_manager_.Esys(), &nv_handle);
+  Esys_TR_Close(*resource_manager_.Esys(), &nv_handle);
   CF_EXPECTF(rc == TSS2_RC_SUCCESS, "Esys_NV_Write failed with return code {} ({})",
              rc, Tss2_RC_Decode(rc));
 
@@ -149,7 +149,7 @@
 }
 
 TPM2_HANDLE TpmStorage::GenerateRandomHandle() {
-  TpmRandomSource random_source{resource_manager_.Esys()};
+  TpmRandomSource random_source{resource_manager_};
   TPM2_HANDLE handle = 0;
   random_source.GenerateRandom(reinterpret_cast<uint8_t*>(&handle), sizeof(handle));
   if (handle == 0) {
@@ -191,22 +191,22 @@
       }
     };
     TPM2B_AUTH auth = { .size = 0, .buffer = {} };
-    Esys_TR_SetAuth(resource_manager_.Esys(), ESYS_TR_RH_OWNER, &auth);
+    Esys_TR_SetAuth(*resource_manager_.Esys(), ESYS_TR_RH_OWNER, &auth);
     ESYS_TR nv_handle;
     auto rc = Esys_NV_DefineSpace(
-      /* esysContext */ resource_manager_.Esys(),
-      /* authHandle */ ESYS_TR_RH_OWNER,
-      /* shandle1 */ ESYS_TR_PASSWORD,
-      /* shandle2 */ ESYS_TR_NONE,
-      /* shandle3 */ ESYS_TR_NONE,
-      /* auth */ &auth,
-      /* publicInfo */ &public_info,
-      /* nvHandle */ &nv_handle);
+        /* esysContext */ *resource_manager_.Esys(),
+        /* authHandle */ ESYS_TR_RH_OWNER,
+        /* shandle1 */ ESYS_TR_PASSWORD,
+        /* shandle2 */ ESYS_TR_NONE,
+        /* shandle3 */ ESYS_TR_NONE,
+        /* auth */ &auth,
+        /* publicInfo */ &public_info,
+        /* nvHandle */ &nv_handle);
     if (rc == TPM2_RC_NV_DEFINED) {
       LOG(VERBOSE) << "Esys_NV_DefineSpace failed with TPM2_RC_NV_DEFINED";
       continue;
     } else if (rc == TPM2_RC_SUCCESS) {
-      Esys_TR_Close(resource_manager_.Esys(), &nv_handle);
+      Esys_TR_Close(*resource_manager_.Esys(), &nv_handle);
       break;
     } else {
       LOG(DEBUG) << "Esys_NV_DefineSpace failed with " << rc << ": "
diff --git a/host/commands/secure_env/tpm_gatekeeper.cpp b/host/commands/secure_env/tpm_gatekeeper.cpp
index add94e1..20fb8f0 100644
--- a/host/commands/secure_env/tpm_gatekeeper.cpp
+++ b/host/commands/secure_env/tpm_gatekeeper.cpp
@@ -85,7 +85,7 @@
 
 void TpmGatekeeper::GetRandom(void* random, uint32_t requested_size) const {
   auto random_uint8 = reinterpret_cast<uint8_t*>(random);
-  TpmRandomSource(resource_manager_.Esys())
+  TpmRandomSource(resource_manager_)
       .GenerateRandom(random_uint8, requested_size);
 }
 
diff --git a/host/commands/secure_env/tpm_hmac.cpp b/host/commands/secure_env/tpm_hmac.cpp
index cbb6a53..75bb662 100644
--- a/host/commands/secure_env/tpm_hmac.cpp
+++ b/host/commands/secure_env/tpm_hmac.cpp
@@ -16,6 +16,7 @@
 #include "tpm_hmac.h"
 
 #include <android-base/logging.h>
+#include <tss2/tss2_esys.h>
 #include <tss2/tss2_rc.h>
 
 #include "host/commands/secure_env/primary_key_builder.h"
@@ -40,15 +41,9 @@
   buffer.size = data_size;
   memcpy(buffer.buffer, data, data_size);
   TPM2B_DIGEST* out_hmac = nullptr;
-  auto rc = Esys_HMAC(
-      resource_manager.Esys(),
-      key_handle,
-      auth.auth1(),
-      auth.auth2(),
-      auth.auth3(),
-      &buffer,
-      TPM2_ALG_NULL,
-      &out_hmac);
+  auto rc =
+      Esys_HMAC(*resource_manager.Esys(), key_handle, auth.auth1(),
+                auth.auth2(), auth.auth3(), &buffer, TPM2_ALG_NULL, &out_hmac);
   if (rc != TPM2_RC_SUCCESS) {
     LOG(ERROR) << "TPM2_HMAC failed: " << Tss2_RC_Decode(rc) << "(" << rc << ")";
     return {};
@@ -77,23 +72,18 @@
     LOG(ERROR) << "No slots available";
     return {};
   }
-  auto rc = Esys_HMAC_Start(
-      resource_manager.Esys(),
-      key_handle,
-      key_auth.auth1(),
-      key_auth.auth2(),
-      key_auth.auth3(),
-      &sequence_auth,
-      TPM2_ALG_NULL,
-      &sequence_handle);
+  auto locked_esys = resource_manager.Esys();
+  auto rc = Esys_HMAC_Start(*locked_esys, key_handle, key_auth.auth1(),
+                            key_auth.auth2(), key_auth.auth3(), &sequence_auth,
+                            TPM2_ALG_NULL, &sequence_handle);
   if (rc != TPM2_RC_SUCCESS) {
     LOG(ERROR) << "TPM2_HMAC_Start failed: " << Tss2_RC_Decode(rc)
                << "(" << rc << ")";
     return {};
   }
   slot->set(sequence_handle);
-  rc = Esys_TR_SetAuth(
-      resource_manager.Esys(), sequence_handle, &sequence_auth);
+  rc = Esys_TR_SetAuth(*resource_manager.Esys(), sequence_handle,
+                       &sequence_auth);
   if (rc != TPM2_RC_SUCCESS) {
     LOG(ERROR) << "Esys_TR_SetAuth failed: " << Tss2_RC_Decode(rc)
                << "(" << rc << ")";
@@ -105,13 +95,8 @@
     buffer.size = TPM2_MAX_DIGEST_BUFFER;
     memcpy(buffer.buffer, &data[hashed], TPM2_MAX_DIGEST_BUFFER);
     hashed += TPM2_MAX_DIGEST_BUFFER;
-    rc = Esys_SequenceUpdate(
-        resource_manager.Esys(),
-        sequence_handle,
-        ESYS_TR_PASSWORD,
-        ESYS_TR_NONE,
-        ESYS_TR_NONE,
-        &buffer);
+    rc = Esys_SequenceUpdate(*locked_esys, sequence_handle, ESYS_TR_PASSWORD,
+                             ESYS_TR_NONE, ESYS_TR_NONE, &buffer);
     if (rc != TPM2_RC_SUCCESS) {
       LOG(ERROR) << "Esys_SequenceUpdate failed: " << Tss2_RC_Decode(rc)
                 << "(" << rc << ")";
@@ -122,16 +107,9 @@
   memcpy(buffer.buffer, &data[hashed], buffer.size);
   TPM2B_DIGEST* out_hmac = nullptr;
   TPMT_TK_HASHCHECK* validation = nullptr;
-  rc = Esys_SequenceComplete(
-      resource_manager.Esys(),
-      sequence_handle,
-      ESYS_TR_PASSWORD,
-      ESYS_TR_NONE,
-      ESYS_TR_NONE,
-      &buffer,
-      TPM2_RH_OWNER,
-      &out_hmac,
-      &validation);
+  rc = Esys_SequenceComplete(*locked_esys, sequence_handle, ESYS_TR_PASSWORD,
+                             ESYS_TR_NONE, ESYS_TR_NONE, &buffer, TPM2_RH_OWNER,
+                             &out_hmac, &validation);
   if (rc != TPM2_RC_SUCCESS) {
     LOG(ERROR) << "Esys_SequenceComplete failed: " << Tss2_RC_Decode(rc)
                << "(" << rc << ")";
@@ -155,7 +133,6 @@
     size_t data_size) {
   auto fn = data_size > TPM2_MAX_DIGEST_BUFFER ? SegmentedHmac : OneshotHmac;
 
-  auto with_tpm = resource_manager.Guard();
   return fn(resource_manager, key_handle, auth, data, data_size);
 }
 
diff --git a/host/commands/secure_env/tpm_keymaster_context.cpp b/host/commands/secure_env/tpm_keymaster_context.cpp
index bca81b8..dbd9e87 100644
--- a/host/commands/secure_env/tpm_keymaster_context.cpp
+++ b/host/commands/secure_env/tpm_keymaster_context.cpp
@@ -80,7 +80,7 @@
     : resource_manager_(resource_manager),
       enforcement_(enforcement),
       key_blob_maker_(new TpmKeyBlobMaker(resource_manager_)),
-      random_source_(new TpmRandomSource(resource_manager_.Esys())),
+      random_source_(new TpmRandomSource(resource_manager_)),
       attestation_context_(new TpmAttestationRecordContext),
       remote_provisioning_context_(
           new TpmRemoteProvisioningContext(resource_manager_)) {
diff --git a/host/commands/secure_env/tpm_keymaster_enforcement.cpp b/host/commands/secure_env/tpm_keymaster_enforcement.cpp
index 10b3164..c3bdeef 100644
--- a/host/commands/secure_env/tpm_keymaster_enforcement.cpp
+++ b/host/commands/secure_env/tpm_keymaster_enforcement.cpp
@@ -168,7 +168,7 @@
     HmacSharingParameters* params) {
   if (!have_saved_params_) {
     saved_params_.seed = {};
-    TpmRandomSource random_source{resource_manager_.Esys()};
+    TpmRandomSource random_source{resource_manager_};
     auto rc = random_source.GenerateRandom(saved_params_.nonce,
                                            sizeof(saved_params_.nonce));
     if (rc != KM_ERROR_OK) {
diff --git a/host/commands/secure_env/tpm_random_source.cpp b/host/commands/secure_env/tpm_random_source.cpp
index 569edbc..9b8e3ae 100644
--- a/host/commands/secure_env/tpm_random_source.cpp
+++ b/host/commands/secure_env/tpm_random_source.cpp
@@ -13,16 +13,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "tpm_random_source.h"
+#include "host/commands/secure_env/tpm_random_source.h"
 
 #include <android-base/logging.h>
+#include "tpm_resource_manager.h"
 #include "tss2/tss2_esys.h"
 #include "tss2/tss2_rc.h"
 
 namespace cuttlefish {
 
-TpmRandomSource::TpmRandomSource(ESYS_CONTEXT* esys) : esys_(esys) {
-}
+TpmRandomSource::TpmRandomSource(TpmResourceManager& resource_manager)
+    : resource_manager_(resource_manager) {}
 
 keymaster_error_t TpmRandomSource::GenerateRandom(
     uint8_t* random, size_t requested_length) const {
@@ -32,9 +33,9 @@
   // TODO(b/158790549): Pipeline these calls.
   TPM2B_DIGEST* generated = nullptr;
   while (requested_length > sizeof(generated->buffer)) {
-    auto rc = Esys_GetRandom(esys_, ESYS_TR_NONE, ESYS_TR_NONE,
-                             ESYS_TR_NONE, sizeof(generated->buffer),
-                             &generated);
+    auto rc =
+        Esys_GetRandom(*resource_manager_.Esys(), ESYS_TR_NONE, ESYS_TR_NONE,
+                       ESYS_TR_NONE, sizeof(generated->buffer), &generated);
     if (rc != TSS2_RC_SUCCESS) {
       LOG(ERROR) << "Esys_GetRandom failed with " << rc << " ("
                  << Tss2_RC_Decode(rc) << ")";
@@ -46,8 +47,9 @@
     requested_length -= sizeof(generated->buffer);
     Esys_Free(generated);
   }
-  auto rc = Esys_GetRandom(esys_, ESYS_TR_NONE, ESYS_TR_NONE,
-                           ESYS_TR_NONE, requested_length, &generated);
+  auto rc =
+      Esys_GetRandom(*resource_manager_.Esys(), ESYS_TR_NONE, ESYS_TR_NONE,
+                     ESYS_TR_NONE, requested_length, &generated);
   if (rc != TSS2_RC_SUCCESS) {
     LOG(ERROR) << "Esys_GetRandom failed with " << rc << " ("
                 << Tss2_RC_Decode(rc) << ")";
@@ -75,12 +77,8 @@
     in_data.size = MAX_STIR_RANDOM_BUFFER_SIZE;
     buffer += MAX_STIR_RANDOM_BUFFER_SIZE;
     size -= MAX_STIR_RANDOM_BUFFER_SIZE;
-    auto rc = Esys_StirRandom(
-        esys_,
-        ESYS_TR_NONE,
-        ESYS_TR_NONE,
-        ESYS_TR_NONE,
-        &in_data);
+    auto rc = Esys_StirRandom(*resource_manager_.Esys(), ESYS_TR_NONE,
+                              ESYS_TR_NONE, ESYS_TR_NONE, &in_data);
     if (rc != TSS2_RC_SUCCESS) {
       LOG(ERROR) << "Esys_StirRandom failed with " << rc << "("
                  << Tss2_RC_Decode(rc) << ")";
@@ -91,12 +89,8 @@
     return KM_ERROR_OK;
   }
   memcpy(in_data.buffer, buffer, size);
-  auto rc = Esys_StirRandom(
-      esys_,
-      ESYS_TR_NONE,
-      ESYS_TR_NONE,
-      ESYS_TR_NONE,
-      &in_data);
+  auto rc = Esys_StirRandom(*resource_manager_.Esys(), ESYS_TR_NONE,
+                            ESYS_TR_NONE, ESYS_TR_NONE, &in_data);
   if (rc != TSS2_RC_SUCCESS) {
     LOG(ERROR) << "Esys_StirRandom failed with " << rc << "("
                 << Tss2_RC_Decode(rc) << ")";
diff --git a/host/commands/secure_env/tpm_random_source.h b/host/commands/secure_env/tpm_random_source.h
index c9a91c7..12d4de7 100644
--- a/host/commands/secure_env/tpm_random_source.h
+++ b/host/commands/secure_env/tpm_random_source.h
@@ -17,7 +17,7 @@
 
 #include <keymaster/random_source.h>
 
-struct ESYS_CONTEXT;
+#include "host/commands/secure_env/tpm_resource_manager.h"
 
 namespace cuttlefish {
 
@@ -28,15 +28,16 @@
  */
 class TpmRandomSource : public keymaster::RandomSource {
 public:
-  TpmRandomSource(ESYS_CONTEXT* esys);
-  virtual ~TpmRandomSource() = default;
+ TpmRandomSource(TpmResourceManager& resource_manager);
+ virtual ~TpmRandomSource() = default;
 
-  keymaster_error_t GenerateRandom(
-      uint8_t* buffer, size_t length) const override;
+ keymaster_error_t GenerateRandom(uint8_t* buffer,
+                                  size_t length) const override;
 
-  keymaster_error_t AddRngEntropy(const uint8_t*, size_t) const;
+ keymaster_error_t AddRngEntropy(const uint8_t*, size_t) const;
+
 private:
-  ESYS_CONTEXT* esys_;
+ TpmResourceManager& resource_manager_;
 };
 
 }  // namespace cuttlefish
diff --git a/host/commands/secure_env/tpm_resource_manager.cpp b/host/commands/secure_env/tpm_resource_manager.cpp
index defe153..c177078 100644
--- a/host/commands/secure_env/tpm_resource_manager.cpp
+++ b/host/commands/secure_env/tpm_resource_manager.cpp
@@ -13,13 +13,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "tpm_resource_manager.h"
+#include "host/commands/secure_env/tpm_resource_manager.h"
+
+#include <mutex>
 
 #include <android-base/logging.h>
+#include <tss2/tss2_esys.h>
 #include <tss2/tss2_rc.h>
 
 namespace cuttlefish {
 
+EsysLock::EsysLock(ESYS_CONTEXT* esys, std::unique_lock<std::mutex> guard)
+    : esys_(esys), guard_(std::move(guard)) {}
+
 TpmResourceManager::ObjectSlot::ObjectSlot(TpmResourceManager* resource_manager)
     : ObjectSlot(resource_manager, ESYS_TR_NONE) {
 }
@@ -65,8 +71,8 @@
   }
 }
 
-ESYS_CONTEXT* TpmResourceManager::Esys() {
-  return esys_;
+EsysLock TpmResourceManager::Esys() {
+  return EsysLock(esys_, std::unique_lock<std::mutex>(mu_));
 }
 
 TpmObjectSlot TpmResourceManager::ReserveSlot() {
diff --git a/host/commands/secure_env/tpm_resource_manager.h b/host/commands/secure_env/tpm_resource_manager.h
index b5bf9ec..0713bfa 100644
--- a/host/commands/secure_env/tpm_resource_manager.h
+++ b/host/commands/secure_env/tpm_resource_manager.h
@@ -24,6 +24,19 @@
 
 namespace cuttlefish {
 
+class EsysLock {
+ public:
+  ESYS_CONTEXT* operator*() const { return esys_; }
+
+ private:
+  EsysLock(ESYS_CONTEXT*, std::unique_lock<std::mutex>);
+
+  ESYS_CONTEXT* esys_;
+  std::unique_lock<std::mutex> guard_;
+
+  friend class TpmResourceManager;
+};
+
 /**
  * Object slot manager for TPM memory. The TPM can only hold a fixed number of
  * objects at once. Some TPM operations are defined to consume slots either
@@ -54,14 +67,12 @@
   TpmResourceManager(ESYS_CONTEXT* esys);
   ~TpmResourceManager();
 
-  ESYS_CONTEXT* Esys();
+  // Returns a wrapped ESYS_CONTEXT* that can be used with Esys calls that also
+  // holds a lock. Callers should not hold onto the inner ESYS_CONTEXT* past the
+  // lifetime of the lock.
+  EsysLock Esys();
   std::shared_ptr<ObjectSlot> ReserveSlot();
 
-  // Return a lock guard to serialize access to the TPM.
-  std::lock_guard<std::mutex> Guard() {
-    return std::lock_guard<std::mutex>(mu_);
-  }
-
  private:
   std::mutex mu_;
   ESYS_CONTEXT* esys_;
diff --git a/host/commands/start/Android.bp b/host/commands/start/Android.bp
index eb55cbd..73ba0fc 100644
--- a/host/commands/start/Android.bp
+++ b/host/commands/start/Android.bp
@@ -27,11 +27,8 @@
     ],
     shared_libs: [
         "libbase",
-        "libcuttlefish_device_config",
-        "libcuttlefish_device_config_proto",
         "libcuttlefish_fs",
         "libcuttlefish_utils",
-        "libfruit",
         "libjsoncpp",
         "libxml2",
         "libz",
@@ -49,11 +46,6 @@
         darwin: {
             enabled: true,
         },
-        linux: {
-            shared_libs: [
-                "libnl",
-            ],
-        },
     },
     defaults: ["cuttlefish_host", "cuttlefish_libicuuc"],
 }
diff --git a/host/frontend/webrtc/libdevice/streamer.cpp b/host/frontend/webrtc/libdevice/streamer.cpp
index 811dc7f..74590b5 100644
--- a/host/frontend/webrtc/libdevice/streamer.cpp
+++ b/host/frontend/webrtc/libdevice/streamer.cpp
@@ -65,6 +65,7 @@
 constexpr auto kControlPanelButtonLidSwitchOpen = "lid_switch_open";
 constexpr auto kControlPanelButtonHingeAngleValue = "hinge_angle_value";
 constexpr auto kCustomControlPanelButtonsField = "custom_control_panel_buttons";
+constexpr auto kGroupIdField = "group_id";
 
 constexpr int kRegistrationRetries = 3;
 constexpr int kRetryFirstIntervalMs = 1000;
@@ -393,6 +394,8 @@
       display[kIsTouchField] = true;
       displays.append(display);
     }
+
+    device_info[kGroupIdField] = config_.group_id;
     device_info[kDisplaysField] = displays;
     Json::Value audio_streams(Json::ValueType::arrayValue);
     for (auto& entry : audio_sources_) {
diff --git a/host/frontend/webrtc/libdevice/streamer.h b/host/frontend/webrtc/libdevice/streamer.h
index 014b3fa..ef12908 100644
--- a/host/frontend/webrtc/libdevice/streamer.h
+++ b/host/frontend/webrtc/libdevice/streamer.h
@@ -42,6 +42,10 @@
 struct StreamerConfig {
   // The id with which to register with the operator server.
   std::string device_id;
+
+  // The group id with which to register with the operator server.
+  std::string group_id;
+
   // The port on which the client files are being served
   int client_files_port;
   ServerConfig operator_server;
diff --git a/host/frontend/webrtc/main.cpp b/host/frontend/webrtc/main.cpp
index 7d85e83..e3fd374 100644
--- a/host/frontend/webrtc/main.cpp
+++ b/host/frontend/webrtc/main.cpp
@@ -63,6 +63,7 @@
 DEFINE_int32(audio_server_fd, -1, "An fd to listen on for audio frames");
 DEFINE_int32(camera_streamer_fd, -1, "An fd to send client camera frames");
 DEFINE_string(client_dir, "webrtc", "Location of the client files");
+DEFINE_string(group_id, "", "The group id of device");
 
 using cuttlefish::AudioHandler;
 using cuttlefish::CfConnectionObserverFactory;
@@ -185,6 +186,7 @@
   StreamerConfig streamer_config;
 
   streamer_config.device_id = instance.webrtc_device_id();
+  streamer_config.group_id = FLAGS_group_id;
   streamer_config.client_files_port = client_server->port();
   streamer_config.tcp_port_range = instance.webrtc_tcp_port_range();
   streamer_config.udp_port_range = instance.webrtc_udp_port_range();
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index f914557..6d74161 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -420,6 +420,10 @@
     // signaling server
     std::string webrtc_device_id() const;
 
+    // The group id the webrtc process should use to register with the
+    // signaling server
+    std::string group_id() const;
+
     // Whether this instance should start the webrtc signaling server
     bool start_webrtc_sig_server() const;
 
@@ -651,6 +655,7 @@
     void set_modem_simulator_ports(const std::string& modem_simulator_ports);
     void set_virtual_disk_paths(const std::vector<std::string>& disk_paths);
     void set_webrtc_device_id(const std::string& id);
+    void set_group_id(const std::string& id);
     void set_start_webrtc_signaling_server(bool start);
     void set_start_webrtc_sig_server_proxy(bool start);
     void set_start_wmediumd(bool start);
diff --git a/host/libs/config/cuttlefish_config_instance.cpp b/host/libs/config/cuttlefish_config_instance.cpp
index bfaeb18..3fad40e 100644
--- a/host/libs/config/cuttlefish_config_instance.cpp
+++ b/host/libs/config/cuttlefish_config_instance.cpp
@@ -1376,6 +1376,15 @@
   return (*Dictionary())[kWebrtcDeviceId].asString();
 }
 
+static constexpr char kGroupId[] = "group_id";
+void CuttlefishConfig::MutableInstanceSpecific::set_group_id(
+    const std::string& id) {
+  (*Dictionary())[kGroupId] = id;
+}
+std::string CuttlefishConfig::InstanceSpecific::group_id() const {
+  return (*Dictionary())[kGroupId].asString();
+}
+
 static constexpr char kStartSigServer[] = "webrtc_start_sig_server";
 void CuttlefishConfig::MutableInstanceSpecific::set_start_webrtc_signaling_server(bool start) {
   (*Dictionary())[kStartSigServer] = start;
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index 2bb3745..5277d99 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -241,6 +241,7 @@
 
 # vendor sepolicy
 BOARD_VENDOR_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/vendor
+BOARD_VENDOR_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/vendor/seriallogging
 BOARD_VENDOR_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/vendor/google
 
 BOARD_SEPOLICY_DIRS += system/bt/vendor_libs/linux/sepolicy
diff --git a/shared/config/init.vendor.rc b/shared/config/init.vendor.rc
index 2b3e28f..5706c4e 100644
--- a/shared/config/init.vendor.rc
+++ b/shared/config/init.vendor.rc
@@ -105,12 +105,6 @@
 service socket_vsock_proxy /vendor/bin/socket_vsock_proxy -server_type=vsock -server_vsock_port=6520 -client_type=tcp -client_tcp_host=0.0.0.0 -client_tcp_port=5555
     user root
 
-service seriallogging /system/bin/logcat -b all -v threadtime -f /dev/hvc2 *:V
-    class main
-    user logd
-    group root logd
-    seclabel u:r:logpersist:s0
-
 service bugreport /system/bin/dumpstate -d -p -z
     class main
     user root
diff --git a/shared/config/seriallogging.rc b/shared/config/seriallogging.rc
new file mode 100644
index 0000000..f6b71b5
--- /dev/null
+++ b/shared/config/seriallogging.rc
@@ -0,0 +1,5 @@
+service seriallogging /system/bin/logcat -b all -v threadtime -f /dev/hvc2 *:V
+    class main
+    user logd
+    group root logd
+    seclabel u:r:logpersist:s0
diff --git a/shared/device.mk b/shared/device.mk
index 1458469..205a26a 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -207,6 +207,7 @@
     device/google/cuttlefish/shared/config/init.vendor.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/init.cutf_cvm.rc \
     device/google/cuttlefish/shared/config/init.product.rc:$(TARGET_COPY_OUT_PRODUCT)/etc/init/init.rc \
     device/google/cuttlefish/shared/config/ueventd.rc:$(TARGET_COPY_OUT_VENDOR)/etc/ueventd.rc \
+    device/google/cuttlefish/shared/config/seriallogging.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/seriallogging.rc \
     device/google/cuttlefish/shared/config/media_codecs.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs.xml \
     device/google/cuttlefish/shared/config/media_codecs_google_video.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_video.xml \
     device/google/cuttlefish/shared/config/media_codecs_performance.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_performance.xml \
diff --git a/shared/minidroid/BoardConfig.mk b/shared/minidroid/BoardConfig.mk
index 9d43df0..2872689 100644
--- a/shared/minidroid/BoardConfig.mk
+++ b/shared/minidroid/BoardConfig.mk
@@ -143,3 +143,5 @@
 
 TARGET_SKIP_OTA_PACKAGE := true
 TARGET_SKIP_OTATOOLS_PACKAGE := true
+
+BOARD_VENDOR_SEPOLICY_DIRS += device/google/cuttlefish/shared/sepolicy/vendor/seriallogging
diff --git a/shared/minidroid/device.mk b/shared/minidroid/device.mk
index 5b8956c..1090fe3 100644
--- a/shared/minidroid/device.mk
+++ b/shared/minidroid/device.mk
@@ -113,6 +113,7 @@
 PRODUCT_COPY_FILES += \
     device/google/cuttlefish/shared/minidroid/init.rc:system/etc/init/hw/init.minidroid.rc \
     packages/modules/Virtualization/microdroid/ueventd.rc:vendor/etc/ueventd.rc \
+    device/google/cuttlefish/shared/config/seriallogging.rc:vendor/etc/init/seriallogging.rc \
 
 DEVICE_MANIFEST_FILE := \
     device/google/cuttlefish/shared/minidroid/minidroid_vendor_manifest.xml
diff --git a/shared/minidroid/init.rc b/shared/minidroid/init.rc
index f824037..650b6fc 100644
--- a/shared/minidroid/init.rc
+++ b/shared/minidroid/init.rc
@@ -14,6 +14,7 @@
 # continue despite that. Remove the import once we can rely on .rc files from
 # /system/etc/init getting loaded automatically.
 import /system/etc/init/logcatd.rc
+import /vendor/etc/init/seriallogging.rc
 
 # Cgroups are mounted right before early-init using list from /etc/cgroups.json
 on early-init
@@ -130,6 +131,8 @@
 
     mkdir /data/misc/authfs 0700 root root
 
+    start seriallogging
+
 on late-fs && property:ro.debuggable=1
     # Ensure that tracefs has the correct permissions.
     # This does not work correctly if it is called in post-fs.
diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts
index d3da904..0dbdd53 100644
--- a/shared/sepolicy/vendor/file_contexts
+++ b/shared/sepolicy/vendor/file_contexts
@@ -17,9 +17,9 @@
 /dev/block/by-name/frp  u:object_r:frp_block_device:s0
 
 /dev/block/zram0  u:object_r:swap_block_device:s0
-/dev/hvc0  u:object_r:serial_device:s0
+# /dev/hvc0 is only used by the kernel directly
 /dev/hvc1  u:object_r:serial_device:s0
-/dev/hvc2  u:object_r:serial_device:s0
+# /dev/hvc2 handled in seriallogging/file_contexts
 /dev/hvc3  u:object_r:keymaster_device:s0
 /dev/hvc4  u:object_r:gatekeeper_device:s0
 
diff --git a/shared/sepolicy/vendor/init.te b/shared/sepolicy/vendor/init.te
index c44dbbe..020001e 100644
--- a/shared/sepolicy/vendor/init.te
+++ b/shared/sepolicy/vendor/init.te
@@ -26,9 +26,6 @@
 # permit mount of virtiofs on /mnt/vendor/shared
 allow init mnt_vendor_file:dir mounton;
 
-# permit init to run logcat to send copy of logs over a serial port
-domain_trans(init, logcat_exec, logpersist)
-
 allow init keymaster_device:chr_file rw_file_perms;
 allow init gatekeeper_device:chr_file rw_file_perms;
 allow init confirmationui_device:chr_file rw_file_perms;
diff --git a/shared/sepolicy/vendor/seriallogging/file_contexts b/shared/sepolicy/vendor/seriallogging/file_contexts
new file mode 100644
index 0000000..ba5285d
--- /dev/null
+++ b/shared/sepolicy/vendor/seriallogging/file_contexts
@@ -0,0 +1 @@
+/dev/hvc2  u:object_r:serial_device:s0
diff --git a/shared/sepolicy/vendor/seriallogging/init.te b/shared/sepolicy/vendor/seriallogging/init.te
new file mode 100644
index 0000000..a24d951
--- /dev/null
+++ b/shared/sepolicy/vendor/seriallogging/init.te
@@ -0,0 +1,2 @@
+# permit init to run logcat to send copy of logs over a serial port
+domain_trans(init, logcat_exec, logpersist)
diff --git a/shared/sepolicy/vendor/logpersist.te b/shared/sepolicy/vendor/seriallogging/logpersist.te
similarity index 100%
rename from shared/sepolicy/vendor/logpersist.te
rename to shared/sepolicy/vendor/seriallogging/logpersist.te
diff --git a/tests/reliability/src/com/android/cuttlefish/tests/CuttlefishReliabilityTest.java b/tests/reliability/src/com/android/cuttlefish/tests/CuttlefishReliabilityTest.java
index c5f5387..c2aae8c 100644
--- a/tests/reliability/src/com/android/cuttlefish/tests/CuttlefishReliabilityTest.java
+++ b/tests/reliability/src/com/android/cuttlefish/tests/CuttlefishReliabilityTest.java
@@ -15,27 +15,31 @@
  */
 package com.android.cuttlefish.tests;
 
+import static org.junit.Assert.fail;
+
 import com.android.tradefed.config.GlobalConfiguration;
 import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionCopier;
 import com.android.tradefed.device.DeviceManager;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.FreeDeviceState;
 import com.android.tradefed.device.IDeviceManager;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.ITestDevice.RecoveryMode;
-import com.android.tradefed.device.cloud.GceAvdInfo;
-import com.android.tradefed.device.cloud.GceAvdInfo.GceStatus;
-import com.android.tradefed.device.cloud.GceManager;
-import com.android.tradefed.device.cloud.GceSshTunnelMonitor;
-import com.android.tradefed.device.cloud.RemoteAndroidVirtualDevice;
 import com.android.tradefed.device.RemoteAvdIDevice;
+import com.android.tradefed.device.cloud.RemoteAndroidVirtualDevice;
+import com.android.tradefed.device.connection.AdbSshConnection;
+import com.android.tradefed.device.connection.DefaultConnection;
+import com.android.tradefed.device.connection.DefaultConnection.ConnectionBuilder;
+import com.android.tradefed.log.ITestLogger;
 import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.result.error.DeviceErrorIdentifier;
-import com.android.tradefed.result.error.ErrorIdentifier;
-import com.android.tradefed.result.proto.TestRecordProto.FailureStatus;
-import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.util.CommandStatus;
+import com.android.tradefed.util.RunUtil;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -46,11 +50,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import static org.junit.Assert.fail;
-
 /**
  * This test runs a reliability test for a Cuttlefish bringup by checking if the cuttlefish
  * successfully boots and passes a set of stability tests.
@@ -80,6 +79,8 @@
     )
     private double mMaxErrorRate = 0.02;
 
+    @Rule public TestLogData mLogs = new TestLogData();
+
     @Test
     public void testCuttlefishBootReliability() throws Exception {
         ExecutorService executorService = Executors.newFixedThreadPool(mConcurrentBringup);
@@ -125,90 +126,41 @@
         }
     }
 
-    public void tryBringupCuttlefish() throws Exception {
-        GceAvdInfo gceAvd;
-        GceSshTunnelMonitor gceSshMonitor = null;
-        RemoteAndroidVirtualDevice device = null;
-        GceManager gceHandler = new GceManager(
-                        getDevice().getDeviceDescriptor(),
-                        getDevice().getOptions(),
-                        getBuild());
-        boolean cvdExceptionOccurred = false;
+    public class ForwardLogger implements ITestLogger {
+        @Override
+        public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
+            mLogs.addTestLog(dataName, dataType, dataStream);
+        }
+    }
 
+    public void tryBringupCuttlefish() throws Exception {
         String initialSerial = String.format("gce-device-tmp-%s", UUID.randomUUID().toString());
         CLog.v("initialSerial: " + initialSerial);
-
+        // TODO: Modify to a temporary null-device to avoid creating placeholders
+        RemoteAvdIDevice idevice = new RemoteAvdIDevice(initialSerial);
+        IDeviceManager manager = GlobalConfiguration.getDeviceManagerInstance();
+        ((DeviceManager) manager).addAvailableDevice(idevice);
+        RemoteAndroidVirtualDevice device =
+                (RemoteAndroidVirtualDevice)
+                        manager.forceAllocateDevice(idevice.getSerialNumber());
+        OptionCopier.copyOptions(getDevice().getOptions(), device.getOptions());
         try {
-            // Attempts to lease a cuttlefish.
+            ConnectionBuilder builder = new ConnectionBuilder(
+                new RunUtil(), device, getBuild(), new ForwardLogger());
+            DefaultConnection con = DefaultConnection.createConnection(builder);
+            if (!(con instanceof AdbSshConnection)) {
+                throw new RuntimeException(
+                        String.format(
+                            "Something went wrong, excepted AdbSshConnection got %s", con));
+            }
+            AdbSshConnection connection = (AdbSshConnection) con;
             try {
-                gceAvd = gceHandler.startGce();
-            } catch (TargetSetupError e) {
-                CLog.e("Failed to lease device: %s", e.getMessage());
-                throw new DeviceNotAvailableException(
-                        "Exception during AVD GCE startup",
-                        e,
-                        initialSerial,
-                        e.getErrorId());
+                connection.initializeConnection();
+            } finally {
+                connection.tearDownConnection();
             }
-
-            // Checks if the GCE AVD failed to boot on the first attempt.
-            // If so, throws a DeviceNotAvailableException with details of the error.
-            if (GceStatus.BOOT_FAIL.equals(gceAvd.getStatus())) {
-                cvdExceptionOccurred = true;
-                throw new DeviceNotAvailableException(
-                        initialSerial + " first time boot error: " + gceAvd.getErrors());
-            }
-            RemoteAvdIDevice idevice = new RemoteAvdIDevice(initialSerial);
-            IDeviceManager manager = GlobalConfiguration.getDeviceManagerInstance();
-            ((DeviceManager) manager).addAvailableDevice(idevice);
-            device =
-                    (RemoteAndroidVirtualDevice)
-                            manager.forceAllocateDevice(idevice.getSerialNumber());
-            device.setAvdInfo(gceAvd);
-
-            // Manage the ssh bridge to the avd device.
-            gceSshMonitor = new GceSshTunnelMonitor(
-                            device, getBuild(), gceAvd.hostAndPort(), getDevice().getOptions());
-            gceSshMonitor.start();
-            device.setGceSshMonitor(gceSshMonitor);
-
-            try {
-                // check if device is online after GCE bringup, i.e. if adb is broken
-                CLog.v("Waiting for device %s online", device.getSerialNumber());
-                device.setRecoveryMode(RecoveryMode.ONLINE);
-                device.waitForDeviceOnline();
-                CLog.i(
-                        "Device is still online, version: %s",
-                        device.getProperty("ro.system.build.version.incremental"));
-            } catch (DeviceNotAvailableException dnae) {
-                String message = "AVD GCE not online after startup";
-                cvdExceptionOccurred = true;
-                throw new DeviceNotAvailableException(initialSerial + ": " + message);
-            }
-
-            // TODO: Invoke powerwash after the device boots up.
         } finally {
-            // Checks if an exception occurred during the cuttlefish boot process and attempts to
-            // collect logs if it did.
-            if (cvdExceptionOccurred) {
-                // TODO: Collect logs
-            }
-            // tear down cuttlefish
-            if (gceHandler != null) {
-                // Stop the bridge
-                if (gceSshMonitor != null) {
-                    gceSshMonitor.shutdown();
-                    try {
-                        gceSshMonitor.joinMonitor();
-                    } catch (InterruptedException e1) {
-                        CLog.i("Interrupted while waiting for GCE SSH monitor to shutdown.");
-                    }
-                }
-                gceHandler.shutdownGce();
-                gceHandler.cleanUp();
-            }
-            // This test is running in delegated-tf mode, there's no need for extra clean-up steps.
+            manager.freeDevice(device, FreeDeviceState.AVAILABLE);
         }
-        return;
     }
 }
diff --git a/vsoc_x86_only/BoardConfig.mk b/vsoc_x86_only/BoardConfig.mk
index 44f38c7..c7d0d49 100644
--- a/vsoc_x86_only/BoardConfig.mk
+++ b/vsoc_x86_only/BoardConfig.mk
@@ -24,7 +24,7 @@
 TARGET_CPU_ABI := x86
 
 TARGET_KERNEL_ARCH ?= i686
-TARGET_KERNEL_USE ?= 5.15
+TARGET_KERNEL_USE ?= 6.1
 KERNEL_MODULES_PATH := device/google/cuttlefish_prebuilts/kernel/$(TARGET_KERNEL_USE)-$(TARGET_KERNEL_ARCH)
 TARGET_KERNEL_PATH := $(KERNEL_MODULES_PATH)/kernel-$(TARGET_KERNEL_USE)
 # FIXME: system_dlkm should be specified as well