Allow the vnc server to run when using crosvm as vmm

The vnc server sends input through unix sockets to crosvm's virtio
input devices and receives screen updates through a vsock socket.
Performance is not the best, but it's usable.

Bug: 128852363
Bug: 127361363
Test: run locally
Change-Id: I2b2be2c3f338e7624e390879293ce61186dded38
diff --git a/guest/hals/hwcomposer/cutf_cvm/Android.bp b/guest/hals/hwcomposer/cutf_cvm/Android.bp
index 7bf1faa..9e3962b 100644
--- a/guest/hals/hwcomposer/cutf_cvm/Android.bp
+++ b/guest/hals/hwcomposer/cutf_cvm/Android.bp
@@ -18,9 +18,27 @@
     relative_install_path: "hw",
     defaults: ["cuttlefish_guest_only"],
     vendor: true,
-    srcs: ["hwcomposer.cpp"],
-    include_dirs: ["device/google/cuttlefish_common"],
+    srcs: [
+        "hwcomposer.cpp",
+        "geometry_utils.cpp",
+        "vsoc_composer.cpp",
+        "base_composer.cpp",
+    ],
+    include_dirs: [
+        "device/google/cuttlefish_common",
+    ],
     export_include_dirs: ["."],
     static_libs: ["libyuv_static", "hwcomposer_common"],
-    shared_libs: ["liblog", "libhardware"],
+    shared_libs: [
+        "liblog",
+        "libhardware",
+        "libbase",
+        "libcutils",
+        "libutils",
+        "libsync",
+        "libhardware",
+        "libjpeg",
+        "libcuttlefish_utils",
+        "libcuttlefish_fs",
+    ],
 }
diff --git a/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp b/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp
new file mode 100644
index 0000000..cda2750
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 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 "base_composer.h"
+
+#include <string.h>
+
+#include <cutils/properties.h>
+#include <hardware/gralloc.h>
+#include <log/log.h>
+
+#include <common/libs/utils/size_utils.h>
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+
+namespace cvd {
+
+BaseComposer::BaseComposer(int64_t vsync_base_timestamp)
+    : vsync_base_timestamp_(vsync_base_timestamp) {
+  vsync_period_ns_ = 1000000000 / frame_buffer_.refresh_rate();
+  hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+                reinterpret_cast<const hw_module_t**>(&gralloc_module_));
+}
+
+BaseComposer::~BaseComposer() {}
+
+void BaseComposer::Dump(char* buff __unused, int buff_len __unused) {}
+
+int BaseComposer::PostFrameBufferTarget(buffer_handle_t buffer_handle) {
+  int fb_index = frame_buffer_.NextScreenBuffer();
+  void* frame_buffer = frame_buffer_.GetBuffer(fb_index);
+  const private_handle_t* p_handle =
+      reinterpret_cast<const private_handle_t*>(buffer_handle);
+  void* buffer;
+  int retval = gralloc_module_->lock(gralloc_module_, buffer_handle,
+                                     GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
+                                     p_handle->x_res, p_handle->y_res, &buffer);
+  if (retval != 0) {
+    ALOGE("Got error code %d from lock function", retval);
+    return -1;
+  }
+  memcpy(frame_buffer, buffer, frame_buffer_.buffer_size());
+  frame_buffer_.Broadcast(fb_index);
+  return 0;
+}  // namespace cvd
+
+int BaseComposer::PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+  // find unsupported overlays
+  for (size_t i = 0; i < num_layers; i++) {
+    if (IS_TARGET_FRAMEBUFFER(layers[i].compositionType)) {
+      continue;
+    }
+    layers[i].compositionType = HWC_FRAMEBUFFER;
+  }
+  return 0;
+}
+
+int BaseComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+  for (size_t idx = 0; idx < num_layers; idx++) {
+    if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+      return PostFrameBufferTarget(layers[idx].handle);
+    }
+  }
+  return -1;
+}
+
+FrameBuffer::FrameBuffer()
+    : screen_server_(cvd::SharedFD::VsockClient(
+          2, property_get_int32("ro.boot.vsock_frames_port", 5580),
+          SOCK_STREAM)),
+      broadcast_thread_([this]() { BroadcastLoop(); }) {
+  // TODO(b/128842613): Get this info from the configuration server
+  int32_t screen_params[4];
+  auto res = screen_server_->Read(screen_params, sizeof(screen_params));
+  if (res != sizeof(screen_params)) {
+    LOG(ERROR) << "Unable to get screen configuration parameters from screen "
+               << "server (" << res << "): " << screen_server_->StrError();
+    return;
+  }
+  x_res_ = screen_params[0];
+  y_res_ = screen_params[1];
+  dpi_ = screen_params[2];
+  refresh_rate_ = screen_params[3];
+  inner_buffer_ = std::vector<char>(FrameBuffer::buffer_size() * 8);
+}
+
+FrameBuffer::~FrameBuffer() {
+  running_ = false;
+  broadcast_thread_.join();
+}
+
+int FrameBuffer::NextScreenBuffer() {
+  int num_buffers = inner_buffer_.size() / buffer_size();
+  last_frame_buffer_ =
+      num_buffers > 0 ? (last_frame_buffer_ + 1) % num_buffers : -1;
+  return last_frame_buffer_;
+}
+
+void FrameBuffer::BroadcastLoop() {
+  int32_t current_seq = 0;
+  int32_t current_offset;
+  while (running_) {
+    {
+      std::unique_lock<std::mutex> lock(mutex_);
+      while (current_seq == current_seq_) {
+        cond_var_.wait(lock);
+      }
+      current_offset = current_offset_;
+      current_seq = current_seq_;
+    }
+    int32_t size = buffer_size();
+    screen_server_->Write(&size, sizeof(size));
+    auto buff = static_cast<char*>(GetBuffer(current_offset));
+    while (size > 0) {
+      auto written = screen_server_->Write(buff, size);
+      size -= written;
+      buff += written;
+    }
+  }
+}
+
+void FrameBuffer::Broadcast(int32_t offset) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  current_offset_ = offset;
+  current_seq_++;
+  cond_var_.notify_all();
+}
+void* FrameBuffer::GetBuffer(int fb_index) {
+  return &inner_buffer_[buffer_size() * fb_index];
+}
+size_t FrameBuffer::buffer_size() {
+  return (line_length() * y_res()) + 4;
+}
+int32_t FrameBuffer::x_res() { return x_res_; }
+int32_t FrameBuffer::y_res() { return y_res_; }
+int32_t FrameBuffer::line_length() {
+  return cvd::AlignToPowerOf2(x_res() * bytes_per_pixel(), 4);
+}
+int32_t FrameBuffer::bytes_per_pixel() { return 4; }
+int32_t FrameBuffer::dpi() { return dpi_; }
+int32_t FrameBuffer::refresh_rate() { return refresh_rate_; }
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/base_composer.h b/guest/hals/hwcomposer/cutf_cvm/base_composer.h
new file mode 100644
index 0000000..5a23176
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/base_composer.h
@@ -0,0 +1,98 @@
+#pragma once
+/*
+ * Copyright (C) 2016 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 <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <hardware/gralloc.h>
+#include <common/libs/fs/shared_fd.h>
+#include "hwcomposer.h"
+
+namespace cvd {
+
+class FrameBuffer{
+ public:
+  FrameBuffer();
+  ~FrameBuffer();
+
+  void Broadcast(int32_t offset);
+  int NextScreenBuffer();
+  void* GetBuffer(int fb_index);
+  size_t buffer_size();
+  int32_t x_res();
+  int32_t y_res();
+  int32_t line_length();
+  int32_t bytes_per_pixel();
+  int32_t dpi();
+  int32_t refresh_rate();
+ private:
+  void BroadcastLoop();
+
+  std::vector<char> inner_buffer_;
+  int last_frame_buffer_ = 0;
+  cvd::SharedFD screen_server_;
+  std::thread broadcast_thread_;
+  int32_t current_offset_ = 0;
+  int32_t current_seq_ = 0;
+  std::mutex mutex_;
+  std::condition_variable cond_var_;
+  bool running_ = true;
+  int32_t x_res_{720};
+  int32_t y_res_{1280};
+  int32_t dpi_{160};
+  int32_t refresh_rate_{60};
+};
+
+class BaseComposer {
+ public:
+  BaseComposer(int64_t vsync_base_timestamp);
+  ~BaseComposer();
+
+  // Sets the composition type of each layer and returns the number of layers
+  // to be composited by the hwcomposer.
+  int PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers);
+  // Returns 0 if successful.
+  int SetLayers(size_t num_layers, vsoc_hwc_layer* layers);
+  void Dump(char* buff, int buff_len);
+
+  int32_t x_res() {
+    return frame_buffer_.x_res();
+  }
+  int32_t y_res() {
+    return frame_buffer_.y_res();
+  }
+  int32_t dpi() {
+    return frame_buffer_.dpi();
+  }
+  int32_t refresh_rate() {
+    return frame_buffer_.refresh_rate();
+  }
+
+ protected:
+  const gralloc_module_t* gralloc_module_;
+  int64_t vsync_base_timestamp_;
+  int32_t vsync_period_ns_;
+  FrameBuffer frame_buffer_;
+
+ private:
+  // Returns buffer offset or negative on error.
+  int PostFrameBufferTarget(buffer_handle_t handle);
+};
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp
new file mode 100644
index 0000000..75b7a61
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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 "geometry_utils.h"
+#include <algorithm>
+#include <utility>
+
+namespace cvd {
+
+bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2) {
+  int left1 = layer1.displayFrame.left;
+  int right1 = layer1.displayFrame.right;
+  int top1 = layer1.displayFrame.top;
+  int bottom1 = layer1.displayFrame.bottom;
+
+  int left2 = layer2.displayFrame.left;
+  int right2 = layer2.displayFrame.right;
+  int top2 = layer2.displayFrame.top;
+  int bottom2 = layer2.displayFrame.bottom;
+
+  bool overlap_x = left1 < right2 && left2 < right1;
+  bool overlap_y = top1 < bottom2 && top2 < bottom1;
+
+  return overlap_x && overlap_y;
+}
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
new file mode 100644
index 0000000..937283f
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
@@ -0,0 +1,24 @@
+#pragma once
+/*
+ * Copyright (C) 2016 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 "hwcomposer.h"
+
+namespace cvd {
+
+bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
index 954688e..3db7f80 100644
--- a/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
+++ b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,207 +14,470 @@
  * limitations under the License.
  */
 
+// Versions of hwcomposer we implement:
+// JB: 0.3
+// JB-MR1 to N : 1.1
+// N-MR1 to ... : We report 1.1 but SurfaceFlinger has the option to use an
+// adapter to treat our 1.1 hwcomposer as a 2.0. If SF stops using that adapter
+// to support 1.1 implementations it can be copied into cuttlefish from
+// frameworks/native/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.*
+
 #define LOG_TAG "hwc.cf_x86"
-#define HWC_REMOVE_DEPRECATED_VERSIONS 1
-#define IS_TARGET_FRAMEBUFFER(x) ((x) == HWC_FRAMEBUFFER_TARGET)
-#define IS_PRIMARY_DISPLAY(x) ((x) == HWC_DISPLAY_PRIMARY)
-#define CUTF_CVM_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_1_1
+
+#include <guest/libs/platform_support/api_level_fixes.h>
 
 #include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <poll.h>
 #include <pthread.h>
 #include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
 #include <string>
 
-#include "log/log.h"
+#define HWC_REMOVE_DEPRECATED_VERSIONS 1
+
+#include <cutils/compiler.h>
+#include <log/log.h>
+#include <cutils/properties.h>
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
 #include "guest/hals/hwcomposer/common/hwcomposer.h"
-#include "guest/libs/platform_support/api_level_fixes.h"
-#include "hardware/hwcomposer.h"
-#include "hardware/hwcomposer_defs.h"
+#include <sync/sync.h>
 
-typedef hwc_composer_device_1_t cutf_cvm_hwc_device;
+#include "base_composer.h"
+#include "geometry_utils.h"
+#include "hwcomposer.h"
+#include "vsoc_composer.h"
 
-struct cutf_cvm_hwc_composer_device_1_t {
-  cutf_cvm_hwc_device base;
+#ifdef USE_OLD_HWCOMPOSER
+typedef cvd::BaseComposer InnerComposerType;
+#else
+typedef cvd::VSoCComposer InnerComposerType;
+#endif
+
+typedef InnerComposerType ComposerType;
+
+struct vsoc_hwc_composer_device_1_t {
+  vsoc_hwc_device base;
   cvd::hwc_composer_device_data_t vsync_data;
+  ComposerType* composer;
 };
 
-static int cutf_cvm_hwc_prepare(cutf_cvm_hwc_device* dev __unused, size_t numDisplays,
-                            hwc_display_contents_1_t** displays) {
-  hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
-  if (!numDisplays || !displays || !list)
-    return 0;
+namespace {
 
-  for (size_t i = 0; i < list->numHwLayers; i++) {
-    if (IS_TARGET_FRAMEBUFFER(list->hwLayers[i].compositionType)) {
-      continue;
-    }
-    list->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
+std::string CompositionString(int type) {
+  switch (type) {
+    case HWC_FRAMEBUFFER:
+      return "Framebuffer";
+    case HWC_OVERLAY:
+      return "Overlay";
+    case HWC_BACKGROUND:
+      return "Background";
+    case HWC_FRAMEBUFFER_TARGET:
+      return "FramebufferTarget";
+#if VSOC_PLATFORM_SDK_AFTER(K)
+    case HWC_SIDEBAND:
+      return "Sideband";
+    case HWC_CURSOR_OVERLAY:
+      return "CursorOverlay";
+#endif
+    default:
+      return std::string("Unknown (") + std::to_string(type) + ")";
   }
+}
+
+void LogLayers(int num_layers, vsoc_hwc_layer* layers, int invalid) {
+  ALOGE("Layers:");
+  for (int idx = 0; idx < num_layers; ++idx) {
+    std::string log_line;
+    if (idx == invalid) {
+      log_line = "Invalid layer: ";
+    }
+    log_line +=
+        "Composition Type: " + CompositionString(layers[idx].compositionType);
+    ALOGE("%s", log_line.c_str());
+  }
+}
+
+// Ensures that the layer does not include any inconsistencies
+bool IsValidLayer(const vsoc_hwc_layer& layer) {
+  if (layer.flags & HWC_SKIP_LAYER) {
+    // A layer we are asked to skip validate should not be marked as skip
+    ALOGE("%s: Layer is marked as skip", __FUNCTION__);
+    return false;
+  }
+  // Check displayFrame
+  if (layer.displayFrame.left > layer.displayFrame.right ||
+      layer.displayFrame.top > layer.displayFrame.bottom) {
+    ALOGE(
+        "%s: Malformed rectangle (displayFrame): [left = %d, right = %d, top = "
+        "%d, bottom = %d]",
+        __FUNCTION__, layer.displayFrame.left, layer.displayFrame.right,
+        layer.displayFrame.top, layer.displayFrame.bottom);
+    return false;
+  }
+  // Validate the handle
+  if (private_handle_t::validate(layer.handle) != 0) {
+    ALOGE("%s: Layer contains an invalid gralloc handle.", __FUNCTION__);
+    return false;
+  }
+  const private_handle_t* p_handle =
+      reinterpret_cast<const private_handle_t*>(layer.handle);
+  // Check sourceCrop
+  if (layer.sourceCrop.left > layer.sourceCrop.right ||
+      layer.sourceCrop.top > layer.sourceCrop.bottom) {
+    ALOGE(
+        "%s: Malformed rectangle (sourceCrop): [left = %d, right = %d, top = "
+        "%d, bottom = %d]",
+        __FUNCTION__, layer.sourceCrop.left, layer.sourceCrop.right,
+        layer.sourceCrop.top, layer.sourceCrop.bottom);
+    return false;
+  }
+  if (layer.sourceCrop.left < 0 || layer.sourceCrop.top < 0 ||
+      layer.sourceCrop.right > p_handle->x_res ||
+      layer.sourceCrop.bottom > p_handle->y_res) {
+    ALOGE(
+        "%s: Invalid sourceCrop for buffer handle: sourceCrop = [left = %d, "
+        "right = %d, top = %d, bottom = %d], handle = [width = %d, height = "
+        "%d]",
+        __FUNCTION__, layer.sourceCrop.left, layer.sourceCrop.right,
+        layer.sourceCrop.top, layer.sourceCrop.bottom, p_handle->x_res,
+        p_handle->y_res);
+    return false;
+  }
+  return true;
+}
+
+bool IsValidComposition(int num_layers, vsoc_hwc_layer* layers, bool on_set) {
+  if (num_layers == 0) {
+    ALOGE("Composition requested with 0 layers");
+    return false;
+  }
+  // Sometimes the hwcomposer receives a prepare and set calls with no other
+  // layer than the FRAMEBUFFER_TARGET with a null handler. We treat this case
+  // independently as a valid composition, but issue a warning about it.
+  if (num_layers == 1 && layers[0].compositionType == HWC_FRAMEBUFFER_TARGET &&
+      layers[0].handle == NULL) {
+    ALOGW("Received request for empty composition, treating as valid noop");
+    return true;
+  }
+  // The FRAMEBUFFER_TARGET layer needs to be sane only if
+  // there is at least one layer marked HWC_FRAMEBUFFER or if there is no layer
+  // marked HWC_OVERLAY (i.e some layers where composed with OpenGL, no layer
+  // marked overlay or framebuffer means that surfaceflinger decided to go for
+  // OpenGL without asking the hwcomposer first)
+  bool check_fb_target = true;
+  for (int idx = 0; idx < num_layers; ++idx) {
+    if (layers[idx].compositionType == HWC_FRAMEBUFFER) {
+      // There is at least one, so it needs to be checked.
+      // It may have been set to false before, so ensure it's set to true.
+      check_fb_target = true;
+      break;
+    }
+    if (layers[idx].compositionType == HWC_OVERLAY) {
+      // At least one overlay, we may not need to.
+      check_fb_target = false;
+    }
+  }
+
+  for (int idx = 0; idx < num_layers; ++idx) {
+    switch (layers[idx].compositionType) {
+    case HWC_FRAMEBUFFER_TARGET:
+      // In the call to prepare() the framebuffer target does not have a valid
+      // buffer_handle, so we don't validate it yet.
+      if (on_set && check_fb_target && !IsValidLayer(layers[idx])) {
+        ALOGE("%s: Invalid layer found", __FUNCTION__);
+        LogLayers(num_layers, layers, idx);
+        return false;
+      }
+      break;
+    case HWC_OVERLAY:
+      if (!(layers[idx].flags & HWC_SKIP_LAYER) &&
+          !IsValidLayer(layers[idx])) {
+        ALOGE("%s: Invalid layer found", __FUNCTION__);
+        LogLayers(num_layers, layers, idx);
+        return false;
+      }
+      break;
+    }
+  }
+  return true;
+}
+
+}
+
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+static int vsoc_hwc_prepare(vsoc_hwc_device* dev, hwc_layer_list_t* list) {
+#else
+static int vsoc_hwc_prepare(vsoc_hwc_device* dev, size_t numDisplays,
+                            hwc_display_contents_1_t** displays) {
+  if (!numDisplays || !displays) return 0;
+
+  hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
+
+  if (!list) return 0;
+#endif
+  if (!IsValidComposition(list->numHwLayers, &list->hwLayers[0], false)) {
+    LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
+    return -1;
+  }
+  reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->PrepareLayers(
+      list->numHwLayers, &list->hwLayers[0]);
   return 0;
 }
 
-static int cutf_cvm_hwc_set(cutf_cvm_hwc_device* dev __unused, size_t numDisplays __unused,
-                        hwc_display_contents_1_t** displays __unused) {
-  // TODO: b/125482910
-  return 0;
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+int vsoc_hwc_set(struct hwc_composer_device* dev, hwc_display_t dpy,
+                 hwc_surface_t sur, hwc_layer_list_t* list) {
+  if (list->numHwLayers == 1 &&
+      layers[0].compositionType == HWC_FRAMEBUFFER_TARGET) {
+    ALOGW("Received request for empty composition, treating as valid noop");
+    return 0;
+  }
+  if (!IsValidComposition(list->numHwLayers, &list->hwLayers[0], true)) {
+    LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
+    return -1;
+  }
+  return reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)
+      ->composer->SetLayers(list->numHwLayers, &list->hwLayers[0]);
 }
+#else
+static int vsoc_hwc_set(vsoc_hwc_device* dev, size_t numDisplays,
+                        hwc_display_contents_1_t** displays) {
+  if (!numDisplays || !displays) return 0;
 
-static void cutf_cvm_hwc_register_procs(cutf_cvm_hwc_device* dev,
+  hwc_display_contents_1_t* contents = displays[HWC_DISPLAY_PRIMARY];
+  if (!contents) return 0;
+
+  vsoc_hwc_layer* layers = &contents->hwLayers[0];
+  if (contents->numHwLayers == 1 &&
+      layers[0].compositionType == HWC_FRAMEBUFFER_TARGET) {
+    ALOGW("Received request for empty composition, treating as valid noop");
+    return 0;
+  }
+  if (!IsValidComposition(contents->numHwLayers, layers, true)) {
+    LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
+    return -1;
+  }
+  int retval =
+      reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->SetLayers(
+          contents->numHwLayers, layers);
+
+  int closedFds = 0;
+  for (size_t index = 0; index < contents->numHwLayers; ++index) {
+    if (layers[index].acquireFenceFd != -1) {
+      close(layers[index].acquireFenceFd);
+      layers[index].acquireFenceFd = -1;
+      ++closedFds;
+    }
+  }
+  if (closedFds) {
+    ALOGI("Saw %zu layers, closed=%d", contents->numHwLayers, closedFds);
+  }
+
+  // TODO(ghartman): This should be set before returning. On the next set it
+  // should be signalled when we load the new frame.
+  contents->retireFenceFd = -1;
+  return retval;
+}
+#endif
+
+static void vsoc_hwc_register_procs(vsoc_hwc_device* dev,
                                     const hwc_procs_t* procs) {
-  struct cutf_cvm_hwc_composer_device_1_t* pdev =
-      (struct cutf_cvm_hwc_composer_device_1_t*)dev;
+  struct vsoc_hwc_composer_device_1_t* pdev =
+      (struct vsoc_hwc_composer_device_1_t*)dev;
   pdev->vsync_data.procs = procs;
 }
 
-static int cutf_cvm_hwc_query(cutf_cvm_hwc_device* dev __unused, int what __unused, int* value) {
-  // TODO: b/125482910
-  struct cutf_cvm_hwc_composer_device_1_t* pdev =
-      (struct cutf_cvm_hwc_composer_device_1_t*)dev;
+static int vsoc_hwc_query(vsoc_hwc_device* dev, int what, int* value) {
+  struct vsoc_hwc_composer_device_1_t* pdev =
+      (struct vsoc_hwc_composer_device_1_t*)dev;
+
   switch (what) {
     case HWC_BACKGROUND_LAYER_SUPPORTED:
-      *value = 0;
+      // we support the background layer
+      value[0] = 0;
       break;
     case HWC_VSYNC_PERIOD:
-      *value = pdev->vsync_data.vsync_period_ns;
+      value[0] = pdev->vsync_data.vsync_period_ns;
       break;
     default:
       // unsupported query
-      ALOGE("%s: badness unsupported query what=%d", __FUNCTION__, what);
+      ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
       return -EINVAL;
   }
   return 0;
 }
 
-static int cutf_cvm_hwc_event_control(
-    cutf_cvm_hwc_device* /*dev*/, int /*dpy*/, int event __unused, int /*enabled*/) {
-  // TODO: b/125482910
+static int vsoc_hwc_event_control(
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+    vsoc_hwc_device* /*dev*/, int event, int /*enabled*/) {
+#else
+    vsoc_hwc_device* /*dev*/, int /*dpy*/, int event, int /*enabled*/) {
+#endif
+
+  if (event == HWC_EVENT_VSYNC) {
+    return 0;
+  }
+  return -EINVAL;
+}
+
+static int vsoc_hwc_blank(vsoc_hwc_device* /*dev*/, int disp, int /*blank*/) {
+  if (!IS_PRIMARY_DISPLAY(disp)) return -EINVAL;
   return 0;
 }
 
-static int cutf_cvm_hwc_blank(cutf_cvm_hwc_device* /*dev*/, int disp __unused, int /*blank*/) {
-  // TODO: b/125482910
-  return 0;
+static void vsoc_hwc_dump(vsoc_hwc_device* dev, char* buff, int buff_len) {
+  reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->Dump(
+      buff, buff_len);
 }
 
-static void cutf_cvm_hwc_dump(cutf_cvm_hwc_device* dev __unused, char* buff __unused, int buff_len __unused) {
-  // TODO: b/125482910
+static int vsoc_hwc_get_display_configs(vsoc_hwc_device* /*dev*/, int disp,
+                                        uint32_t* configs, size_t* numConfigs) {
+  if (*numConfigs == 0) return 0;
+
+  if (IS_PRIMARY_DISPLAY(disp)) {
+    configs[0] = 0;
+    *numConfigs = 1;
+    return 0;
+  }
+
+  return -EINVAL;
 }
 
-static int cutf_cvm_hwc_get_display_configs(cutf_cvm_hwc_device* /*dev*/, int disp __unused,
-                                        uint32_t* configs __unused, size_t* numConfigs __unused) {
-  // TODO: b/125482910
-  return 0;
-}
-
-static int32_t cutf_cvm_hwc_attribute(struct cutf_cvm_hwc_composer_device_1_t* pdev,
+#if VSOC_PLATFORM_SDK_AFTER(J)
+static int32_t vsoc_hwc_attribute(struct vsoc_hwc_composer_device_1_t* pdev,
                                   const uint32_t attribute) {
-  // TODO: b/125482910
   switch (attribute) {
     case HWC_DISPLAY_VSYNC_PERIOD:
       return pdev->vsync_data.vsync_period_ns;
     case HWC_DISPLAY_WIDTH:
-      return 720;
+      return pdev->composer->x_res();
     case HWC_DISPLAY_HEIGHT:
-      return 1280;
+      return pdev->composer->y_res();
     case HWC_DISPLAY_DPI_X:
-      ALOGI("%s: Reporting DPI_X of %d", __FUNCTION__, 1);
+      ALOGI("Reporting DPI_X of %d", pdev->composer->dpi());
       // The number of pixels per thousand inches
-      return 240000;
+      return pdev->composer->dpi() * 1000;
     case HWC_DISPLAY_DPI_Y:
-      ALOGI("%s: Reporting DPI_Y of %d", __FUNCTION__, 1);
+      ALOGI("Reporting DPI_Y of %d", pdev->composer->dpi());
       // The number of pixels per thousand inches
-      return 240000;
+      return pdev->composer->dpi() * 1000;
     default:
-      ALOGE("%s: unknown display attribute %u", __FUNCTION__, attribute);
+      ALOGE("unknown display attribute %u", attribute);
       return -EINVAL;
   }
 }
 
-static int cutf_cvm_hwc_get_display_attributes(cutf_cvm_hwc_device* dev __unused, int disp __unused,
+static int vsoc_hwc_get_display_attributes(vsoc_hwc_device* dev, int disp,
                                            uint32_t config __unused,
-                                           const uint32_t* attributes __unused,
-                                           int32_t* values __unused) {
-  struct cutf_cvm_hwc_composer_device_1_t* pdev =
-      (struct cutf_cvm_hwc_composer_device_1_t*)dev;
+                                           const uint32_t* attributes,
+                                           int32_t* values) {
+  struct vsoc_hwc_composer_device_1_t* pdev =
+      (struct vsoc_hwc_composer_device_1_t*)dev;
 
   if (!IS_PRIMARY_DISPLAY(disp)) {
-    ALOGE("%s: unknown display type %u", __FUNCTION__, disp);
+    ALOGE("unknown display type %u", disp);
     return -EINVAL;
   }
 
   for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
-    values[i] = cutf_cvm_hwc_attribute(pdev, attributes[i]);
+    values[i] = vsoc_hwc_attribute(pdev, attributes[i]);
   }
 
   return 0;
 }
+#endif
 
-static int cutf_cvm_hwc_close(hw_device_t* device) {
-  struct cutf_cvm_hwc_composer_device_1_t* dev =
-      (struct cutf_cvm_hwc_composer_device_1_t*)device;
+static int vsoc_hwc_close(hw_device_t* device) {
+  struct vsoc_hwc_composer_device_1_t* dev =
+      (struct vsoc_hwc_composer_device_1_t*)device;
+  ALOGE("vsoc_hwc_close");
+  pthread_kill(dev->vsync_data.vsync_thread, SIGTERM);
+  pthread_join(dev->vsync_data.vsync_thread, NULL);
+  delete dev->composer;
   delete dev;
   return 0;
 }
 
-static int cutf_cvm_hwc_open(const struct hw_module_t* module, const char* name,
+static int vsoc_hwc_open(const struct hw_module_t* module, const char* name,
                          struct hw_device_t** device) {
   ALOGI("%s", __FUNCTION__);
-  if (strcmp(name, HWC_HARDWARE_COMPOSER) != 0) {
-    ALOGE("%s: called with bad name %s", __FUNCTION__, name);
+  if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
+    ALOGE("%s called with bad name %s", __FUNCTION__, name);
     return -EINVAL;
   }
 
-  cutf_cvm_hwc_composer_device_1_t* dev = new cutf_cvm_hwc_composer_device_1_t();
+  vsoc_hwc_composer_device_1_t* dev = new vsoc_hwc_composer_device_1_t();
   if (!dev) {
-    ALOGE("%s: failed to allocate dev", __FUNCTION__);
+    ALOGE("%s failed to allocate dev", __FUNCTION__);
     return -ENOMEM;
   }
 
-  const int refreshRate = 60;
-  const int nsPerSec = 1000000000;
-  dev->vsync_data.vsync_period_ns = nsPerSec / refreshRate;
   struct timespec rt;
-  if (clock_gettime(CLOCK_MONOTONIC, &rt) != 0) {
-    ALOGE("%s: error in clock_gettime: %s", __FUNCTION__, strerror(errno));
+  if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+    ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
+          strerror(errno));
   }
-  dev->vsync_data.vsync_base_timestamp = int64_t(rt.tv_sec) * nsPerSec + rt.tv_nsec;
+  dev->vsync_data.vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
 
   dev->base.common.tag = HARDWARE_DEVICE_TAG;
-  dev->base.common.version = CUTF_CVM_HWC_DEVICE_API_VERSION;
+  dev->base.common.version = VSOC_HWC_DEVICE_API_VERSION;
   dev->base.common.module = const_cast<hw_module_t*>(module);
-  dev->base.common.close = cutf_cvm_hwc_close;
+  dev->base.common.close = vsoc_hwc_close;
 
-  dev->base.prepare = cutf_cvm_hwc_prepare;
-  dev->base.set = cutf_cvm_hwc_set;
-  dev->base.query = cutf_cvm_hwc_query;
-  dev->base.registerProcs = cutf_cvm_hwc_register_procs;
-  dev->base.dump = cutf_cvm_hwc_dump;
-  dev->base.blank = cutf_cvm_hwc_blank;
-  dev->base.eventControl = cutf_cvm_hwc_event_control;
-  dev->base.getDisplayConfigs = cutf_cvm_hwc_get_display_configs;
-  dev->base.getDisplayAttributes = cutf_cvm_hwc_get_display_attributes;
-
-  int ret = pthread_create(&dev->vsync_data.vsync_thread, NULL, cvd::hwc_vsync_thread, &dev->vsync_data);
-  if (ret != 0) {
-    ALOGE("%s: failed to start vsync thread: %s", __FUNCTION__, strerror(ret));
-    dev->base.common.close(&dev->base.common);
-    return -ret;
+  dev->base.prepare = vsoc_hwc_prepare;
+  dev->base.set = vsoc_hwc_set;
+  dev->base.query = vsoc_hwc_query;
+  dev->base.registerProcs = vsoc_hwc_register_procs;
+  dev->base.dump = vsoc_hwc_dump;
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+  static hwc_methods_t hwc_methods = {vsoc_hwc_event_control};
+  dev->base.methods = &hwc_methods;
+#else
+  dev->base.blank = vsoc_hwc_blank;
+  dev->base.eventControl = vsoc_hwc_event_control;
+  dev->base.getDisplayConfigs = vsoc_hwc_get_display_configs;
+  dev->base.getDisplayAttributes = vsoc_hwc_get_display_attributes;
+#endif
+  dev->composer = new ComposerType(dev->vsync_data.vsync_base_timestamp);
+  dev->vsync_data.vsync_period_ns = 1000000000 / dev->composer->refresh_rate();
+  int ret = pthread_create(&dev->vsync_data.vsync_thread,
+                           NULL, cvd::hwc_vsync_thread, &dev->vsync_data);
+  if (ret) {
+    ALOGE("failed to start vsync thread: %s", strerror(ret));
+    ret = -ret;
+    delete dev;
+  } else {
+    *device = &dev->base.common;
   }
 
-  *device = &dev->base.common;
   return ret;
 }
 
-static struct hw_module_methods_t cutf_cvm_hwc_module_methods = {
-    cutf_cvm_hwc_open,
+static struct hw_module_methods_t vsoc_hwc_module_methods = {
+    vsoc_hwc_open,
 };
 
 hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG,
                                      HWC_MODULE_API_VERSION_0_1,
                                      HARDWARE_HAL_API_VERSION,
                                      HWC_HARDWARE_MODULE_ID,
-                                     "cutf_cvm hwcomposer module",
+                                     "VSOC hwcomposer module",
                                      "Google",
-                                     &cutf_cvm_hwc_module_methods,
+                                     &vsoc_hwc_module_methods,
                                      NULL,
                                      {0}}};
diff --git a/guest/hals/hwcomposer/cutf_cvm/hwcomposer.h b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.h
new file mode 100644
index 0000000..a6b8121
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.h
@@ -0,0 +1,35 @@
+#pragma once
+/*
+ * Copyright (C) 2016 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 <guest/libs/platform_support/api_level_fixes.h>
+
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+typedef hwc_composer_device_t vsoc_hwc_device;
+typedef hwc_layer_t vsoc_hwc_layer;
+#define IS_TARGET_FRAMEBUFFER(x) false
+#define IS_PRIMARY_DISPLAY(x) true
+#define VSOC_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_0_3
+#else
+typedef hwc_composer_device_1_t vsoc_hwc_device;
+typedef hwc_layer_1_t vsoc_hwc_layer;
+#define IS_TARGET_FRAMEBUFFER(x) ((x) == HWC_FRAMEBUFFER_TARGET)
+#define IS_PRIMARY_DISPLAY(x) ((x) == HWC_DISPLAY_PRIMARY)
+#define VSOC_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_1_1
+#endif
diff --git a/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
new file mode 100644
index 0000000..5b55e84
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2016 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 "vsoc_composer.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <utility>
+#include <vector>
+
+#include <log/log.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <libyuv.h>
+#include <system/graphics.h>
+
+#include <common/libs/utils/size_utils.h>
+
+#include "geometry_utils.h"
+
+namespace cvd {
+
+namespace {
+
+bool LayerNeedsScaling(const vsoc_hwc_layer& layer) {
+  int from_w = layer.sourceCrop.right - layer.sourceCrop.left;
+  int from_h = layer.sourceCrop.bottom - layer.sourceCrop.top;
+  int to_w = layer.displayFrame.right - layer.displayFrame.left;
+  int to_h = layer.displayFrame.bottom - layer.displayFrame.top;
+
+  bool not_rot_scale = from_w != to_w || from_h != to_h;
+  bool rot_scale = from_w != to_h || from_h != to_w;
+
+  bool needs_rot = layer.transform & HAL_TRANSFORM_ROT_90;
+
+  return needs_rot ? rot_scale : not_rot_scale;
+}
+
+bool LayerNeedsBlending(const vsoc_hwc_layer& layer) {
+  return layer.blending != HWC_BLENDING_NONE;
+}
+
+bool LayerNeedsAttenuation(const vsoc_hwc_layer& layer) {
+  return layer.blending == HWC_BLENDING_COVERAGE;
+}
+
+struct BufferSpec;
+typedef int (*ConverterFunction)(const BufferSpec& src, const BufferSpec& dst,
+                                 bool v_flip);
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip);
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool v_flip);
+ConverterFunction GetConverter(uint32_t format) {
+  switch (format) {
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+    case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+      return &DoCopy;
+
+    case HAL_PIXEL_FORMAT_YV12:
+      return &ConvertFromYV12;
+
+    // Unsupported formats
+    // TODO(jemoreira): Conversion from these formats should be implemented as
+    // we find evidence of its usage.
+    // case HAL_PIXEL_FORMAT_BGRA_8888:
+
+    // case HAL_PIXEL_FORMAT_RGB_888:
+    // case HAL_PIXEL_FORMAT_RGB_565:
+
+    // case HAL_PIXEL_FORMAT_sRGB_A_8888:
+    // case HAL_PIXEL_FORMAT_sRGB_X_8888:
+
+    // case HAL_PIXEL_FORMAT_Y8:
+    // case HAL_PIXEL_FORMAT_Y16:
+
+    // case HAL_PIXEL_FORMAT_RAW_SENSOR:
+    // case HAL_PIXEL_FORMAT_BLOB:
+
+    // case HAL_PIXEL_FORMAT_YCbCr_420_888:
+    // case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+    // case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+    // case HAL_PIXEL_FORMAT_YCbCr_422_I:
+    default:
+      ALOGW("Unsupported format: 0x%04x, returning null converter function",
+            format);
+  }
+  return NULL;
+}
+
+// Whether we support a given format
+bool IsFormatSupported(uint32_t format) { return GetConverter(format) != NULL; }
+
+bool CanCompositeLayer(const vsoc_hwc_layer& layer) {
+  if (layer.handle == NULL) {
+    ALOGW("%s received a layer with a null handler", __FUNCTION__);
+    return false;
+  }
+  int format = reinterpret_cast<const private_handle_t*>(layer.handle)->format;
+  if (!IsFormatSupported(format)) {
+    ALOGD("Unsupported pixel format: 0x%x, doing software composition instead",
+          format);
+    return false;
+  }
+  return true;
+}
+
+/*******************************************************************************
+Libyuv's convert functions only allow the combination of any rotation (multiple
+of 90 degrees) and a vertical flip, but not horizontal flips.
+Surfaceflinger's transformations are expressed in terms of a vertical flip, a
+horizontal flip and/or a single 90 degrees clockwise rotation (see
+NATIVE_WINDOW_TRANSFORM_HINT documentation on system/window.h for more insight).
+The following code allows to turn a horizontal flip into a 180 degrees rotation
+and a vertical flip.
+*******************************************************************************/
+libyuv::RotationMode GetRotationFromTransform(uint32_t transform) {
+  uint32_t rotation =
+      (transform & HAL_TRANSFORM_ROT_90) ? 1 : 0;          // 1 * ROT90 bit
+  rotation += (transform & HAL_TRANSFORM_FLIP_H) ? 2 : 0;  // 2 * VFLIP bit
+  return static_cast<libyuv::RotationMode>(90 * rotation);
+}
+
+bool GetVFlipFromTransform(uint32_t transform) {
+  // vertical flip xor horizontal flip
+  return ((transform & HAL_TRANSFORM_FLIP_V) >> 1) ^
+         (transform & HAL_TRANSFORM_FLIP_H);
+}
+
+struct BufferSpec {
+  uint8_t* buffer;
+  size_t size;
+  int width;
+  int height;
+  int stride;
+  int crop_x;
+  int crop_y;
+  int crop_width;
+  int crop_height;
+  uint32_t format;
+
+  BufferSpec(uint8_t* buffer, size_t size, int width, int height, int stride)
+      : buffer(buffer),
+        size(size),
+        width(width),
+        height(height),
+        stride(stride),
+        crop_x(0),
+        crop_y(0),
+        crop_width(width),
+        crop_height(height),
+        format(HAL_PIXEL_FORMAT_RGBA_8888) {}
+};
+
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  // use the stride in pixels as the source width
+  int stride_in_pixels = src.stride / formatToBytesPerPixel(src.format);
+
+  // The following calculation of plane offsets and alignments are based on
+  // swiftshader's Sampler::setTextureLevel() implementation
+  // (Renderer/Sampler.cpp:225)
+  uint8_t* src_y = src.buffer;
+  int stride_y = stride_in_pixels;
+  uint8_t* src_v = src_y + stride_y * src.height;
+  int stride_v = cvd::AlignToPowerOf2(stride_y / 2, 4);
+  uint8_t* src_u = src_v + stride_v *  src.height / 2;
+  int stride_u = cvd::AlignToPowerOf2(stride_y / 2, 4);
+
+  // Adjust for crop
+  src_y += src.crop_y * stride_y + src.crop_x;
+  src_v += (src.crop_y / 2) * stride_v + (src.crop_x / 2);
+  src_u += (src.crop_y / 2) * stride_u + (src.crop_x / 2);
+  uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+                        dst.crop_x * formatToBytesPerPixel(dst.format);
+
+  // YV12 is the same as I420, with the U and V planes swapped
+  return libyuv::I420ToARGB(src_y, stride_y, src_v, stride_v, src_u, stride_u,
+                            dst_buffer, dst.stride, dst.crop_width,
+                            v_flip ? -dst.crop_height : dst.crop_height);
+}
+
+int DoConversion(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  return (*GetConverter(src.format))(src, dst, v_flip);
+}
+
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  // Point to the upper left corner of the crop rectangle
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+                        dst.crop_x * formatToBytesPerPixel(dst.format);
+  int width = src.crop_width;
+  int height = src.crop_height;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  // HAL formats are named based on the order of the pixel componets on the
+  // byte stream, while libyuv formats are named based on the order of those
+  // pixel components in an integer written from left to right. So
+  // libyuv::FOURCC_ARGB is equivalent to HAL_PIXEL_FORMAT_BGRA_8888.
+  return libyuv::ARGBCopy(src_buffer, src.stride, dst_buffer, dst.stride, width,
+                          height);
+}
+
+int DoRotation(const BufferSpec& src, const BufferSpec& dst,
+               libyuv::RotationMode rotation, bool v_flip) {
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+                        dst.crop_x * formatToBytesPerPixel(dst.format);
+  int width = src.crop_width;
+  int height = src.crop_height;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  return libyuv::ARGBRotate(src_buffer, src.stride, dst_buffer, dst.stride,
+                            width, height, rotation);
+}
+
+int DoScaling(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+                        dst.crop_x * formatToBytesPerPixel(dst.format);
+  int src_width = src.crop_width;
+  int src_height = src.crop_height;
+  int dst_width = dst.crop_width;
+  int dst_height = dst.crop_height;
+
+  if (v_flip) {
+    src_height = -src_height;
+  }
+
+  return libyuv::ARGBScale(src_buffer, src.stride, src_width, src_height,
+                           dst_buffer, dst.stride, dst_width, dst_height,
+                           libyuv::kFilterBilinear);
+}
+
+int DoAttenuation(const BufferSpec& src, const BufferSpec& dest, bool v_flip) {
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dest.buffer + dest.crop_y * dest.stride +
+                        dest.crop_x * formatToBytesPerPixel(dest.format);
+  int width = dest.crop_width;
+  int height = dest.crop_height;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  return libyuv::ARGBAttenuate(src_buffer, src.stride, dst_buffer, dest.stride,
+                               width, height);
+}
+
+int DoBlending(const BufferSpec& src, const BufferSpec& dest, bool v_flip) {
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dest.buffer + dest.crop_y * dest.stride +
+                        dest.crop_x * formatToBytesPerPixel(dest.format);
+  int width = dest.crop_width;
+  int height = dest.crop_height;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  // libyuv's ARGB format is hwcomposer's BGRA format, since blending only cares
+  // for the position of alpha in the pixel and not the position of the colors
+  // this function is perfectly usable.
+  return libyuv::ARGBBlend(src_buffer, src.stride, dst_buffer, dest.stride,
+                           dst_buffer, dest.stride, width, height);
+}
+
+}  // namespace
+
+void VSoCComposer::CompositeLayer(vsoc_hwc_layer* src_layer,
+                                  int buffer_idx) {
+  libyuv::RotationMode rotation =
+      GetRotationFromTransform(src_layer->transform);
+
+  const private_handle_t* src_priv_handle =
+      reinterpret_cast<const private_handle_t*>(src_layer->handle);
+
+  // TODO(jemoreira): Remove the hardcoded fomat.
+  bool needs_conversion = src_priv_handle->format != HAL_PIXEL_FORMAT_RGBX_8888;
+  bool needs_scaling = LayerNeedsScaling(*src_layer);
+  bool needs_rotation = rotation != libyuv::kRotate0;
+  bool needs_transpose = needs_rotation && rotation != libyuv::kRotate180;
+  bool needs_vflip = GetVFlipFromTransform(src_layer->transform);
+  bool needs_attenuation = LayerNeedsAttenuation(*src_layer);
+  bool needs_blending = LayerNeedsBlending(*src_layer);
+  bool needs_copy = !(needs_conversion || needs_scaling || needs_rotation ||
+                      needs_vflip || needs_attenuation || needs_blending);
+
+  uint8_t* src_buffer;
+  uint8_t* dst_buffer = reinterpret_cast<uint8_t*>(
+      frame_buffer_.GetBuffer(buffer_idx));
+  int retval = gralloc_module_->lock(
+      gralloc_module_, src_layer->handle, GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
+      src_priv_handle->x_res, src_priv_handle->y_res,
+      reinterpret_cast<void**>(&src_buffer));
+  if (retval) {
+    ALOGE("Got error code %d from lock function", retval);
+    return;
+  }
+  if (retval) {
+    ALOGE("Got error code %d from lock function", retval);
+    // TODO(jemoreira): Use a lock_guard-like object.
+    gralloc_module_->unlock(gralloc_module_, src_priv_handle);
+    return;
+  }
+
+  BufferSpec src_layer_spec(src_buffer, src_priv_handle->total_size,
+                            src_priv_handle->x_res, src_priv_handle->y_res,
+                            src_priv_handle->stride_in_pixels *
+                                formatToBytesPerPixel(src_priv_handle->format));
+  src_layer_spec.crop_x = src_layer->sourceCrop.left;
+  src_layer_spec.crop_y = src_layer->sourceCrop.top;
+  src_layer_spec.crop_width =
+      src_layer->sourceCrop.right - src_layer->sourceCrop.left;
+  src_layer_spec.crop_height =
+      src_layer->sourceCrop.bottom - src_layer->sourceCrop.top;
+  src_layer_spec.format = src_priv_handle->format;
+
+  BufferSpec dst_layer_spec(dst_buffer, frame_buffer_.buffer_size(),
+                            frame_buffer_.x_res(), frame_buffer_.y_res(),
+                            frame_buffer_.line_length());
+  dst_layer_spec.crop_x = src_layer->displayFrame.left;
+  dst_layer_spec.crop_y = src_layer->displayFrame.top;
+  dst_layer_spec.crop_width =
+      src_layer->displayFrame.right - src_layer->displayFrame.left;
+  dst_layer_spec.crop_height =
+      src_layer->displayFrame.bottom - src_layer->displayFrame.top;
+  // TODO(jemoreira): Remove the hardcoded fomat.
+  dst_layer_spec.format = HAL_PIXEL_FORMAT_RGBX_8888;
+
+  // Add the destination layer to the bottom of the buffer stack
+  std::vector<BufferSpec> dest_buffer_stack(1, dst_layer_spec);
+
+  // If more than operation is to be performed, a temporary buffer is needed for
+  // each additional operation
+
+  // N operations need N destination buffers, the destination layer (the
+  // framebuffer) is one of them, so only N-1 temporary buffers are needed.
+  // Vertical flip is not taken into account because it can be done together
+  // with any other operation.
+  int needed_tmp_buffers = (needs_conversion ? 1 : 0) +
+                           (needs_scaling ? 1 : 0) + (needs_rotation ? 1 : 0) +
+                           (needs_attenuation ? 1 : 0) +
+                           (needs_blending ? 1 : 0) + (needs_copy ? 1 : 0) - 1;
+
+  int x_res = src_layer->displayFrame.right - src_layer->displayFrame.left;
+  int y_res = src_layer->displayFrame.bottom - src_layer->displayFrame.top;
+  size_t output_frame_size =
+      x_res *
+    cvd::AlignToPowerOf2(y_res * frame_buffer_.bytes_per_pixel(), 4);
+  while (needed_tmp_buffers > 0) {
+    BufferSpec tmp(RotateTmpBuffer(needed_tmp_buffers), output_frame_size,
+                   x_res, y_res,
+                   cvd::AlignToPowerOf2(
+                       x_res * frame_buffer_.bytes_per_pixel(), 4));
+    dest_buffer_stack.push_back(tmp);
+    needed_tmp_buffers--;
+  }
+
+  // Conversion and scaling should always be the first operations, so that every
+  // other operation works on equally sized frames (garanteed to fit in the tmp
+  // buffers)
+
+  // TODO(jemoreira): We are converting to ARGB as the first step under the
+  // assumption that scaling ARGB is faster than scaling I420 (the most common).
+  // This should be confirmed with testing.
+  if (needs_conversion) {
+    BufferSpec& dst_buffer_spec = dest_buffer_stack.back();
+    if (needs_scaling || needs_transpose) {
+      // If a rotation or a scaling operation are needed the dimensions at the
+      // top of the buffer stack are wrong (wrong sizes for scaling, swapped
+      // width and height for 90 and 270 rotations).
+      // Make width and height match the crop sizes on the source
+      int src_width = src_layer_spec.crop_width;
+      int src_height = src_layer_spec.crop_height;
+      int dst_stride = cvd::AlignToPowerOf2(
+          src_width * frame_buffer_.bytes_per_pixel(), 4);
+      size_t needed_size = dst_stride * src_height;
+      dst_buffer_spec.width = src_width;
+      dst_buffer_spec.height = src_height;
+      // Ajust the stride accordingly
+      dst_buffer_spec.stride = dst_stride;
+      // Crop sizes also need to be adjusted
+      dst_buffer_spec.crop_width = src_width;
+      dst_buffer_spec.crop_height = src_height;
+      dst_buffer_spec.size = needed_size;
+      // crop_x and y are fine at 0, format is already set to match destination
+
+      // In case of a scale, the source frame may be bigger than the default tmp
+      // buffer size
+      if (needed_size > tmp_buffer_.size() / kNumTmpBufferPieces) {
+        dst_buffer_spec.buffer = GetSpecialTmpBuffer(needed_size);
+      }
+    }
+    retval = DoConversion(src_layer_spec, dst_buffer_spec, needs_vflip);
+    if (retval) {
+      ALOGE("Got error code %d from DoConversion function", retval);
+    }
+    needs_vflip = false;
+    src_layer_spec = dst_buffer_spec;
+    dest_buffer_stack.pop_back();
+  }
+
+  if (needs_scaling) {
+    BufferSpec& dst_buffer_spec = dest_buffer_stack.back();
+    if (needs_transpose) {
+      // If a rotation is needed, the temporary buffer has the correct size but
+      // needs to be transposed and have its stride updated accordingly. The
+      // crop sizes also needs to be transposed, but not the x and y since they
+      // are both zero in a temporary buffer (and it is a temporary buffer
+      // because a rotation will be performed next).
+      std::swap(dst_buffer_spec.width, dst_buffer_spec.height);
+      std::swap(dst_buffer_spec.crop_width, dst_buffer_spec.crop_height);
+      // TODO (jemoreira): Aligment (To align here may cause the needed size to
+      // be bigger than the buffer, so care should be taken)
+      dst_buffer_spec.stride =
+          dst_buffer_spec.width * frame_buffer_.bytes_per_pixel();
+    }
+    retval = DoScaling(src_layer_spec, dst_buffer_spec, needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoScaling function", retval);
+    }
+    src_layer_spec = dst_buffer_spec;
+    dest_buffer_stack.pop_back();
+  }
+
+  if (needs_rotation) {
+    retval = DoRotation(src_layer_spec, dest_buffer_stack.back(), rotation,
+                        needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoTransform function", retval);
+    }
+    src_layer_spec = dest_buffer_stack.back();
+    dest_buffer_stack.pop_back();
+  }
+
+  if (needs_attenuation) {
+    retval =
+        DoAttenuation(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoBlending function", retval);
+    }
+    src_layer_spec = dest_buffer_stack.back();
+    dest_buffer_stack.pop_back();
+  }
+
+  if (needs_copy) {
+    retval = DoCopy(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoBlending function", retval);
+    }
+    src_layer_spec = dest_buffer_stack.back();
+    dest_buffer_stack.pop_back();
+  }
+
+  // Blending (if needed) should always be the last operation, so that it reads
+  // and writes in the destination layer and not some temporary buffer.
+  if (needs_blending) {
+    retval = DoBlending(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoBlending function", retval);
+    }
+    // Don't need to assign destination to source in the last one
+    dest_buffer_stack.pop_back();
+  }
+
+  gralloc_module_->unlock(gralloc_module_, src_priv_handle);
+}
+
+/* static */ const int VSoCComposer::kNumTmpBufferPieces = 2;
+
+VSoCComposer::VSoCComposer(int64_t vsync_base_timestamp)
+    : BaseComposer(vsync_base_timestamp),
+      tmp_buffer_(kNumTmpBufferPieces *
+                  frame_buffer_.buffer_size()) {}
+
+VSoCComposer::~VSoCComposer() {}
+
+int VSoCComposer::PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+  int composited_layers_count = 0;
+
+  // Loop over layers in inverse order of z-index
+  for (size_t layer_index = num_layers; layer_index > 0;) {
+    // Decrement here to be able to compare unsigned integer with 0 in the
+    // loop condition
+    --layer_index;
+    if (IS_TARGET_FRAMEBUFFER(layers[layer_index].compositionType)) {
+      continue;
+    }
+    if (layers[layer_index].flags & HWC_SKIP_LAYER) {
+      continue;
+    }
+    if (layers[layer_index].compositionType == HWC_BACKGROUND) {
+      layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+      continue;
+    }
+    layers[layer_index].compositionType = HWC_OVERLAY;
+    // Hwcomposer cannot draw below software-composed layers, so we need
+    // to mark those HWC_FRAMEBUFFER as well.
+    for (size_t top_idx = layer_index + 1; top_idx < num_layers; ++top_idx) {
+      // layers marked as skip are in a state that makes them unreliable to
+      // read, so it's best to assume they cover the whole screen
+      if (layers[top_idx].flags & HWC_SKIP_LAYER ||
+          (layers[top_idx].compositionType == HWC_FRAMEBUFFER &&
+           LayersOverlap(layers[layer_index], layers[top_idx]))) {
+        layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+        break;
+      }
+    }
+    if (layers[layer_index].compositionType == HWC_OVERLAY &&
+        !CanCompositeLayer(layers[layer_index])) {
+      layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+    }
+    if (layers[layer_index].compositionType == HWC_OVERLAY) {
+      ++composited_layers_count;
+    }
+  }
+  return composited_layers_count;
+}
+
+int VSoCComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+  int targetFbs = 0;
+  int buffer_idx = frame_buffer_.NextScreenBuffer();
+
+  // The framebuffer target layer should be composed if at least one layers was
+  // marked HWC_FRAMEBUFFER or if it's the only layer in the composition
+  // (unlikely)
+  bool fb_target = true;
+  for (size_t idx = 0; idx < num_layers; idx++) {
+    if (layers[idx].compositionType == HWC_FRAMEBUFFER) {
+      // At least one was found
+      fb_target = true;
+      break;
+    }
+    if (layers[idx].compositionType == HWC_OVERLAY) {
+      // Not the only layer in the composition
+      fb_target = false;
+    }
+  }
+
+  // When the framebuffer target needs to be composed, it has to go first.
+  if (fb_target) {
+    for (size_t idx = 0; idx < num_layers; idx++) {
+      if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+        CompositeLayer(&layers[idx], buffer_idx);
+        break;
+      }
+    }
+  }
+
+  for (size_t idx = 0; idx < num_layers; idx++) {
+    if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+      ++targetFbs;
+    }
+    if (layers[idx].compositionType == HWC_OVERLAY &&
+        !(layers[idx].flags & HWC_SKIP_LAYER)) {
+      CompositeLayer(&layers[idx], buffer_idx);
+    }
+  }
+  if (targetFbs != 1) {
+    ALOGW("Saw %zu layers, posted=%d", num_layers, targetFbs);
+  }
+  frame_buffer_.Broadcast(buffer_idx);
+  return 0;
+}
+
+uint8_t* VSoCComposer::RotateTmpBuffer(unsigned int order) {
+  return &tmp_buffer_[(order % kNumTmpBufferPieces) * tmp_buffer_.size() /
+                      kNumTmpBufferPieces];
+}
+
+uint8_t* VSoCComposer::GetSpecialTmpBuffer(size_t needed_size) {
+  special_tmp_buffer_.resize(needed_size);
+  return &special_tmp_buffer_[0];
+}
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
new file mode 100644
index 0000000..7007931
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
@@ -0,0 +1,48 @@
+#pragma once
+/*
+ * Copyright (C) 2016 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 <vector>
+
+#include <hardware/gralloc.h>
+
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+
+#include "base_composer.h"
+#include "hwcomposer.h"
+
+namespace cvd {
+
+class VSoCComposer : public BaseComposer {
+ public:
+  VSoCComposer(int64_t vsync_base_timestamp);
+  ~VSoCComposer();
+
+  // override
+  int PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers);
+  // override
+  int SetLayers(size_t num_layers, vsoc_hwc_layer* layers);
+
+ protected:
+  static const int kNumTmpBufferPieces;
+  uint8_t* RotateTmpBuffer(unsigned int order);
+  uint8_t* GetSpecialTmpBuffer(size_t needed_size);
+  void CompositeLayer(vsoc_hwc_layer* src_layer, int32_t fb_offset);
+  std::vector<uint8_t> tmp_buffer_;
+  std::vector<uint8_t> special_tmp_buffer_;
+};
+
+}  // namespace cvd
diff --git a/host/commands/launch/flags.cc b/host/commands/launch/flags.cc
index 6266b07..3f62a50 100644
--- a/host/commands/launch/flags.cc
+++ b/host/commands/launch/flags.cc
@@ -186,6 +186,8 @@
                                "guest to host. One of [serial, vsock]");
 DEFINE_int32(logcat_vsock_port, vsoc::GetPerInstanceDefault(5620),
              "The port for logcat over vsock");
+DEFINE_int32(frames_vsock_port, vsoc::GetPerInstanceDefault(5580),
+             "The vsock port to receive frames from the guest on");
 namespace {
 
 template<typename S, typename T>
@@ -420,6 +422,11 @@
 
   tmp_config_obj.set_logcat_mode(FLAGS_logcat_mode);
   tmp_config_obj.set_logcat_vsock_port(FLAGS_logcat_vsock_port);
+  tmp_config_obj.set_frames_vsock_port(FLAGS_frames_vsock_port);
+  if (!tmp_config_obj.enable_ivserver()) {
+    tmp_config_obj.add_kernel_cmdline(concat("androidboot.vsock_frames_port=",
+                                             FLAGS_frames_vsock_port));
+  }
 
   tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
 
@@ -494,8 +501,6 @@
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
   SetCommandLineOptionWithMode("run_e2e_test", "false",
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
-  SetCommandLineOptionWithMode("start_vnc_server", "false",
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
   SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatVsockMode,
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
 }
diff --git a/host/commands/launch/launch.cc b/host/commands/launch/launch.cc
index 8ef31c6..766efb0 100644
--- a/host/commands/launch/launch.cc
+++ b/host/commands/launch/launch.cc
@@ -189,6 +189,16 @@
                                    GetOnSubprocessExitCallback(config));
 }
 
+cvd::SharedFD CreateVncInputServer(const std::string& path) {
+  auto server = cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
+  if (!server->IsOpen()) {
+    LOG(ERROR) << "Unable to create mouse server: "
+               << server->StrError();
+    return cvd::SharedFD();
+  }
+  return server;
+}
+
 void LaunchVNCServerIfEnabled(const vsoc::CuttlefishConfig& config,
                               cvd::ProcessMonitor* process_monitor,
                               std::function<bool(MonitorEntry*)> callback) {
@@ -197,6 +207,33 @@
     auto port_options = "-port=" + std::to_string(config.vnc_server_port());
     cvd::Command vnc_server(config.vnc_server_binary());
     vnc_server.AddParameter(port_options);
+    if (!config.enable_ivserver()) {
+      // When the ivserver is not enabled, the vnc touch_server needs to serve
+      // on unix sockets and send input events to whoever connects to it (namely
+      // crosvm)
+      auto touch_server = CreateVncInputServer(config.touch_socket_path());
+      if (!touch_server->IsOpen()) {
+        return;
+      }
+      vnc_server.AddParameter("-touch_fd=", touch_server);
+
+      auto keyboard_server =
+          CreateVncInputServer(config.keyboard_socket_path());
+      if (!keyboard_server->IsOpen()) {
+        return;
+      }
+      vnc_server.AddParameter("-keyboard_fd=", keyboard_server);
+      // TODO(b/128852363): This should be handled through the wayland mock
+      //  instead.
+      // Additionally it receives the frame updates from a virtual socket
+      // instead
+      auto frames_server =
+          cvd::SharedFD::VsockServer(config.frames_vsock_port(), SOCK_STREAM);
+      if (!frames_server->IsOpen()) {
+        return;
+      }
+      vnc_server.AddParameter("-frame_server_fd=", frames_server);
+    }
     process_monitor->StartSubprocess(std::move(vnc_server), callback);
   }
 }
diff --git a/host/frontend/vnc_server/Android.bp b/host/frontend/vnc_server/Android.bp
index 241df83..fae5a17 100644
--- a/host/frontend/vnc_server/Android.bp
+++ b/host/frontend/vnc_server/Android.bp
@@ -20,6 +20,7 @@
         "frame_buffer_watcher.cpp",
         "jpeg_compressor.cpp",
         "main.cpp",
+        "screen_connector.cpp",
         "simulated_hw_composer.cpp",
         "virtual_inputs.cpp",
         "vnc_client_connection.cpp",
diff --git a/host/frontend/vnc_server/screen_connector.cpp b/host/frontend/vnc_server/screen_connector.cpp
new file mode 100644
index 0000000..ea82f7e
--- /dev/null
+++ b/host/frontend/vnc_server/screen_connector.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2019 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/frontend/vnc_server/screen_connector.h"
+
+#include <atomic>
+#include <condition_variable>
+
+#include <gflags/gflags.h>
+
+#include <common/vsoc/lib/screen_region_view.h>
+#include <host/libs/config/cuttlefish_config.h>
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+DEFINE_int32(frame_server_fd, -1, "");
+
+namespace cvd {
+namespace vnc {
+
+namespace {
+class VSoCScreenConnector : public ScreenConnector {
+ public:
+  int WaitForNewFrameSince(std::uint32_t* seq_num) override {
+    if (!screen_view_) return -1;
+    return screen_view_->WaitForNewFrameSince(seq_num);
+  }
+
+  void* GetBuffer(int buffer_idx) override {
+    if (!screen_view_) return nullptr;
+    return screen_view_->GetBuffer(buffer_idx);
+  }
+
+ private:
+  vsoc::screen::ScreenRegionView* screen_view_ =
+      vsoc::screen::ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
+};
+
+// TODO(b/128852363): Substitute with one based on memory shared with the
+//  wayland mock
+class SocketBasedScreenConnector : public ScreenConnector {
+ public:
+  SocketBasedScreenConnector(vsoc::CuttlefishConfig* config) : config_(config) {
+    screen_server_thread_ = std::thread([this]() { ServerLoop(); });
+  }
+
+  int WaitForNewFrameSince(std::uint32_t* seq_num) override {
+    std::unique_lock<std::mutex> lock(new_frame_mtx_);
+    while (seq_num_ == *seq_num) {
+      new_frame_cond_var_.wait(lock);
+    }
+    return newest_buffer_;
+  }
+
+  void* GetBuffer(int buffer_idx) override {
+    if (buffer_idx < 0) return nullptr;
+    buffer_idx %= NUM_BUFFERS_;
+    return &buffer_[buffer_idx * ScreenSizeInBytes()];
+  }
+
+ private:
+  static constexpr int NUM_BUFFERS_ = 4;
+
+  void ServerLoop() {
+    if (FLAGS_frame_server_fd < 0) {
+      LOG(FATAL) << "Invalid file descriptor: " << FLAGS_frame_server_fd;
+      return;
+    }
+    auto server = SharedFD::Dup(FLAGS_frame_server_fd);
+    close(FLAGS_frame_server_fd);
+    if (!server->IsOpen()) {
+      LOG(FATAL) << "Unable to dup screen server: " << server->StrError();
+      return;
+    }
+
+    int current_buffer = 0;
+
+    while (1) {
+      auto conn = SharedFD::Accept(*server);
+      while (conn->IsOpen()) {
+        SendScreenParameters(conn);
+
+        int32_t size = 0;
+        conn->Read(&size, sizeof(size));
+        auto buff = reinterpret_cast<uint8_t*>(GetBuffer(current_buffer));
+        while (size > 0) {
+          auto read = conn->Read(buff, size);
+          if (read < 0) {
+            LOG(ERROR) << "Failed to read from hwcomposer: "
+                       << conn->StrError();
+            return;
+          }
+          size -= read;
+          buff += read;
+        }
+        BroadcastNewFrame(current_buffer);
+        current_buffer = (current_buffer + 1) % NUM_BUFFERS_;
+      }
+    }
+  }
+
+  void SendScreenParameters(SharedFD conn) const {
+    // TODO(b/128842613): Send this info from the configuration server
+    int32_t screen_params[4];
+    screen_params[0] = config_->x_res();
+    screen_params[1] = config_->y_res();
+    screen_params[2] = config_->dpi();
+    screen_params[3] = config_->refresh_rate_hz();
+    int buff_size = sizeof(screen_params);
+    int res = conn->Write(screen_params, buff_size);
+    if (res != buff_size) {
+          LOG(FATAL)
+              << "Unable to send full screen parameters to the hwcomposer ("
+              << res << "): " << conn->StrError();
+        }
+  }
+
+  void 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();
+  }
+
+  vsoc::CuttlefishConfig* config_;
+  std::vector<std::uint8_t> buffer_ =
+      std::vector<std::uint8_t>(NUM_BUFFERS_ * ScreenSizeInBytes());
+  std::uint32_t seq_num_{0};
+  int newest_buffer_ = 0;
+  std::condition_variable new_frame_cond_var_;
+  std::mutex new_frame_mtx_;
+  std::thread screen_server_thread_;
+};
+}  // namespace
+
+ScreenConnector* ScreenConnector::Get() {
+  auto config = vsoc::CuttlefishConfig::Get();
+  if (config->enable_ivserver()) {
+    return new VSoCScreenConnector();
+  } else {
+    return new SocketBasedScreenConnector(config);
+  }
+}
+
+}  // namespace vnc
+}  // namespace cvd
\ No newline at end of file
diff --git a/host/frontend/vnc_server/screen_connector.h b/host/frontend/vnc_server/screen_connector.h
new file mode 100644
index 0000000..9c7b9e0
--- /dev/null
+++ b/host/frontend/vnc_server/screen_connector.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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 <cstdint>
+
+namespace cvd {
+namespace vnc {
+
+class ScreenConnector {
+ public:
+  static ScreenConnector* Get();
+
+  virtual ~ScreenConnector() = default;
+
+  virtual int WaitForNewFrameSince(std::uint32_t* seq_num) = 0;
+  virtual void* GetBuffer(int buffer_idx) = 0;
+
+ protected:
+  ScreenConnector() = default;
+};
+
+}  // namespace vnc
+}  // namespace cvd
\ No newline at end of file
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index c8b7f03..f7061ef 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -18,7 +18,6 @@
 
 #include "host/frontend/vnc_server/vnc_utils.h"
 #include "host/libs/config/cuttlefish_config.h"
-#include "common/vsoc/lib/screen_region_view.h"
 
 using cvd::vnc::SimulatedHWComposer;
 using vsoc::screen::ScreenRegionView;
@@ -74,12 +73,11 @@
   auto screen_height = ActualScreenHeight();
   Message raw_screen;
   std::uint64_t stripe_seq_num = 1;
-  auto screen_view = ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
   while (!closed()) {
     bb_->WaitForAtLeastOneClientConnection();
-    int buffer_idx = screen_view->WaitForNewFrameSince(&previous_seq_num);
+    int buffer_idx = screen_connector_->WaitForNewFrameSince(&previous_seq_num);
     const char* frame_start =
-        static_cast<char*>(screen_view->GetBuffer(buffer_idx));
+        static_cast<char*>(screen_connector_->GetBuffer(buffer_idx));
     raw_screen.assign(frame_start, frame_start + ScreenSizeInBytes());
 
     for (int i = 0; i < kNumStripes; ++i) {
diff --git a/host/frontend/vnc_server/simulated_hw_composer.h b/host/frontend/vnc_server/simulated_hw_composer.h
index 5d6e3e2..802cc7f 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.h
+++ b/host/frontend/vnc_server/simulated_hw_composer.h
@@ -26,6 +26,7 @@
 #include "common/libs/thread_safe_queue/thread_safe_queue.h"
 #include "common/libs/threads/thread_annotations.h"
 #include "host/frontend/vnc_server/blackboard.h"
+#include "host/frontend/vnc_server/screen_connector.h"
 
 namespace cvd {
 namespace vnc {
@@ -59,6 +60,7 @@
   BlackBoard* bb_{};
   ThreadSafeQueue<Stripe> stripes_;
   std::thread stripe_maker_;
+  std::shared_ptr<ScreenConnector> screen_connector_{ScreenConnector::Get()};
 };
 }  // namespace vnc
 }  // namespace cvd
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
index b2f519c..e7e3757 100644
--- a/host/frontend/vnc_server/virtual_inputs.cpp
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -22,11 +22,20 @@
 #include <mutex>
 #include "keysyms.h"
 
+#include <common/libs/fs/shared_select.h>
+#include <host/libs/config/cuttlefish_config.h>
+
 using cvd::vnc::VirtualInputs;
 using vsoc::input_events::InputEventsRegionView;
 
+DEFINE_int32(touch_fd, -1,
+             "A fd for a socket where to accept touch connections");
+
+DEFINE_int32(keyboard_fd, -1,
+             "A fd for a socket where to accept keyboard connections");
+
 namespace {
-void AddKeyMappings(std::map<uint32_t, uint32_t>* key_mapping) {
+void AddKeyMappings(std::map<uint32_t, uint16_t>* key_mapping) {
   (*key_mapping)[cvd::xk::AltLeft] = KEY_LEFTALT;
   (*key_mapping)[cvd::xk::ControlLeft] = KEY_LEFTCTRL;
   (*key_mapping)[cvd::xk::ShiftLeft] = KEY_LEFTSHIFT;
@@ -214,30 +223,132 @@
   (*key_mapping)[cvd::xk::Menu] = KEY_MENU;
   (*key_mapping)[cvd::xk::VNCMenu] = KEY_MENU;
 }
+
+void InitInputEvent(struct input_event* evt, uint16_t type, uint16_t code,
+                    int32_t value) {
+  evt->type = type;
+  evt->code = code;
+  evt->value = value;
+}
+
 }  // namespace
 
-VirtualInputs::VirtualInputs()
-  : input_events_region_view_{
-      vsoc::input_events::InputEventsRegionView::GetInstance(
-          vsoc::GetDomain().c_str())} {
-  if (!input_events_region_view_) {
-    LOG(FATAL) << "Failed to open Input events region view";
+class VSoCVirtualInputs : public VirtualInputs {
+ public:
+  VSoCVirtualInputs()
+      : input_events_region_view_{
+            vsoc::input_events::InputEventsRegionView::GetInstance(
+                vsoc::GetDomain().c_str())} {
+    if (!input_events_region_view_) {
+      LOG(FATAL) << "Failed to open Input events region view";
+    }
   }
-  AddKeyMappings(&keymapping_);
-}
 
-void VirtualInputs::GenerateKeyPressEvent(int key_code, bool down) {
-  if (keymapping_.count(key_code)) {
-    input_events_region_view_->HandleKeyboardEvent(down, keymapping_[key_code]);
+  void GenerateKeyPressEvent(int code, bool down) override {
+    if (keymapping_.count(code)) {
+      input_events_region_view_->HandleKeyboardEvent(down, keymapping_[code]);
+    } else {
+      LOG(ERROR) << "Unknown keycode" << code;
+    }
+  }
+
+  void PressPowerButton(bool down) override {
+    input_events_region_view_->HandlePowerButtonEvent(down);
+  }
+
+  void HandlePointerEvent(bool touch_down, int x, int y) override {
+    input_events_region_view_->HandleSingleTouchEvent(touch_down, x, y);
+  }
+
+ private:
+  vsoc::input_events::InputEventsRegionView* input_events_region_view_{};
+};
+
+class SocketVirtualInputs : public VirtualInputs {
+ public:
+  SocketVirtualInputs()
+      : client_connector_([this]() { ClientConnectorLoop(); }) {}
+
+  void GenerateKeyPressEvent(int key_code, bool down) override {
+    struct input_event events[2];
+    InitInputEvent(&events[0], EV_KEY, keymapping_[key_code], down);
+    InitInputEvent(&events[1], EV_SYN, 0, 0);
+
+    SendEvents(keyboard_socket_, events, sizeof(events));
+  }
+
+  void PressPowerButton(bool down) override {
+    struct input_event events[2];
+    InitInputEvent(&events[0], EV_KEY, KEY_POWER, down);
+    InitInputEvent(&events[1], EV_SYN, 0, 0);
+
+    SendEvents(keyboard_socket_, events, sizeof(events));
+  }
+
+  void HandlePointerEvent(bool touch_down, int x, int y) override {
+    // TODO(b/124121375): Use multitouch when available
+    struct input_event events[4];
+    InitInputEvent(&events[0], EV_ABS, ABS_X, x);
+    InitInputEvent(&events[1], EV_ABS, ABS_Y, y);
+    InitInputEvent(&events[2], EV_KEY, BTN_TOUCH, touch_down);
+    InitInputEvent(&events[3], EV_SYN, 0, 0);
+
+    SendEvents(touch_socket_, events, sizeof(events));
+  }
+
+ private:
+  void SendEvents(cvd::SharedFD socket, void* event_buffer, int byte_count) {
+    std::lock_guard<std::mutex> lock(socket_mutex_);
+    if (!socket->IsOpen()) {
+      // This is unlikely as it would only happen between the start of the vnc
+      // server and the connection of the VMM to the socket.
+      // If it happens, just drop the events as the VM is not yet ready to
+      // handle it.
+      return;
+    }
+    auto ret = socket->Write(event_buffer, byte_count);
+    if (ret < 0) {
+      LOG(ERROR) << "Error sending input event: " << socket->StrError();
+    }
+  }
+
+  void ClientConnectorLoop() {
+    auto touch_server = cvd::SharedFD::Dup(FLAGS_touch_fd);
+    close(FLAGS_touch_fd);
+    FLAGS_touch_fd = -1;
+
+    auto keyboard_server = cvd::SharedFD::Dup(FLAGS_keyboard_fd);
+    close(FLAGS_keyboard_fd);
+    FLAGS_keyboard_fd = -1;
+
+    while (1) {
+      cvd::SharedFDSet read_set;
+      read_set.Set(touch_server);
+      read_set.Set(keyboard_server);
+      cvd::Select(&read_set, nullptr, nullptr, nullptr);
+      {
+        std::lock_guard<std::mutex> lock(socket_mutex_);
+        if (read_set.IsSet(touch_server)) {
+          touch_socket_ = cvd::SharedFD::Accept(*touch_server);
+        }
+        if (read_set.IsSet(keyboard_server)) {
+          keyboard_socket_ = cvd::SharedFD::Accept(*keyboard_server);
+        }
+      }
+    }
+  }
+  cvd::SharedFD touch_socket_;
+  cvd::SharedFD keyboard_socket_;
+  std::thread client_connector_;
+  std::mutex socket_mutex_;
+};
+
+VirtualInputs::VirtualInputs() { AddKeyMappings(&keymapping_); }
+
+VirtualInputs* VirtualInputs::Get() {
+  if (vsoc::CuttlefishConfig::Get()->enable_ivserver()) {
+    return new VSoCVirtualInputs();
   } else {
-    LOG(INFO) << "Unknown keycode" << key_code;
+    return new SocketVirtualInputs();
   }
 }
-
-void VirtualInputs::PressPowerButton(bool down) {
-  input_events_region_view_->HandlePowerButtonEvent(down);
-}
-
-void VirtualInputs::HandlePointerEvent(bool touch_down, int x, int y) {
-  input_events_region_view_->HandleSingleTouchEvent(touch_down, x, y);
-}
diff --git a/host/frontend/vnc_server/virtual_inputs.h b/host/frontend/vnc_server/virtual_inputs.h
index c937a12..f92693b 100644
--- a/host/frontend/vnc_server/virtual_inputs.h
+++ b/host/frontend/vnc_server/virtual_inputs.h
@@ -28,15 +28,18 @@
 
 class VirtualInputs {
  public:
+  static VirtualInputs* Get();
+
+  virtual ~VirtualInputs() = default;
+
+  virtual void GenerateKeyPressEvent(int code, bool down) = 0;
+  virtual void PressPowerButton(bool down) = 0;
+  virtual void HandlePointerEvent(bool touch_down, int x, int y) = 0;
+
+ protected:
   VirtualInputs();
 
-  void GenerateKeyPressEvent(int code, bool down);
-  void PressPowerButton(bool down);
-  void HandlePointerEvent(bool touch_down, int x, int y);
-
- private:
-  vsoc::input_events::InputEventsRegionView* input_events_region_view_{};
-  std::map<uint32_t, uint32_t> keymapping_;
+  std::map<uint32_t, uint16_t> keymapping_;
 };
 
 }  // namespace vnc
diff --git a/host/frontend/vnc_server/vnc_client_connection.cpp b/host/frontend/vnc_server/vnc_client_connection.cpp
index 9bed6ff..a8faf47 100644
--- a/host/frontend/vnc_server/vnc_client_connection.cpp
+++ b/host/frontend/vnc_server/vnc_client_connection.cpp
@@ -133,9 +133,9 @@
 }  // namespace vnc
 }  // namespace cvd
 
-VncClientConnection::VncClientConnection(ClientSocket client,
-                                         VirtualInputs* virtual_inputs,
-                                         BlackBoard* bb, bool aggressive)
+VncClientConnection::VncClientConnection(
+    ClientSocket client, std::shared_ptr<VirtualInputs> virtual_inputs,
+    BlackBoard* bb, bool aggressive)
     : client_{std::move(client)}, virtual_inputs_{virtual_inputs}, bb_{bb} {
   frame_buffer_request_handler_tid_ = std::thread(
       &VncClientConnection::FrameBufferUpdateRequestHandler, this, aggressive);
diff --git a/host/frontend/vnc_server/vnc_client_connection.h b/host/frontend/vnc_server/vnc_client_connection.h
index 80aaad1..73beae1 100644
--- a/host/frontend/vnc_server/vnc_client_connection.h
+++ b/host/frontend/vnc_server/vnc_client_connection.h
@@ -35,7 +35,8 @@
 
 class VncClientConnection {
  public:
-  VncClientConnection(ClientSocket client, VirtualInputs* virtual_inputs,
+  VncClientConnection(ClientSocket client,
+                      std::shared_ptr<VirtualInputs> virtual_inputs,
                       BlackBoard* bb, bool aggressive);
   VncClientConnection(const VncClientConnection&) = delete;
   VncClientConnection& operator=(const VncClientConnection&) = delete;
@@ -139,7 +140,7 @@
   ClientSocket client_;
   bool control_key_down_ = false;
   bool meta_key_down_ = false;
-  VirtualInputs* virtual_inputs_{};
+  std::shared_ptr<VirtualInputs> virtual_inputs_{};
 
   FrameBufferUpdateRequest previous_update_request_{};
   BlackBoard* bb_;
diff --git a/host/frontend/vnc_server/vnc_server.cpp b/host/frontend/vnc_server/vnc_server.cpp
index 03f5dbe..3aba467 100644
--- a/host/frontend/vnc_server/vnc_server.cpp
+++ b/host/frontend/vnc_server/vnc_server.cpp
@@ -28,7 +28,10 @@
 using cvd::vnc::VncServer;
 
 VncServer::VncServer(int port, bool aggressive)
-    : server_(port), frame_buffer_watcher_{&bb_}, aggressive_{aggressive} {}
+    : server_(port),
+    virtual_inputs_(VirtualInputs::Get()),
+    frame_buffer_watcher_{&bb_},
+    aggressive_{aggressive} {}
 
 void VncServer::MainLoop() {
   while (true) {
@@ -50,7 +53,7 @@
   // data members. In the current setup, if the VncServer is destroyed with
   // clients still running, the clients will all be left with dangling
   // pointers.
-  VncClientConnection client(std::move(sock), &virtual_inputs_, &bb_,
+  VncClientConnection client(std::move(sock), virtual_inputs_, &bb_,
                              aggressive_);
   client.StartSession();
 }
diff --git a/host/frontend/vnc_server/vnc_server.h b/host/frontend/vnc_server/vnc_server.h
index d88835c..66e17e0 100644
--- a/host/frontend/vnc_server/vnc_server.h
+++ b/host/frontend/vnc_server/vnc_server.h
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+#include <memory>
 #include <string>
 #include <thread>
 #include <utility>
@@ -46,7 +47,7 @@
   void StartClientThread(ClientSocket sock);
 
   ServerSocket server_;
-  VirtualInputs virtual_inputs_;
+  std::shared_ptr<VirtualInputs> virtual_inputs_;
   BlackBoard bb_;
   FrameBufferWatcher frame_buffer_watcher_;
   bool aggressive_{};
diff --git a/host/frontend/vnc_server/vnc_utils.h b/host/frontend/vnc_server/vnc_utils.h
index 3eac9f3..194e828 100644
--- a/host/frontend/vnc_server/vnc_utils.h
+++ b/host/frontend/vnc_server/vnc_utils.h
@@ -21,6 +21,7 @@
 #include <utility>
 #include <vector>
 
+#include "common/libs/utils/size_utils.h"
 #include "common/libs/tcp_socket/tcp_socket.h"
 #include "common/vsoc/lib/screen_region_view.h"
 #include "host/libs/config/cuttlefish_config.h"
@@ -68,16 +69,12 @@
 
 // The width of the screen regardless of orientation. Does not change.
 inline int ActualScreenWidth() {
-  return vsoc::screen::ScreenRegionView::GetInstance(
-             vsoc::GetDomain().c_str())
-      ->x_res();
+  return AlignToPowerOf2(vsoc::CuttlefishConfig::Get()->x_res(), 4);
 }
 
 // The height of the screen regardless of orientation. Does not change.
 inline int ActualScreenHeight() {
-  return vsoc::screen::ScreenRegionView::GetInstance(
-             vsoc::GetDomain().c_str())
-      ->y_res();
+  return vsoc::CuttlefishConfig::Get()->y_res();
 }
 
 inline int ScreenSizeInBytes() {
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 82ac8e3..2c4ba95 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -154,7 +154,8 @@
 const char* kBlankDataImageFmt = "blank_data_image_fmt";
 
 const char* kLogcatMode = "logcat_mode";
-const char* kLogcatVsockPort = "logcat_vsock_mode";
+const char* kLogcatVsockPort = "logcat_vsock_port";
+const char* kFramesVsockPort = "frames_vsock_port";
 const char* kLogcatReceiverBinary = "logcat_receiver_binary";
 }  // namespace
 
@@ -793,6 +794,14 @@
   return (*dictionary_)[kLogcatVsockPort].asInt();
 }
 
+void CuttlefishConfig::set_frames_vsock_port(int port) {
+  (*dictionary_)[kFramesVsockPort] = port;
+}
+
+int CuttlefishConfig::frames_vsock_port() const {
+  return (*dictionary_)[kFramesVsockPort].asInt();
+}
+
 void CuttlefishConfig::set_logcat_receiver_binary(const std::string& binary) {
   SetPath(kLogcatReceiverBinary, binary);
 }
@@ -801,11 +810,18 @@
   return (*dictionary_)[kLogcatReceiverBinary].asString();
 }
 
-
 bool CuttlefishConfig::enable_ivserver() const {
   return hardware_name() == "cutf_ivsh";
 }
 
+std::string CuttlefishConfig::touch_socket_path() const {
+  return PerInstancePath("touch.sock");
+}
+
+std::string CuttlefishConfig::keyboard_socket_path() const {
+  return PerInstancePath("keyboard.sock");
+}
+
 // Creates the (initially empty) config object and populates it with values from
 // the config file if the CUTTLEFISH_CONFIG_FILE env variable is present.
 // Returns nullptr if there was an error loading from file
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 9f87399..75dbddc 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -302,8 +302,14 @@
   void set_logcat_vsock_port(int port);
   int logcat_vsock_port() const;
 
+  void set_frames_vsock_port(int port);
+  int frames_vsock_port() const;
+
   bool enable_ivserver() const;
 
+  std::string touch_socket_path() const;
+  std::string keyboard_socket_path() const;
+
  private:
   std::unique_ptr<Json::Value> dictionary_;
 
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index b623157..2cee6b3 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -85,6 +85,9 @@
   command.AddParameter("--rwdisk=", config_->metadata_image_path());
   command.AddParameter("--socket=", GetControlSocketPath(config_));
   command.AddParameter("--android-fstab=", config_->gsi_fstab_path());
+  command.AddParameter("--single-touch=", config_->touch_socket_path(), ":",
+                       config_->x_res(), ":", config_->y_res());
+  command.AddParameter("--keyboard=", config_->keyboard_socket_path());
 
   AddTapFdParameter(&command, config_->wifi_tap_name());
   AddTapFdParameter(&command, config_->mobile_tap_name());