Merge "Revert "Add stable aidl memtrack HAL to product packages""
diff --git a/Android.bp b/Android.bp
index 5de274d..f8600e1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,14 +15,8 @@
 
 cc_library_headers {
     name: "cuttlefish_common_headers",
-    vendor: true,
-    export_include_dirs: ["."],
-    host_supported: true,
-}
-
-cc_library_headers {
-    name: "cuttlefish_common_headers_product",
-    product_specific: true,
+    vendor_available: true,
+    product_available: true,
     export_include_dirs: ["."],
     host_supported: true,
 }
@@ -45,37 +39,29 @@
         },
     },
     cflags: ["-Werror", "-Wall", "-D_FILE_OFFSET_BITS=64"],
-    vendor: true,
 }
 
 cc_defaults {
     name: "cuttlefish_guest_product_only",
     product_specific: true,
-    gnu_extensions: false,
-    header_libs: [
-        "cuttlefish_common_headers_product",
-    ],
-    target: {
-        host: {
-            host_ldlibs: ["-lrt"],
-            cflags: ["-DCUTTLEFISH_HOST"],
-            compile_multilib: "64",
-        },
-        // We don't need Darwin host-side builds
-        darwin: {
-            enabled: false,
-        },
-    },
-    cflags: ["-Werror", "-Wall"],
+    defaults: ["cuttlefish_base"],
+}
+
+cc_defaults {
+    name: "cuttlefish_guest_system_ext_only",
+    system_ext_specific: true,
+    defaults: ["cuttlefish_base"],
 }
 
 cc_defaults {
     name: "cuttlefish_guest_only",
+    vendor: true,
     defaults: ["cuttlefish_base"],
 }
 
 cc_defaults {
     name: "cuttlefish_host_only",
+    vendor: true,
     device_supported: false,
     host_supported: true,
     defaults: ["cuttlefish_base"],
@@ -83,6 +69,7 @@
 
 cc_defaults {
     name: "cuttlefish_host_and_guest",
+    vendor: true,
     host_supported: true,
     defaults: ["cuttlefish_base"],
 }
diff --git a/guest/hals/hwcomposer/base_composer.cpp b/guest/hals/hwcomposer/base_composer.cpp
index 1e97bd2..ed42143 100644
--- a/guest/hals/hwcomposer/base_composer.cpp
+++ b/guest/hals/hwcomposer/base_composer.cpp
@@ -30,9 +30,6 @@
 void BaseComposer::Dump(char* buff __unused, int buff_len __unused) {}
 
 int BaseComposer::PostFrameBufferTarget(buffer_handle_t buffer_handle) {
-  auto buffer_id = screen_view_->NextBuffer();
-  void* frame_buffer = screen_view_->GetBuffer(buffer_id);
-
   auto imported_buffer_opt = gralloc_.Import(buffer_handle);
   if (!imported_buffer_opt) {
     ALOGE("Failed to Import() framebuffer for post.");
@@ -52,11 +49,15 @@
     ALOGE("Failed to get buffer from view for post.");
     return -1;
   }
+  void* gralloc_buffer = *buffer_opt;
 
-  void* buffer = *buffer_opt;
-  memcpy(frame_buffer, buffer, screen_view_buffer_size_);
+  // TODO(b/173523487): remove hard coded display number.
+  const std::uint32_t display_number = 0;
 
-  screen_view_->Broadcast(buffer_id);
+  std::uint8_t* frame_buffer = screen_view_->AcquireNextBuffer(display_number);
+  std::size_t frame_buffer_size = ScreenView::ScreenSizeBytes(display_number);
+  memcpy(frame_buffer, gralloc_buffer, frame_buffer_size);
+  screen_view_->PresentAcquiredBuffer(display_number);
   return 0;
 }  // namespace cuttlefish
 
diff --git a/guest/hals/hwcomposer/cpu_composer.cpp b/guest/hals/hwcomposer/cpu_composer.cpp
index 42c3dba..086d610 100644
--- a/guest/hals/hwcomposer/cpu_composer.cpp
+++ b/guest/hals/hwcomposer/cpu_composer.cpp
@@ -651,11 +651,9 @@
 
 int CpuComposer::SetLayers(size_t num_layers, hwc_layer_1_t* layers) {
   int targetFbs = 0;
-  int buffer_idx = screen_view_->NextBuffer();
 
   const std::uint32_t display_number = 0;
-  std::uint8_t* dst_buffer =
-      reinterpret_cast<uint8_t*>(screen_view_->GetBuffer(buffer_idx));
+  std::uint8_t* dst_buffer = screen_view_->AcquireNextBuffer(display_number);
   std::uint32_t dst_width = ScreenView::ScreenWidth(display_number);
   std::uint32_t dst_height = ScreenView::ScreenHeight(display_number);
   std::uint32_t dst_stride_bytes = ScreenView::ScreenStrideBytes(display_number);
@@ -709,7 +707,7 @@
   if (targetFbs != 1) {
     ALOGW("Saw %zu layers, posted=%d", num_layers, targetFbs);
   }
-  screen_view_->Broadcast(buffer_idx);
+  screen_view_->PresentAcquiredBuffer(display_number);
   return 0;
 }
 
diff --git a/guest/hals/hwcomposer/screen_view.cpp b/guest/hals/hwcomposer/screen_view.cpp
index 703dc04..18625bd 100644
--- a/guest/hals/hwcomposer/screen_view.cpp
+++ b/guest/hals/hwcomposer/screen_view.cpp
@@ -86,10 +86,4 @@
   return ScreenStrideBytes(display_num) * ScreenHeight(display_num) + kMysteriousSwiftShaderPadding;
 }
 
-int ScreenView::NextBuffer() {
-  int num_buffers = this->num_buffers();
-  last_buffer_ = num_buffers > 0 ? (last_buffer_ + 1) % num_buffers : -1;
-  return last_buffer_;
-}
-
 }  // namespace cuttlefish
diff --git a/guest/hals/hwcomposer/screen_view.h b/guest/hals/hwcomposer/screen_view.h
index fdaf692..9e7bb0f 100644
--- a/guest/hals/hwcomposer/screen_view.h
+++ b/guest/hals/hwcomposer/screen_view.h
@@ -35,15 +35,17 @@
 class ScreenView {
  public:
   ScreenView() = default;
-  ScreenView(const ScreenView&) = delete;
   virtual ~ScreenView() = default;
 
+  ScreenView(const ScreenView&) = delete;
   ScreenView& operator=(const ScreenView&) = delete;
 
-  virtual void Broadcast(int buffer_id,
-                         const CompositionStats* stats = nullptr) = 0;
-  virtual int NextBuffer();
-  virtual void* GetBuffer(int buffer_id) = 0;
+  // Gets the buffer for the next frame that should be sent to the host.
+  virtual std::uint8_t* AcquireNextBuffer(std::uint32_t display_number) = 0;
+
+  // Mark that the next buffer has been populated with the next frame and is
+  // ready to be sent to the host.
+  virtual void PresentAcquiredBuffer(std::uint32_t display_number) = 0;
 
   static std::uint32_t ScreenCount();
   static std::uint32_t ScreenWidth(std::uint32_t display_number);
@@ -53,10 +55,5 @@
   static std::uint32_t ScreenStrideBytes(std::uint32_t display_number);
   static std::uint32_t ScreenSizeBytes(std::uint32_t display_number);
   static constexpr std::uint32_t ScreenBytesPerPixel() { return 4; }
-
-  virtual int num_buffers() const = 0;
-
- private:
-  int last_buffer_ = 0;
 };
 }  // namespace cuttlefish
\ No newline at end of file
diff --git a/guest/hals/hwcomposer/stats_keeper.h b/guest/hals/hwcomposer/stats_keeper.h
index 771ac5f..8219fbf 100644
--- a/guest/hals/hwcomposer/stats_keeper.h
+++ b/guest/hals/hwcomposer/stats_keeper.h
@@ -153,20 +153,18 @@
       : screen_view_(std::move(screen_view)), stats_getter_(stats_getter) {}
   virtual ~WrappedScreenView() = default;
 
-  void Broadcast(int buffer_id, const CompositionStats*) override {
+  std::uint8_t* AcquireNextBuffer(std::uint32_t display_number) override {
+    return screen_view_->AcquireNextBuffer(display_number);
+  }
+
+  void PresentAcquiredBuffer(std::uint32_t display_number) override {
     // The composer object in stats_keeper produces null stats, use the ones
     // provided by the stats_keeper instead.
     CompositionStats stats;
     stats_getter_(&stats);
-    return screen_view_->Broadcast(buffer_id, &stats);
+    return screen_view_->PresentAcquiredBuffer(display_number);
   }
 
-  void* GetBuffer(int buffer_id) override {
-    return screen_view_->GetBuffer(buffer_id);
-  }
-
-  int num_buffers() const override { return screen_view_->num_buffers(); }
-
  private:
   std::unique_ptr<ScreenView> screen_view_;
   std::function<void(CompositionStats*)> stats_getter_;
diff --git a/guest/hals/hwcomposer/vsocket_screen_view.cpp b/guest/hals/hwcomposer/vsocket_screen_view.cpp
index c6ae365..8fea483 100644
--- a/guest/hals/hwcomposer/vsocket_screen_view.cpp
+++ b/guest/hals/hwcomposer/vsocket_screen_view.cpp
@@ -18,21 +18,23 @@
 
 #include "guest/hals/hwcomposer/vsocket_screen_view.h"
 
+#include <algorithm>
+
 #include <android-base/logging.h>
 #include <cutils/properties.h>
 #include <log/log.h>
 
 #include "common/libs/device_config/device_config.h"
+#include "common/libs/fs/shared_buf.h"
 
 namespace cuttlefish {
 
-VsocketScreenView::VsocketScreenView()
-    : broadcast_thread_([this]() { BroadcastLoop(); }) {
-  GetScreenParameters();
-  // inner_buffer needs to be initialized after the final values of the screen
-  // parameters are set (either from the config server or default).
-  inner_buffer_size_ = ScreenSizeBytes(/*display_number=*/0);
-  inner_buffer_ = std::vector<char>(inner_buffer_size_ * 8);
+VsocketScreenView::VsocketScreenView() {
+  for (std::uint32_t i = 0; i < ScreenCount(); i++) {
+    display_helpers_.emplace_back(new DisplayHelper(i));
+  }
+
+  broadcast_thread_ = std::thread([this]() { BroadcastLoop(); });
 }
 
 VsocketScreenView::~VsocketScreenView() {
@@ -40,22 +42,6 @@
   broadcast_thread_.join();
 }
 
-void VsocketScreenView::GetScreenParameters() {
-  auto device_config_helper = DeviceConfigHelper::Get();
-  if (!device_config_helper) {
-    ALOGI(
-        "Failed to obtain device configuration from server, running in "
-        "headless mode");
-    // It is impossible to ensure host and guest agree on the screen parameters
-    // if these could not be read from the host configuration server. It's best
-    // to not attempt to send frames in this case.
-    running_ = false;
-    // Do a phony Broadcast to ensure the broadcaster thread exits.
-    Broadcast(-1);
-    return;
-  }
-}
-
 bool VsocketScreenView::ConnectToScreenServer() {
   auto vsock_frames_port = property_get_int64("ro.boot.vsock_frames_port", -1);
   if (vsock_frames_port <= 0) {
@@ -81,54 +67,42 @@
         "Compositions will occur, but frames won't be sent anywhere");
     return;
   }
+
   // The client detector thread needs to be started after the connection to the
   // socket has been made
   client_detector_thread_ = std::thread([this]() { ClientDetectorLoop(); });
 
-  unsigned int current_seq = 0;
-  unsigned int last_sent_seq = 0;
-  int current_offset;
   ALOGI("Broadcaster thread loop starting");
   while (true) {
     {
       std::unique_lock<std::mutex> lock(mutex_);
-      while (running_ && current_seq == current_seq_ &&
-             (!send_frames_ || last_sent_seq == current_seq)) {
+
+      while (running_) {
+        if (std::any_of(display_helpers_.begin(),
+                        display_helpers_.end(),
+                        [](const auto& display_helper) {
+                          return display_helper->HasPresentBuffer();
+                        })) {
+          break;
+        }
+
         cond_var_.wait(lock);
       }
       if (!running_) {
         ALOGI("Broadcaster thread exiting");
         return;
       }
-      current_offset = current_offset_;
-      current_seq = current_seq_;
     }
-    if (send_frames_ && last_sent_seq != current_seq) {
-      last_sent_seq = current_seq;
-      if (!SendFrame(current_offset)) {
-        break;
+
+    for (auto& display_helper : display_helpers_) {
+      if (!display_helper->SendPresentBufferIfAvailable(&screen_server_)) {
+        ALOGE("Broadcaster thread failed to send frame. Exiting...");
+        return;
       }
     }
   }
 }
 
-bool VsocketScreenView::SendFrame(int offset) {
-  int32_t size = static_cast<int32_t>(inner_buffer_size_);
-  screen_server_->Write(&size, sizeof(size));
-  auto buff = static_cast<char*>(GetBuffer(offset));
-  while (size > 0) {
-    auto written = screen_server_->Write(buff, size);
-    if (written == -1) {
-      ALOGE("Broadcaster thread failed to write frame: %s",
-            screen_server_->StrError());
-      return false;
-    }
-    size -= written;
-    buff += written;
-  }
-  return true;
-}
-
 void VsocketScreenView::ClientDetectorLoop() {
   char buffer[8];
   while (running_) {
@@ -151,19 +125,110 @@
   }
 }
 
-void VsocketScreenView::Broadcast(int offset, const CompositionStats*) {
+std::uint8_t* VsocketScreenView::AcquireNextBuffer(
+    std::uint32_t display_number) {
+  CHECK_GE(display_helpers_.size(), display_number);
+  return display_helpers_[display_number]->AcquireNextBuffer();
+}
+
+void VsocketScreenView::PresentAcquiredBuffer(std::uint32_t display_number) {
+  CHECK_GE(display_helpers_.size(), display_number);
+  display_helpers_[display_number]->PresentAcquiredBuffer();
+
   std::lock_guard<std::mutex> lock(mutex_);
-  current_offset_ = offset;
-  current_seq_++;
   cond_var_.notify_all();
 }
 
-void* VsocketScreenView::GetBuffer(int buffer_id) {
-  return &inner_buffer_[inner_buffer_size_ * buffer_id];
+VsocketScreenView::DisplayHelper::DisplayHelper(
+    std::uint32_t display_number) : display_number_(display_number) {
+  buffer_size_ = ScreenSizeBytes(display_number);
+  buffers_.resize(kNumBuffersPerDisplay * buffer_size_);
+
+  for (std::uint32_t i = 0; i < kNumBuffersPerDisplay; i++) {
+    acquirable_buffers_indexes_.push_back(i);
+  }
 }
 
-int VsocketScreenView::num_buffers() const {
-  return inner_buffer_.size() / inner_buffer_size_;
+std::uint8_t* VsocketScreenView::DisplayHelper::AcquireNextBuffer() {
+  std::uint32_t acquired = 0;
+  {
+    std::lock_guard<std::mutex> lock(acquire_mutex_);
+
+    CHECK(!acquirable_buffers_indexes_.empty());
+    CHECK(!acquired_buffer_index_.has_value());
+
+    acquired = acquirable_buffers_indexes_.front();
+    acquirable_buffers_indexes_.pop_front();
+    acquired_buffer_index_ = acquired;
+  }
+
+  return GetBuffer(acquired);
 }
 
-}  // namespace cuttlefish
+void VsocketScreenView::DisplayHelper::PresentAcquiredBuffer() {
+  {
+    std::lock_guard<std::mutex> present_lock(present_mutex_);
+    {
+      std::lock_guard<std::mutex> acquire_lock(acquire_mutex_);
+      CHECK(acquired_buffer_index_.has_value());
+
+      if (present_buffer_index_) {
+        acquirable_buffers_indexes_.push_back(*present_buffer_index_);
+      }
+
+      present_buffer_index_ = *acquired_buffer_index_;
+      acquired_buffer_index_.reset();
+    }
+  }
+}
+
+bool VsocketScreenView::DisplayHelper::HasPresentBuffer() {
+  std::lock_guard<std::mutex> present_lock(present_mutex_);
+  return present_buffer_index_.has_value();
+}
+
+
+bool VsocketScreenView::DisplayHelper::SendPresentBufferIfAvailable(
+    cuttlefish::SharedFD* connection) {
+  {
+    std::lock_guard<std::mutex> present_lock(present_mutex_);
+
+    if (present_buffer_index_) {
+      std::uint32_t frame_buffer_index = *present_buffer_index_;
+      std::uint8_t* frame_bytes = GetBuffer(frame_buffer_index);
+      std::size_t frame_size_bytes = buffer_size_;
+
+      if (WriteAllBinary(*connection, &display_number_) <= 0) {
+        ALOGE("Failed to write: %s", strerror(errno));
+        return false;
+      }
+      int32_t size = static_cast<int32_t>(frame_size_bytes);
+      if (WriteAllBinary(*connection, &size) <= 0) {
+        ALOGE("Failed to write: %s", strerror(errno));
+        return false;
+      }
+      auto* raw = reinterpret_cast<const char*>(frame_bytes);
+      if (WriteAll(*connection, raw, frame_size_bytes) <= 0) {
+        ALOGE("Failed to write: %s", strerror(errno));
+        return false;
+      }
+
+      {
+        std::lock_guard<std::mutex> acquire_lock(acquire_mutex_);
+        acquirable_buffers_indexes_.push_back(frame_buffer_index);
+      }
+
+      present_buffer_index_.reset();
+    }
+
+    return true;
+  }
+}
+
+std::uint8_t* VsocketScreenView::DisplayHelper::GetBuffer(
+    std::uint32_t buffer_index) {
+  buffer_index %= kNumBuffersPerDisplay;
+  return &buffers_[buffer_index * buffer_size_];
+}
+
+}  // namespace cuttlefish
\ No newline at end of file
diff --git a/guest/hals/hwcomposer/vsocket_screen_view.h b/guest/hals/hwcomposer/vsocket_screen_view.h
index e0901c7..9a2049e 100644
--- a/guest/hals/hwcomposer/vsocket_screen_view.h
+++ b/guest/hals/hwcomposer/vsocket_screen_view.h
@@ -16,8 +16,10 @@
 #pragma once
 
 #include <condition_variable>
+#include <deque>
 #include <functional>
 #include <mutex>
+#include <optional>
 #include <thread>
 #include <vector>
 
@@ -31,30 +33,63 @@
   VsocketScreenView();
   virtual ~VsocketScreenView();
 
-  void Broadcast(int buffer_id,
-                 const CompositionStats* stats = nullptr) override;
-  void* GetBuffer(int fb_index) override;
+  std::uint8_t* AcquireNextBuffer(std::uint32_t display_number) override;
 
-  int num_buffers() const override;
+  void PresentAcquiredBuffer(std::uint32_t display_number) override;
 
  private:
   bool ConnectToScreenServer();
-  void GetScreenParameters();
-  void BroadcastLoop();
-  void ClientDetectorLoop();
-  bool SendFrame(int offset);
 
-  std::uint32_t inner_buffer_size_;
-  std::vector<char> inner_buffer_;
+  void BroadcastLoop();
+
+  void ClientDetectorLoop();
+
+  class DisplayHelper {
+   public:
+    DisplayHelper(std::uint32_t display_number);
+
+    DisplayHelper(const DisplayHelper&) = delete;
+    DisplayHelper& operator=(const DisplayHelper&) = delete;
+
+    DisplayHelper(DisplayHelper&&) = delete;
+    DisplayHelper& operator=(DisplayHelper&&) = delete;
+
+    std::uint8_t* AcquireNextBuffer();
+
+    void PresentAcquiredBuffer();
+
+    // Returns true if this display has a new frame ready to be sent.
+    bool HasPresentBuffer();
+
+    bool SendPresentBufferIfAvailable(cuttlefish::SharedFD* connection);
+
+   private:
+    std::uint8_t* GetBuffer(std::uint32_t index);
+
+    static constexpr std::uint32_t kNumBuffersPerDisplay = 8;
+
+    std::uint32_t display_number_ = 0;
+
+    std::size_t buffer_size_ = 0;
+    std::vector<std::uint8_t> buffers_;
+
+    std::mutex acquire_mutex_;
+    std::deque<std::uint32_t> acquirable_buffers_indexes_;
+    std::optional<std::uint32_t> acquired_buffer_index_;
+
+    std::mutex present_mutex_;
+    std::optional<std::uint32_t> present_buffer_index_;
+  };
+
+  std::vector<std::unique_ptr<DisplayHelper>> display_helpers_;
+
   cuttlefish::SharedFD screen_server_;
   std::thread broadcast_thread_;
   std::thread client_detector_thread_;
-  int current_offset_ = 0;
-  unsigned int current_seq_ = 0;
+  bool send_frames_ = false;
   std::mutex mutex_;
   std::condition_variable cond_var_;
   bool running_ = true;
-  bool send_frames_{false};
 };
 
-}  // namespace cuttlefish
+}  // namespace cuttlefish
\ No newline at end of file
diff --git a/guest/hals/ril/reference-libril/ril.h b/guest/hals/ril/reference-libril/ril.h
index 7a070de..194ab5c 100644
--- a/guest/hals/ril/reference-libril/ril.h
+++ b/guest/hals/ril/reference-libril/ril.h
@@ -111,6 +111,7 @@
  *                    RIL_REQUEST_SET_ALLOWED_NETWORK_TYPE_BITMAP
  *                    RIL_REQUEST_SET_DATA_THROTTLING
  *                    RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS
+ *                    RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP
  */
 #define RIL_VERSION 12
 #define LAST_IMPRECISE_RIL_VERSION 12 // Better self-documented name
@@ -7466,7 +7467,28 @@
  */
 #define RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS 167
 
-#define RIL_REQUEST_LAST RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS
+/**
+ * RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP
+ *
+ * Request previously set allowed network types from the radio.
+ *
+ * Valid errors:
+ *  SUCCESS
+ *  RADIO_NOT_AVAILABLE (radio resetting)
+ *  OPERATION_NOT_ALLOWED
+ *  MODE_NOT_SUPPORTED
+ *  NO_MEMORY
+ *  INTERNAL_ERR
+ *  SYSTEM_ERR
+ *  INVALID_ARGUMENTS
+ *  MODEM_ERR
+ *  REQUEST_NOT_SUPPORTED
+ *  NO_RESOURCES
+ *  CANCELLED
+ */
+#define RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP 168
+
+#define RIL_REQUEST_LAST RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP
 
 /***********************************************************************/
 
diff --git a/guest/hals/ril/reference-libril/ril_commands.h b/guest/hals/ril/reference-libril/ril_commands.h
index 031593e..8080181 100644
--- a/guest/hals/ril/reference-libril/ril_commands.h
+++ b/guest/hals/ril/reference-libril/ril_commands.h
@@ -181,5 +181,7 @@
     {RIL_REQUEST_START_HANDOVER, radio_1_6::startHandoverResponse},
     {RIL_REQUEST_CANCEL_HANDOVER, radio_1_6::cancelHandoverResponse},
     {RIL_REQUEST_SET_ALLOWED_NETWORK_TYPE_BITMAP, radio_1_6::setAllowedNetworkTypeBitmapResponse},
-    {RIL_REQUEST_SET_DATA_THROTTLING, radio_1_6::setDataThrottlingResponse}
+    {RIL_REQUEST_SET_DATA_THROTTLING, radio_1_6::setDataThrottlingResponse},
+    {RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, radio_1_6::getSystemSelectionChannelsResponse},
+    {RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP, radio_1_6::getAllowedNetworkTypeBitmapResponse}
 
diff --git a/guest/hals/ril/reference-libril/ril_service.cpp b/guest/hals/ril/reference-libril/ril_service.cpp
index 33635ad..8bb3230 100644
--- a/guest/hals/ril/reference-libril/ril_service.cpp
+++ b/guest/hals/ril/reference-libril/ril_service.cpp
@@ -190,6 +190,8 @@
 
     Return<void> getCurrentCalls(int32_t serial);
 
+    Return<void> getCurrentCalls_1_6(int32_t serial);
+
     Return<void> dial(int32_t serial, const Dial& dialInfo);
 
     Return<void> getImsiForApp(int32_t serial,
@@ -626,6 +628,7 @@
     Return<void> getSystemSelectionChannels(int32_t serial);
     Return<void> getVoiceRegistrationState_1_6(int32_t serial);
     Return<void> getDataRegistrationState_1_6(int32_t serial);
+    Return<void> getAllowedNetworkTypeBitmap(uint32_t serial);
 };
 
 struct OemHookImpl : public IOemHook {
@@ -1124,6 +1127,14 @@
     return Void();
 }
 
+Return<void> RadioImpl_1_6::getCurrentCalls_1_6(int32_t serial) {
+#if VDBG
+    RLOGD("getCurrentCalls_1_6: serial %d", serial);
+#endif
+    dispatchVoid(serial, mSlotId, RIL_REQUEST_GET_CURRENT_CALLS);
+    return Void();
+}
+
 Return<void> RadioImpl_1_6::dial(int32_t serial, const Dial& dialInfo) {
 #if VDBG
     RLOGD("dial: serial %d", serial);
@@ -3816,6 +3827,14 @@
     return Void();
 }
 
+Return<void> RadioImpl_1_6::getAllowedNetworkTypeBitmap(uint32_t serial) {
+#if VDBG
+    RLOGD("getAllowedNetworkTypeBitmap: serial %d", serial);
+#endif
+    dispatchVoid(serial, mSlotId, RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP);
+    return Void();
+}
+
 Return<void> RadioImpl_1_6::getSignalStrength_1_4(int32_t serial) {
 #if VDBG
     RLOGD("getSignalStrength_1_4: serial %d", serial);
@@ -4647,6 +4666,21 @@
     return ret;
 }
 
+int responseInt_1_6(::android::hardware::radio::V1_6::RadioResponseInfo &responseInfo, int serial, int responseType, RIL_Errno e,
+               void *response, size_t responseLen) {
+    populateResponseInfo_1_6(responseInfo, serial, responseType, e);
+    int ret = -1;
+
+    if (response == NULL || responseLen != sizeof(int)) {
+        RLOGE("responseInt_1_6: Invalid response");
+        if (e == RIL_E_SUCCESS) responseInfo.error = ::android::hardware::radio::V1_6::RadioError::INVALID_RESPONSE;
+    } else {
+        int *p_int = (int *) response;
+        ret = p_int[0];
+    }
+    return ret;
+}
+
 int radio_1_6::getIccCardStatusResponse(int slotId,
                                    int responseType, int serial, RIL_Errno e,
                                    void *response, size_t responseLen) {
@@ -7638,6 +7672,30 @@
     return 0;
 }
 
+int radio_1_6::getAllowedNetworkTypeBitmapResponse(int slotId,
+                                          int responseType, int serial, RIL_Errno e,
+                                          void *response, size_t responseLen) {
+#if VDBG
+    RLOGD("getAllowedNetworkTypeBitmapResponse: serial %d", serial);
+#endif
+
+    if (radioService[slotId]->mRadioResponseV1_6 != NULL) {
+      V1_6::RadioResponseInfo responseInfo = {};
+        int ret = responseInt_1_6(responseInfo, serial, responseType, e, response, responseLen);
+        Return<void> retStatus
+                = radioService[slotId]->mRadioResponseV1_6->getAllowedNetworkTypeBitmapResponse(
+                responseInfo,
+                (const ::android::hardware::hidl_bitfield<
+                ::android::hardware::radio::V1_4::RadioAccessFamily>) ret);
+        radioService[slotId]->checkReturnStatus(retStatus);
+    } else {
+        RLOGE("getAllowedNetworkTypeBitmapResponse: radioService[%d]->mRadioResponseV1_6 == NULL",
+                slotId);
+    }
+
+    return 0;
+}
+
 int radio_1_6::getPreferredNetworkTypeResponse(int slotId,
                                           int responseType, int serial, RIL_Errno e,
                                           void *response, size_t responseLen) {
diff --git a/guest/hals/ril/reference-libril/ril_service.h b/guest/hals/ril/reference-libril/ril_service.h
index 1373451..36652fd 100644
--- a/guest/hals/ril/reference-libril/ril_service.h
+++ b/guest/hals/ril/reference-libril/ril_service.h
@@ -815,6 +815,10 @@
 int setDataThrottlingResponse(int slotId, int responseType, int serial,
                               RIL_Errno e, void *response, size_t responselen);
 
+int getAllowedNetworkTypeBitmapResponse(int slotId, int responseType, int serial,
+                                  RIL_Errno e, void *response, size_t responselen);
+
+
 pthread_rwlock_t * getRadioServiceRwlock(int slotId);
 
 void setNitzTimeReceived(int slotId, long timeReceived);
diff --git a/guest/hals/ril/reference-ril/Android.bp b/guest/hals/ril/reference-ril/Android.bp
index 77e4e65..54b1c9f 100644
--- a/guest/hals/ril/reference-ril/Android.bp
+++ b/guest/hals/ril/reference-ril/Android.bp
@@ -45,7 +45,4 @@
         "libril-modem-lib",
         "libutils",
     ],
-    static_libs: [
-        "libqemu_pipe",
-    ],
 }
diff --git a/guest/hals/ril/reference-ril/reference-ril.c b/guest/hals/ril/reference-ril/reference-ril.c
index 333e4da..6580c0f 100644
--- a/guest/hals/ril/reference-ril/reference-ril.c
+++ b/guest/hals/ril/reference-ril/reference-ril.c
@@ -38,7 +38,6 @@
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
 #include <termios.h>
-#include <qemu_pipe.h>
 #include <sys/wait.h>
 #include <stdbool.h>
 #include <net/if.h>
@@ -326,6 +325,7 @@
 
 static RIL_RadioState sState = RADIO_STATE_UNAVAILABLE;
 static bool isNrDualConnectivityEnabled = true;
+static unsigned int allowedNetworkTypeBitmap = UINT_MAX;
 
 static pthread_mutex_t s_state_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t s_state_cond = PTHREAD_COND_INITIALIZER;
@@ -4797,8 +4797,17 @@
             requestSetPreferredNetworkType(request, data, datalen, t);
             break;
         case RIL_REQUEST_SET_ALLOWED_NETWORK_TYPE_BITMAP:
+            if (data == NULL || datalen != sizeof(int)) {
+              RIL_onRequestComplete(t, RIL_E_INTERNAL_ERR, NULL, 0);
+              break;
+            }
+            allowedNetworkTypeBitmap = *(int *)data;
             RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
             break;
+        case RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP:
+            RIL_onRequestComplete(t, RIL_E_SUCCESS, &allowedNetworkTypeBitmap,
+                    sizeof(allowedNetworkTypeBitmap));
+            break;
         case RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY:
             if (data == NULL || datalen != sizeof(int)) {
                 RIL_onRequestComplete(t, RIL_E_INTERNAL_ERR, NULL, 0);
diff --git a/guest/services/suspend_blocker/Android.bp b/guest/services/suspend_blocker/Android.bp
index c87c4da..023befa 100644
--- a/guest/services/suspend_blocker/Android.bp
+++ b/guest/services/suspend_blocker/Android.bp
@@ -16,5 +16,5 @@
     name: "suspend_blocker",
     srcs: ["suspend_blocker.cpp"],
     shared_libs: ["libpower"],
-    defaults: ["cuttlefish_guest_product_only"],
+    defaults: ["cuttlefish_guest_system_ext_only"],
 }
diff --git a/host/commands/assemble_cvd/boot_image_utils.cc b/host/commands/assemble_cvd/boot_image_utils.cc
index 23892e6..3022a09 100644
--- a/host/commands/assemble_cvd/boot_image_utils.cc
+++ b/host/commands/assemble_cvd/boot_image_utils.cc
@@ -68,6 +68,7 @@
 
   return true;
 }
+} // namespace
 
 void RepackVendorRamdisk(const std::string& kernel_modules_ramdisk_path,
                          const std::string& original_ramdisk_path,
@@ -106,7 +107,6 @@
   std::ifstream ramdisk_b(kernel_modules_ramdisk_path, std::ios_base::binary);
   final_rd << ramdisk_a.rdbuf() << ramdisk_b.rdbuf();
 }
-} // namespace
 
 bool RepackBootImage(const std::string& new_kernel_path,
                      const std::string& boot_image_path,
@@ -141,6 +141,12 @@
     return false;
   }
 
+  auto fd = SharedFD::Open(tmp_boot_image_path, O_RDWR);
+  auto original_size = FileSize(boot_image_path);
+  CHECK(fd->Truncate(original_size) == 0)
+    << "`truncate --size=" << original_size << " " << tmp_boot_image_path << "` "
+    << "failed: " << fd->StrError();
+
   return DeleteTmpFileIfNotChanged(tmp_boot_image_path, new_boot_image_path);
 }
 
@@ -200,6 +206,12 @@
     return false;
   }
 
+  auto fd = SharedFD::Open(tmp_vendor_boot_image_path, O_RDWR);
+  auto original_size = FileSize(vendor_boot_image_path);
+  CHECK(fd->Truncate(original_size) == 0)
+    << "`truncate --size=" << original_size << " " << tmp_vendor_boot_image_path << "` "
+    << "failed: " << fd->StrError();
+
   return DeleteTmpFileIfNotChanged(tmp_vendor_boot_image_path, new_vendor_boot_image_path);
 }
 
diff --git a/host/commands/assemble_cvd/boot_image_utils.h b/host/commands/assemble_cvd/boot_image_utils.h
index 43938a7..2f6a9d1 100644
--- a/host/commands/assemble_cvd/boot_image_utils.h
+++ b/host/commands/assemble_cvd/boot_image_utils.h
@@ -30,4 +30,8 @@
     const std::string& vendor_boot_image_path,
     const std::string& new_vendor_boot_image_path,
     const std::string& tmp_artifact_dir);
+void RepackVendorRamdisk(const std::string& kernel_modules_ramdisk_path,
+                         const std::string& original_ramdisk_path,
+                         const std::string& new_ramdisk_path,
+                         const std::string& tmp_artifact_dir);
 }
diff --git a/host/commands/assemble_cvd/disk_flags.cc b/host/commands/assemble_cvd/disk_flags.cc
index 273dede..129fce3 100644
--- a/host/commands/assemble_cvd/disk_flags.cc
+++ b/host/commands/assemble_cvd/disk_flags.cc
@@ -418,19 +418,33 @@
       SetCommandLineOptionWithMode("vendor_boot_image", new_vendor_boot_image_path.c_str(),
                                    google::FlagSettingMode::SET_FLAGS_DEFAULT);
     }
-  } else if (!FLAGS_use_bootloader && (!foreign_kernel.size() || foreign_ramdisk.size())) {
+  } else if (!FLAGS_use_bootloader) {
     // This code path is taken when the virtual device kernel is launched
     // directly by the hypervisor instead of the bootloader.
     // This code path takes care of all the ramdisk processing that the
     // bootloader normally does.
-    const std::string& vendor_ramdisk_path =
-      config->initramfs_path().size() ? config->initramfs_path()
+    bool success;
+    if (!foreign_ramdisk.size()) {
+      const std::string& vendor_ramdisk_path =
+        config->initramfs_path().size() ? config->initramfs_path()
                                       : config->vendor_ramdisk_image_path();
-    bool success = ConcatRamdisks(
-        config->final_ramdisk_path(),
-        config->ramdisk_image_path(),
-        vendor_ramdisk_path);
-    CHECK(success) << "Failed to concatenate ramdisk and vendor ramdisk";
+      success = ConcatRamdisks(
+          config->final_ramdisk_path(),
+          config->ramdisk_image_path(),
+          vendor_ramdisk_path);
+    } else {
+      std::string vendor_ramdisk_repacked_path = config->AssemblyPath("vendor_ramdisk_repacked");
+      RepackVendorRamdisk(
+          config->initramfs_path(),
+          config->vendor_ramdisk_image_path(),
+          vendor_ramdisk_repacked_path,
+          config->assembly_dir());
+      success = ConcatRamdisks(
+          config->final_ramdisk_path(),
+          config->ramdisk_image_path(),
+          vendor_ramdisk_repacked_path);
+    }
+    CHECK(success) << "Failed to concatenate boot ramdisk and vendor ramdisk";
   }
 
   if (config->decompress_kernel()) {
diff --git a/host/commands/powerwash_cvd/powerwash_cvd.cc b/host/commands/powerwash_cvd/powerwash_cvd.cc
index 4782e58..483ee61 100644
--- a/host/commands/powerwash_cvd/powerwash_cvd.cc
+++ b/host/commands/powerwash_cvd/powerwash_cvd.cc
@@ -49,7 +49,7 @@
 DEFINE_int32(instance_num, cuttlefish::GetInstance(),
              "Which instance to powerwash");
 
-DEFINE_int32(wait_for_launcher, 5,
+DEFINE_int32(wait_for_launcher, 30,
              "How many seconds to wait for the launcher to respond to the status "
              "command. A value of zero means wait indefinetly");
 
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index 354e62d..da9cd59 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -73,7 +73,7 @@
 }
 
 void SimulatedHWComposer::MakeStripes() {
-  std::uint32_t previous_frame_number = 0;
+  std::uint32_t frame_number = 0;
   const std::uint32_t display_number = 0;
   const std::uint32_t display_w =
       ScreenConnector::ScreenWidth(display_number);
@@ -89,8 +89,13 @@
   Message raw_screen;
   std::uint64_t stripe_seq_num = 1;
 
-  const FrameCallback frame_callback = [&](uint32_t frame_number,
+  const FrameCallback frame_callback = [&](uint32_t display_number,
                                            uint8_t* frame_pixels) {
+    // TODO(171305898): handle multiple displays.
+    if (display_number != 0) {
+      return;
+    }
+
     raw_screen.assign(frame_pixels,
                       frame_pixels + display_size_bytes);
 
@@ -104,8 +109,7 @@
           display_h / kNumStripes +
           (i + 1 == kNumStripes ? display_h % kNumStripes : 0);
       const auto* raw_start = &raw_screen[y * display_w * display_bpp];
-      const auto* raw_end =
-          raw_start + (height * display_w * display_bpp);
+      const auto* raw_end = raw_start + (height * display_w * display_bpp);
       // creating a named object and setting individual data members in order
       // to make klp happy
       // TODO (haining) construct this inside the call when not compiling
@@ -124,13 +128,13 @@
       stripes_.Push(std::move(s));
     }
 
-    previous_frame_number = frame_number;
+    ++frame_number;
   };
 
   while (!closed()) {
     bb_->WaitForAtLeastOneClientConnection();
 
-    screen_connector_->OnFrameAfter(previous_frame_number, frame_callback);
+    screen_connector_->OnNextFrame(frame_callback);
   }
 }
 
diff --git a/host/frontend/webrtc/connection_observer.cpp b/host/frontend/webrtc/connection_observer.cpp
index 2a22ec0..08ecef4 100644
--- a/host/frontend/webrtc/connection_observer.cpp
+++ b/host/frontend/webrtc/connection_observer.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "ConnectionObserver"
+
 #include "host/frontend/webrtc/connection_observer.h"
 
 #include <linux/input.h>
@@ -44,6 +46,13 @@
   int32_t value;
 };
 
+struct multitouch_slot {
+  int32_t id;
+  int32_t slot;
+  int32_t x;
+  int32_t y;
+};
+
 struct InputEventBuffer {
   virtual ~InputEventBuffer() = default;
   virtual void AddEvent(uint16_t type, uint16_t code, int32_t value) = 0;
@@ -67,6 +76,7 @@
   std::vector<T> buffer_;
 };
 
+// TODO: we could add an arg here to specify whether we want the multitouch buffer?
 std::unique_ptr<InputEventBuffer> GetEventBuffer() {
   if (FLAGS_write_virtio_input) {
     return std::unique_ptr<InputEventBuffer>(
@@ -107,8 +117,10 @@
       display_handler->SendLastFrame();
     }
   }
+
   void OnTouchEvent(const std::string & /*display_label*/, int x, int y,
                     bool down) override {
+
     auto buffer = GetEventBuffer();
     if (!buffer) {
       LOG(ERROR) << "Failed to allocate event buffer";
@@ -117,16 +129,62 @@
     buffer->AddEvent(EV_ABS, ABS_X, x);
     buffer->AddEvent(EV_ABS, ABS_Y, y);
     buffer->AddEvent(EV_KEY, BTN_TOUCH, down);
-    buffer->AddEvent(EV_SYN, 0, 0);
+    buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
     cuttlefish::WriteAll(input_sockets_.touch_client,
                          reinterpret_cast<const char *>(buffer->data()),
                          buffer->size());
   }
-  void OnMultiTouchEvent(const std::string &display_label, int /*id*/,
-                         int /*slot*/, int x, int y,
-                         bool initialDown) override {
-    OnTouchEvent(display_label, x, y, initialDown);
+
+  void OnMultiTouchEvent(const std::string & /*display_label*/, Json::Value id,
+                         Json::Value slot, Json::Value x, Json::Value y,
+                         bool down, int size) override {
+    static bool button_touch = false;
+    static bool finger = false;
+
+    auto buffer = GetEventBuffer();
+    if (!buffer) {
+      LOG(ERROR) << "Failed to allocate event buffer";
+      return;
+    }
+
+    if (!finger) {
+      buffer->AddEvent(EV_KEY, BTN_TOOL_FINGER, 1);
+      finger = true;
+    }
+
+    for (int i=0; i<size; i++) {
+      buffer->AddEvent(EV_ABS, ABS_MT_SLOT, slot[i].asInt());
+
+      auto thisId = id[i].asInt();
+      if (thisId < 0 || down) {
+        buffer->AddEvent(EV_ABS, ABS_MT_TRACKING_ID, thisId);
+      }
+
+      if (thisId >= 0) {
+        // ABS_MT_POSITION_X: Reports the X coordinate of the tool.
+        // ABS_MT_POSITION_Y: Reports the Y coordinate of the tool.
+        buffer->AddEvent(EV_ABS, ABS_MT_POSITION_X, x[i].asInt());
+        buffer->AddEvent(EV_ABS, ABS_MT_POSITION_Y, y[i].asInt());
+        // ABS_{X,Y} must be reported with the location of the touch.
+        buffer->AddEvent(EV_ABS, ABS_X, x[i].asInt());
+        buffer->AddEvent(EV_ABS, ABS_Y, y[i].asInt());
+      }
+
+      if ((!button_touch && down) || (button_touch && !down)) {
+        // BTN_TOUCH must be used to report when a touch is active on the screen.
+        // BTN_TOUCH: Indicates whether the tool is touching the device.
+        buffer->AddEvent(EV_KEY, BTN_TOUCH, down);
+        LOG(VERBOSE) << "BTN_TOUCH " << down;
+        button_touch = !button_touch;
+      }
+    }
+
+    buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
+    cuttlefish::WriteAll(input_sockets_.touch_client,
+    reinterpret_cast<const char *>(buffer->data()),
+    buffer->size());
   }
+
   void OnKeyboardEvent(uint16_t code, bool down) override {
     auto buffer = GetEventBuffer();
     if (!buffer) {
@@ -134,11 +192,12 @@
       return;
     }
     buffer->AddEvent(EV_KEY, code, down);
-    buffer->AddEvent(EV_SYN, 0, 0);
+    buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
     cuttlefish::WriteAll(input_sockets_.keyboard_client,
                          reinterpret_cast<const char *>(buffer->data()),
                          buffer->size());
   }
+
   void OnAdbChannelOpen(std::function<bool(const uint8_t *, size_t)>
                             adb_message_sender) override {
     LOG(VERBOSE) << "Adb Channel open";
diff --git a/host/frontend/webrtc/display_handler.cpp b/host/frontend/webrtc/display_handler.cpp
index 86d6e8d..0d5f493 100644
--- a/host/frontend/webrtc/display_handler.cpp
+++ b/host/frontend/webrtc/display_handler.cpp
@@ -31,11 +31,16 @@
   const int display_w = screen_connector_->ScreenWidth(display_num);
   const int display_h = screen_connector_->ScreenHeight(display_num);
   const int display_stride_bytes = screen_connector_->ScreenStrideBytes(display_num);
-  std::uint32_t frame_num = 0;
+
   for (;;) {
-    auto have_frame = screen_connector_->OnFrameAfter(
-        frame_num, [&, this](std::uint32_t fn, std::uint8_t* frame) {
-          frame_num = fn;
+    auto have_frame = screen_connector_->OnNextFrame(
+        [&, this](
+            std::uint32_t display_number,
+            std::uint8_t* frame) {
+          // TODO(171305898): handle multiple displays.
+          if (display_number != 0) {
+            return;
+          }
           std::shared_ptr<CvdVideoFrameBuffer> buffer(
               new CvdVideoFrameBuffer(display_w, display_h));
           libyuv::ABGRToI420(frame, display_stride_bytes,
diff --git a/host/frontend/webrtc/lib/client_handler.cpp b/host/frontend/webrtc/lib/client_handler.cpp
index d506238..6ea61fc 100644
--- a/host/frontend/webrtc/lib/client_handler.cpp
+++ b/host/frontend/webrtc/lib/client_handler.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "ClientHandler"
+
 #include "host/frontend/webrtc/lib/client_handler.h"
 
 #include <vector>
@@ -205,24 +207,26 @@
   } else if (event_type == "multi-touch") {
     auto result =
         ValidationResult::ValidateJsonObject(evt, "multi-touch",
-                           {{"id", Json::ValueType::intValue},
-                            {"initialDown", Json::ValueType::intValue},
-                            {"x", Json::ValueType::intValue},
-                            {"y", Json::ValueType::intValue},
-                            {"slot", Json::ValueType::intValue},
+                           {{"id", Json::ValueType::arrayValue},
+                            {"down", Json::ValueType::intValue},
+                            {"x", Json::ValueType::arrayValue},
+                            {"y", Json::ValueType::arrayValue},
+                            {"slot", Json::ValueType::arrayValue},
                             {"display_label", Json::ValueType::stringValue}});
     if (!result.ok()) {
       LOG(ERROR) << result.error();
       return;
     }
-    auto label = evt["display_label"].asString();
-    int32_t id = evt["id"].asInt();
-    int32_t initialDown = evt["initialDown"].asInt();
-    int32_t x = evt["x"].asInt();
-    int32_t y = evt["y"].asInt();
-    int32_t slot = evt["slot"].asInt();
 
-    observer_->OnMultiTouchEvent(label, id, slot, x, y, initialDown);
+    auto label = evt["display_label"].asString();
+    auto idArr = evt["id"];
+    int32_t down = evt["down"].asInt();
+    auto xArr = evt["x"];
+    auto yArr = evt["y"];
+    auto slotArr = evt["slot"];
+    int size = evt["id"].size();
+
+    observer_->OnMultiTouchEvent(label, idArr, slotArr, xArr, yArr, down, size);
   } else if (event_type == "keyboard") {
     auto result =
         ValidationResult::ValidateJsonObject(evt, "keyboard",
diff --git a/host/frontend/webrtc/lib/connection_observer.h b/host/frontend/webrtc/lib/connection_observer.h
index 80965e9..9cd7286 100644
--- a/host/frontend/webrtc/lib/connection_observer.h
+++ b/host/frontend/webrtc/lib/connection_observer.h
@@ -32,8 +32,8 @@
       std::function<void(const uint8_t*, size_t, bool)> ctrl_msg_sender) = 0;
   virtual void OnTouchEvent(const std::string& display_label, int x, int y,
                             bool down) = 0;
-  virtual void OnMultiTouchEvent(const std::string& label, int id, int slot,
-                                 int x, int y, bool initialDown) = 0;
+  virtual void OnMultiTouchEvent(const std::string& label, Json::Value id, Json::Value slot,
+                                 Json::Value x, Json::Value y, bool down, int size) = 0;
   virtual void OnKeyboardEvent(uint16_t keycode, bool down) = 0;
   virtual void OnAdbChannelOpen(
       std::function<bool(const uint8_t*, size_t)> adb_message_sender) = 0;
diff --git a/host/frontend/webrtc/lib/local_recorder.cpp b/host/frontend/webrtc/lib/local_recorder.cpp
index 54382c9..28f71b0 100644
--- a/host/frontend/webrtc/lib/local_recorder.cpp
+++ b/host/frontend/webrtc/lib/local_recorder.cpp
@@ -38,6 +38,11 @@
 namespace cuttlefish {
 namespace webrtc_streaming {
 
+constexpr double kRtpTicksPerSecond = 90000.;
+constexpr double kRtpTicksPerMs = kRtpTicksPerSecond / 1000.;
+constexpr double kRtpTicksPerUs = kRtpTicksPerMs / 1000.;
+constexpr double kRtpTicksPerNs = kRtpTicksPerUs / 1000.;
+
 class LocalRecorder::Display
     : public webrtc::EncodedImageCallback
     , public rtc::VideoSinkInterface<webrtc::VideoFrame> {
@@ -60,7 +65,6 @@
   std::shared_ptr<webrtc::VideoTrackSourceInterface> source_;
   std::unique_ptr<webrtc::VideoEncoder> video_encoder_;
   uint64_t video_track_number_;
-  std::chrono::time_point<std::chrono::steady_clock> start_timestamp_;
 
   // TODO(schuffelen): Use a WebRTC task queue?
   std::thread encoder_thread_;
@@ -94,6 +98,9 @@
     return {};
   }
 
+  impl->segment_.AccurateClusterDuration(true);
+  impl->segment_.set_estimate_file_duration(true);
+
   impl->encoder_factory_ = webrtc::CreateBuiltinVideoEncoderFactory();
   if (!impl->encoder_factory_) {
     LOG(ERROR) << "Failed to create webRTC built-in video encoder factory";
@@ -180,8 +187,7 @@
   impl_->segment_.Finalize();
 }
 
-LocalRecorder::Display::Display(LocalRecorder::Impl& impl)
-    : impl_(impl), start_timestamp_(std::chrono::steady_clock::now()) {
+LocalRecorder::Display::Display(LocalRecorder::Impl& impl) : impl_(impl) {
 }
 
 void LocalRecorder::Display::OnFrame(const webrtc::VideoFrame& frame) {
@@ -196,6 +202,9 @@
 }
 
 void LocalRecorder::Display::EncoderLoop() {
+  int frames_since_keyframe = 0;
+  std::chrono::time_point<std::chrono::steady_clock> start_timestamp;
+  auto last_keyframe_time = std::chrono::steady_clock::now();
   while (encoder_running_) {
     std::unique_ptr<webrtc::VideoFrame> frame;
     {
@@ -210,8 +219,27 @@
           std::move(encode_queue_.front()));
       encode_queue_.pop_front();
     }
-    std::vector<webrtc::VideoFrameType> types{
-      webrtc::VideoFrameType::kVideoFrameDelta};
+
+    auto now = std::chrono::steady_clock::now();
+    if (start_timestamp.time_since_epoch().count() == 0) {
+      start_timestamp = now;
+    }
+    auto timestamp_diff =
+        std::chrono::duration_cast<std::chrono::microseconds>(
+              now - start_timestamp);
+    frame->set_timestamp_us(timestamp_diff.count());
+    frame->set_timestamp(timestamp_diff.count() * kRtpTicksPerUs);
+
+    std::vector<webrtc::VideoFrameType> types;
+    auto time_since_keyframe = now - last_keyframe_time;
+    const auto min_keyframe_time = std::chrono::seconds(10);
+    if (frames_since_keyframe > 60 || time_since_keyframe > min_keyframe_time) {
+      last_keyframe_time = now;
+      frames_since_keyframe = 0;
+      types.push_back(webrtc::VideoFrameType::kVideoFrameKey);
+    } else {
+      types.push_back(webrtc::VideoFrameType::kVideoFrameDelta);
+    }
     auto rc = video_encoder_->Encode(*frame, &types);
     if (rc != 0) {
       LOG(ERROR) << "Failed to encode frame";
@@ -231,18 +259,18 @@
     const webrtc::EncodedImage& encoded_image,
     const webrtc::CodecSpecificInfo* codec_specific_info,
     const webrtc::RTPFragmentationHeader* fragmentation) {
-  auto timestamp_diff =
-      std::chrono::duration_cast<std::chrono::nanoseconds>(
-          std::chrono::steady_clock::now() - start_timestamp_).count();
+  uint64_t timestamp = encoded_image.Timestamp() / kRtpTicksPerNs;
 
   std::lock_guard(impl_.mkv_mutex_);
 
+  bool is_key =
+      encoded_image._frameType == webrtc::VideoFrameType::kVideoFrameKey;
   bool success = impl_.segment_.AddFrame(
       encoded_image.data(),
       encoded_image.size(),
       video_track_number_,
-      timestamp_diff,
-      /* is_key */ false);
+      timestamp,
+      is_key);
 
   webrtc::EncodedImageCallback::Result result(
       success
diff --git a/host/frontend/webrtc_operator/assets/js/app.js b/host/frontend/webrtc_operator/assets/js/app.js
index 46931b5..5846e22 100644
--- a/host/frontend/webrtc_operator/assets/js/app.js
+++ b/host/frontend/webrtc_operator/assets/js/app.js
@@ -41,6 +41,8 @@
   let buttonsWaitingOnAdbConnection = [];
   let mouseIsDown = false;
   let deviceConnection;
+  let touchIdSlotMap = new Map();
+  let touchSlots = new Array();
 
   let rotation = 0;
   let screenHasBeenResized = false;
@@ -305,7 +307,7 @@
     // console.log("mousedown at " + e.pageX + " / " + e.pageY);
     mouseIsDown = true;
 
-    sendMouseUpdate(true, e);
+    sendEventUpdate(true, e);
   }
 
   function onEndDrag(e) {
@@ -314,7 +316,7 @@
     // console.log("mouseup at " + e.pageX + " / " + e.pageY);
     mouseIsDown = false;
 
-    sendMouseUpdate(false, e);
+    sendEventUpdate(false, e);
   }
 
   function onContinueDrag(e) {
@@ -323,29 +325,33 @@
     // console.log("mousemove at " + e.pageX + " / " + e.pageY + ", down=" +
     // mouseIsDown);
     if (mouseIsDown) {
-      sendMouseUpdate(true, e);
+      sendEventUpdate(true, e);
     }
   }
 
-  function sendMouseUpdate(down, e) {
+  function sendEventUpdate(down, e) {
     console.assert(deviceConnection, 'Can\'t send mouse update without device');
-    var x = e.offsetX;
-    var y = e.offsetY;
+    var eventType = e.type.substring(0, 5);
 
-    let elementWidth, elementHeight;
-    if (screenHasBeenResized) {
-      elementWidth = parseInt(deviceScreen.style.width, 10);
-      elementHeight = parseInt(deviceScreen.style.height, 10);
-    } else {
-      // Before the first video frame arrives there is no way to know width and
-      // height of the device's screen, so turn every click into a click at 0x0.
-      // A click at that position is not more dangerous than anywhere else since
-      // the user is clicking blind anyways.
-      elementWidth = deviceScreen.offsetWidth? deviceScreen.offsetWidth: 1;
-      elementHeight = deviceScreen.offsetHeight? deviceScreen.offsetHeight: 1;
-    }
+    // Before the first video frame arrives there is no way to know width and
+    // height of the device's screen, so turn every click into a click at 0x0.
+    // A click at that position is not more dangerous than anywhere else since
+    // the user is clicking blind anyways.
     const videoWidth = deviceScreen.videoWidth? deviceScreen.videoWidth: 1;
     const videoHeight = deviceScreen.videoHeight? deviceScreen.videoHeight: 1;
+    const elementWidth = deviceScreen.offsetWidth? deviceScreen.offsetWidth: 1;
+    const elementHeight = deviceScreen.offsetHeight? deviceScreen.offsetHeight: 1;
+
+    // vh*ew > eh*vw? then scale h instead of w
+    const scaleHeight = videoHeight * elementWidth > videoWidth * elementHeight;
+    var elementScaling = 0, videoScaling = 0;
+    if (scaleHeight) {
+      elementScaling = elementHeight;
+      videoScaling = videoHeight;
+    } else {
+      elementScaling = elementWidth;
+      videoScaling = videoWidth;
+    }
 
     // The screen uses the 'object-fit: cover' property in order to completely
     // fill the element while maintaining the screen content's aspect ratio.
@@ -372,23 +378,101 @@
     //       - The sent ABS_X and ABS_Y values need to be scaled based on the
     //         ratio between the max size (video size) and in-browser size.
     const scaling = scaleWidth ? videoWidth / elementWidth : videoHeight / elementHeight;
-    x = x * scaling;
-    y = y * scaling;
 
-    // Substract the offset produced by the difference in aspect ratio, if any.
-    if (scaleWidth) {
-      // Width was scaled, leaving excess content height, so subtract from y.
-      y -= (elementHeight * scaling - videoHeight) / 2;
-    } else {
-      // Height was scaled, leaving excess content width, so subtract from x.
-      x -= (elementWidth * scaling - videoWidth) / 2;
+    var xArr = [];
+    var yArr = [];
+    var idArr = [];
+    var slotArr = [];
+
+    console.log('e.type: ' + e.type);
+    if (eventType == "mouse" || eventType == "point") {
+      xArr.push(e.offsetX);
+      yArr.push(e.offsetY);
+
+      let thisId = -1;
+      if (eventType == "point") {
+        thisId = e.pointerId;
+      }
+
+      slotArr.push(0);
+      idArr.push(thisId);
+    } else if (eventType == "touch") {
+      // touchstart: list of touch points that became active
+      // touchmove: list of touch points that changed
+      // touchend: list of touch points that were removed
+      let changes = e.changedTouches;
+      let rect = e.target.getBoundingClientRect();
+      for (var i=0; i < changes.length; i++) {
+        xArr.push(changes[i].pageX - rect.left);
+        yArr.push(changes[i].pageY - rect.top);
+        idArr.push(changes[i].identifier);
+        if (touchIdSlotMap.has(changes[i].identifier)) {
+          let slot = touchIdSlotMap.get(changes[i].identifier);
+
+          slotArr.push(slot);
+          if (e.type == 'touchstart') {
+            // error
+            console.error('touchstart when already have slot');
+            return;
+          } else if (e.type == 'touchmove') {
+            idArr.push(changes[i].identifier);
+          } else if (e.type == 'touchend') {
+            touchSlots[slot] = false;
+            touchIdSlotMap.delete(changes[i].identifier);
+            idArr.push(-1);
+          }
+        } else {
+          if (e.type == 'touchstart') {
+            let slot = -1;
+            for (var j=0; j < touchSlots.length; j++) {
+              if (!touchSlots[j]) {
+                slot = j;
+                break;
+              }
+            }
+            if (slot == -1) {
+              slot = touchSlots.length;
+              touchSlots.push(true);
+            }
+            slotArr.push(slot);
+            touchSlots[slot] = true;
+            touchIdSlotMap.set(changes[i].identifier, slot);
+            idArr.push(changes[i].identifier);
+          } else if (e.type == 'touchmove') {
+            // error
+            console.error('touchmove when no slot');
+            return;
+          } else if (e.type == 'touchend') {
+            // error
+            console.error('touchend when no slot');
+            return;
+          }
+        }
+      }
+    }
+
+    for (var i=0; i < xArr.length; i++) {
+      xArr[i] = xArr[i] * scaling;
+      yArr[i] = yArr[i] * scaling;
+
+      // Substract the offset produced by the difference in aspect ratio, if any.
+      if (scaleWidth) {
+        // Width was scaled, leaving excess content height, so subtract from y.
+        yArr[i] -= (elementHeight * scaling - videoHeight) / 2;
+      } else {
+        // Height was scaled, leaving excess content width, so subtract from x.
+        xArr[i] -= (elementWidth * scaling - videoWidth) / 2;
+      }
+
+      xArr[i] = Math.trunc(xArr[i]);
+      yArr[i] = Math.trunc(yArr[i]);
     }
 
     // NOTE: Rotation is handled automatically because the CSS rotation through
     // transforms also rotates the coordinates of events on the object.
 
-    deviceConnection.sendMousePosition(
-        {x: Math.trunc(x), y: Math.trunc(y), down, display_label});
+    deviceConnection.sendMultiTouch(
+    {idArr, xArr, yArr, down, slotArr, display_label});
   }
 
   function onKeyEvent(e) {
diff --git a/host/frontend/webrtc_operator/assets/js/cf_webrtc.js b/host/frontend/webrtc_operator/assets/js/cf_webrtc.js
index ac40070..fb9114b 100644
--- a/host/frontend/webrtc_operator/assets/js/cf_webrtc.js
+++ b/host/frontend/webrtc_operator/assets/js/cf_webrtc.js
@@ -154,15 +154,15 @@
 
   // TODO (b/124121375): This should probably be an array of pointer events and
   // have different properties.
-  sendMultiTouch({id, x, y, initialDown, slot, display_label}) {
+  sendMultiTouch({idArr, xArr, yArr, down, slotArr, display_label}) {
     this._sendJsonInput({
       type: 'multi-touch',
-      id,
-      x,
-      y,
-      initialDown: initialDown ? 1 : 0,
-      slot,
-      display_label,
+      id: idArr,
+      x: xArr,
+      y: yArr,
+      down: down ? 1 : 0,
+      slot: slotArr,
+      display_label: display_label,
     });
   }
 
diff --git a/host/libs/screen_connector/screen_connector.h b/host/libs/screen_connector/screen_connector.h
index b71dea4..dc0a367 100644
--- a/host/libs/screen_connector/screen_connector.h
+++ b/host/libs/screen_connector/screen_connector.h
@@ -24,7 +24,7 @@
 
 namespace cuttlefish {
 
-using FrameCallback = std::function<void(std::uint32_t /*frame_number*/,
+using FrameCallback = std::function<void(std::uint32_t /*display_number*/,
                                          std::uint8_t* /*frame_pixels*/)>;
 
 class ScreenConnector {
@@ -33,10 +33,9 @@
 
   virtual ~ScreenConnector() = default;
 
-  // Runs the given callback on the next available frame after the given
-  // frame number and returns true if successful.
-  virtual bool OnFrameAfter(std::uint32_t frame_number,
-                            const FrameCallback& frame_callback) = 0;
+  // Runs the given callback on the next available frame and returns true if
+  // successful.
+  virtual bool OnNextFrame(const FrameCallback& frame_callback) = 0;
 
   // Let the screen connector know when there are clients connected
   virtual void ReportClientsConnected(bool have_clients);
diff --git a/host/libs/screen_connector/socket_based_screen_connector.cpp b/host/libs/screen_connector/socket_based_screen_connector.cpp
index 9f5252f..93b3bc2 100644
--- a/host/libs/screen_connector/socket_based_screen_connector.cpp
+++ b/host/libs/screen_connector/socket_based_screen_connector.cpp
@@ -23,34 +23,103 @@
 namespace cuttlefish {
 
 SocketBasedScreenConnector::SocketBasedScreenConnector(int frames_fd) {
+  for (std::uint32_t i = 0; i < ScreenCount(); i++) {
+    display_helpers_.emplace_back(new DisplayHelper(i));
+  }
+
   screen_server_thread_ =
     std::thread([this, frames_fd]() { ServerLoop(frames_fd); });
-
-  buffer_size_ = ScreenSizeInBytes(/*display_number=*/0);
-  buffer_.resize(kNumBuffersPerDisplay * buffer_size_);
 }
 
-bool SocketBasedScreenConnector::OnFrameAfter(
-    std::uint32_t frame_number, const FrameCallback& frame_callback) {
-  int buffer_idx = WaitForNewFrameSince(&frame_number);
-  void* buffer = GetBuffer(buffer_idx);
-  frame_callback(frame_number, reinterpret_cast<uint8_t*>(buffer));
-  return true;
-}
+SocketBasedScreenConnector::DisplayHelper::DisplayHelper(
+    std::uint32_t display_number) : display_number_(display_number) {
+  buffer_size_ = ScreenSizeInBytes(display_number);
+  buffers_.resize(kNumBuffersPerDisplay * buffer_size_);
 
-int SocketBasedScreenConnector::WaitForNewFrameSince(std::uint32_t* seq_num) {
-  std::unique_lock<std::mutex> lock(new_frame_mtx_);
-  while (seq_num_ == *seq_num) {
-    new_frame_cond_var_.wait(lock);
+  for (std::uint32_t i = 0; i < kNumBuffersPerDisplay; i++) {
+    acquirable_buffers_indexes_.push_back(i);
   }
-  *seq_num = seq_num_;
-  return newest_buffer_;
 }
 
-void* SocketBasedScreenConnector::GetBuffer(int buffer_idx) {
-  if (buffer_idx < 0) return nullptr;
-  buffer_idx %= kNumBuffersPerDisplay;
-  return &buffer_[buffer_idx * buffer_size_];
+std::uint8_t* SocketBasedScreenConnector::DisplayHelper::AcquireNextBuffer() {
+  std::uint32_t acquired = 0;
+  {
+    std::lock_guard<std::mutex> lock(acquire_mutex_);
+
+    CHECK(!acquirable_buffers_indexes_.empty());
+    CHECK(!acquired_buffer_index_.has_value());
+
+    acquired = acquirable_buffers_indexes_.front();
+    acquirable_buffers_indexes_.pop_front();
+    acquired_buffer_index_ = acquired;
+  }
+
+  return GetBuffer(acquired);
+}
+
+void SocketBasedScreenConnector::DisplayHelper::PresentAcquiredBuffer() {
+  {
+    std::lock_guard<std::mutex> present_lock(present_mutex_);
+    {
+      std::lock_guard<std::mutex> acquire_lock(acquire_mutex_);
+      CHECK(acquired_buffer_index_.has_value());
+
+      if (present_buffer_index_) {
+        acquirable_buffers_indexes_.push_back(*present_buffer_index_);
+      }
+
+      present_buffer_index_ = *acquired_buffer_index_;
+      acquired_buffer_index_.reset();
+    }
+  }
+}
+
+bool SocketBasedScreenConnector::DisplayHelper::ConsumePresentBuffer(
+    const FrameCallback& frame_callback) {
+  {
+    std::lock_guard<std::mutex> present_lock(present_mutex_);
+
+    if (present_buffer_index_) {
+      std::uint32_t frame_buffer_index = *present_buffer_index_;
+      std::uint8_t* frame_bytes = GetBuffer(frame_buffer_index);
+      frame_callback(display_number_, frame_bytes);
+
+      {
+        std::lock_guard<std::mutex> acquire_lock(acquire_mutex_);
+        acquirable_buffers_indexes_.push_back(frame_buffer_index);
+      }
+
+      present_buffer_index_.reset();
+      return true;
+    } else {
+      return false;
+    }
+  }
+}
+
+std::uint8_t* SocketBasedScreenConnector::DisplayHelper::GetBuffer(
+    std::uint32_t buffer_index) {
+  return &buffers_[buffer_index * buffer_size_];
+}
+
+bool SocketBasedScreenConnector::OnNextFrame(const FrameCallback& frame_callback) {
+  while (true) {
+    std::unique_lock<std::mutex> lock(frame_available_mutex_);
+
+    for (std::size_t i = 0; i < display_helpers_.size(); i++) {
+      auto& display_helper = display_helpers_[frame_available_display_index];
+      if (display_helper->ConsumePresentBuffer(frame_callback)) {
+        return true;
+      }
+
+      frame_available_display_index =
+        (frame_available_display_index + 1) % display_helpers_.size();
+    }
+
+    frame_available_cond_var_.wait(lock);
+  }
+
+  return false;
 }
 
 void SocketBasedScreenConnector::ServerLoop(int frames_fd) {
@@ -65,8 +134,6 @@
     return;
   }
 
-  int current_buffer = 0;
-
   while (1) {
     LOG(DEBUG) << "Screen Connector accepting connections...";
     client_connection_ = SharedFD::Accept(*server);
@@ -76,12 +143,21 @@
     }
     ReportClientsConnected(have_clients_);
     while (client_connection_->IsOpen()) {
+      uint32_t display_number = 0;
+      if (client_connection_->Read(&display_number, sizeof(display_number)) < 0) {
+        LOG(ERROR) << "Failed to read from hwcomposer: " << client_connection_->StrError();
+        break;
+      }
+
       int32_t size = 0;
       if (client_connection_->Read(&size, sizeof(size)) < 0) {
         LOG(ERROR) << "Failed to read from hwcomposer: " << client_connection_->StrError();
         break;
       }
-      auto buff = reinterpret_cast<uint8_t*>(GetBuffer(current_buffer));
+
+      auto& display_helper = display_helpers_[display_number];
+
+      std::uint8_t* buff = display_helper->AcquireNextBuffer();
       while (size > 0) {
         auto read = client_connection_->Read(buff, size);
         if (read < 0) {
@@ -92,8 +168,10 @@
         size -= read;
         buff += read;
       }
-      BroadcastNewFrame(current_buffer);
-      current_buffer = (current_buffer + 1) % kNumBuffersPerDisplay;
+
+
+      display_helper->PresentAcquiredBuffer();
+      frame_available_cond_var_.notify_all();
     }
   }
 }
@@ -104,12 +182,4 @@
   (void)client_connection_->Write(&buffer, sizeof(buffer));
 }
 
-void SocketBasedScreenConnector::BroadcastNewFrame(int buffer_idx) {
-  {
-    std::lock_guard<std::mutex> lock(new_frame_mtx_);
-    seq_num_++;
-    newest_buffer_ = buffer_idx;
-  }
-  new_frame_cond_var_.notify_all();
-}
 } // namespace cuttlefish
diff --git a/host/libs/screen_connector/socket_based_screen_connector.h b/host/libs/screen_connector/socket_based_screen_connector.h
index 5033f5a..fad33bb 100644
--- a/host/libs/screen_connector/socket_based_screen_connector.h
+++ b/host/libs/screen_connector/socket_based_screen_connector.h
@@ -33,28 +33,55 @@
  public:
   explicit SocketBasedScreenConnector(int frames_fd);
 
-  bool OnFrameAfter(std::uint32_t frame_number,
-                    const FrameCallback& frame_callback) override;
+  bool OnNextFrame(const FrameCallback& frame_callback) override;
 
   void ReportClientsConnected(bool have_clients) override;
 
  private:
-  static constexpr std::uint32_t kNumBuffersPerDisplay = 4;
-
-  int WaitForNewFrameSince(std::uint32_t* seq_num);
-  void* GetBuffer(int buffer_idx);
   void ServerLoop(int frames_fd);
-  void BroadcastNewFrame(int buffer_idx);
 
-  std::size_t buffer_size_ = 0;
-  std::vector<std::uint8_t> buffer_;
-  std::uint32_t seq_num_{0};
-  int newest_buffer_ = 0;
-  std::condition_variable new_frame_cond_var_;
-  std::mutex new_frame_mtx_;
+  class DisplayHelper {
+   public:
+    DisplayHelper(std::uint32_t display_number);
+
+    DisplayHelper(const DisplayHelper&) = delete;
+    DisplayHelper& operator=(const DisplayHelper&) = delete;
+
+    DisplayHelper(DisplayHelper&&) = delete;
+    DisplayHelper& operator=(DisplayHelper&&) = delete;
+
+    std::uint8_t* AcquireNextBuffer();
+
+    void PresentAcquiredBuffer();
+
+    bool ConsumePresentBuffer(const FrameCallback& frame_callback);
+
+   private:
+    std::uint8_t* GetBuffer(std::uint32_t index);
+
+    static constexpr std::uint32_t kNumBuffersPerDisplay = 4;
+
+    std::uint32_t display_number_ = 0;
+
+    std::size_t buffer_size_ = 0;
+    std::vector<std::uint8_t> buffers_;
+
+    std::mutex acquire_mutex_;
+    std::deque<std::uint32_t> acquirable_buffers_indexes_;
+    std::optional<std::uint32_t> acquired_buffer_index_;
+
+    std::mutex present_mutex_;
+    std::optional<std::uint32_t> present_buffer_index_;
+  };
+
   std::thread screen_server_thread_;
   cuttlefish::SharedFD client_connection_;
   bool have_clients_ = false;
+  std::vector<std::unique_ptr<DisplayHelper>> display_helpers_;
+
+  std::mutex frame_available_mutex_;
+  std::condition_variable frame_available_cond_var_;
+  std::size_t frame_available_display_index = 0;
 };
 
 } // namespace cuttlefish
diff --git a/host/libs/screen_connector/wayland_screen_connector.cpp b/host/libs/screen_connector/wayland_screen_connector.cpp
index 7713214..6c8b9c6 100644
--- a/host/libs/screen_connector/wayland_screen_connector.cpp
+++ b/host/libs/screen_connector/wayland_screen_connector.cpp
@@ -19,8 +19,6 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-#include <future>
-
 #include <android-base/logging.h>
 
 #include "host/libs/wayland/wayland_server.h"
@@ -35,13 +33,8 @@
   server_.reset(new wayland::WaylandServer(wayland_fd));
 }
 
-bool WaylandScreenConnector::OnFrameAfter(
-    std::uint32_t frame_number, const FrameCallback& frame_callback) {
-  std::future<void> frame_callback_completed_future =
-      server_->OnFrameAfter(frame_number, frame_callback);
-
-  frame_callback_completed_future.get();
-
+bool WaylandScreenConnector::OnNextFrame(const FrameCallback& frame_callback) {
+  server_->OnNextFrame(frame_callback);
   return true;
 }
 
diff --git a/host/libs/screen_connector/wayland_screen_connector.h b/host/libs/screen_connector/wayland_screen_connector.h
index a3ab518..fd742bd 100644
--- a/host/libs/screen_connector/wayland_screen_connector.h
+++ b/host/libs/screen_connector/wayland_screen_connector.h
@@ -28,8 +28,7 @@
  public:
   WaylandScreenConnector(int frames_fd);
 
-  bool OnFrameAfter(std::uint32_t frame_number,
-                    const FrameCallback& frame_callback) override;
+  bool OnNextFrame(const FrameCallback& frame_callback) override;
 
  private:
   std::unique_ptr<wayland::WaylandServer> server_;
diff --git a/host/libs/wayland/Android.bp b/host/libs/wayland/Android.bp
index 397ee12..ec143ee 100644
--- a/host/libs/wayland/Android.bp
+++ b/host/libs/wayland/Android.bp
@@ -23,6 +23,7 @@
         "wayland_server.cpp",
         "wayland_subcompositor.cpp",
         "wayland_surface.cpp",
+        "wayland_surfaces.cpp",
     ],
     shared_libs: [
         "libbase",
diff --git a/host/libs/wayland/wayland_compositor.cpp b/host/libs/wayland/wayland_compositor.cpp
index 96fdd6d..6179092 100644
--- a/host/libs/wayland/wayland_compositor.cpp
+++ b/host/libs/wayland/wayland_compositor.cpp
@@ -21,6 +21,7 @@
 #include <wayland-server-core.h>
 #include <wayland-server-protocol.h>
 
+#include "host/libs/wayland/wayland_surface.h"
 #include "host/libs/wayland/wayland_utils.h"
 
 namespace wayland {
@@ -190,7 +191,12 @@
                << " compositor=" << compositor
                << " id=" << id;
 
-  Surface* surface = GetUserData<Surface>(compositor);
+  // Wayland seems to use a single global id space for all objects.
+  static std::atomic<std::uint32_t> sNextDisplayId{0};
+  uint32_t display_id = sNextDisplayId++;
+
+  Surfaces* surfaces = GetUserData<Surfaces>(compositor);
+  Surface* surface = surfaces->GetOrCreateSurface(display_id);
 
   wl_resource* surface_resource = wl_resource_create(
       client, &wl_surface_interface, wl_resource_get_version(compositor), id);
@@ -239,9 +245,9 @@
 
 }  // namespace
 
-void BindCompositorInterface(wl_display* display, Surface* surface) {
+void BindCompositorInterface(wl_display* display, Surfaces* surfaces) {
   wl_global_create(display, &wl_compositor_interface, kCompositorVersion,
-                   surface, bind_compositor);
+                   surfaces, bind_compositor);
 }
 
 }  // namespace wayland
diff --git a/host/libs/wayland/wayland_compositor.h b/host/libs/wayland/wayland_compositor.h
index 5054c40..18f0d7e 100644
--- a/host/libs/wayland/wayland_compositor.h
+++ b/host/libs/wayland/wayland_compositor.h
@@ -21,11 +21,11 @@
 
 #include <wayland-server-core.h>
 
-#include "host/libs/wayland/wayland_surface.h"
+#include "host/libs/wayland/wayland_surfaces.h"
 
 namespace wayland {
 
 // Binds the compositor interface to the given wayland server.
-void BindCompositorInterface(wl_display* display, Surface* surface);
+void BindCompositorInterface(wl_display* display, Surfaces* surfaces);
 
 }  // namespace wayland
\ No newline at end of file
diff --git a/host/libs/wayland/wayland_server.cpp b/host/libs/wayland/wayland_server.cpp
index 699fda2..4dc4b88 100644
--- a/host/libs/wayland/wayland_server.cpp
+++ b/host/libs/wayland/wayland_server.cpp
@@ -35,7 +35,7 @@
 struct WaylandServerState {
   struct wl_display* display_ = nullptr;
 
-  Surface surface_;
+  Surfaces surfaces_;
 };
 
 }  // namespace internal
@@ -78,7 +78,7 @@
 
   wl_display_init_shm(server_state_->display_);
 
-  BindCompositorInterface(server_state_->display_, &server_state_->surface_);
+  BindCompositorInterface(server_state_->display_, &server_state_->surfaces_);
   BindDmabufInterface(server_state_->display_);
   BindSubcompositorInterface(server_state_->display_);
   BindSeatInterface(server_state_->display_);
@@ -94,20 +94,8 @@
   wl_display_destroy(server_state_->display_);
 }
 
-std::future<void> WaylandServer::OnFrameAfter(
-    uint32_t frame_number,
-    const FrameCallback& frame_callback) {
-  // Wraps the given callback in a callback that can be waited on using
-  // std::future.
-  Surface::FrameCallbackPackaged packaged([&frame_callback](std::uint32_t frame_number,
-                                                   std::uint8_t* frame_pixels){
-    frame_callback(frame_number, frame_pixels);
-  });
-
-  std::future<void> frame_callback_complete = packaged.get_future();
-
-  server_state_->surface_.OnFrameAfter(frame_number, std::move(packaged));
-  return frame_callback_complete;
+void WaylandServer::OnNextFrame(const Surfaces::FrameCallback& callback) {
+  server_state_->surfaces_.OnNextFrame(callback);
 }
 
-}  // namespace wayland
\ No newline at end of file
+}  // namespace wayland
diff --git a/host/libs/wayland/wayland_server.h b/host/libs/wayland/wayland_server.h
index e73d74a..b51818a 100644
--- a/host/libs/wayland/wayland_server.h
+++ b/host/libs/wayland/wayland_server.h
@@ -24,15 +24,14 @@
 #include <string>
 #include <thread>
 
+#include "host/libs/wayland/wayland_surfaces.h"
+
 namespace wayland {
 
 namespace internal {
 struct WaylandServerState;
 }  // namespace internal
 
-using FrameCallback = std::function<void(std::uint32_t /*frame_number*/,
-                                         std::uint8_t* /*frame_pixels*/)>;
-
 // A Wayland compositing server that provides an interface for receiving frame
 // updates from a connected client.
 class WaylandServer {
@@ -49,10 +48,8 @@
     WaylandServer(WaylandServer&& rhs) = delete;
     WaylandServer& operator=(WaylandServer&& rhs) = delete;
 
-    // Registers a callback to run on the next frame available after the given
-    // frame number.
-    std::future<void> OnFrameAfter(std::uint32_t frame_number,
-                                   const FrameCallback& frame_callback);
+    // Blocks until the given callback is run on the next frame available.
+    void OnNextFrame(const Surfaces::FrameCallback& callback);
 
   private:
     void ServerLoop(int wayland_socket_fd);
diff --git a/host/libs/wayland/wayland_surface.cpp b/host/libs/wayland/wayland_surface.cpp
index eb4b401..f4c80ca 100644
--- a/host/libs/wayland/wayland_surface.cpp
+++ b/host/libs/wayland/wayland_surface.cpp
@@ -19,8 +19,13 @@
 #include <android-base/logging.h>
 #include <wayland-server-protocol.h>
 
+#include "host/libs/wayland/wayland_surfaces.h"
+
 namespace wayland {
 
+Surface::Surface(std::uint32_t display_number, Surfaces& surfaces)
+  : display_number_(display_number), surfaces_(surfaces) {}
+
 void Surface::SetRegion(const Region& region) {
   std::unique_lock<std::mutex> lock(state_mutex_);
   state_.region = region;
@@ -36,29 +41,22 @@
   state_.current_buffer = state_.pending_buffer;
   state_.pending_buffer = nullptr;
 
-  {
-    std::unique_lock<std::mutex> lock(callback_mutex_);
-    if (callback_ && callback_->frame_number < state_.current_frame_number) {
-      struct wl_shm_buffer* shm_buffer =
-          wl_shm_buffer_get(state_.current_buffer);
-      CHECK(shm_buffer != nullptr);
+  struct wl_shm_buffer* shm_buffer = wl_shm_buffer_get(state_.current_buffer);
+  CHECK(shm_buffer != nullptr);
 
-      wl_shm_buffer_begin_access(shm_buffer);
+  wl_shm_buffer_begin_access(shm_buffer);
 
-      const int32_t buffer_w = wl_shm_buffer_get_width(shm_buffer);
-      CHECK(buffer_w == state_.region.w);
-      const int32_t buffer_h = wl_shm_buffer_get_height(shm_buffer);
-      CHECK(buffer_h == state_.region.h);
+  const int32_t buffer_w = wl_shm_buffer_get_width(shm_buffer);
+  CHECK(buffer_w == state_.region.w);
+  const int32_t buffer_h = wl_shm_buffer_get_height(shm_buffer);
+  CHECK(buffer_h == state_.region.h);
 
-      uint8_t* buffer_pixels =
-          reinterpret_cast<uint8_t*>(wl_shm_buffer_get_data(shm_buffer));
+  uint8_t* buffer_pixels =
+      reinterpret_cast<uint8_t*>(wl_shm_buffer_get_data(shm_buffer));
 
-      callback_->frame_callback(state_.current_frame_number, buffer_pixels);
-      callback_.reset();
+  surfaces_.HandleSurfaceFrame(display_number_, buffer_pixels);
 
-      wl_shm_buffer_end_access(shm_buffer);
-    }
-  }
+  wl_shm_buffer_end_access(shm_buffer);
 
   wl_buffer_send_release(state_.current_buffer);
   wl_client_flush(wl_resource_get_client(state_.current_buffer));
@@ -67,11 +65,4 @@
   state_.current_frame_number++;
 }
 
-void Surface::OnFrameAfter(uint32_t frame_number,
-                           FrameCallbackPackaged frame_callback) {
-  std::unique_lock<std::mutex> lock(callback_mutex_);
-  callback_.emplace(PendingFrameCallback{frame_number,
-                                         std::move(frame_callback)});
-}
-
 }  // namespace wayland
\ No newline at end of file
diff --git a/host/libs/wayland/wayland_surface.h b/host/libs/wayland/wayland_surface.h
index e0d0a12..ceb85b1 100644
--- a/host/libs/wayland/wayland_surface.h
+++ b/host/libs/wayland/wayland_surface.h
@@ -17,19 +17,19 @@
 
 #pragma once
 
-#include <optional>
+#include <mutex>
 #include <stdint.h>
-#include <thread>
-#include <future>
 
 #include <wayland-server-core.h>
 
 namespace wayland {
 
+class Surfaces;
+
 // Tracks the buffer associated with a Wayland surface.
 class Surface {
  public:
-  Surface() = default;
+  Surface(std::uint32_t display_number, Surfaces& surfaces);
   virtual ~Surface() = default;
 
   Surface(const Surface& rhs) = delete;
@@ -53,16 +53,10 @@
   // Commits the pending frame state.
   void Commit();
 
-  using FrameCallbackPackaged =
-    std::packaged_task<void(std::uint32_t /*frame_number*/,
-                            std::uint8_t* /*frame_pixels*/)>;
-
-  // Registers a callback that should be invoked on the first frame after
-  // the given frame number.
-  void OnFrameAfter(uint32_t frame_number,
-                    FrameCallbackPackaged frame_callback);
-
  private:
+  std::uint32_t display_number_;
+  Surfaces& surfaces_;
+
   struct State {
     uint32_t current_frame_number = 0;
 
@@ -78,15 +72,6 @@
 
   std::mutex state_mutex_;
   State state_;
-
-  struct PendingFrameCallback {
-    uint32_t frame_number;
-
-    FrameCallbackPackaged frame_callback;
-  };
-
-  std::mutex callback_mutex_;
-  std::optional<PendingFrameCallback> callback_;
 };
 
-}  // namespace wayland
\ No newline at end of file
+}  // namespace wayland
diff --git a/host/libs/wayland/wayland_surfaces.cpp b/host/libs/wayland/wayland_surfaces.cpp
new file mode 100644
index 0000000..ca83b51
--- /dev/null
+++ b/host/libs/wayland/wayland_surfaces.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/libs/wayland/wayland_surfaces.h"
+
+#include <android-base/logging.h>
+#include <wayland-server-protocol.h>
+
+#include "host/libs/wayland/wayland_surface.h"
+
+namespace wayland {
+
+Surface* Surfaces::GetOrCreateSurface(std::uint32_t id) {
+  std::unique_lock<std::mutex> lock(surfaces_mutex_);
+
+  auto [it, inserted] = surfaces_.try_emplace(id, nullptr);
+
+  std::unique_ptr<Surface>& surface_ptr = it->second;
+  if (inserted) {
+    surface_ptr.reset(new Surface(id, *this));
+  }
+  return surface_ptr.get();
+}
+
+void Surfaces::OnNextFrame(const FrameCallback& frame_callback) {
+  // Wraps the given callback in a std::package_task that can be waited upon
+  // for completion.
+  Surfaces::FrameCallbackPackaged frame_callback_packaged(
+    [&frame_callback](std::uint32_t display_number,
+                      std::uint8_t* frame_pixels){
+    frame_callback(display_number, frame_pixels);
+  });
+
+  {
+    std::unique_lock<std::mutex> lock(callback_mutex_);
+    callback_.emplace(&frame_callback_packaged);
+  }
+
+  // Blocks until the frame_callback_packaged was called.
+  frame_callback_packaged.get_future().get();
+}
+
+void Surfaces::HandleSurfaceFrame(std::uint32_t display_number,
+                                  std::uint8_t* frame_bytes) {
+  std::unique_lock<std::mutex> lock(callback_mutex_);
+  if (callback_) {
+    (*callback_.value())(display_number, frame_bytes);
+    callback_.reset();
+  }
+}
+
+}  // namespace wayland
\ No newline at end of file
diff --git a/host/libs/wayland/wayland_surfaces.h b/host/libs/wayland/wayland_surfaces.h
new file mode 100644
index 0000000..5d825c8
--- /dev/null
+++ b/host/libs/wayland/wayland_surfaces.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#pragma once
+
+#include <future>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <stdint.h>
+#include <thread>
+#include <unordered_map>
+
+namespace wayland {
+
+class Surface;
+
+class Surfaces {
+ public:
+  Surfaces() = default;
+  virtual ~Surfaces() = default;
+
+  Surfaces(const Surfaces& rhs) = delete;
+  Surfaces& operator=(const Surfaces& rhs) = delete;
+
+  Surfaces(Surfaces&& rhs) = delete;
+  Surfaces& operator=(Surfaces&& rhs) = delete;
+
+  Surface* GetOrCreateSurface(std::uint32_t id);
+
+  using FrameCallback = std::function<void(std::uint32_t /*display_number*/,
+                                           std::uint8_t* /*frame_pixels*/)>;
+
+  // Blocking
+  void OnNextFrame(const FrameCallback& callback);
+
+ private:
+  friend class Surface;
+  void HandleSurfaceFrame(std::uint32_t display_number,
+                          std::uint8_t* frame_bytes);
+
+  std::mutex surfaces_mutex_;
+  std::unordered_map<std::uint32_t, std::unique_ptr<Surface>> surfaces_;
+
+  using FrameCallbackPackaged =
+    std::packaged_task<void(std::uint32_t /*display_number*/,
+                            std::uint8_t* /*frame_bytes*/)>;
+
+  std::mutex callback_mutex_;
+  std::optional<FrameCallbackPackaged*> callback_;
+};
+
+}  // namespace wayland
diff --git a/rustfmt.toml b/rustfmt.toml
deleted file mode 100644
index 617d425..0000000
--- a/rustfmt.toml
+++ /dev/null
@@ -1,5 +0,0 @@
-# Android Format Style
-
-edition = "2018"
-use_small_heuristics = "Max"
-newline_style = "Unix"
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 120000
index 0000000..475ba8f
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+../../../build/soong/scripts/rustfmt.toml
\ No newline at end of file
diff --git a/shared/auto/device.mk b/shared/auto/device.mk
index fe15d28..347a29c 100644
--- a/shared/auto/device.mk
+++ b/shared/auto/device.mk
@@ -31,7 +31,6 @@
     packages/services/Car/car_product/init/init.bootstat.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw//init.bootstat.rc \
     packages/services/Car/car_product/init/init.car.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw//init.car.rc
 
-
 ifneq ($(LOCAL_SENSOR_FILE_OVERRIDES),true)
     PRODUCT_COPY_FILES += \
         frameworks/native/data/etc/android.hardware.sensor.accelerometer.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.accelerometer.xml \
diff --git a/shared/config/init.product.rc b/shared/config/init.product.rc
index 111a4c6..22371a3 100644
--- a/shared/config/init.product.rc
+++ b/shared/config/init.product.rc
@@ -6,9 +6,3 @@
     class late_start
     group system
     user root
-
-# TODO: disable this service once cuttlefish implements system suspend
-service suspend_blocker /product/bin/suspend_blocker
-    class early_hal # Star together with system_suspend HAL
-    group system
-    user root
diff --git a/shared/config/init.system_ext.rc b/shared/config/init.system_ext.rc
new file mode 100644
index 0000000..6c34305
--- /dev/null
+++ b/shared/config/init.system_ext.rc
@@ -0,0 +1,5 @@
+# TODO: disable this service once cuttlefish implements system suspend
+service suspend_blocker /system_ext/bin/suspend_blocker
+    class early_hal # Start together with system_suspend HAL
+    group system
+    user root
diff --git a/shared/device.mk b/shared/device.mk
index e9748fd..a3ab713 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -63,6 +63,7 @@
 PRODUCT_PRODUCT_PROPERTIES += \
     persist.adb.tcp.port=5555 \
     ro.com.google.locationfeatures=1 \
+    persist.sys.fuse.passthrough.enable=true \
 
 # Explanation of specific properties:
 #   debug.hwui.swap_with_damage avoids boot failure on M http://b/25152138
@@ -226,6 +227,7 @@
     hardware/google/camera/devices/EmulatedCamera/hwl/configs/emu_camera_depth.json:$(TARGET_COPY_OUT_VENDOR)/etc/config/emu_camera_depth.json \
     device/google/cuttlefish/shared/config/init.vendor.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw/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/init.system_ext.rc:$(TARGET_COPY_OUT_SYSTEM_EXT)/etc/init/init.rc \
     device/google/cuttlefish/shared/config/ueventd.rc:$(TARGET_COPY_OUT_VENDOR)/ueventd.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 \
@@ -281,7 +283,8 @@
 PRODUCT_COPY_FILES += \
     frameworks/native/data/etc/android.hardware.vulkan.level-0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.vulkan.level.xml \
     frameworks/native/data/etc/android.hardware.vulkan.version-1_0_3.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.vulkan.version.xml \
-    frameworks/native/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.vulkan.deqp.level.xml
+    frameworks/native/data/etc/android.software.vulkan.deqp.level-2021-03-01.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.vulkan.deqp.level.xml \
+    frameworks/native/data/etc/android.software.opengles.deqp.level-2021-03-01.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.opengles.deqp.level.xml
 endif
 
 # Packages for HAL implementations
@@ -449,6 +452,15 @@
     $(LOCAL_KEYMASTER_PRODUCT_PACKAGE)
 
 #
+# KeyMint HAL
+#
+ifeq ($(LOCAL_KEYMINT_PRODUCT_PACKAGE),)
+       LOCAL_KEYMINT_PRODUCT_PACKAGE := android.hardware.security.keymint-service
+endif
+PRODUCT_PACKAGES += \
+    $(LOCAL_KEYMINT_PRODUCT_PACKAGE)
+
+#
 # Power HAL
 #
 PRODUCT_PACKAGES += \
diff --git a/shared/sepolicy/product/private/file_contexts b/shared/sepolicy/product/private/file_contexts
index f0255a0..0b92144 100644
--- a/shared/sepolicy/product/private/file_contexts
+++ b/shared/sepolicy/product/private/file_contexts
@@ -1,5 +1,4 @@
 #############################
 # Product files
 #
-/product/bin/suspend_blocker     u:object_r:suspend_blocker_exec:s0
 /product/bin/tombstone_transmit  u:object_r:tombstone_transmit_exec:s0
diff --git a/shared/sepolicy/system_ext/private/file_contexts b/shared/sepolicy/system_ext/private/file_contexts
index fa35cd3..5d68b20 100644
--- a/shared/sepolicy/system_ext/private/file_contexts
+++ b/shared/sepolicy/system_ext/private/file_contexts
@@ -1 +1,6 @@
 /data/vendor/radio(/.*)?               u:object_r:radio_vendor_data_file:s0
+
+#############################
+# System_ext files
+#
+/system_ext/bin/suspend_blocker     u:object_r:suspend_blocker_exec:s0
diff --git a/tests/hal/hal_implementation_test.cpp b/tests/hal/hal_implementation_test.cpp
index ab0affd..840c739 100644
--- a/tests/hal/hal_implementation_test.cpp
+++ b/tests/hal/hal_implementation_test.cpp
@@ -107,11 +107,6 @@
     // b/170144267
     "android.system.keystore2.",
 
-    // The keymint service implementation is in progress but we cannot register
-    // the service by default til all essential features landed.
-    // b/171429297
-    "android.hardware.keymint.",
-
     // These KeyMaster types are in an AIDL types-only HAL because they're used
     // by the Identity Credential AIDL HAL. Remove this when fully porting
     // KeyMaster to AIDL.