[automerger skipped] SIGKILL crosvm since `crosvm stop` may be slow am: 004598c980 am: 60e7dced74 -s ours

am skip reason: Merged-In Iec376c1bb02c98dd32ff869c708cace40ad86ff9 with SHA-1 91041ad910 is already in history

Original change: https://googleplex-android-review.googlesource.com/c/device/google/cuttlefish/+/23746824

Change-Id: I271346fc6d4b9da32beccd3c6ec3f330ce33f02c
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/Android.mk b/Android.mk
index 78b607f..fe6abb5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -27,8 +27,8 @@
 $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
 $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,init.cutf_cvm.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
 $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,bt_vhci_forwarder.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.f2fs,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.ext4,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.f2fs.cts,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.ext4.cts,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
 $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,init.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
 $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,audio_policy.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
 
diff --git a/AndroidProducts.mk b/AndroidProducts.mk
index d345316..d990da1 100644
--- a/AndroidProducts.mk
+++ b/AndroidProducts.mk
@@ -28,6 +28,7 @@
 	aosp_cf_x86_64_tv:$(LOCAL_DIR)/vsoc_x86_64/tv/aosp_cf.mk \
 	aosp_cf_x86_64_foldable:$(LOCAL_DIR)/vsoc_x86_64/phone/aosp_cf_foldable.mk \
 	aosp_cf_x86_64_only_phone:$(LOCAL_DIR)/vsoc_x86_64_only/phone/aosp_cf.mk \
+	aosp_cf_x86_64_only_phone_hsum:$(LOCAL_DIR)/vsoc_x86_64_only/phone/aosp_cf_hsum.mk \
 	aosp_cf_x86_64_slim:$(LOCAL_DIR)/vsoc_x86_64_only/slim/aosp_cf.mk \
 	aosp_cf_x86_auto:$(LOCAL_DIR)/vsoc_x86/auto/aosp_cf.mk \
 	aosp_cf_x86_pasan:$(LOCAL_DIR)/vsoc_x86/pasan/aosp_cf.mk \
@@ -40,6 +41,7 @@
 COMMON_LUNCH_CHOICES := \
 	aosp_cf_arm64_auto-userdebug \
 	aosp_cf_arm64_phone-userdebug \
+	aosp_cf_x86_64_only_phone_hsum-userdebug \
 	aosp_cf_x86_64_pc-userdebug \
 	aosp_cf_x86_64_phone-userdebug \
 	aosp_cf_x86_64_foldable-userdebug \
diff --git a/default-permissions.xml b/default-permissions.xml
index 48b0f17..2d5020a 100644
--- a/default-permissions.xml
+++ b/default-permissions.xml
@@ -133,4 +133,9 @@
         <!-- Notifications -->
         <permission name="android.permission.POST_NOTIFICATIONS" fixed="false"/>
     </exception>
+    <exception
+        package="com.google.android.euicc">
+        <!-- Notifications -->
+        <permission name="android.permission.POST_NOTIFICATIONS" fixed="false"/>
+    </exception>
 </exceptions>
diff --git a/guest/commands/bt_vhci_forwarder/main.cpp b/guest/commands/bt_vhci_forwarder/main.cpp
index 2f1aab2..d1bb588 100644
--- a/guest/commands/bt_vhci_forwarder/main.cpp
+++ b/guest/commands/bt_vhci_forwarder/main.cpp
@@ -128,6 +128,9 @@
         send(vhci_fd, HCI_ISODATA_PKT, raw_iso.data(), raw_iso.size());
       },
       []() { LOG(INFO) << "HCI socket device disconnected"; });
+
+  bool before_first_command = true;
+
   while (true) {
     int ret = TEMP_FAILURE_RETRY(poll(fds, 2, -1));
     if (ret < 0) {
@@ -141,6 +144,7 @@
       if (c < 0) {
         PLOG(ERROR) << "vhci to virtio-console failed";
       }
+      before_first_command = false;
     }
     if (fds[1].revents & POLLHUP) {
       LOG(ERROR) << "PollHUP";
@@ -148,6 +152,16 @@
       continue;
     }
     if (fds[1].revents & (POLLIN | POLLERR)) {
+      if (before_first_command) {
+        // Drop any data left in the virtio-console from a previous reset.
+        ssize_t bytes = TEMP_FAILURE_RETRY(read(virtio_fd, buf, kBufferSize));
+        if (bytes < 0) {
+          LOG(ERROR) << "virtio_fd ready, but read failed " << strerror(errno);
+        } else {
+          LOG(INFO) << "Discarding " << bytes << " bytes from virtio_fd.";
+        }
+        continue;
+      }
       // 'virtio-console to vhci' depends on H4Packetizer because vhci expects
       // full packet, but the data from virtio-console could be partial.
       h4.OnDataReady(virtio_fd);
diff --git a/guest/commands/setup_wifi/main.cpp b/guest/commands/setup_wifi/main.cpp
index 4a91442..2727fb4 100644
--- a/guest/commands/setup_wifi/main.cpp
+++ b/guest/commands/setup_wifi/main.cpp
@@ -33,6 +33,7 @@
 #include "common/libs/net/network_interface_manager.h"
 
 DEFINE_string(mac_prefix, "", "mac prefix to use for wlan0");
+DEFINE_string(interface, "eth2", "interface to create wlan wrapper on");
 
 static std::array<unsigned char, 6> prefix_to_mac(
     const std::string& mac_prefix) {
@@ -138,9 +139,9 @@
 
   gflags::ParseCommandLineFlags(&argc, &argv, true);
 
-  int renamed_eth2 = RenameNetwork("eth2", "buried_eth2");
-  if (renamed_eth2 != 0) {
-    return renamed_eth2;
+  int renamed_if = RenameNetwork(FLAGS_interface, "buried_" + FLAGS_interface);
+  if (renamed_if != 0) {
+    return renamed_if;
   }
-  return CreateWifiWrapper("buried_eth2", "wlan0");
+  return CreateWifiWrapper("buried_" + FLAGS_interface, "wlan0");
 }
diff --git a/guest/commands/setup_wifi/setup_wifi.rc b/guest/commands/setup_wifi/setup_wifi.rc
index a2f1eb5..993e957 100644
--- a/guest/commands/setup_wifi/setup_wifi.rc
+++ b/guest/commands/setup_wifi/setup_wifi.rc
@@ -1,2 +1,2 @@
-service setup_wifi /vendor/bin/setup_wifi
+service setup_wifi /vendor/bin/setup_wifi --interface=${ro.vendor.virtwifi.port}
     oneshot
diff --git a/guest/hals/audio/effects/manifest.xml b/guest/hals/audio/effects/manifest.xml
new file mode 100644
index 0000000..6b4e832
--- /dev/null
+++ b/guest/hals/audio/effects/manifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+** limitations under the License.
+*/
+-->
+
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.audio.effect</name>
+        <transport>hwbinder</transport>
+        <version>7.0</version>
+        <interface>
+            <name>IEffectsFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/guest/hals/keymint/remote/remote_keymint_operation.cpp b/guest/hals/keymint/remote/remote_keymint_operation.cpp
index b88715a..aa05035 100644
--- a/guest/hals/keymint/remote/remote_keymint_operation.cpp
+++ b/guest/hals/keymint/remote/remote_keymint_operation.cpp
@@ -50,13 +50,17 @@
 }
 
 ScopedAStatus RemoteKeyMintOperation::updateAad(
-    const vector<uint8_t>& input,
-    const optional<HardwareAuthToken>& /* authToken */,
+    const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
     const optional<TimeStampToken>& /* timestampToken */) {
   UpdateOperationRequest request(impl_.message_version());
   request.op_handle = opHandle_;
   request.additional_params.push_back(TAG_ASSOCIATED_DATA, input.data(),
                                       input.size());
+  if (authToken) {
+    auto tokenAsVec(authToken2AidlVec(*authToken));
+    request.additional_params.push_back(keymaster::TAG_AUTH_TOKEN,
+                                        tokenAsVec.data(), tokenAsVec.size());
+  }
 
   UpdateOperationResponse response(impl_.message_version());
   impl_.UpdateOperation(request, &response);
@@ -65,8 +69,7 @@
 }
 
 ScopedAStatus RemoteKeyMintOperation::update(
-    const vector<uint8_t>& input,
-    const optional<HardwareAuthToken>& /* authToken */,
+    const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
     const optional<TimeStampToken>&
     /* timestampToken */,
     vector<uint8_t>* output) {
@@ -75,6 +78,11 @@
   UpdateOperationRequest request(impl_.message_version());
   request.op_handle = opHandle_;
   request.input.Reinitialize(input.data(), input.size());
+  if (authToken) {
+    auto tokenAsVec(authToken2AidlVec(*authToken));
+    request.additional_params.push_back(keymaster::TAG_AUTH_TOKEN,
+                                        tokenAsVec.data(), tokenAsVec.size());
+  }
 
   UpdateOperationResponse response(impl_.message_version());
   impl_.UpdateOperation(request, &response);
@@ -92,7 +100,7 @@
 ScopedAStatus RemoteKeyMintOperation::finish(
     const optional<vector<uint8_t>>& input,      //
     const optional<vector<uint8_t>>& signature,  //
-    const optional<HardwareAuthToken>& /* authToken */,
+    const optional<HardwareAuthToken>& authToken,
     const optional<TimeStampToken>& /* timestampToken */,
     const optional<vector<uint8_t>>& /* confirmationToken */,
     vector<uint8_t>* output) {
@@ -104,8 +112,14 @@
   FinishOperationRequest request(impl_.message_version());
   request.op_handle = opHandle_;
   if (input) request.input.Reinitialize(input->data(), input->size());
-  if (signature)
+  if (signature) {
     request.signature.Reinitialize(signature->data(), signature->size());
+  }
+  if (authToken) {
+    auto tokenAsVec(authToken2AidlVec(*authToken));
+    request.additional_params.push_back(keymaster::TAG_AUTH_TOKEN,
+                                        tokenAsVec.data(), tokenAsVec.size());
+  }
 
   FinishOperationResponse response(impl_.message_version());
   impl_.FinishOperation(request, &response);
diff --git a/host/commands/assemble_cvd/disk_flags.cc b/host/commands/assemble_cvd/disk_flags.cc
index c06d7d7..ce7d569 100644
--- a/host/commands/assemble_cvd/disk_flags.cc
+++ b/host/commands/assemble_cvd/disk_flags.cc
@@ -80,7 +80,7 @@
 DEFINE_string(otheros_root_image, "",
               "Location of cuttlefish otheros root filesystem image.");
 
-DEFINE_int32(blank_metadata_image_mb, 16,
+DEFINE_int32(blank_metadata_image_mb, 64,
              "The size of the blank metadata image to generate, MB.");
 DEFINE_int32(blank_sdcard_image_mb, 2048,
              "If enabled, the size of the blank sdcard image to generate, MB.");
@@ -677,7 +677,8 @@
  private:
   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
   Result<void> ResultSetup() override {
-    if (FileExists(FLAGS_metadata_image)) {
+    if (FileExists(FLAGS_metadata_image) &&
+        FileSize(FLAGS_metadata_image) == FLAGS_blank_metadata_image_mb << 20) {
       return {};
     }
 
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 08c2b68..a68a475 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -597,15 +597,6 @@
       }
   }
 
-  // The device needs to avoid having both hwcomposer2.4 and hwcomposer3
-  // services running at the same time so warn the user to manually build
-  // in drm_hwcomposer when needed.
-  if (tmp_config_obj.hwcomposer() == kHwComposerAuto) {
-    LOG(WARNING) << "In order to run with --hwcomposer=drm. Please make sure "
-                    "Cuttlefish was built with "
-                    "TARGET_ENABLE_DRMHWCOMPOSER=true.";
-  }
-
   tmp_config_obj.set_enable_gpu_udmabuf(FLAGS_enable_gpu_udmabuf);
   tmp_config_obj.set_enable_gpu_angle(FLAGS_enable_gpu_angle);
 
@@ -768,7 +759,7 @@
     const auto vsock_guest_cid = FLAGS_vsock_guest_cid + num - GetInstance();
     instance.set_vsock_guest_cid(vsock_guest_cid);
     auto calc_vsock_port = [vsock_guest_cid](const int base_port) {
-      // a base (vsock) port is like 9200 for modem_simulator, etc
+      // a base (vsock) port is like 9600 for modem_simulator, etc
       return cuttlefish::GetVsockServerPort(base_port, vsock_guest_cid);
     };
     instance.set_session_id(iface_config.mobile_tap.session_id);
@@ -891,10 +882,10 @@
     if (modem_simulator_count > 0) {
       std::stringstream modem_ports;
       for (auto index {0}; index < modem_simulator_count - 1; index++) {
-        auto port = 9200 + (modem_simulator_count * (num - 1)) + index;
+        auto port = 9600 + (modem_simulator_count * (num - 1)) + index;
         modem_ports << calc_vsock_port(port) << ",";
       }
-      auto port = 9200 + (modem_simulator_count * (num - 1)) +
+      auto port = 9600 + (modem_simulator_count * (num - 1)) +
                   modem_simulator_count - 1;
       modem_ports << calc_vsock_port(port);
       instance.set_modem_simulator_ports(modem_ports.str());
diff --git a/host/commands/modem_simulator/call_service.cpp b/host/commands/modem_simulator/call_service.cpp
index 674c153..7e40870 100644
--- a/host/commands/modem_simulator/call_service.cpp
+++ b/host/commands/modem_simulator/call_service.cpp
@@ -37,7 +37,6 @@
   auto instance = nvram_config->ForInstance(service_id_);
   in_emergency_mode_ = instance.emergency_mode();
 
-  last_active_call_index_ = 1;
   mute_on_ = false;
 }
 
@@ -216,7 +215,7 @@
     call_status.is_mobile_terminated = false;
     call_status.call_state = CallStatus::CALL_STATE_DIALING;
     call_status.remote_client = remote_client;
-    int index = last_active_call_index_++;
+    auto index = FindFreeCallIndex();
 
     auto call_token = std::make_pair(index, call_status.number);
     call_status.timeout_serial = thread_looper_->Post(
@@ -232,7 +231,7 @@
     CallStatus call_status(number);
     call_status.is_mobile_terminated = false;
     call_status.call_state = CallStatus::CALL_STATE_DIALING;
-    auto index = last_active_call_index_++;
+    auto index = FindFreeCallIndex();
     active_calls_[index] = call_status;
 
     if (emergency_number) {
@@ -707,7 +706,7 @@
       call_status.remote_client = client.client_fd;
       call_status.call_state = CallStatus::CALL_STATE_INCOMING;
 
-      auto index = last_active_call_index_++;
+      auto index = FindFreeCallIndex();
       active_calls_[index] = call_status;
       break;
     }
diff --git a/host/commands/modem_simulator/call_service.h b/host/commands/modem_simulator/call_service.h
index 1870372..f57eb05 100644
--- a/host/commands/modem_simulator/call_service.h
+++ b/host/commands/modem_simulator/call_service.h
@@ -42,6 +42,13 @@
   void HandleCancelUssd(const Client& client, const std::string& command);
   void HandleEmergencyMode(const Client& client, const std::string& command);
   void HandleRemoteCall(const Client& client, const std::string& command);
+  int FindFreeCallIndex() const {
+    for (int index = 1; true; index++) {
+      if (active_calls_.find(index) == active_calls_.end()) {
+        return index;
+      }
+    }
+  }
 
  private:
   void InitializeServiceState();
@@ -141,7 +148,6 @@
   // private data members
   SimService* sim_service_;
   NetworkService* network_service_;
-  int32_t last_active_call_index_;
   std::map<int, CallStatus> active_calls_;
   bool in_emergency_mode_;
   bool mute_on_;
diff --git a/host/frontend/webrtc/client/js/cf_webrtc.js b/host/frontend/webrtc/client/js/cf_webrtc.js
index 5c91383..073ea36 100644
--- a/host/frontend/webrtc/client/js/cf_webrtc.js
+++ b/host/frontend/webrtc/client/js/cf_webrtc.js
@@ -422,7 +422,7 @@
     this.#pc.addIceCandidate(iceCandidate);
   }
 
-  ConnectDevice(pc) {
+  ConnectDevice(pc, infraConfig) {
     this.#pc = pc;
     console.debug('ConnectDevice');
     // ICE candidates will be generated when we add the offer. Adding it here
@@ -432,7 +432,8 @@
     this.#pc.addEventListener('icecandidate', evt => {
       if (evt.candidate) this.#sendIceCandidate(evt.candidate);
     });
-    this.#serverConnector.sendToDevice({type: 'request-offer'});
+    this.#serverConnector.sendToDevice(
+        {type: 'request-offer', ice_servers: infraConfig.ice_servers});
   }
 
   async renegotiateConnection() {
@@ -445,10 +446,7 @@
 }
 
 function createPeerConnection(infra_config) {
-  let pc_config = {iceServers: []};
-  for (const stun of infra_config.ice_servers) {
-    pc_config.iceServers.push({urls: 'stun:' + stun});
-  }
+  let pc_config = {iceServers: infra_config.ice_servers};
   let pc = new RTCPeerConnection(pc_config);
 
   pc.addEventListener('icecandidate', evt => {
@@ -470,12 +468,6 @@
   let infraConfig = requestRet.infraConfig;
   console.debug('Device available:');
   console.debug(deviceInfo);
-  let pc_config = {iceServers: []};
-  if (infraConfig.ice_servers && infraConfig.ice_servers.length > 0) {
-    for (const server of infraConfig.ice_servers) {
-      pc_config.iceServers.push(server);
-    }
-  }
   let pc = createPeerConnection(infraConfig);
 
   let control = new Controller(serverConnector);
@@ -491,6 +483,6 @@
         reject(evt);
       }
     });
-    control.ConnectDevice(pc);
+    control.ConnectDevice(pc, infraConfig);
   });
 }
diff --git a/host/frontend/webrtc/lib/client_handler.cpp b/host/frontend/webrtc/lib/client_handler.cpp
index 9be1ca0..94aa175 100644
--- a/host/frontend/webrtc/lib/client_handler.cpp
+++ b/host/frontend/webrtc/lib/client_handler.cpp
@@ -457,22 +457,65 @@
                          msg_data + msg.size());
 }
 
+std::vector<webrtc::PeerConnectionInterface::IceServer>
+ClientHandler::ParseIceServersMessage(const Json::Value &message) {
+  std::vector<webrtc::PeerConnectionInterface::IceServer> ret;
+  if (!message.isMember("ice_servers") || !message["ice_servers"].isArray()) {
+    // Log as verbose since the ice_servers field is optional in some messages
+    LOG(VERBOSE) << "ice_servers field not present in json object or not an array";
+    return ret;
+  }
+  auto& servers = message["ice_servers"];
+  for (const auto& server: servers) {
+    webrtc::PeerConnectionInterface::IceServer ice_server;
+    if (!server.isMember("urls") || !server["urls"].isArray()) {
+      // The urls field is required
+      LOG(WARNING)
+          << "ICE server specification missing urls field or not an array: "
+          << server.toStyledString();
+      continue;
+    }
+    auto urls = server["urls"];
+    for (int url_idx = 0; url_idx < urls.size(); url_idx++) {
+      auto url = urls[url_idx];
+      if (!url.isString()) {
+        LOG(WARNING) << "Non string 'urls' field in ice server: "
+                     << url.toStyledString();
+        continue;
+      }
+      ice_server.urls.push_back(url.asString());
+    }
+    if (server.isMember("credential") && server["credential"].isString()) {
+      ice_server.password = server["credential"].asString();
+    }
+    if (server.isMember("username") && server["username"].isString()) {
+      ice_server.username = server["username"].asString();
+    }
+    ret.push_back(ice_server);
+  }
+  return ret;
+}
+
 std::shared_ptr<ClientHandler> ClientHandler::Create(
     int client_id, std::shared_ptr<ConnectionObserver> observer,
+    PeerConnectionBuilder &connection_builder,
     std::function<void(const Json::Value &)> send_to_client_cb,
     std::function<void(bool)> on_connection_changed_cb) {
-  return std::shared_ptr<ClientHandler>(new ClientHandler(
-      client_id, observer, send_to_client_cb, on_connection_changed_cb));
+  return std::shared_ptr<ClientHandler>(
+      new ClientHandler(client_id, observer, connection_builder,
+                        send_to_client_cb, on_connection_changed_cb));
 }
 
 ClientHandler::ClientHandler(
     int client_id, std::shared_ptr<ConnectionObserver> observer,
+    PeerConnectionBuilder &connection_builder,
     std::function<void(const Json::Value &)> send_to_client_cb,
     std::function<void(bool)> on_connection_changed_cb)
     : client_id_(client_id),
       observer_(observer),
       send_to_client_(send_to_client_cb),
       on_connection_changed_cb_(on_connection_changed_cb),
+      connection_builder_(connection_builder),
       camera_track_(new ClientVideoTrackImpl()) {}
 
 ClientHandler::~ClientHandler() {
@@ -481,54 +524,37 @@
   }
 }
 
-bool ClientHandler::SetPeerConnection(
-    rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection) {
-  peer_connection_ = peer_connection;
-
-  // libwebrtc configures the video encoder with a start bitrate of just 300kbs
-  // which causes it to drop the first 4 frames it receives. Any value over 2Mbs
-  // will be capped at 2Mbs when passed to the encoder by the peer_connection
-  // object, so we pass the maximum possible value here.
-  webrtc::BitrateSettings bitrate_settings;
-  bitrate_settings.start_bitrate_bps = 2000000; // 2Mbs
-  peer_connection_->SetBitrate(bitrate_settings);
-  // At least one data channel needs to be created on the side that makes the
-  // SDP offer (the device) for data channels to be enabled at all.
-  // This channel is meant to carry control commands from the client.
-  auto control_channel = peer_connection_->CreateDataChannel(
-      "device-control", nullptr /* config */);
-  if (!control_channel) {
-    LOG(ERROR) << "Failed to create control data channel";
-    return false;
-  }
-  control_handler_.reset(new ControlChannelHandler(control_channel, observer_));
-  return true;
-}
-
 bool ClientHandler::AddDisplay(
     rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track,
     const std::string &label) {
-  // Send each track as part of a different stream with the label as id
-  auto err_or_sender =
-      peer_connection_->AddTrack(video_track, {label} /* stream_id */);
-  if (!err_or_sender.ok()) {
-    LOG(ERROR) << "Failed to add video track to the peer connection";
-    return false;
+  displays_.emplace_back(video_track, label);
+  if (peer_connection_) {
+    // Send each track as part of a different stream with the label as id
+    auto err_or_sender =
+        peer_connection_->AddTrack(video_track, {label} /* stream_id */);
+    if (!err_or_sender.ok()) {
+      LOG(ERROR) << "Failed to add video track to the peer connection";
+      return false;
+    }
+    // TODO (b/154138394): use the returned sender (err_or_sender.value()) to
+    // remove the display from the connection.
   }
-  // TODO (b/154138394): use the returned sender (err_or_sender.value()) to
-  // remove the display from the connection.
   return true;
 }
 
 bool ClientHandler::AddAudio(
     rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track,
     const std::string &label) {
-  // Send each track as part of a different stream with the label as id
-  auto err_or_sender =
-      peer_connection_->AddTrack(audio_track, {label} /* stream_id */);
-  if (!err_or_sender.ok()) {
-    LOG(ERROR) << "Failed to add video track to the peer connection";
-    return false;
+  // Store the audio track for when the peer connection is created
+  audio_streams_.emplace_back(audio_track, label);
+  if (peer_connection_) {
+    // Send each track as part of a different stream with the label as id
+    auto err_or_sender =
+        peer_connection_->AddTrack(audio_track, {label} /* stream_id */);
+    if (!err_or_sender.ok()) {
+      LOG(ERROR) << "Failed to add video track to the peer connection";
+      return false;
+    }
   }
   return true;
 }
@@ -558,6 +584,56 @@
   pending_ice_candidates_.clear();
 }
 
+bool ClientHandler::BuildPeerConnection(const Json::Value &message) {
+  auto ice_servers = ParseIceServersMessage(message);
+  peer_connection_ = connection_builder_.Build(this, ice_servers);
+  if (!peer_connection_) {
+    return false;
+  }
+
+  // Re-add the video and audio tracks after the peer connection has been
+  // created
+  decltype(displays_) tmp_displays;
+  tmp_displays.swap(displays_);
+  for (auto &pair : tmp_displays) {
+    auto &video_track = pair.first;
+    auto &label = pair.second;
+    if (!AddDisplay(video_track, label)) {
+      return false;
+    }
+  }
+  decltype(audio_streams_) tmp_audio_streams;
+  tmp_audio_streams.swap(audio_streams_);
+  for (auto &pair : tmp_audio_streams) {
+    auto &audio_track = pair.first;
+    auto &label = pair.second;
+    if (!AddAudio(audio_track, label)) {
+      return false;
+    }
+  }
+
+  // libwebrtc configures the video encoder with a start bitrate of just 300kbs
+  // which causes it to drop the first 4 frames it receives. Any value over 2Mbs
+  // will be capped at 2Mbs when passed to the encoder by the peer_connection
+  // object, so we pass the maximum possible value here.
+  webrtc::BitrateSettings bitrate_settings;
+  bitrate_settings.start_bitrate_bps = 2000000;  // 2Mbs
+  peer_connection_->SetBitrate(bitrate_settings);
+
+  // At least one data channel needs to be created on the side that makes the
+  // SDP offer (the device) for data channels to be enabled at all.
+  // This channel is meant to carry control commands from the client.
+  auto control_channel = peer_connection_->CreateDataChannel(
+      "device-control", nullptr /* config */);
+  if (!control_channel) {
+    LOG(ERROR) << "Failed to create control data channel";
+    return false;
+  }
+  control_handler_.reset(new ControlChannelHandler(control_channel, observer_));
+
+  return true;
+}
+
 void ClientHandler::OnCreateSDPSuccess(
     webrtc::SessionDescriptionInterface *desc) {
   std::string offer_str;
@@ -607,9 +683,15 @@
   }
   auto type = message["type"].asString();
   if (type == "request-offer") {
-    // Can't check for state being different that kNew because renegotiation can
-    // start in any state after the answer is returned.
-    if (state_ == State::kCreatingOffer) {
+    if (state_ == State::kNew) {
+      // The peer connection must be created on the first request-offer
+      if (!BuildPeerConnection(message)) {
+        LogAndReplyError("Failed to create peer connection");
+        return;
+      }
+      // Renegotiation can start in any state after the answer is returned, not
+      // just kNew.
+    } else if (state_ == State::kCreatingOffer) {
       // An offer has been requested already
       LogAndReplyError("Multiple requests for offer received from single client");
       return;
diff --git a/host/frontend/webrtc/lib/client_handler.h b/host/frontend/webrtc/lib/client_handler.h
index ea58552..2617e64 100644
--- a/host/frontend/webrtc/lib/client_handler.h
+++ b/host/frontend/webrtc/lib/client_handler.h
@@ -42,19 +42,24 @@
 
 class ClientVideoTrackInterface;
 class ClientVideoTrackImpl;
+class PeerConnectionBuilder;
 
 class ClientHandler : public webrtc::PeerConnectionObserver,
                       public std::enable_shared_from_this<ClientHandler> {
  public:
+  // Checks if the message contains an "ice_servers" array field and parses it
+  // into a vector of webrtc ICE servers. Returns an empty vector if the field
+  // isn't present.
+  static std::vector<webrtc::PeerConnectionInterface::IceServer>
+  ParseIceServersMessage(const Json::Value& message);
+
   static std::shared_ptr<ClientHandler> Create(
       int client_id, std::shared_ptr<ConnectionObserver> observer,
+      PeerConnectionBuilder& connection_builder,
       std::function<void(const Json::Value&)> send_client_cb,
       std::function<void(bool)> on_connection_changed_cb);
   ~ClientHandler() override;
 
-  bool SetPeerConnection(
-      rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection);
-
   bool AddDisplay(rtc::scoped_refptr<webrtc::VideoTrackInterface> track,
                   const std::string& label);
 
@@ -113,6 +118,7 @@
       kFailed,
   };
   ClientHandler(int client_id, std::shared_ptr<ConnectionObserver> observer,
+                PeerConnectionBuilder& connection_builder,
                 std::function<void(const Json::Value&)> send_client_cb,
                 std::function<void(bool)> on_connection_changed_cb);
 
@@ -121,12 +127,14 @@
 
   void LogAndReplyError(const std::string& error_msg) const;
   void AddPendingIceCandidates();
+  bool BuildPeerConnection(const Json::Value& message);
 
   int client_id_;
   State state_ = State::kNew;
   std::shared_ptr<ConnectionObserver> observer_;
   std::function<void(const Json::Value&)> send_to_client_;
   std::function<void(bool)> on_connection_changed_cb_;
+  PeerConnectionBuilder& connection_builder_;
   rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
   std::vector<rtc::scoped_refptr<webrtc::DataChannelInterface>> data_channels_;
   std::unique_ptr<InputChannelHandler> input_handler_;
@@ -138,6 +146,12 @@
   bool remote_description_added_ = false;
   std::vector<std::unique_ptr<webrtc::IceCandidateInterface>>
       pending_ice_candidates_;
+  std::vector<
+      std::pair<rtc::scoped_refptr<webrtc::VideoTrackInterface>, std::string>>
+      displays_;
+  std::vector<
+      std::pair<rtc::scoped_refptr<webrtc::AudioTrackInterface>, std::string>>
+      audio_streams_;
 };
 
 class ClientVideoTrackInterface {
@@ -148,5 +162,14 @@
       const rtc::VideoSinkWants& wants) = 0;
 };
 
+class PeerConnectionBuilder {
+ public:
+  virtual ~PeerConnectionBuilder() = default;
+  virtual rtc::scoped_refptr<webrtc::PeerConnectionInterface> Build(
+      webrtc::PeerConnectionObserver* observer,
+      const std::vector<webrtc::PeerConnectionInterface::IceServer>&
+          per_connection_servers) = 0;
+};
+
 }  // namespace webrtc_streaming
 }  // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/streamer.cpp b/host/frontend/webrtc/lib/streamer.cpp
index 088b25b..9dd8fea 100644
--- a/host/frontend/webrtc/lib/streamer.cpp
+++ b/host/frontend/webrtc/lib/streamer.cpp
@@ -136,6 +136,7 @@
 
 
 class Streamer::Impl : public ServerConnectionObserver,
+                       public PeerConnectionBuilder,
                        public std::enable_shared_from_this<ServerConnectionObserver> {
  public:
   std::shared_ptr<ClientHandler> CreateClientHandler(int client_id);
@@ -155,6 +156,12 @@
   void HandleConfigMessage(const Json::Value& msg);
   void HandleClientMessage(const Json::Value& server_message);
 
+  // PeerConnectionBuilder
+  rtc::scoped_refptr<webrtc::PeerConnectionInterface> Build(
+      webrtc::PeerConnectionObserver* observer,
+      const std::vector<webrtc::PeerConnectionInterface::IceServer>&
+          per_connection_servers) override;
+
   // All accesses to these variables happen from the signal_thread, so there is
   // no need for extra synchronization mechanisms (mutex)
   StreamerConfig config_;
@@ -474,39 +481,8 @@
 void Streamer::Impl::HandleConfigMessage(const Json::Value& server_message) {
   CHECK(signal_thread_->IsCurrent())
       << __FUNCTION__ << " called from the wrong thread";
-  if (server_message.isMember("ice_servers") &&
-      server_message["ice_servers"].isArray()) {
-    auto servers = server_message["ice_servers"];
-    operator_config_.servers.clear();
-    for (int server_idx = 0; server_idx < servers.size(); server_idx++) {
-      auto server = servers[server_idx];
-      webrtc::PeerConnectionInterface::IceServer ice_server;
-      if (!server.isMember("urls") || !server["urls"].isArray()) {
-        // The urls field is required
-        LOG(WARNING)
-            << "Invalid ICE server specification obtained from server: "
-            << server.toStyledString();
-        continue;
-      }
-      auto urls = server["urls"];
-      for (int url_idx = 0; url_idx < urls.size(); url_idx++) {
-        auto url = urls[url_idx];
-        if (!url.isString()) {
-          LOG(WARNING) << "Non string 'urls' field in ice server: "
-                       << url.toStyledString();
-          continue;
-        }
-        ice_server.urls.push_back(url.asString());
-        if (server.isMember("credential") && server["credential"].isString()) {
-          ice_server.password = server["credential"].asString();
-        }
-        if (server.isMember("username") && server["username"].isString()) {
-          ice_server.username = server["username"].asString();
-        }
-        operator_config_.servers.push_back(ice_server);
-      }
-    }
-  }
+  operator_config_.servers =
+      ClientHandler::ParseIceServersMessage(server_message);
 }
 
 void Streamer::Impl::HandleClientMessage(const Json::Value& server_message) {
@@ -598,7 +574,7 @@
   auto observer = connection_observer_factory_->CreateObserver();
 
   auto client_handler = ClientHandler::Create(
-      client_id, observer,
+      client_id, observer, *this,
       [this, client_id](const Json::Value& msg) {
         SendMessageToClient(client_id, msg);
       },
@@ -610,28 +586,6 @@
         }
       });
 
-  webrtc::PeerConnectionInterface::RTCConfiguration config;
-  config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
-  config.enable_dtls_srtp = true;
-  config.servers.insert(config.servers.end(), operator_config_.servers.begin(),
-                        operator_config_.servers.end());
-  webrtc::PeerConnectionDependencies dependencies(client_handler.get());
-  // PortRangeSocketFactory's super class' constructor needs to be called on the
-  // network thread or have it as a parameter
-  dependencies.packet_socket_factory.reset(new PortRangeSocketFactory(
-      network_thread_.get(), config_.udp_port_range, config_.tcp_port_range));
-  auto peer_connection = peer_connection_factory_->CreatePeerConnection(
-      config, std::move(dependencies));
-
-  if (!peer_connection) {
-    LOG(ERROR) << "Failed to create peer connection";
-    return nullptr;
-  }
-
-  if (!client_handler->SetPeerConnection(std::move(peer_connection))) {
-    return nullptr;
-  }
-
   for (auto& entry : displays_) {
     auto& label = entry.first;
     auto& video_source = entry.second.source;
@@ -652,6 +606,32 @@
   return client_handler;
 }
 
+rtc::scoped_refptr<webrtc::PeerConnectionInterface> Streamer::Impl::Build(
+    webrtc::PeerConnectionObserver* observer,
+    const std::vector<webrtc::PeerConnectionInterface::IceServer>&
+        per_connection_servers) {
+  webrtc::PeerConnectionInterface::RTCConfiguration config;
+  config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
+  config.enable_dtls_srtp = true;
+  config.servers.insert(config.servers.end(), operator_config_.servers.begin(),
+                        operator_config_.servers.end());
+  config.servers.insert(config.servers.end(), per_connection_servers.begin(),
+                        per_connection_servers.end());
+  webrtc::PeerConnectionDependencies dependencies(observer);
+  // PortRangeSocketFactory's super class' constructor needs to be called on the
+  // network thread or have it as a parameter
+  dependencies.packet_socket_factory.reset(new PortRangeSocketFactory(
+      network_thread_.get(), config_.udp_port_range, config_.tcp_port_range));
+  auto peer_connection = peer_connection_factory_->CreatePeerConnection(
+      config, std::move(dependencies));
+
+  if (!peer_connection) {
+    LOG(ERROR) << "Failed to create peer connection";
+    return nullptr;
+  }
+  return peer_connection;
+}
+
 void Streamer::Impl::SendMessageToClient(int client_id,
                                          const Json::Value& msg) {
   LOG(VERBOSE) << "Sending to client: " << msg.toStyledString();
diff --git a/host/libs/config/bootconfig_args.cpp b/host/libs/config/bootconfig_args.cpp
index 00673f6..d8ef114 100644
--- a/host/libs/config/bootconfig_args.cpp
+++ b/host/libs/config/bootconfig_args.cpp
@@ -154,8 +154,8 @@
                                      instance.modem_simulator_ports()));
   }
 
-  bootconfig_args.push_back(concat("androidboot.fstab_suffix=",
-                                   config.userdata_format()));
+  std::string fstab_suffix = fmt::format("cf.{}.{}", config.userdata_format(), "cts");
+  bootconfig_args.push_back(concat("androidboot.fstab_suffix=", fstab_suffix));
 
   bootconfig_args.push_back(
       concat("androidboot.wifi_mac_prefix=", instance.wifi_mac_prefix()));
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index 68609a3..1adb55b 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -77,9 +77,11 @@
     return {
       "androidboot.cpuvulkan.version=0",
       "androidboot.hardware.gralloc=minigbm",
-      "androidboot.hardware.hwcomposer=drm",
+      "androidboot.hardware.hwcomposer=ranchu",
+      "androidboot.hardware.hwcomposer.mode=client",
       "androidboot.hardware.egl=mesa",
-    };
+      // No "hardware" Vulkan support, yet
+      "androidboot.opengles.version=196608"};  // OpenGL ES 3.0
   }
   if (config.gpu_mode() == kGpuModeGfxStream) {
     std::string gles_impl = config.enable_gpu_angle() ? "angle" : "emulation";
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index adedf04..4efb053 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -131,21 +131,23 @@
     // with properities lead to non-deterministic behavior while loading the
     // HALs.
     return {
-        "androidboot.cpuvulkan.version=" + std::to_string(VK_API_VERSION_1_1),
+        "androidboot.cpuvulkan.version=" + std::to_string(VK_API_VERSION_1_2),
         "androidboot.hardware.gralloc=minigbm",
         "androidboot.hardware.hwcomposer=" + config.hwcomposer(),
         "androidboot.hardware.egl=angle",
         "androidboot.hardware.vulkan=pastel",
-    };
+        "androidboot.opengles.version=196609"};  // OpenGL ES 3.1
   }
 
   if (config.gpu_mode() == kGpuModeDrmVirgl) {
     return {
       "androidboot.cpuvulkan.version=0",
       "androidboot.hardware.gralloc=minigbm",
-      "androidboot.hardware.hwcomposer=drm",
+      "androidboot.hardware.hwcomposer=ranchu",
+      "androidboot.hardware.hwcomposer.mode=client",
       "androidboot.hardware.egl=mesa",
-    };
+      // No "hardware" Vulkan support, yet
+      "androidboot.opengles.version=196608"};  // OpenGL ES 3.0
   }
 
   return {};
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index b59b39c..6e1b95c 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -23,7 +23,9 @@
 include build/make/target/board/BoardConfigMainlineCommon.mk
 TARGET_NO_BOOTLOADER := $(__TARGET_NO_BOOTLOADER)
 
+ifndef TARGET_BOOTLOADER_BOARD_NAME
 TARGET_BOOTLOADER_BOARD_NAME := cutf
+endif
 
 BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE := $(TARGET_RO_FILE_SYSTEM_TYPE)
 
@@ -204,7 +206,7 @@
 
 TARGET_RECOVERY_PIXEL_FORMAT := ABGR_8888
 TARGET_RECOVERY_UI_LIB := librecovery_ui_cuttlefish
-TARGET_RECOVERY_FSTAB ?= device/google/cuttlefish/shared/config/fstab.f2fs
+TARGET_RECOVERY_FSTAB_GENRULE := gen_fstab_cf_f2fs_cts
 
 BOARD_SUPER_PARTITION_SIZE := 7516192768  # 7GiB
 BOARD_SUPER_PARTITION_GROUPS := google_system_dynamic_partitions google_vendor_dynamic_partitions
diff --git a/shared/auto/device_vendor.mk b/shared/auto/device_vendor.mk
index 277eaa3..a03044e 100644
--- a/shared/auto/device_vendor.mk
+++ b/shared/auto/device_vendor.mk
@@ -115,6 +115,7 @@
 ENABLE_MOCK_EVSHAL ?= true
 ENABLE_CAREVSSERVICE_SAMPLE ?= true
 ENABLE_SAMPLE_EVS_APP ?= true
+ENABLE_CARTELEMETRY_SERVICE ?= true
 
 ifeq ($(ENABLE_MOCK_EVSHAL), true)
 CUSTOMIZE_EVS_SERVICE_PARAMETER := true
@@ -130,6 +131,9 @@
 PRODUCT_COPY_FILES += \
     device/google/cuttlefish/shared/auto/evs/evs_app_config.json:$(TARGET_COPY_OUT_SYSTEM)/etc/automotive/evs/config_override.json
 BOARD_SEPOLICY_DIRS += packages/services/Car/cpp/evs/apps/sepolicy/private
+ifeq ($(ENABLE_CARTELEMETRY_SERVICE), true)
+BOARD_SEPOLICY_DIRS += packages/services/Car/cpp/evs/apps/sepolicy/cartelemetry
+endif
 endif
 
 BOARD_IS_AUTOMOTIVE := true
diff --git a/shared/auto/overlay/frameworks/base/core/res/res/values/config.xml b/shared/auto/overlay/frameworks/base/core/res/res/values/config.xml
index bf07d05..7eaea59 100644
--- a/shared/auto/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/shared/auto/overlay/frameworks/base/core/res/res/values/config.xml
@@ -34,4 +34,11 @@
       Handle volume keys directly in CarAudioService without passing them to the foreground app
     -->
     <bool name="config_handleVolumeKeysInWindowManager">true</bool>
+
+    <!-- Controls if local secondary displays should be private or not. Value specified in the array
+         represents physical port address of each display and display in this list will be marked
+         as private. {@see android.view.Display#FLAG_PRIVATE} -->
+    <integer-array translatable="false" name="config_localPrivateDisplayPorts">
+        <item>1</item> <!-- ClusterDisplay -->
+    </integer-array>
 </resources>
diff --git a/shared/auto/preinstalled-packages-product-car-cuttlefish.xml b/shared/auto/preinstalled-packages-product-car-cuttlefish.xml
index 9f22200..0aca16b 100644
--- a/shared/auto/preinstalled-packages-product-car-cuttlefish.xml
+++ b/shared/auto/preinstalled-packages-product-car-cuttlefish.xml
@@ -94,10 +94,6 @@
     <install-in-user-type package="com.android.cameraextensions">
         <install-in user-type="SYSTEM" />
         <install-in user-type="FULL" />
-   </install-in-user-type>
-   <install-in-user-type package="com.android.car.messenger">
-	<install-in user-type="FULL" />
-        <install-in user-type="SYSTEM" />
     </install-in-user-type>
     <install-in-user-type package="com.android.apps.tag">
         <install-in user-type="FULL" />
@@ -124,9 +120,6 @@
     <install-in-user-type package="com.android.car.datacenter">
         <install-in user-type="FULL" />
     </install-in-user-type>
-    <install-in-user-type package="com.android.car.dialer">
-        <install-in user-type="FULL" />
-    </install-in-user-type>
     <install-in-user-type package="com.android.car.goldilocks">
         <install-in user-type="FULL" />
     </install-in-user-type>
diff --git a/shared/config/Android.bp b/shared/config/Android.bp
index f2d94e4..40c3f93 100644
--- a/shared/config/Android.bp
+++ b/shared/config/Android.bp
@@ -89,3 +89,33 @@
     name: "[email protected]",
     srcs: ["[email protected]"]
 }
+
+genrule {
+    name: "gen_fstab_cf_f2fs_cts",
+    srcs: ["fstab.in"],
+    out: ["fstab.cf.f2fs.cts"],
+    tool_files: [ "sed.f2fs" ],
+    cmd: "sed -f $(location sed.f2fs) $(in) > $(out)",
+}
+
+genrule {
+    name: "gen_fstab_cf_ext4_cts",
+    srcs: ["fstab.in"],
+    out: ["fstab.cf.ext4.cts"],
+    tool_files: [ "sed.ext4" ],
+    cmd: "sed -f $(location sed.ext4) $(in) > $(out)",
+}
+
+prebuilt_etc {
+    name: "fstab.cf.f2fs.cts",
+    src: ":gen_fstab_cf_f2fs_cts",
+    vendor: true,
+    vendor_ramdisk_available: true,
+}
+
+prebuilt_etc {
+    name: "fstab.cf.ext4.cts",
+    src: ":gen_fstab_cf_ext4_cts",
+    vendor: true,
+    vendor_ramdisk_available: true,
+}
diff --git a/shared/config/fstab.f2fs b/shared/config/fstab.f2fs
deleted file mode 100644
index 41162ed..0000000
--- a/shared/config/fstab.f2fs
+++ /dev/null
@@ -1,30 +0,0 @@
-# Non-dynamic, boot critical partitions
-/dev/block/by-name/boot /boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=boot
-/dev/block/by-name/init_boot /init_boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=init_boot
-/dev/block/by-name/vendor_boot /vendor_boot emmc defaults recoveryonly,slotselect
-system /system erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta_system,avb_keys=/avb
-system /system ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta_system,avb_keys=/avb
-# Add all non-dynamic partitions except system, after this comment
-/dev/block/by-name/userdata /data f2fs nodev,noatime,nosuid,inlinecrypt,reserve_root=32768 latemount,wait,check,quota,formattable,fileencryption=aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized,fscompress,keydirectory=/metadata/vold/metadata_encryption,checkpoint=fs
-/dev/block/by-name/metadata /metadata ext4 nodev,noatime,nosuid,errors=panic wait,formattable,first_stage_mount,check
-/dev/block/by-name/misc /misc emmc defaults defaults
-# Add all dynamic partitions except system, after this comment
-odm /odm erofs ro wait,logical,first_stage_mount,slotselect,avb
-odm /odm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
-product /product erofs ro wait,logical,first_stage_mount,slotselect,avb
-product /product ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
-system_ext /system_ext erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta_system
-system_ext /system_ext ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta_system
-vendor /vendor erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta
-vendor /vendor ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta
-vendor_dlkm /vendor_dlkm erofs ro wait,logical,first_stage_mount,slotselect,avb
-vendor_dlkm /vendor_dlkm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
-odm_dlkm /odm_dlkm erofs ro wait,logical,first_stage_mount,slotselect,avb
-odm_dlkm /odm_dlkm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
-system_dlkm /system_dlkm erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta
-system_dlkm /system_dlkm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta
-# ZRAM, SD-Card and virtiofs shares
-/dev/block/zram0 none swap defaults zramsize=75%
-/dev/block/vdc1 /sdcard vfat defaults recoveryonly
-/devices/*/block/vdc auto auto defaults voldmanaged=sdcard1:auto,encryptable=userdata
-shared /mnt/vendor/shared virtiofs nosuid,nodev,noatime nofail
diff --git a/shared/config/fstab.ext4 b/shared/config/fstab.in
similarity index 86%
rename from shared/config/fstab.ext4
rename to shared/config/fstab.in
index 4d3fe9b..d9b3c13 100644
--- a/shared/config/fstab.ext4
+++ b/shared/config/fstab.in
@@ -5,8 +5,8 @@
 system /system erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta_system,avb_keys=/avb
 system /system ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta_system,avb_keys=/avb
 # Add all non-dynamic partitions except system, after this comment
-/dev/block/by-name/userdata /data ext4 nodev,noatime,nosuid,errors=panic latemount,wait,check,quota,formattable,fileencryption=aes-256-xts:aes-256-cts,keydirectory=/metadata/vold/metadata_encryption,checkpoint=block
-/dev/block/by-name/metadata /metadata ext4 nodev,noatime,nosuid,errors=panic wait,formattable,first_stage_mount,check
+/dev/block/by-name/userdata /data @userdata_fs_type@ nodev,noatime,nosuid,@userdata_mount_flags@ latemount,wait,check,quota,formattable,keydirectory=/metadata/vold/metadata_encryption,@userdata_fsmgr_flags@
+/dev/block/by-name/metadata /metadata f2fs nodev,noatime,nosuid wait,check,formattable,first_stage_mount
 /dev/block/by-name/misc /misc emmc defaults defaults
 # Add all dynamic partitions except system, after this comment
 odm /odm erofs ro wait,logical,first_stage_mount,slotselect,avb
diff --git a/shared/config/init.vendor.rc b/shared/config/init.vendor.rc
index 8861ca3..83a52c4 100644
--- a/shared/config/init.vendor.rc
+++ b/shared/config/init.vendor.rc
@@ -6,6 +6,7 @@
     setprop debug.sf.vsync_reactor_ignore_present_fences true
     setprop ro.hardware.gralloc ${ro.boot.hardware.gralloc}
     setprop ro.hardware.hwcomposer ${ro.boot.hardware.hwcomposer}
+    setprop ro.vendor.hwcomposer.display_finder_mode ${ro.boot.hardware.hwcomposer.display_finder_mode}
     setprop ro.vendor.hwcomposer.mode ${ro.boot.hardware.hwcomposer.mode}
     setprop ro.hardware.vulkan ${ro.boot.hardware.vulkan}
     setprop ro.cpuvulkan.version ${ro.boot.cpuvulkan.version}
@@ -54,7 +55,7 @@
     # set RLIMIT_MEMLOCK to 64MB
     setrlimit 8 67108864 67108864
 
-on post-fs-data
+on post-fs-data && property:ro.vendor.disable_rename_eth0=
     # works around framework netiface enumeration issue
     # TODO(b/202731768): Add this `start rename_eth0` command to the init.rc for rename_netiface
     start rename_eth0
diff --git a/shared/config/manifest.xml b/shared/config/manifest.xml
index 126557f..893dbfa 100644
--- a/shared/config/manifest.xml
+++ b/shared/config/manifest.xml
@@ -18,15 +18,6 @@
 -->
 <manifest version="1.0" type="device" target-level="7">
     <hal format="hidl">
-        <name>android.hardware.audio.effect</name>
-        <transport>hwbinder</transport>
-        <version>7.0</version>
-        <interface>
-            <name>IEffectsFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl">
         <name>android.hardware.authsecret</name>
         <transport>hwbinder</transport>
         <version>1.0</version>
diff --git a/shared/config/sed.ext4 b/shared/config/sed.ext4
new file mode 100644
index 0000000..af302aa
--- /dev/null
+++ b/shared/config/sed.ext4
@@ -0,0 +1,3 @@
+s/@userdata_fs_type@/ext4/
+s/@userdata_mount_flags@/errors=panic/
+s/@userdata_fsmgr_flags@/fileencryption=aes-256-xts:aes-256-cts,checkpoint=block/
diff --git a/shared/config/sed.f2fs b/shared/config/sed.f2fs
new file mode 100644
index 0000000..eab04e8
--- /dev/null
+++ b/shared/config/sed.f2fs
@@ -0,0 +1,3 @@
+s/@userdata_fs_type@/f2fs/
+s/@userdata_mount_flags@/inlinecrypt,reserve_root=32768/
+s/@userdata_fsmgr_flags@/fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized,fscompress,checkpoint=fs/
diff --git a/shared/device.mk b/shared/device.mk
index 04dedbd..b67eeff 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -66,7 +66,6 @@
 AB_OTA_UPDATER := true
 AB_OTA_PARTITIONS += \
     boot \
-    init_boot \
     odm \
     odm_dlkm \
     product \
@@ -79,6 +78,11 @@
     vendor_boot \
     vendor_dlkm \
 
+TARGET_USES_INITBOOT ?= true
+ifeq ($(TARGET_USES_INITBOOT),true)
+AB_OTA_PARTITIONS += init_boot
+endif
+
 # Enable Virtual A/B
 $(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/android_t_baseline.mk)
 PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD := gz
@@ -339,13 +343,11 @@
     device/google/cuttlefish/shared/config/input/Crosvm_Virtio_Multitouch_Touchscreen_3.idc:$(TARGET_COPY_OUT_VENDOR)/usr/idc/Crosvm_Virtio_Multitouch_Touchscreen_3.idc
 endif
 
-PRODUCT_COPY_FILES += \
-    device/google/cuttlefish/shared/config/fstab.f2fs:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.f2fs \
-    device/google/cuttlefish/shared/config/fstab.f2fs:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.f2fs \
-    device/google/cuttlefish/shared/config/fstab.f2fs:$(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/fstab.f2fs \
-    device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.ext4 \
-    device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.ext4 \
-    device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/fstab.ext4
+PRODUCT_PACKAGES += \
+    fstab.cf.f2fs.cts \
+    fstab.cf.f2fs.cts.vendor_ramdisk \
+    fstab.cf.ext4.cts \
+    fstab.cf.ext4.cts.vendor_ramdisk \
 
 ifeq ($(TARGET_VULKAN_SUPPORT),true)
 ifneq ($(LOCAL_PREFER_VENDOR_APEX),true)
@@ -403,7 +405,6 @@
 ifeq ($(TARGET_ENABLE_DRMHWCOMPOSER),true)
 DEVICE_MANIFEST_FILE += \
     device/google/cuttlefish/shared/config/[email protected]
-
 PRODUCT_PACKAGES += \
     [email protected] \
     hwcomposer.drm
@@ -468,6 +469,8 @@
     android.hardware.audio.service \
     [email protected] \
     [email protected]
+DEVICE_MANIFEST_FILE += \
+    device/google/cuttlefish/guest/hals/audio/effects/manifest.xml
 endif
 
 ifndef LOCAL_AUDIO_PRODUCT_COPY_FILES
@@ -767,6 +770,12 @@
 PRODUCT_COPY_FILES += \
     device/google/cuttlefish/shared/config/wpa_supplicant.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/wpa_supplicant.rc
 
+# VirtWifi interface configuration
+ifeq ($(DEVICE_VIRTWIFI_PORT),)
+    DEVICE_VIRTWIFI_PORT := eth2
+endif
+PRODUCT_VENDOR_PROPERTIES += ro.vendor.virtwifi.port=${DEVICE_VIRTWIFI_PORT}
+
 # WLAN driver configuration files
 ifndef LOCAL_WPA_SUPPLICANT_OVERLAY
 LOCAL_WPA_SUPPLICANT_OVERLAY := $(LOCAL_PATH)/config/wpa_supplicant_overlay.conf
@@ -825,19 +834,11 @@
 
 # Enable GPU-intensive background blur support on Cuttlefish when requested by apps
 PRODUCT_VENDOR_PROPERTIES += \
-    ro.surface_flinger.supports_background_blur 1
+    ro.surface_flinger.supports_background_blur=1
 
-# Set support one-handed mode
-PRODUCT_PRODUCT_PROPERTIES += \
-    ro.support_one_handed_mode=true
-
-# Set one_handed_mode screen translate offset percentage
-PRODUCT_PRODUCT_PROPERTIES += \
-    persist.debug.one_handed_offset_percentage=50
-
-# Set one_handed_mode translate animation duration milliseconds
-PRODUCT_PRODUCT_PROPERTIES += \
-    persist.debug.one_handed_translate_animation_duration=300
+# Disable GPU-intensive background blur for widget picker
+PRODUCT_SYSTEM_PROPERTIES += \
+    ro.launcher.depth.widget=0
 
 # Vendor Dlkm Locader
 PRODUCT_PACKAGES += \
diff --git a/shared/foldable/device_state_configuration.xml b/shared/foldable/device_state_configuration.xml
index 9618b11..877a583 100644
--- a/shared/foldable/device_state_configuration.xml
+++ b/shared/foldable/device_state_configuration.xml
@@ -34,4 +34,11 @@
       </lid-switch>
     </conditions>
   </device-state>
+  <device-state>
+    <identifier>3</identifier>
+    <name>REAR_DISPLAY_MODE</name>
+    <flags>
+      <flag>FLAG_EMULATED_ONLY</flag>
+    </flags>
+  </device-state>
 </device-state-config>
diff --git a/shared/foldable/display_layout_configuration.xml b/shared/foldable/display_layout_configuration.xml
index 54b76b1..2c50e76 100644
--- a/shared/foldable/display_layout_configuration.xml
+++ b/shared/foldable/display_layout_configuration.xml
@@ -38,4 +38,17 @@
       <address>4619827551948147201</address>
     </display>
   </layout>
+
+  <layout>
+  <!-- REAR_DISPLAY_MODE: display0 disabled, display1 enabled -->
+    <state>3</state>
+
+    <display enabled="false">
+      <address>4619827259835644672</address>
+    </display>
+
+    <display enabled="true" defaultDisplay="true">
+      <address>4619827551948147201</address>
+    </display>
+  </layout>
 </layouts>
diff --git a/shared/foldable/overlay/frameworks/base/core/res/res/values/config.xml b/shared/foldable/overlay/frameworks/base/core/res/res/values/config.xml
index b0b42ab..8186c97 100644
--- a/shared/foldable/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/shared/foldable/overlay/frameworks/base/core/res/res/values/config.xml
@@ -24,6 +24,7 @@
       <item>0:1</item> <!-- CLOSED : STATE_FLAT -->
       <item>1:2</item> <!-- HALF_OPENED : STATE_HALF_OPENED -->
       <item>2:3</item> <!-- OPENED : STATE_FLIPPED -->
+      <item>3:1</item> <!-- REAR_DISPLAY: STATE_FLAT -->
   </string-array>
   <!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
        display fold controller. -->
@@ -41,4 +42,13 @@
   <bool name="config_supportsSplitScreenMultiWindow">true</bool>
   <!-- Radius of the software rounded corners. -->
   <dimen name="rounded_corner_radius">34px</dimen>
+
+  <!-- List of the labels of requestable device state config values -->
+  <string-array name="config_deviceStatesAvailableForAppRequests">
+    <item>config_deviceStateRearDisplay</item>
+  </string-array>
+
+  <!-- Device state that corresponds to rear display mode, feature provided
+         through Jetpack WindowManager -->
+  <integer name="config_deviceStateRearDisplay">3</integer>
 </resources>
diff --git a/shared/overlays/core/res/values/config.xml b/shared/overlays/core/res/values/config.xml
new file mode 100644
index 0000000..bfcec6c
--- /dev/null
+++ b/shared/overlays/core/res/values/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2023, 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show the "Adaptive Brightness" toggle. -->
+    <bool name="config_automatic_brightness_available">true</bool>
+</resources>
\ No newline at end of file
diff --git a/shared/permissions/cuttlefish_excluded_hardware.xml b/shared/permissions/cuttlefish_excluded_hardware.xml
index c3d03d5..3fba9f6 100644
--- a/shared/permissions/cuttlefish_excluded_hardware.xml
+++ b/shared/permissions/cuttlefish_excluded_hardware.xml
@@ -15,5 +15,4 @@
 -->
 <permissions>
     <unavailable-feature name="android.software.print" />
-    <unavailable-feature name="android.software.voice_recognizers" />
 </permissions>
diff --git a/shared/sepolicy/vendor/e2fs.te b/shared/sepolicy/vendor/e2fs.te
new file mode 100644
index 0000000..54af273
--- /dev/null
+++ b/shared/sepolicy/vendor/e2fs.te
@@ -0,0 +1,3 @@
+# make_f2fs wants to read /sys/devices/*/block/*
+allow e2fs sysfs_devices_block:dir search;
+allow e2fs sysfs_devices_block:file r_file_perms;
diff --git a/shared/sepolicy/vendor/genfs_contexts b/shared/sepolicy/vendor/genfs_contexts
index 542db04..cbbd88f 100644
--- a/shared/sepolicy/vendor/genfs_contexts
+++ b/shared/sepolicy/vendor/genfs_contexts
@@ -65,7 +65,7 @@
 
 # qemu (arm64)
 cf_pci_block_device(/devices/platform/4010000000.pcie/pci0000:00, 0xa, 9)
-cf_pci_gpu_device(/devices/platform/4010000000.pcie/pci0000:00, 0x2)
+cf_pci_gpu_device(/devices/platform/4010000000.pcie/pci0000:00, 0x1)
 ## find /sys/devices/platform/* -type d -name 'rtc[0-9]' | sed 's,/rtc[0-9],,'
 genfscon sysfs /devices/platform/9010000.pl031/rtc u:object_r:sysfs_rtc:s0
 ## find /sys/devices/platform/* -type d -name 'wakeup[0-9]'
@@ -73,7 +73,7 @@
 
 # qemu (arm)
 cf_pci_block_device(/devices/platform/3f000000.pcie/pci0000:00, 0xa, 9)
-cf_pci_gpu_device(/devices/platform/3f000000.pcie/pci0000:00, 0x2)
+cf_pci_gpu_device(/devices/platform/3f000000.pcie/pci0000:00, 0x1)
 genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0
 genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup3 u:object_r:sysfs_wakeup:s0
 
diff --git a/shared/sepolicy/vendor/property_contexts b/shared/sepolicy/vendor/property_contexts
index 9b98ed1..235b35a 100644
--- a/shared/sepolicy/vendor/property_contexts
+++ b/shared/sepolicy/vendor/property_contexts
@@ -14,6 +14,7 @@
 ro.vendor.boot_security_patch u:object_r:vendor_boot_security_patch_level_prop:s0
 vendor.bt.rootcanal_mac_address  u:object_r:vendor_bt_rootcanal_prop:s0
 vendor.bt.rootcanal_test_console  u:object_r:vendor_bt_rootcanal_prop:s0
+ro.vendor.hwcomposer.display_finder_mode  u:object_r:vendor_hwcomposer_prop:s0 exact string
 ro.vendor.hwcomposer.mode  u:object_r:vendor_hwcomposer_prop:s0 exact string
 ro.vendor.hwcomposer.pmem  u:object_r:vendor_hwcomposer_prop:s0 exact string
 vendor.wlan.firmware.version   u:object_r:vendor_wlan_versions_prop:s0 exact string
diff --git a/vsoc_x86_64/phone/OWNERS b/vsoc_x86_64/phone/OWNERS
new file mode 100644
index 0000000..e0d597b
--- /dev/null
+++ b/vsoc_x86_64/phone/OWNERS
@@ -0,0 +1 @@
+per-file *hsum*.mk = file:platform/frameworks/base:/MULTIUSER_OWNERS
diff --git a/vsoc_x86_64_only/phone/aosp_cf_hsum.mk b/vsoc_x86_64_only/phone/aosp_cf_hsum.mk
new file mode 100644
index 0000000..3abca8c
--- /dev/null
+++ b/vsoc_x86_64_only/phone/aosp_cf_hsum.mk
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+# Inherit mostly from aosp_cf_x86_64_phone
+$(call inherit-product, device/google/cuttlefish/vsoc_x86_64_only/phone/aosp_cf.mk)
+PRODUCT_NAME := aosp_cf_x86_64_only_phone_hsum
+PRODUCT_MODEL := Cuttlefish x86_64 phone 64-bit only Headless System User Mode
+
+# Set Headless System User Mode
+PRODUCT_SYSTEM_DEFAULT_PROPERTIES = \
+    ro.fw.mu.headless_system_user=true
+
+# TODO(b/204071542): add package allow-list; something like
+# PRODUCT_COPY_FILES += \
+#    device/google/cuttlefish/SOME_PATH/preinstalled-packages.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/sysconfig/preinstalled-packages-cf_phone.xml