Revert deleting vsoc

Bug: 144973127
Test: git diff 698c8df9e00602c8cd82bbcc872693509a169fe9
Change-Id: I19d15f093a69e18f6fafa00e4808620297d37df8
diff --git a/Android.bp b/Android.bp
index 9fed081..a9ee724 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,3 +110,132 @@
     host_supported: true,
     defaults: ["cuttlefish_base"],
 }
+
+cc_library_shared {
+    name: "vsoc_lib",
+    srcs: [
+        "common/vsoc/lib/compat.cpp",
+        "common/vsoc/lib/audio_data_layout.cpp",
+        "common/vsoc/lib/e2e_test_region_layout.cpp",
+        "common/vsoc/lib/gralloc_layout.cpp",
+        "common/vsoc/lib/input_events_layout.cpp",
+        "common/vsoc/lib/input_events_region_view.cpp",
+        "common/vsoc/lib/lock_common.cpp",
+        "common/vsoc/lib/managed_e2e_test_region_layout.cpp",
+        "common/vsoc/lib/region_view.cpp",
+        "common/vsoc/lib/screen_layout.cpp",
+        "common/vsoc/lib/screen_region_view.cpp",
+        "common/vsoc/lib/socket_forward_layout.cpp",
+        "common/vsoc/lib/socket_forward_region_view.cpp",
+        "common/vsoc/lib/vsoc_memory.cpp",
+    ],
+    header_libs: ["cuttlefish_glog"],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+        "liblog",
+    ],
+    target: {
+        host: {
+            srcs: [
+                "host/vsoc/lib/gralloc_buffer_region_view.cpp",
+                "host/vsoc/lib/host_lock.cpp",
+                "host/vsoc/lib/region_control.cpp",
+                "host/vsoc/lib/region_view.cpp",
+            ],
+        },
+        android: {
+            srcs: [
+                "guest/vsoc/lib/gralloc_region_view.cpp",
+                "guest/vsoc/lib/guest_lock.cpp",
+                "guest/vsoc/lib/region_control.cpp",
+                "guest/vsoc/lib/region_view.cpp",
+            ],
+        },
+    },
+    defaults: ["cuttlefish_host_and_guest"],
+}
+
+cc_test_host {
+    name: "circqueue_test",
+    srcs: [
+        "common/vsoc/lib/circqueue_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libbase",
+    ],
+    defaults: ["cuttlefish_host_only"],
+    test_suites: ["general-tests"],
+}
+
+cc_binary_host {
+    name: "vsoc_screen_region_view_test",
+    srcs: [
+        "common/vsoc/lib/screen_region_view_test.cpp",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libjsoncpp",
+        "libgflags",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libbase",
+        "libcuttlefish_utils",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
+
+cc_test_host {
+    name: "lock_test",
+    srcs: [
+        "common/vsoc/lib/lock_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libgtest_host",
+    ],
+    defaults: ["cuttlefish_host_only"],
+    test_suites: ["general-tests"],
+}
+
+cc_test_host {
+    name: "vsoc_graphics_test",
+    srcs: [
+        "common/vsoc/lib/graphics_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libbase",
+    ],
+    defaults: ["cuttlefish_host_only"],
+    test_suites: ["general-tests"],
+}
+
+cc_binary_host {
+    name: "host_region_e2e_test",
+    srcs: [
+        "host/vsoc/lib/host_region_e2e_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libcuttlefish_fs",
+        "libcuttlefish_utils",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libjsoncpp",
+        "libgtest_host",
+        "libgflags",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/common/frontend/Android.bp b/common/frontend/Android.bp
index 39a4dbb..a7a4de5 100644
--- a/common/frontend/Android.bp
+++ b/common/frontend/Android.bp
@@ -14,5 +14,6 @@
 // limitations under the License.
 
 subdirs = [
+    "socket_forward_proxy",
     "socket_vsock_proxy",
 ]
diff --git a/common/frontend/socket_forward_proxy/Android.bp b/common/frontend/socket_forward_proxy/Android.bp
new file mode 100644
index 0000000..74ef53a
--- /dev/null
+++ b/common/frontend/socket_forward_proxy/Android.bp
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2018 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.
+
+cc_binary {
+    name: "socket_forward_proxy",
+    srcs: [
+        "main.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcuttlefish_fs",
+        "libcuttlefish_utils",
+        "libcuttlefish_strings",
+        "cuttlefish_auto_resources",
+        "vsoc_lib",
+        "liblog",
+    ],
+    static_libs: [
+        "libgflags",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    target: {
+        host: {
+            static_libs: [
+                "libcuttlefish_host_config",
+                "libjsoncpp",
+            ],
+        },
+    },
+    defaults: ["cuttlefish_host_and_guest"]
+}
diff --git a/common/frontend/socket_forward_proxy/main.cpp b/common/frontend/socket_forward_proxy/main.cpp
new file mode 100644
index 0000000..4c21459
--- /dev/null
+++ b/common/frontend/socket_forward_proxy/main.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2018 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 <array>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+#include <glog/logging.h>
+#include <gflags/gflags.h>
+
+#include <unistd.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/strings/str_split.h"
+#include "common/vsoc/lib/socket_forward_region_view.h"
+
+#ifdef CUTTLEFISH_HOST
+#include "host/libs/config/cuttlefish_config.h"
+#endif
+
+using vsoc::socket_forward::Packet;
+using vsoc::socket_forward::SocketForwardRegionView;
+
+#ifdef CUTTLEFISH_HOST
+DEFINE_string(guest_ports, "",
+              "Comma-separated list of ports on which to forward TCP "
+              "connections to the guest.");
+DEFINE_string(host_ports, "",
+              "Comma-separated list of ports on which to run TCP servers on "
+              "the host.");
+#endif
+
+namespace {
+// Sends packets, Shutdown(SHUT_WR) on destruction
+class SocketSender {
+ public:
+  explicit SocketSender(cvd::SharedFD socket) : socket_{std::move(socket)} {}
+
+  SocketSender(SocketSender&&) = default;
+  SocketSender& operator=(SocketSender&&) = default;
+
+  SocketSender(const SocketSender&&) = delete;
+  SocketSender& operator=(const SocketSender&) = delete;
+
+  ~SocketSender() {
+    if (socket_.operator->()) {  // check that socket_ was not moved-from
+      socket_->Shutdown(SHUT_WR);
+    }
+  }
+
+  ssize_t SendAll(const Packet& packet) {
+    ssize_t written{};
+    while (written < static_cast<ssize_t>(packet.payload_length())) {
+      if (!socket_->IsOpen()) {
+        return -1;
+      }
+      auto just_written =
+          socket_->Send(packet.payload() + written,
+                        packet.payload_length() - written, MSG_NOSIGNAL);
+      if (just_written <= 0) {
+        LOG(INFO) << "Couldn't write to client: "
+                  << strerror(socket_->GetErrno());
+        return just_written;
+      }
+      written += just_written;
+    }
+    return written;
+  }
+
+ private:
+  cvd::SharedFD socket_;
+};
+
+class SocketReceiver {
+ public:
+  explicit SocketReceiver(cvd::SharedFD socket) : socket_{std::move(socket)} {}
+
+  SocketReceiver(SocketReceiver&&) = default;
+  SocketReceiver& operator=(SocketReceiver&&) = default;
+
+  SocketReceiver(const SocketReceiver&&) = delete;
+  SocketReceiver& operator=(const SocketReceiver&) = delete;
+
+  // *packet will be empty if Read returns 0 or error
+  void Recv(Packet* packet) {
+    auto size = socket_->Read(packet->payload(), sizeof packet->payload());
+    if (size < 0) {
+      size = 0;
+    }
+    packet->set_payload_length(size);
+  }
+
+ private:
+  cvd::SharedFD socket_;
+};
+
+void SocketToShm(SocketReceiver socket_receiver,
+                 SocketForwardRegionView::ShmSender shm_sender) {
+  while (true) {
+    auto packet = Packet::MakeData();
+    socket_receiver.Recv(&packet);
+    if (packet.empty() || !shm_sender.Send(packet)) {
+      break;
+    }
+  }
+  LOG(INFO) << "Socket to shm exiting";
+}
+
+void ShmToSocket(SocketSender socket_sender,
+                 SocketForwardRegionView::ShmReceiver shm_receiver) {
+  auto packet = Packet{};
+  while (true) {
+    shm_receiver.Recv(&packet);
+    CHECK(packet.IsData());
+    if (packet.empty()) {
+      break;
+    }
+    if (socket_sender.SendAll(packet) < 0) {
+      break;
+    }
+  }
+  LOG(INFO) << "Shm to socket exiting";
+}
+
+// One thread for reading from shm and writing into a socket.
+// One thread for reading from a socket and writing into shm.
+void HandleConnection(SocketForwardRegionView::ShmSenderReceiverPair shm_sender_and_receiver,
+                      cvd::SharedFD socket) {
+  auto socket_to_shm =
+      std::thread(SocketToShm, SocketReceiver{socket}, std::move(shm_sender_and_receiver.first));
+  ShmToSocket(SocketSender{socket}, std::move(shm_sender_and_receiver.second));
+  socket_to_shm.join();
+}
+
+#ifdef CUTTLEFISH_HOST
+struct PortPair {
+  int guest_port;
+  int host_port;
+};
+
+enum class QueueState {
+  kFree,
+  kUsed,
+};
+
+struct SocketConnectionInfo {
+  std::mutex lock{};
+  std::condition_variable cv{};
+  cvd::SharedFD socket{};
+  int guest_port{};
+  QueueState state = QueueState::kFree;
+};
+
+static constexpr auto kNumHostThreads =
+    vsoc::layout::socket_forward::kNumQueues;
+
+using SocketConnectionInfoCollection =
+    std::array<SocketConnectionInfo, kNumHostThreads>;
+
+void MarkAsFree(SocketConnectionInfo* conn) {
+  std::lock_guard<std::mutex> guard{conn->lock};
+  conn->socket = cvd::SharedFD{};
+  conn->guest_port = 0;
+  conn->state = QueueState::kFree;
+}
+
+std::pair<int, cvd::SharedFD> WaitForConnection(SocketConnectionInfo* conn) {
+  std::unique_lock<std::mutex> guard{conn->lock};
+  while (conn->state != QueueState::kUsed) {
+    conn->cv.wait(guard);
+  }
+  return {conn->guest_port, conn->socket};
+}
+
+[[noreturn]] void host_thread(SocketForwardRegionView::ShmConnectionView view,
+                              SocketConnectionInfo* conn) {
+  while (true) {
+    int guest_port{};
+    cvd::SharedFD socket{};
+    // TODO structured binding in C++17
+    std::tie(guest_port, socket) = WaitForConnection(conn);
+
+    LOG(INFO) << "Establishing connection to guest port " << guest_port
+              << " with connection_id: " << view.connection_id();
+    HandleConnection(view.EstablishConnection(guest_port), std::move(socket));
+    LOG(INFO) << "Connection to guest port " << guest_port
+              << " closed. Marking queue " << view.connection_id()
+              << " as free.";
+    MarkAsFree(conn);
+  }
+}
+
+bool TryAllocateConnection(SocketConnectionInfo* conn, int guest_port,
+                           cvd::SharedFD socket) {
+  bool success = false;
+  {
+    std::lock_guard<std::mutex> guard{conn->lock};
+    if (conn->state == QueueState::kFree) {
+      conn->socket = std::move(socket);
+      conn->guest_port = guest_port;
+      conn->state = QueueState::kUsed;
+      success = true;
+    }
+  }
+  if (success) {
+    conn->cv.notify_one();
+  }
+  return success;
+}
+
+void AllocateWorkers(cvd::SharedFD socket,
+                     SocketConnectionInfoCollection* socket_connection_info,
+                     int guest_port) {
+  while (true) {
+    for (auto& conn : *socket_connection_info) {
+      if (TryAllocateConnection(&conn, guest_port, socket)) {
+        return;
+      }
+    }
+    LOG(INFO) << "no queues available. sleeping and retrying";
+    sleep(5);
+  }
+}
+
+[[noreturn]] void host_impl(
+    SocketForwardRegionView* shm,
+    SocketConnectionInfoCollection* socket_connection_info,
+    std::vector<PortPair> ports, std::size_t index) {
+  // launch a worker for the following port before handling the current port.
+  // recursion (instead of a loop) removes the need fore any join() or having
+  // the main thread do no work.
+  if (index + 1 < ports.size()) {
+    std::thread(host_impl, shm, socket_connection_info, ports, index + 1)
+        .detach();
+  }
+  auto guest_port = ports[index].guest_port;
+  auto host_port = ports[index].host_port;
+  LOG(INFO) << "starting server on " << host_port << " for guest port "
+            << guest_port;
+  auto server = cvd::SharedFD::SocketLocalServer(host_port, SOCK_STREAM);
+  CHECK(server->IsOpen()) << "Could not start server on port " << host_port;
+  while (true) {
+    LOG(INFO) << "waiting for client connection";
+    auto client_socket = cvd::SharedFD::Accept(*server);
+    CHECK(client_socket->IsOpen()) << "error creating client socket";
+    LOG(INFO) << "client socket accepted";
+    AllocateWorkers(std::move(client_socket), socket_connection_info,
+                    guest_port);
+  }
+}
+
+[[noreturn]] void host(SocketForwardRegionView* shm,
+                       std::vector<PortPair> ports) {
+  CHECK(!ports.empty());
+
+  SocketConnectionInfoCollection socket_connection_info{};
+
+  auto conn_info_iter = std::begin(socket_connection_info);
+  for (auto& shm_connection_view : shm->AllConnections()) {
+    CHECK_NE(conn_info_iter, std::end(socket_connection_info));
+    std::thread(host_thread, std::move(shm_connection_view), &*conn_info_iter)
+        .detach();
+    ++conn_info_iter;
+  }
+  CHECK_EQ(conn_info_iter, std::end(socket_connection_info));
+  host_impl(shm, &socket_connection_info, ports, 0);
+}
+
+std::vector<PortPair> ParsePortsList(const std::string& guest_ports_str,
+                                     const std::string& host_ports_str) {
+  std::vector<PortPair> ports{};
+  auto guest_ports = cvd::StrSplit(guest_ports_str, ',');
+  auto host_ports = cvd::StrSplit(host_ports_str, ',');
+  CHECK(guest_ports.size() == host_ports.size());
+  for (std::size_t i = 0; i < guest_ports.size(); ++i) {
+    ports.push_back({std::stoi(guest_ports[i]), std::stoi(host_ports[i])});
+  }
+  return ports;
+}
+
+#else
+cvd::SharedFD OpenSocketConnection(int port) {
+  while (true) {
+    auto sock = cvd::SharedFD::SocketLocalClient(port, SOCK_STREAM);
+    if (sock->IsOpen()) {
+      return sock;
+    }
+    LOG(WARNING) << "could not connect on port " << port
+                 << ". sleeping for 1 second";
+    sleep(1);
+  }
+}
+
+[[noreturn]] void guest_thread(
+    SocketForwardRegionView::ShmConnectionView view) {
+  while (true) {
+    LOG(INFO) << "waiting for new connection";
+    auto shm_sender_and_receiver = view.WaitForNewConnection();
+    LOG(INFO) << "new connection for port " << view.port();
+    HandleConnection(std::move(shm_sender_and_receiver), OpenSocketConnection(view.port()));
+    LOG(INFO) << "connection closed on port " << view.port();
+  }
+}
+
+[[noreturn]] void guest(SocketForwardRegionView* shm) {
+  LOG(INFO) << "Starting guest mainloop";
+  auto connection_views = shm->AllConnections();
+  for (auto&& shm_connection_view : connection_views) {
+    std::thread(guest_thread, std::move(shm_connection_view)).detach();
+  }
+  while (true) {
+    sleep(std::numeric_limits<unsigned int>::max());
+  }
+}
+
+#endif
+
+SocketForwardRegionView* GetShm() {
+  auto shm = SocketForwardRegionView::GetInstance(
+#ifdef CUTTLEFISH_HOST
+      vsoc::GetDomain().c_str()
+#endif
+  );
+  if (!shm) {
+    LOG(FATAL) << "Could not open SHM. Aborting.";
+  }
+  shm->CleanUpPreviousConnections();
+  return shm;
+}
+
+// makes sure we're running as root on the guest, no-op on the host
+void assert_correct_user() {
+#ifndef CUTTLEFISH_HOST
+  CHECK_EQ(getuid(), 0u) << "must run as root!";
+#endif
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  assert_correct_user();
+
+  auto shm = GetShm();
+  auto worker = shm->StartWorker();
+
+#ifdef CUTTLEFISH_HOST
+  CHECK(!FLAGS_guest_ports.empty()) << "Must specify --guest_ports flag";
+  CHECK(!FLAGS_host_ports.empty()) << "Must specify --host_ports flag";
+  host(shm, ParsePortsList(FLAGS_guest_ports, FLAGS_host_ports));
+#else
+  guest(shm);
+#endif
+}
diff --git a/common/frontend/socket_vsock_proxy/main.cpp b/common/frontend/socket_vsock_proxy/main.cpp
index 31cff0b..ac421a3 100644
--- a/common/frontend/socket_vsock_proxy/main.cpp
+++ b/common/frontend/socket_vsock_proxy/main.cpp
@@ -20,103 +20,13 @@
 #include <gflags/gflags.h>
 
 #include "common/libs/fs/shared_fd.h"
+#include "common/vsoc/lib/socket_forward_region_view.h"
 
 #ifdef CUTTLEFISH_HOST
 #include "host/libs/config/cuttlefish_config.h"
 #endif
 
-struct Header {
-  std::uint32_t payload_length;
-  enum MessageType : std::uint32_t {
-    DATA = 0,
-    BEGIN,
-    END,
-    RECV_CLOSED,  // indicate that this side's receive end is closed
-    RESTART,
-  };
-  MessageType message_type;
-};
-
-constexpr std::size_t kMaxPacketSize = 8192;
-constexpr std::size_t kMaxPayloadSize = kMaxPacketSize - sizeof(Header);
-
-struct Packet {
- private:
-  Header header_;
-  using Payload = char[kMaxPayloadSize];
-  Payload payload_data_;
-
-  static constexpr Packet MakePacket(Header::MessageType type) {
-    Packet packet{};
-    packet.header_.message_type = type;
-    return packet;
-  }
-
- public:
-  // port is only revelant on the host-side.
-  static Packet MakeBegin(std::uint16_t port);
-
-  static constexpr Packet MakeEnd() { return MakePacket(Header::END); }
-
-  static constexpr Packet MakeRecvClosed() {
-    return MakePacket(Header::RECV_CLOSED);
-  }
-
-  static constexpr Packet MakeRestart() { return MakePacket(Header::RESTART); }
-
-  // NOTE payload and payload_length must still be set.
-  static constexpr Packet MakeData() { return MakePacket(Header::DATA); }
-
-  bool empty() const { return IsData() && header_.payload_length == 0; }
-
-  void set_payload_length(std::uint32_t length) {
-    CHECK_LE(length, sizeof payload_data_);
-    header_.payload_length = length;
-  }
-
-  Payload& payload() { return payload_data_; }
-
-  const Payload& payload() const { return payload_data_; }
-
-  constexpr std::uint32_t payload_length() const {
-    return header_.payload_length;
-  }
-
-  constexpr bool IsBegin() const {
-    return header_.message_type == Header::BEGIN;
-  }
-
-  constexpr bool IsEnd() const { return header_.message_type == Header::END; }
-
-  constexpr bool IsData() const { return header_.message_type == Header::DATA; }
-
-  constexpr bool IsRecvClosed() const {
-    return header_.message_type == Header::RECV_CLOSED;
-  }
-
-  constexpr bool IsRestart() const {
-    return header_.message_type == Header::RESTART;
-  }
-
-  constexpr std::uint16_t port() const {
-    CHECK(IsBegin());
-    std::uint16_t port_number{};
-    CHECK_EQ(payload_length(), sizeof port_number);
-    std::memcpy(&port_number, payload(), sizeof port_number);
-    return port_number;
-  }
-
-  char* raw_data() { return reinterpret_cast<char*>(this); }
-
-  const char* raw_data() const { return reinterpret_cast<const char*>(this); }
-
-  constexpr size_t raw_data_length() const {
-    return payload_length() + sizeof header_;
-  }
-};
-
-static_assert(sizeof(Packet) == kMaxPacketSize, "");
-static_assert(std::is_pod<Packet>{}, "");
+using vsoc::socket_forward::Packet;
 
 DEFINE_uint32(tcp_port, 0, "TCP port (server on host, client on guest)");
 DEFINE_uint32(vsock_port, 0, "vsock port (client on host, server on guest");
diff --git a/common/vsoc/lib/audio_data_layout.cpp b/common/vsoc/lib/audio_data_layout.cpp
new file mode 100644
index 0000000..4b5172c
--- /dev/null
+++ b/common/vsoc/lib/audio_data_layout.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/audio_data_layout.h"
+
+namespace vsoc {
+namespace layout {
+namespace audio_data {
+
+// static
+const char *const AudioDataLayout::region_name = "audio_data";
+
+}  // namespace audio_data
+}  // namespace layout
+}  // namespace vsoc
+
diff --git a/common/vsoc/lib/audio_data_region_view.h b/common/vsoc/lib/audio_data_region_view.h
new file mode 100644
index 0000000..42d05df
--- /dev/null
+++ b/common/vsoc/lib/audio_data_region_view.h
@@ -0,0 +1,39 @@
+#pragma once
+/*
+ * Copyright (C) 2018 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 <memory>
+
+#include <android-base/macros.h>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/audio_data_layout.h"
+
+namespace vsoc {
+namespace audio_data {
+
+class AudioDataRegionView
+    : public vsoc::TypedRegionView<
+        AudioDataRegionView,
+        vsoc::layout::audio_data::AudioDataLayout> {
+public:
+    AudioDataRegionView() = default;
+    AudioDataRegionView(const AudioDataRegionView &) = delete;
+    AudioDataRegionView &operator=(const AudioDataRegionView &) = delete;
+};
+
+}  // namespace audio_data
+}  // namespace vsoc
diff --git a/common/vsoc/lib/circqueue_impl.h b/common/vsoc/lib/circqueue_impl.h
new file mode 100644
index 0000000..8b0fa90
--- /dev/null
+++ b/common/vsoc/lib/circqueue_impl.h
@@ -0,0 +1,234 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <cerrno>
+#include <cstring>
+
+#include <sys/uio.h>
+
+#include "common/vsoc/lib/region_signaling_interface.h"
+#include "common/vsoc/shm/circqueue.h"
+
+// Increases the given index until it is naturally aligned for T.
+template <typename T>
+uintptr_t align(uintptr_t index) {
+  return (index + sizeof(T) - 1) & ~(sizeof(T) - 1);
+}
+
+namespace vsoc {
+class RegionSignalingInterface;
+namespace layout {
+
+template <uint32_t SizeLog2>
+void CircularQueueBase<SizeLog2>::CopyInRange(const char* buffer_in,
+                                              const Range& t) {
+  size_t bytes = t.end_idx - t.start_idx;
+  uint32_t index = t.start_idx & (BufferSize - 1);
+  if (index + bytes <= BufferSize) {
+    std::memcpy(buffer_ + index, buffer_in, bytes);
+  } else {
+    size_t part1_size = BufferSize - index;
+    size_t part2_size = bytes - part1_size;
+    std::memcpy(buffer_ + index, buffer_in, part1_size);
+    std::memcpy(buffer_, buffer_in + part1_size, part2_size);
+  }
+}
+
+template <uint32_t SizeLog2>
+void CircularQueueBase<SizeLog2>::CopyOutRange(const Range& t,
+                                               char* buffer_out) {
+  uint32_t index = t.start_idx & (BufferSize - 1);
+  size_t total_size = t.end_idx - t.start_idx;
+  if (index + total_size <= BufferSize) {
+    std::memcpy(buffer_out, buffer_ + index, total_size);
+  } else {
+    uint32_t part1_size = BufferSize - index;
+    uint32_t part2_size = total_size - part1_size;
+    std::memcpy(buffer_out, buffer_ + index, part1_size);
+    std::memcpy(buffer_out + part1_size, buffer_, part2_size);
+  }
+}
+
+template <uint32_t SizeLog2>
+void CircularQueueBase<SizeLog2>::WaitForDataLocked(
+    RegionSignalingInterface* r) {
+  while (1) {
+    uint32_t o_w_pub = w_pub_;
+    // We don't have data. Wait until some appears and try again
+    if (r_released_ != o_w_pub) {
+      return;
+    }
+    lock_.Unlock();
+    r->WaitForSignal(&w_pub_, o_w_pub);
+    lock_.Lock();
+  }
+}
+
+template <uint32_t SizeLog2>
+intptr_t CircularQueueBase<SizeLog2>::WriteReserveLocked(
+    RegionSignalingInterface* r, size_t bytes, Range* t, bool non_blocking) {
+  // Can't write more than the buffer will hold
+  if (bytes > BufferSize) {
+    return -ENOSPC;
+  }
+  while (true) {
+    uint32_t o_w_pub = w_pub_;
+    uint32_t o_r_release = r_released_;
+    uint32_t bytes_in_use = o_w_pub - o_r_release;
+    size_t available = BufferSize - bytes_in_use;
+    if (available >= bytes) {
+      t->start_idx = o_w_pub;
+      t->end_idx = o_w_pub + bytes;
+      break;
+    }
+    if (non_blocking) {
+      return -EWOULDBLOCK;
+    }
+    // If we can't write at the moment wait for a reader to release
+    // some bytes.
+    lock_.Unlock();
+    r->WaitForSignal(&r_released_, o_r_release);
+    lock_.Lock();
+  }
+  return t->end_idx - t->start_idx;
+}
+
+template <uint32_t SizeLog2>
+intptr_t CircularByteQueue<SizeLog2>::Read(RegionSignalingInterface* r,
+                                           char* buffer_out, size_t max_size) {
+  this->lock_.Lock();
+  this->WaitForDataLocked(r);
+  Range t;
+  t.start_idx = this->r_released_;
+  t.end_idx = this->w_pub_;
+  // The lock is still held here...
+  // Trim the range if we got more than the reader wanted
+  if ((t.end_idx - t.start_idx) > max_size) {
+    t.end_idx = t.start_idx + max_size;
+  }
+  this->CopyOutRange(t, buffer_out);
+  this->r_released_ = t.end_idx;
+  this->lock_.Unlock();
+  r->SendSignal(layout::Sides::Both, &this->r_released_);
+  return t.end_idx - t.start_idx;
+}
+
+template <uint32_t SizeLog2>
+intptr_t CircularByteQueue<SizeLog2>::Write(RegionSignalingInterface* r,
+                                            const char* buffer_in, size_t bytes,
+                                            bool non_blocking) {
+  Range range;
+  this->lock_.Lock();
+  intptr_t rval = this->WriteReserveLocked(r, bytes, &range, non_blocking);
+  if (rval < 0) {
+    this->lock_.Unlock();
+    return rval;
+  }
+  this->CopyInRange(buffer_in, range);
+  // We can't publish until all of the previous write allocations where
+  // published.
+  this->w_pub_ = range.end_idx;
+  this->lock_.Unlock();
+  r->SendSignal(layout::Sides::Both, &this->w_pub_);
+  return bytes;
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::CalculateBufferedSize(
+    size_t payload) {
+  return align<uint32_t>(sizeof(uint32_t) + payload);
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::Read(
+    RegionSignalingInterface* r, char* buffer_out, size_t max_size) {
+  this->lock_.Lock();
+  this->WaitForDataLocked(r);
+  uint32_t packet_size = *reinterpret_cast<uint32_t*>(
+      this->buffer_ + (this->r_released_ & (this->BufferSize - 1)));
+  if (packet_size > max_size) {
+    this->lock_.Unlock();
+    return -ENOSPC;
+  }
+  Range t;
+  t.start_idx = this->r_released_ + sizeof(uint32_t);
+  t.end_idx = t.start_idx + packet_size;
+  this->CopyOutRange(t, buffer_out);
+  this->r_released_ += this->CalculateBufferedSize(packet_size);
+  this->lock_.Unlock();
+  r->SendSignal(layout::Sides::Both, &this->r_released_);
+  return packet_size;
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::Write(
+    RegionSignalingInterface* r, const char* buffer_in, uint32_t bytes,
+    bool non_blocking) {
+  iovec iov;
+  iov.iov_base = const_cast<char *>(buffer_in);
+  iov.iov_len = bytes;
+  return Writev(r, &iov, 1 /* iov_count */, non_blocking);
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::Writev(
+      RegionSignalingInterface *r,
+      const iovec *iov,
+      size_t iov_count,
+      bool non_blocking) {
+  size_t bytes = 0;
+  for (size_t i = 0; i < iov_count; ++i) {
+    bytes += iov[i].iov_len;
+  }
+
+  if (bytes > MaxPacketSize) {
+    return -ENOSPC;
+  }
+
+  Range range;
+  size_t buffered_size = this->CalculateBufferedSize(bytes);
+  this->lock_.Lock();
+  intptr_t rval =
+      this->WriteReserveLocked(r, buffered_size, &range, non_blocking);
+  if (rval < 0) {
+    this->lock_.Unlock();
+    return rval;
+  }
+  Range header = range;
+  header.end_idx = header.start_idx + sizeof(uint32_t);
+  Range payload{
+      static_cast<uint32_t>(range.start_idx + sizeof(uint32_t)),
+      static_cast<uint32_t>(range.start_idx + sizeof(uint32_t) + bytes)};
+  this->CopyInRange(reinterpret_cast<const char*>(&bytes), header);
+
+  Range subRange = payload;
+  for (size_t i = 0; i < iov_count; ++i) {
+    subRange.end_idx = subRange.start_idx + iov[i].iov_len;
+    this->CopyInRange(static_cast<const char *>(iov[i].iov_base), subRange);
+
+    subRange.start_idx = subRange.end_idx;
+  }
+
+  this->w_pub_ = range.end_idx;
+  this->lock_.Unlock();
+  r->SendSignal(layout::Sides::Both, &this->w_pub_);
+  return bytes;
+}
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/lib/circqueue_test.cpp b/common/vsoc/lib/circqueue_test.cpp
new file mode 100644
index 0000000..34aca33
--- /dev/null
+++ b/common/vsoc/lib/circqueue_test.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2017 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 <thread>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/mock_region_view.h"
+
+#define EXPECT_BLOCK(region, tid) \
+    EXPECT_TRUE(region.IsBlocking(tid))
+
+namespace {
+
+constexpr int kQueueSizeLog2 = 16;
+constexpr int kQueueCapacity = 1 << kQueueSizeLog2;
+constexpr int kMaxPacketSize = 1024;
+
+constexpr int kNumReadingThread = 5;
+constexpr int kNumWritingThread = 5;
+
+struct CircQueueTestRegionLayout : public vsoc::layout::RegionLayout {
+  vsoc::layout::CircularByteQueue<kQueueSizeLog2> byte_queue;
+  vsoc::layout::CircularPacketQueue<kQueueSizeLog2, kMaxPacketSize> packet_queue;
+};
+
+typedef vsoc::test::MockRegionView<CircQueueTestRegionLayout> CircQueueRegionView;
+
+class CircQueueTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    region_.Open();
+  }
+
+  CircQueueRegionView region_;
+};
+
+intptr_t ReadBytes(CircQueueRegionView* region, int bytes) {
+  char buffer_out[bytes];
+  CircQueueTestRegionLayout* layout = region->data();
+  return layout->byte_queue.Read(region, buffer_out, bytes);
+}
+
+intptr_t WriteBytes(CircQueueRegionView* region, int bytes) {
+  char buffer_in[bytes];
+  CircQueueTestRegionLayout* layout = region->data();
+  return layout->byte_queue.Write(region, buffer_in, bytes);
+}
+
+intptr_t ReadPacket(CircQueueRegionView* region, int max_size) {
+  char buffer_out[max_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  return layout->packet_queue.Read(region, buffer_out, max_size);
+}
+
+intptr_t WritePacket(CircQueueRegionView* region, int packet_size) {
+  char buffer_in[packet_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  return layout->packet_queue.Write(region, buffer_in, packet_size);
+}
+
+void ReadBytesInChunk(CircQueueRegionView* region, int total_size, int chuck_size) {
+  char buffer_out[chuck_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  int total_read = 0;
+  int remaining = total_size;
+  while (remaining >= chuck_size) {
+    int ret = layout->byte_queue.Read(region, buffer_out, chuck_size);
+    total_read += ret;
+    remaining -= ret;
+  }
+  if (remaining > 0) {
+    total_read += layout->byte_queue.Write(region, buffer_out, remaining);
+  }
+  EXPECT_EQ(total_read, total_size);
+}
+
+void WriteBytesInChunk(CircQueueRegionView* region, int total_size, int chuck_size) {
+  char buffer_in[chuck_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  int total_write = 0;
+  int remaining = total_size;
+  while (remaining >= chuck_size) {
+    int ret = layout->byte_queue.Write(region, buffer_in, chuck_size);
+    total_write += ret;
+    remaining -= ret;
+  }
+  if (remaining > 0) {
+    total_write += layout->byte_queue.Write(region, buffer_in, remaining);
+  }
+  EXPECT_EQ(total_write, total_size);
+}
+
+void ReadManyPackets(CircQueueRegionView* region, int num_packets, int packet_size) {
+  char buffer_out[packet_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  int total_read = 0;
+  int remaining = num_packets;
+  while (remaining > 0) {
+    int ret = layout->packet_queue.Read(region, buffer_out, packet_size);
+    total_read += ret;
+    remaining--;
+  }
+  EXPECT_EQ(total_read, num_packets * packet_size);
+}
+
+void WriteManyPackets(CircQueueRegionView* region, int num_packets, int packet_size) {
+  char buffer_in[packet_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  int total_write = 0;
+  int remaining = num_packets;
+  while (remaining > 0) {
+    int ret = layout->packet_queue.Write(region, buffer_in, packet_size);
+    total_write += ret;
+    remaining--;
+  }
+  EXPECT_EQ(total_write, num_packets * packet_size);
+}
+
+// ByteQueue Tests
+
+// Test writing bytes
+TEST_F(CircQueueTest, ByteQueueSimpleWrite) {
+  const int num_bytes = 8;
+  EXPECT_EQ(num_bytes, WriteBytes(&this->region_, num_bytes));
+}
+
+// Test reading bytes
+TEST_F(CircQueueTest, ByteQueueSimpleRead) {
+  const int num_bytes = 8;
+  EXPECT_EQ(num_bytes, WriteBytes(&this->region_, num_bytes));
+  EXPECT_EQ(num_bytes, ReadBytes(&this->region_, num_bytes));
+}
+
+// Test reading on an empty queue. Expect blocking.
+TEST_F(CircQueueTest, ByteQueueReadOnEmpty) {
+  const int num_bytes = 8;
+
+  // Spawn a thread to read from queue. Expect it to block.
+  std::thread reading_thread(ReadBytes, &this->region_, num_bytes);
+  EXPECT_BLOCK(region_, reading_thread.get_id());
+
+  // Write expected bytes in so that we can clean up properly.
+  std::thread writing_thread(WriteBytes, &this->region_, num_bytes);
+  writing_thread.join();
+
+  reading_thread.join();
+}
+
+// Test writing on a full queue. Expect blocking.
+TEST_F(CircQueueTest, ByteQueueWriteOnFull) {
+  // Fill the queue.
+  const int capacity_bytes = kQueueCapacity;
+  EXPECT_EQ(capacity_bytes, WriteBytes(&this->region_, capacity_bytes));
+
+  // Now the queue is full, any further write would block.
+  const int num_bytes = 8;
+  std::thread writing_thread(WriteBytes, &this->region_, num_bytes);
+  EXPECT_BLOCK(region_, writing_thread.get_id());
+
+  // Read the extra bytes out so that we can clean up properly.
+  std::thread reading_thread(ReadBytes, &this->region_, num_bytes);
+  reading_thread.join();
+
+  writing_thread.join();
+}
+
+// Test if bytes being read out are the same as ones being written in.
+TEST_F(CircQueueTest, ByteQueueContentIntegrity) {
+  const int num_bytes = 8;
+  CircQueueTestRegionLayout* layout = this->region_.data();
+
+  char buffer_in[num_bytes] = {'a'};
+  layout->byte_queue.Write(&this->region_, buffer_in, num_bytes);
+
+  char buffer_out[num_bytes] = {'b'};
+  layout->byte_queue.Read(&this->region_, buffer_out, num_bytes);
+
+  for (int i=0; i<num_bytes; i++) {
+    EXPECT_EQ(buffer_in[i], buffer_out[i]);
+  }
+}
+
+// Test writing more bytes than capacity
+TEST_F(CircQueueTest, ByteQueueWriteTooManyBytes) {
+  const int extra_bytes = 8;
+  const int num_bytes = kQueueCapacity + extra_bytes;
+  EXPECT_EQ(-ENOSPC, WriteBytes(&this->region_, num_bytes));
+}
+
+// Test multiple bytes read/write
+TEST_F(CircQueueTest, ByteQueueMultipleReadWrite) {
+  const int chunk_size = 7;
+  const int total_size = 3.3 * kQueueCapacity;
+  std::vector<std::thread> reading_threads;
+  std::vector<std::thread> writing_threads;
+  for (int i=0; i<kNumReadingThread; i++) {
+    reading_threads.emplace_back(
+        std::thread(ReadBytesInChunk, &this->region_, total_size, chunk_size));
+  }
+  for (int i=0; i<kNumWritingThread; i++) {
+    writing_threads.emplace_back(
+        std::thread(WriteBytesInChunk, &this->region_, total_size, chunk_size));
+  }
+  std::for_each(reading_threads.begin(), reading_threads.end(), [](std::thread& t) { t.join(); });
+  std::for_each(writing_threads.begin(), writing_threads.end(), [](std::thread& t) { t.join(); });
+}
+
+// PacketQueue Tests
+
+// Test writing packet
+TEST_F(CircQueueTest, PacketQueueSimpleWrite) {
+  const int packet_size = 8;
+  EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
+}
+
+// Test reading packet
+TEST_F(CircQueueTest, PacketQueueSimpleRead) {
+  const int packet_size = 8;
+  EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
+  EXPECT_EQ(packet_size, ReadPacket(&this->region_, packet_size));
+}
+
+// Test reading on an empty queue. Expect blocking.
+TEST_F(CircQueueTest, PacketQueueReadOnEmpty) {
+  const int packet_size = 8;
+
+  // Spawn a thread to read from queue. Expect it to block.
+  std::thread reading_thread(ReadPacket, &this->region_, packet_size);
+  EXPECT_BLOCK(region_, reading_thread.get_id());
+
+  // Write expected bytes in so that we can clean up properly.
+  std::thread writing_thread(WritePacket, &this->region_, packet_size);
+  writing_thread.join();
+
+  reading_thread.join();
+}
+
+// Test writing on a full queue. Expect blocking.
+TEST_F(CircQueueTest, PacketQueueWriteOnFull) {
+  // Fill the queue.
+  const int packet_size = kMaxPacketSize;
+  int capacity_bytes = kQueueCapacity;
+  while (capacity_bytes >= packet_size) {
+    EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
+    capacity_bytes -= (packet_size + sizeof(uint32_t));
+  }
+
+  // Now the queue is full, any further write would block.
+  std::thread writing_thread(WritePacket, &this->region_, packet_size);
+  EXPECT_BLOCK(region_, writing_thread.get_id());
+
+  // Read the extra bytes out so that we can clean up properly.
+  std::thread reading_thread(ReadPacket, &this->region_, packet_size);
+  reading_thread.join();
+
+  writing_thread.join();
+}
+
+// Test if packet being read out are the same as one being written in.
+TEST_F(CircQueueTest, PacketQueueContentIntegrity) {
+  const int packet_size = 8;
+  CircQueueTestRegionLayout* layout = this->region_.data();
+
+  char buffer_in[packet_size] = {'a'};
+  layout->packet_queue.Write(&this->region_, buffer_in, packet_size);
+
+  char buffer_out[packet_size] = {'b'};
+  layout->packet_queue.Read(&this->region_, buffer_out, packet_size);
+
+  for (int i=0; i<packet_size; i++) {
+    EXPECT_EQ(buffer_in[i], buffer_out[i]);
+  }
+}
+
+// Test writing packet larger than capacity
+TEST_F(CircQueueTest, PacketQueueWriteTooLargePacket) {
+  const int extra_bytes = 8;
+  const int packet_size = kQueueCapacity + extra_bytes;
+  EXPECT_EQ(-ENOSPC, WritePacket(&this->region_, packet_size));
+}
+
+// Test reading packet larger than can handle
+TEST_F(CircQueueTest, PacketQueueReadTooLargePacket) {
+  const int extra_bytes = 8;
+  const int small_packet = 8;
+  const int large_packet = small_packet + extra_bytes;
+
+  WritePacket(&this->region_, large_packet);
+  char buffer_out[small_packet];
+  CircQueueTestRegionLayout* layout = this->region_.data();
+  EXPECT_EQ(-ENOSPC, layout->packet_queue.Read(&this->region_, buffer_out, small_packet));
+}
+
+// Test multiple packets read/write
+TEST_F(CircQueueTest, PacketQueueMultipleReadWrite) {
+  const int packet_size = kMaxPacketSize;
+  const int num_packets = 1.5 * (kQueueCapacity / packet_size);
+  std::vector<std::thread> reading_threads;
+  std::vector<std::thread> writing_threads;
+  for (int i=0; i<kNumReadingThread; i++) {
+    reading_threads.emplace_back(
+        std::thread(ReadManyPackets, &this->region_, num_packets, packet_size));
+  }
+  for (int i=0; i<kNumWritingThread; i++) {
+    writing_threads.emplace_back(
+        std::thread(WriteManyPackets, &this->region_, num_packets, packet_size));
+  }
+  std::for_each(reading_threads.begin(), reading_threads.end(), [](std::thread& t) { t.join(); });
+  std::for_each(writing_threads.begin(), writing_threads.end(), [](std::thread& t) { t.join(); });
+}
+
+}  // namespace
diff --git a/common/vsoc/lib/compat.cpp b/common/vsoc/lib/compat.cpp
new file mode 100644
index 0000000..3b8deb2
--- /dev/null
+++ b/common/vsoc/lib/compat.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/compat.h"
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+namespace vsoc {
+
+#ifdef CUTTLEFISH_HOST
+uint32_t gettid() {
+  thread_local uint32_t tid = syscall(SYS_gettid);
+  return tid;
+}
+#endif
+}
diff --git a/common/vsoc/lib/compat.h b/common/vsoc/lib/compat.h
new file mode 100644
index 0000000..8f3355c
--- /dev/null
+++ b/common/vsoc/lib/compat.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Utilities to make the host and the guest a bit more compatible
+
+#include <stdint.h>
+
+namespace vsoc {
+#ifdef CUTTLEFISH_HOST
+// Things that are missing on the host
+uint32_t gettid();
+#endif
+}
diff --git a/common/vsoc/lib/e2e_test_region_layout.cpp b/common/vsoc/lib/e2e_test_region_layout.cpp
new file mode 100644
index 0000000..9a787bd
--- /dev/null
+++ b/common/vsoc/lib/e2e_test_region_layout.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/e2e_test_region_layout.h"
+
+namespace vsoc {
+namespace layout {
+namespace e2e_test {
+
+const char* E2EPrimaryTestRegionLayout::region_name = "e2e_primary";
+const char
+    E2EPrimaryTestRegionLayout::guest_pattern[E2EMemoryFill::kOwnedFieldSize] =
+        "primary guest e2e string";
+const char
+    E2EPrimaryTestRegionLayout::host_pattern[E2EMemoryFill::kOwnedFieldSize] =
+        "primary host e2e string";
+
+const char* E2ESecondaryTestRegionLayout::region_name = "e2e_secondary";
+const char E2ESecondaryTestRegionLayout::guest_pattern
+    [E2EMemoryFill::kOwnedFieldSize] = "secondary guest e2e string";
+const char
+    E2ESecondaryTestRegionLayout::host_pattern[E2EMemoryFill::kOwnedFieldSize] =
+        "secondary host e2e string";
+
+const char* E2EUnfindableRegionLayout::region_name = "e2e_must_not_exist";
+
+}  // namespace e2e_test
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/lib/e2e_test_region_view.h b/common/vsoc/lib/e2e_test_region_view.h
new file mode 100644
index 0000000..70a3db6
--- /dev/null
+++ b/common/vsoc/lib/e2e_test_region_view.h
@@ -0,0 +1,104 @@
+#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 <memory>
+#include <functional>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/e2e_test_region_layout.h"
+
+namespace vsoc {
+template <typename Layout>
+class E2ERegionView : public vsoc::TypedRegionView<
+                      E2ERegionView<Layout>,
+                      Layout> {
+ public:
+
+  uint32_t read_guest_self_register() const {
+    return this->data().guest_self_register;
+  }
+
+  void write_guest_self_register(uint32_t val) {
+    this->data()->guest_self_register = val;
+  }
+
+  void signal_guest_self_register() {
+    this->SendSignal(layout::Sides::OurSide,
+                     &this->data()->guest_self_register);
+  }
+
+  int wait_guest_self_register(uint32_t expected_value) {
+    return this->WaitForSignal(
+        &this->data()->guest_self_register, expected_value);
+  }
+
+  void signal_guest_to_host_register() {
+    this->SendSignal(layout::Sides::OurSide,
+                     &this->data()->guest_to_host_signal);
+  }
+
+  uint32_t guest_to_host_signal_offset() const {
+    return this->pointer_to_region_offset(&this->data().guest_to_host_signal);
+  }
+
+  uint32_t host_to_guest_signal_offset() const {
+    return this->pointer_to_region_offset(&this->data().host_to_guest_signal);
+  }
+
+  const char* guest_string(size_t index) const {
+    return const_cast<const char*>(this->data().data[index].guest_writable);
+  }
+
+  const char* host_string(size_t index) const {
+    return const_cast<const char*>(this->data().data[index].host_writable);
+  }
+
+  bool set_guest_string(size_t index, const char* value) {
+    strcpy(const_cast<char*>(this->data()->data[index].guest_writable),
+           value);
+    return true;
+  }
+
+  bool set_host_string(size_t index, const char* value) {
+    strcpy(const_cast<char*>(this->data()->data[index].host_writable),
+           value);
+    return true;
+  }
+
+  size_t string_size() const {
+    return Layout::NumFillRecords(this->control_->region_data_size());
+  }
+
+  void guest_status(vsoc::layout::e2e_test::E2ETestStage stage) {
+    this->data()->guest_status.set_value(stage);
+  }
+
+  void host_status(vsoc::layout::e2e_test::E2ETestStage stage) {
+    this->data()->host_status.set_value(stage);
+  }
+};
+
+using E2EPrimaryRegionView =
+  vsoc::E2ERegionView<layout::e2e_test::E2EPrimaryTestRegionLayout>;
+
+using E2ESecondaryRegionView =
+  vsoc::E2ERegionView<layout::e2e_test::E2ESecondaryTestRegionLayout>;
+
+using E2EUnfindableRegionView =
+  vsoc::E2ERegionView<layout::e2e_test::E2EUnfindableRegionLayout>;
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/gralloc_layout.cpp b/common/vsoc/lib/gralloc_layout.cpp
new file mode 100644
index 0000000..9be790a
--- /dev/null
+++ b/common/vsoc/lib/gralloc_layout.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/gralloc_layout.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace gralloc {
+
+const char* GrallocManagerLayout::region_name = "gralloc_manager";
+const char* GrallocBufferLayout::region_name = "gralloc_memory";
+
+}  // gralloc
+}  // layout
+}  // vsoc
diff --git a/common/vsoc/lib/graphics_common.h b/common/vsoc/lib/graphics_common.h
new file mode 100644
index 0000000..d2db6c4
--- /dev/null
+++ b/common/vsoc/lib/graphics_common.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/graphics.h"
+
+namespace vsoc {
+
+// Returns BytesPerPixel given a PixelFormat. Code should rely on this rather
+// than accessing the mask directly.
+static inline std::size_t BytesPerPixel(PixelFormat in) {
+    return 1 + (in >> PixelFormatBase::SubformatSize) &
+        (PixelFormatBase::MaxBytesPerPixel - 1);
+}
+}  // vsoc
diff --git a/common/vsoc/lib/graphics_test.cpp b/common/vsoc/lib/graphics_test.cpp
new file mode 100644
index 0000000..b43e56e
--- /dev/null
+++ b/common/vsoc/lib/graphics_test.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/graphics.h"
+
+#include <gtest/gtest.h>
+
+TEST(GraphicsTest, Basic) {
+  // This is a shared memory layout and is mostly tested via static asserts.
+  // If this can compile then the test has passed.
+}
diff --git a/common/vsoc/lib/input_events_layout.cpp b/common/vsoc/lib/input_events_layout.cpp
new file mode 100644
index 0000000..3f1da61
--- /dev/null
+++ b/common/vsoc/lib/input_events_layout.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/input_events_layout.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace input_events {
+
+const char* InputEventsLayout::region_name = "input_events";
+
+}  // namespace input_events
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/lib/input_events_region_view.cpp b/common/vsoc/lib/input_events_region_view.cpp
new file mode 100644
index 0000000..66bc697
--- /dev/null
+++ b/common/vsoc/lib/input_events_region_view.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/input_events_region_view.h"
+
+#include <linux/input.h>
+#include <linux/uinput.h>
+
+#include "common/vsoc/lib/circqueue_impl.h"
+
+using vsoc::layout::input_events::InputEventsLayout;
+
+namespace vsoc {
+namespace input_events {
+
+namespace {
+void InitInputEvent(InputEvent* evt, uint16_t type, uint16_t code, uint32_t value) {
+  evt->type = type;
+  evt->code = code;
+  evt->value = value;
+}
+}  // namespace
+
+const int InputEventsRegionView::kMaxEventsPerPacket = 4;
+
+bool InputEventsRegionView::HandleSingleTouchEvent(bool down, int x, int y) {
+  // TODO(jemoreira): Use multitouch when available
+  InputEvent events[4];
+  // Make sure to modify kMaxEventPerPacket if more events are sent.
+  InitInputEvent(&events[0], EV_ABS, ABS_X, x);
+  InitInputEvent(&events[1], EV_ABS, ABS_Y, y);
+  InitInputEvent(&events[2], EV_KEY, BTN_TOUCH, down);
+  InitInputEvent(&events[3], EV_SYN, 0, 0);
+  return 0 <
+         data()->touch_screen_queue.Write(
+             this, reinterpret_cast<char*>(&events[0]), sizeof(events), true);
+}
+
+bool InputEventsRegionView::HandlePowerButtonEvent(bool down) {
+  InputEvent events[2];
+  InitInputEvent(&events[0], EV_KEY, KEY_POWER, down);
+  InitInputEvent(&events[1], EV_SYN, 0, 0);
+  return 0 < data()->power_button_queue.Write(
+                 this, reinterpret_cast<char*>(&events[0]),
+                 sizeof(events), true);
+}
+
+bool InputEventsRegionView::HandleKeyboardEvent(bool down, uint16_t key_code) {
+  InputEvent events[2];
+  InitInputEvent(&events[0], EV_KEY, key_code, down);
+  InitInputEvent(&events[1], EV_SYN, 0, 0);
+  return 0 <
+         data()->keyboard_queue.Write(this, reinterpret_cast<char*>(&events[0]),
+                                      sizeof(events), true);
+}
+
+intptr_t InputEventsRegionView::GetScreenEventsOrWait(InputEvent* evt,
+                                                      int max_event_count) {
+  intptr_t ret = this->data()->touch_screen_queue.Read(
+      this, reinterpret_cast<char*>(evt), sizeof(InputEvent) * max_event_count);
+  if (ret < 0) {
+    return ret;
+  }
+  return ret / sizeof(InputEvent);
+}
+
+intptr_t InputEventsRegionView::GetKeyboardEventsOrWait(InputEvent* evt,
+                                                        int max_event_count) {
+  intptr_t ret = this->data()->keyboard_queue.Read(
+      this, reinterpret_cast<char*>(evt), sizeof(InputEvent) * max_event_count);
+  if (ret < 0) {
+    return ret;
+  }
+  return ret / sizeof(InputEvent);
+}
+
+intptr_t InputEventsRegionView::GetPowerButtonEventsOrWait(
+    InputEvent* evt, int max_event_count) {
+  intptr_t ret = this->data()->power_button_queue.Read(
+      this, reinterpret_cast<char*>(evt), sizeof(InputEvent) * max_event_count);
+  if (ret < 0) {
+    return ret;
+  }
+  return ret / sizeof(InputEvent);
+}
+
+}  // namespace input_events
+}  // namespace vsoc
diff --git a/common/vsoc/lib/input_events_region_view.h b/common/vsoc/lib/input_events_region_view.h
new file mode 100644
index 0000000..3355631
--- /dev/null
+++ b/common/vsoc/lib/input_events_region_view.h
@@ -0,0 +1,57 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <memory>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/input_events_layout.h"
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+namespace input_events {
+
+struct InputEvent {
+  uint16_t type;
+  uint16_t code;
+  uint32_t value;
+};
+
+class InputEventsRegionView
+    : public vsoc::TypedRegionView<
+          InputEventsRegionView,
+          vsoc::layout::input_events::InputEventsLayout> {
+ public:
+  static const int kMaxEventsPerPacket;
+  // Generates a touch event, may returns true if successful, false if there was
+  // an error, most likely that the queue is full.
+  bool HandleSingleTouchEvent(bool down, int x, int y);
+  bool HandlePowerButtonEvent(bool down);
+  bool HandleKeyboardEvent(bool down, uint16_t key_code);
+
+  // TODO(jemoreira): HandleMultiTouchEvent()...
+
+  // Read input events from the queue, waits if there are none available.
+  // Returns the number of events read or a negative value in case of an error
+  // (most likely the next packet in the queue is larger than the buffer
+  // provided).
+  intptr_t GetScreenEventsOrWait(InputEvent* buffer, int max_event_count);
+  intptr_t GetKeyboardEventsOrWait(InputEvent* buffer, int max_event_count);
+  intptr_t GetPowerButtonEventsOrWait(InputEvent* buffer, int max_event_count);
+
+};
+}  // namespace input_events
+}  // namespace vsoc
diff --git a/common/vsoc/lib/lock_common.cpp b/common/vsoc/lib/lock_common.cpp
new file mode 100644
index 0000000..c100574
--- /dev/null
+++ b/common/vsoc/lib/lock_common.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/lib/compat.h"
+#include "common/vsoc/lib/region_view.h"
+
+#include <stdlib.h>
+
+using vsoc::layout::Sides;
+
+namespace {
+
+const uint32_t LockFree = 0U;
+const uint32_t GuestWaitingFlag = (Sides::Guest << 30);
+const uint32_t HostWaitingFlag = (Sides::Host << 30);
+const uint32_t OurWaitingFlag = (Sides::OurSide << 30);
+static_assert(GuestWaitingFlag, "GuestWaitingFlag is 0");
+static_assert(HostWaitingFlag, "HostWaitingFlag is 0");
+static_assert((GuestWaitingFlag & HostWaitingFlag) == 0,
+              "Waiting flags should not share bits");
+
+// Set if the current owner is the host
+const uint32_t HostIsOwner = 0x20000000U;
+
+// PID_MAX_LIMIT appears to be 0x00400000U, so we're probably ok here
+const uint32_t OwnerMask = 0x3FFFFFFFU;
+
+uint32_t MakeOwnerTid(uint32_t raw_tid) {
+  if (Sides::OurSide == Sides::Host) {
+    return (raw_tid | HostIsOwner) & OwnerMask;
+  } else {
+    return raw_tid & (OwnerMask & ~HostIsOwner);
+  }
+}
+
+};  // namespace
+
+namespace vsoc {
+/**
+ * This is a generic synchronization primitive that provides space for the
+ * owner of the lock to write platform-specific information.
+ */
+bool vsoc::layout::WaitingLockBase::TryLock(uint32_t tid,
+                                            uint32_t* expected_out) {
+  uint32_t masked_tid = MakeOwnerTid(tid);
+  uint32_t expected = LockFree;
+  while (1) {
+    // First try to lock assuming that the mutex is free
+    if (lock_uint32_.compare_exchange_strong(expected, masked_tid)) {
+      // We got the lock.
+      return true;
+    }
+    // We didn't get the lock and our wait flag is already set. It's safe to
+    // try to sleep
+    if (expected & OurWaitingFlag) {
+      *expected_out = expected;
+      return false;
+    }
+    // Attempt to set the wait flag. This will fail if the lock owner changes.
+    while (1) {
+      uint32_t add_wait_flag = expected | OurWaitingFlag;
+      if (lock_uint32_.compare_exchange_strong(expected, add_wait_flag)) {
+        // We set the waiting flag. Try to sleep.
+        *expected_out = expected;
+        return false;
+      }
+      // The owner changed, but we at least we got the wait flag.
+      // Try sleeping
+      if (expected & OurWaitingFlag) {
+        *expected_out = expected;
+        return false;
+      }
+      // Special case: the lock was just freed. Stop trying to set the
+      // waiting flag and try to grab the lock.
+      if (expected == LockFree) {
+        break;
+      }
+      // The owner changed and we have no wait flag
+      // Try to set the wait flag again
+    }
+    // This only happens if the lock was freed while we attempt the set the
+    // wait flag. Try to grab the lock again
+  }
+  // Never reached.
+}
+
+layout::Sides vsoc::layout::WaitingLockBase::UnlockCommon(uint32_t tid) {
+  uint32_t expected_state = lock_uint32_;
+
+  // We didn't hold the lock. This process is insane and must die before it
+  // does damage.
+  uint32_t marked_tid = MakeOwnerTid(tid);
+  if ((marked_tid ^ expected_state) & OwnerMask) {
+    LOG(FATAL) << tid << " unlocking " << this << " owned by "
+               << expected_state;
+  }
+  // If contention is just starting this may fail twice (once for each bit)
+  // expected_state updates on each failure. When this finishes we have
+  // one bit for each waiter
+  while (1) {
+    if (lock_uint32_.compare_exchange_strong(expected_state, LockFree)) {
+      break;
+    }
+  }
+  if ((expected_state ^ marked_tid) & OwnerMask) {
+    LOG(FATAL) << "Lock owner of " << this << " changed from " << tid << " to "
+               << expected_state << " during unlock";
+  }
+  switch (expected_state & (GuestWaitingFlag | HostWaitingFlag)) {
+    case 0:
+      return Sides::NoSides;
+    case GuestWaitingFlag:
+      return Sides::Guest;
+    case HostWaitingFlag:
+      return Sides::Host;
+    default:
+      return Sides::Both;
+  }
+}
+
+bool vsoc::layout::WaitingLockBase::RecoverSingleSided() {
+  // No need to signal because the caller ensured that there were no other
+  // threads...
+  return lock_uint32_.exchange(LockFree) != LockFree;
+}
+
+void layout::GuestAndHostLock::Lock(RegionView* region) {
+  uint32_t expected;
+  uint32_t tid = gettid();
+  while (1) {
+    if (TryLock(tid, &expected)) {
+      return;
+    }
+    region->WaitForSignal(&lock_uint32_, expected);
+  }
+}
+
+void layout::GuestAndHostLock::Unlock(RegionView* region) {
+  region->SendSignal(UnlockCommon(gettid()), &lock_uint32_);
+}
+
+bool layout::GuestAndHostLock::Recover(RegionView* region) {
+  uint32_t expected_state = lock_uint32_;
+  uint32_t expected_owner_bit = (Sides::OurSide == Sides::Host) ? HostIsOwner : 0;
+  // This avoids check then act by reading exactly once and then
+  // eliminating the states where Recover should be a noop.
+  if (expected_state == LockFree) {
+    return false;
+  }
+  // Owned by the other side, do nothing.
+  if ((expected_state & HostIsOwner) != expected_owner_bit) {
+    return false;
+  }
+  // At this point we know two things:
+  //   * The lock was held by our side
+  //   * There are no other threads running on our side (precondition
+  //     for calling Recover())
+  // Therefore, we know that the current expected value should still
+  // be in the lock structure. Use the normal unlock logic, providing
+  // the tid that we observed in the lock.
+  region->SendSignal(UnlockCommon(expected_state), &lock_uint32_);
+  return true;
+}
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/lock_guard.h b/common/vsoc/lib/lock_guard.h
new file mode 100644
index 0000000..8fbcc7d
--- /dev/null
+++ b/common/vsoc/lib/lock_guard.h
@@ -0,0 +1,84 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+namespace vsoc {
+
+class RegionView;
+
+/*
+ * Implements std::lock_guard like functionality for the vsoc locks.
+ */
+
+template <typename Lock>
+class LockGuard {
+ public:
+  explicit LockGuard(Lock* lock) : lock_(lock) {
+    lock_->Lock();
+  }
+
+  LockGuard(LockGuard&& o) noexcept {
+    lock_ = o.lock_;
+    o.lock_ = nullptr;
+  }
+
+  LockGuard(const LockGuard&) = delete;
+  LockGuard& operator=(const LockGuard&) = delete;
+
+  ~LockGuard() {
+    if (lock_) {
+      lock_->Unlock();
+    }
+  }
+
+ private:
+  Lock* lock_;
+};
+
+template <>
+class LockGuard<::vsoc::layout::GuestAndHostLock> {
+  using Lock = ::vsoc::layout::GuestAndHostLock;
+
+ public:
+  LockGuard(Lock* lock, RegionView* region) : lock_(lock), region_(region) {
+    lock_->Lock(region_);
+  }
+
+  LockGuard(LockGuard&& o) noexcept {
+    lock_ = o.lock_;
+    o.lock_ = nullptr;
+    region_ = o.region_;
+    o.region_ = nullptr;
+  }
+
+  LockGuard(const LockGuard&) = delete;
+  LockGuard& operator=(const LockGuard&) = delete;
+
+  ~LockGuard() {
+    if (lock_) {
+      lock_->Unlock(region_);
+    }
+  }
+
+ private:
+  Lock* lock_;
+  RegionView* region_;
+};
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/lock_test.cpp b/common/vsoc/lib/lock_test.cpp
new file mode 100644
index 0000000..d09b5a4
--- /dev/null
+++ b/common/vsoc/lib/lock_test.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "common/vsoc/lib/region_view.h"
+
+#ifdef CUTTLEFISH_HOST
+using MyLock = vsoc::layout::HostLock;
+#else
+using MyLock = vsoc::layout::GuestLock;
+#endif
+
+class SimpleLocker {
+ public:
+  enum State {
+    BEFORE_EXECUTION,
+    BEFORE_LOCK,
+    IN_CRITICAL_SECTION,
+    DONE,
+    JOINED
+  };
+
+  explicit SimpleLocker(MyLock* lock)
+      : lock_(lock), thread_(&SimpleLocker::Work, this) {}
+
+  void Work() {
+    state_ = BEFORE_LOCK;
+    lock_->Lock();
+    state_ = IN_CRITICAL_SECTION;
+    InCriticalSection();
+    lock_->Unlock();
+    state_ = DONE;
+  }
+
+  void InCriticalSection() {}
+
+  void Join() {
+    thread_.join();
+    state_ = JOINED;
+  }
+
+ protected:
+  MyLock* lock_;
+  volatile State state_{BEFORE_EXECUTION};
+  std::thread thread_;
+};
+
+TEST(LockTest, Basic) {
+  // In production regions are always 0 filled on allocation. That's not
+  // true on the stack, so initialize the lock when we declare it.
+  MyLock lock{};
+  SimpleLocker a(&lock);
+  SimpleLocker b(&lock);
+  a.Join();
+  b.Join();
+}
diff --git a/common/vsoc/lib/managed_e2e_test_region_layout.cpp b/common/vsoc/lib/managed_e2e_test_region_layout.cpp
new file mode 100644
index 0000000..1cc794b
--- /dev/null
+++ b/common/vsoc/lib/managed_e2e_test_region_layout.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/managed_e2e_test_region_layout.h"
+
+namespace vsoc {
+namespace layout {
+namespace e2e_test {
+
+const char* E2EManagedTestRegionLayout::region_name = "e2e_managed";
+
+const char* E2EManagerTestRegionLayout::region_name = "e2e_manager";
+
+}  // namespace e2e_test
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/lib/mock_region_view.h b/common/vsoc/lib/mock_region_view.h
new file mode 100644
index 0000000..f9c3006
--- /dev/null
+++ b/common/vsoc/lib/mock_region_view.h
@@ -0,0 +1,119 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <linux/futex.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+#include "common/vsoc/lib/region_signaling_interface.h"
+
+namespace vsoc {
+namespace test {
+
+/**
+ * MockRegionView mocks a region in the shared memory window by calloc and
+ * futex. It supports only one-sided signal as in it doesn't do anything on
+ * sending or waiting interrupt. This is to test if a particular layout
+ * behaves correctly when there are multiple threads accessing it.
+ */
+template <typename Layout>
+class MockRegionView : public vsoc::RegionSignalingInterface {
+ public:
+  explicit MockRegionView(){};
+  virtual ~MockRegionView() {
+    if (region_base_) {
+      free(region_base_);
+      region_base_ = nullptr;
+    }
+  };
+
+  bool Open() {
+    region_base_ = calloc(sizeof(Layout), 1);
+    return !region_base_;
+  };
+
+  virtual void SendSignal(vsoc::layout::Sides /* sides_to_signal */,
+                          std::atomic<uint32_t>* uaddr) {
+    syscall(SYS_futex, reinterpret_cast<int32_t*>(uaddr), FUTEX_WAKE, -1,
+            nullptr, nullptr, 0);
+  }
+
+  virtual int WaitForSignal(std::atomic<uint32_t>* uaddr,
+                             uint32_t expected_value) {
+    {
+      std::lock_guard<std::mutex> guard(mutex_);
+      if (tid_to_addr_.find(std::this_thread::get_id()) != tid_to_addr_.end()) {
+        // Same thread tries to wait
+        return 0;
+      }
+      tid_to_addr_.emplace(std::this_thread::get_id(), uaddr);
+      map_changed_.notify_one();
+    }
+
+    syscall(SYS_futex, uaddr, FUTEX_WAIT, expected_value, nullptr, nullptr, 0);
+
+    {
+      std::lock_guard<std::mutex> guard(mutex_);
+      tid_to_addr_.erase(std::this_thread::get_id());
+    }
+    return 0;
+  }
+
+  // Mock methods from TypedRegionView
+  Layout* data() { return reinterpret_cast<Layout*>(region_base_); };
+
+  // Check wait status on a specificy thread
+  bool IsBlocking(std::thread::id tid) {
+    while (1) {
+      std::unique_lock<std::mutex> lock(mutex_);
+      if (tid_to_addr_.find(tid) != tid_to_addr_.end()) {
+        return true;
+      }
+      // Allow some time as tid map might not be updated yet
+      while (tid_to_addr_.find(tid) == tid_to_addr_.end()) {
+        if (map_changed_.wait_for(lock,
+                                  std::chrono::seconds(kWaitTimeoutInSec)) ==
+            std::cv_status::timeout) {
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+
+ private:
+  // Timeout to avoid a race on checking if a thread is blocked
+  static constexpr int kWaitTimeoutInSec = 5;
+
+  void* region_base_{};
+  std::mutex mutex_;
+  std::condition_variable map_changed_;
+  std::unordered_map<std::thread::id, std::atomic<uint32_t>*> tid_to_addr_;
+};
+
+template <typename Layout>
+constexpr int MockRegionView<Layout>::kWaitTimeoutInSec;
+
+}  // namespace test
+}  // namespace vsoc
diff --git a/common/vsoc/lib/region_control.h b/common/vsoc/lib/region_control.h
new file mode 100644
index 0000000..7762603
--- /dev/null
+++ b/common/vsoc/lib/region_control.h
@@ -0,0 +1,111 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <sys/mman.h>
+#include <stdint.h>
+#include <memory>
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+
+/**
+ * Base class for side-specific utility functions that work on regions.
+ * The methods in this class do not assume that the region is mapped in memory.
+ * This makes is appropriate for ManagedRegions and certain low-level tests
+ * of VSoC shared memory. Most other users will want to use TypedRegions with
+ * a defined RegionLayout.
+ *
+ * This class is not directly instantiable because it must be specialized with
+ * additional fields for the host and guest.
+ */
+class RegionControl {
+ public:
+  virtual ~RegionControl() {
+    if (region_base_) {
+      munmap(region_base_, region_size());
+    }
+    region_base_ = nullptr;
+  }
+
+#if defined(CUTTLEFISH_HOST)
+  static std::shared_ptr<RegionControl> Open(const char* region_name,
+                                             const char* domain);
+#else
+  static std::shared_ptr<RegionControl> Open(const char* region_name);
+#endif
+
+  const vsoc_device_region& region_desc() const { return region_desc_; }
+
+  // Returns the size of the entire region, including the signal tables.
+  uint32_t region_size() const {
+    return region_desc_.region_end_offset - region_desc_.region_begin_offset;
+  }
+
+  // Returns the size of the region that is usable for region-specific data.
+  uint32_t region_data_size() const {
+    return region_size() - region_desc_.offset_of_region_data;
+  }
+
+  // Creates a FdScopedPermission. Returns the file descriptor or -1 on
+  // failure. FdScopedPermission is not supported on the host, so -1 is
+  // always returned there.
+  virtual int CreateFdScopedPermission(const char* managed_region_name,
+                                       uint32_t owner_offset,
+                                       uint32_t owned_value,
+                                       uint32_t begin_offset,
+                                       uint32_t end_offset) = 0;
+
+  // Interrupt our peer, causing it to scan the outgoing_signal_table
+  virtual bool InterruptPeer() = 0;
+
+  // Wake the local signal table scanner. Primarily used during shutdown
+  virtual void InterruptSelf() = 0;
+
+  // Maps the entire region at an address, returning a pointer to the mapping
+  virtual void* Map() = 0;
+
+  // Wait for an interrupt from our peer
+  virtual void WaitForInterrupt() = 0;
+
+  // Signals local waiters at the given region offset.
+  // Defined only on the guest.
+  // Return value is negative on error.
+  virtual int SignalSelf(uint32_t offset) = 0;
+
+  // Waits for a signal at the given region offset.
+  // Defined only on the guest.
+  // Return value is negative on error. The number of false wakes is returned
+  // on success.
+  virtual int WaitForSignal(uint32_t offset, uint32_t expected_value) = 0;
+
+  template <typename T>
+  T* region_offset_to_pointer(uint32_t offset) {
+    if (offset > region_size()) {
+      LOG(FATAL) << __FUNCTION__ << ": " << offset << " not in region @"
+                 << region_base_;
+    }
+    return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(region_base_) +
+                                offset);
+  }
+
+ protected:
+  RegionControl() {}
+  void* region_base_{};
+  vsoc_device_region region_desc_{};
+};
+}  // namespace vsoc
diff --git a/common/vsoc/lib/region_signaling_interface.h b/common/vsoc/lib/region_signaling_interface.h
new file mode 100644
index 0000000..c1399e1
--- /dev/null
+++ b/common/vsoc/lib/region_signaling_interface.h
@@ -0,0 +1,64 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <stdlib.h>
+#include <atomic>
+
+#include "common/vsoc/shm/base.h"
+
+namespace vsoc {
+
+/**
+ * Interface that defines signaling and waiting for signal.
+ */
+class RegionSignalingInterface {
+ public:
+  virtual ~RegionSignalingInterface(){};
+
+  // Post a signal to the guest, the host, or both.
+  // See futex(2) FUTEX_WAKE for details.
+  //
+  //   sides_to_signal: controls where the signal is sent
+  //
+  //   signal_addr: the memory location to signal. Must be within the region.
+  virtual void SendSignal(layout::Sides sides_to_signal,
+                          std::atomic<uint32_t>* signal_addr) = 0;
+
+  // This implements the following:
+  // if (*signal_addr == last_observed_value)
+  //   wait_for_signal_at(signal_addr);
+  //
+  // Note: the caller still needs to check the value at signal_addr because
+  // this function may return early for reasons that are implementation-defined.
+  // See futex(2) FUTEX_WAIT for details.
+  //
+  //   signal_addr: the memory that will be signaled. Must be within the region.
+  //
+  //   last_observed_value: the value that motivated the calling code to wait.
+  //
+  // The return values are:
+  //   -1 on failure
+  //    0 indicates success with no tuning information
+  //    >0 indicates success. The number indicates how many times the thread
+  //       woke before it could return.
+  //       Large values indicate that the regions should be tuned.
+  virtual int WaitForSignal(std::atomic<uint32_t>* signal_addr,
+                             uint32_t last_observed_value) = 0;
+};
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/region_view.cpp b/common/vsoc/lib/region_view.cpp
new file mode 100644
index 0000000..dc1088c
--- /dev/null
+++ b/common/vsoc/lib/region_view.cpp
@@ -0,0 +1,201 @@
+#include "common/vsoc/lib/region_view.h"
+
+#include <sys/mman.h>
+
+#include "common/libs/glog/logging.h"
+
+namespace {
+const uint32_t UADDR_OFFSET_MASK = 0xFFFFFFFC;
+const uint32_t UADDR_OFFSET_ROUND_TRIP_FLAG = 1;
+}  // namespace
+
+using vsoc::layout::Sides;
+
+vsoc::RegionWorker::RegionWorker(RegionView* region,
+                                 std::shared_ptr<RegionControl> control)
+    : control_(control),
+      region_(region),
+      stopping_(false) {}
+
+void vsoc::RegionWorker::start() {
+  CHECK(!thread_.joinable());
+  thread_ = std::thread(&vsoc::RegionWorker::Work, this);
+}
+
+void vsoc::RegionWorker::Work() {
+  while (!stopping_) {
+    region_->WaitForInterrupt();
+    if (stopping_) {
+      return;
+    }
+    region_->ProcessSignalsFromPeer([this](uint32_t offset) {
+        control_->SignalSelf(offset);
+    });
+  }
+}
+
+vsoc::RegionWorker::~RegionWorker() {
+  stopping_ = true;
+
+  if (thread_.joinable()) {
+    region_->InterruptSelf();
+    thread_.join();
+  }
+}
+
+vsoc::RegionView::~RegionView() {
+  // region_base_ is borrowed here. It's owned by control_, which is
+  // responsible for unmapping the memory
+  region_base_ = nullptr;
+}
+
+#if defined(CUTTLEFISH_HOST)
+bool vsoc::RegionView::Open(const char* name, const char* domain) {
+  control_ = vsoc::RegionControl::Open(name, domain);
+  if (!control_) {
+    return false;
+  }
+  region_base_ = control_->Map();
+  return region_base_ != nullptr;
+}
+#else
+bool vsoc::RegionView::Open(const char* name) {
+  control_ = vsoc::RegionControl::Open(name);
+  if (!control_) {
+    return false;
+  }
+  region_base_ = control_->Map();
+  return region_base_ != nullptr;
+}
+#endif
+
+// Interrupt our peer, causing it to scan the outgoing_signal_table
+bool vsoc::RegionView::MaybeInterruptPeer() {
+  if (region_offset_to_pointer<std::atomic<uint32_t>>(
+          outgoing_signal_table().interrupt_signalled_offset)
+          ->exchange(1)) {
+    return false;
+  }
+  return control_->InterruptPeer();
+}
+
+// Wait for an interrupt from our peer
+void vsoc::RegionView::WaitForInterrupt() {
+  while (1) {
+    if (region_offset_to_pointer<std::atomic<uint32_t>>(
+            incoming_signal_table().interrupt_signalled_offset)
+            ->exchange(0)) {
+      return;
+    }
+    control_->WaitForInterrupt();
+  }
+}
+
+void vsoc::RegionView::ProcessSignalsFromPeer(
+    std::function<void(uint32_t)> signal_handler) {
+  const vsoc_signal_table_layout& table = incoming_signal_table();
+  const size_t num_offsets = (1 << table.num_nodes_lg2);
+  std::atomic<uint32_t>* offsets =
+      region_offset_to_pointer<std::atomic<uint32_t>>(
+          table.futex_uaddr_table_offset);
+  for (size_t i = 0; i < num_offsets; ++i) {
+    uint32_t raw_offset = offsets[i].exchange(0);
+    if (raw_offset) {
+      bool round_trip = raw_offset & UADDR_OFFSET_ROUND_TRIP_FLAG;
+      uint32_t offset = raw_offset & UADDR_OFFSET_MASK;
+      signal_handler(offset);
+      if (round_trip) {
+        SendSignalToPeer(
+            region_offset_to_pointer<std::atomic<uint32_t>>(offset), false);
+      }
+    }
+  }
+}
+
+void vsoc::RegionView::SendSignal(Sides sides_to_signal,
+                                  std::atomic<uint32_t>* uaddr) {
+  if (sides_to_signal & Sides::Peer) {
+    // If we should also be signalling our side set the round trip flag on
+    // the futex signal.
+    bool round_trip = sides_to_signal & Sides::OurSide;
+    SendSignalToPeer(uaddr, round_trip);
+    // Return without signaling our waiters to give the other side a chance
+    // to run.
+    return;
+  }
+  if (sides_to_signal & Sides::OurSide) {
+    control_->SignalSelf(pointer_to_region_offset(uaddr));
+  }
+}
+
+void vsoc::RegionView::SendSignalToPeer(std::atomic<uint32_t>* uaddr,
+                                        bool round_trip) {
+  const vsoc_signal_table_layout& table = outgoing_signal_table();
+  std::atomic<uint32_t>* offsets =
+      region_offset_to_pointer<std::atomic<uint32_t>>(
+          table.futex_uaddr_table_offset);
+  // maximum index in the node that can hold an offset;
+  const size_t max_index = (1 << table.num_nodes_lg2) - 1;
+  uint32_t offset = pointer_to_region_offset(uaddr);
+  if (offset & ~UADDR_OFFSET_MASK) {
+    LOG(FATAL) << "uaddr offset is not naturally aligned " << uaddr;
+  }
+  // Guess at where this offset should go in the table.
+  // Do this before we set the round-trip flag.
+  size_t hash = (offset >> 2) & max_index;
+  if (round_trip) {
+    offset |= UADDR_OFFSET_ROUND_TRIP_FLAG;
+  }
+  while (1) {
+    uint32_t expected = 0;
+    if (offsets[hash].compare_exchange_strong(expected, offset)) {
+      // We stored the offset. Send the interrupt.
+      this->MaybeInterruptPeer();
+      break;
+    }
+    // We didn't store, but the value was already in the table with our flag.
+    // Return without interrupting.
+    if (expected == offset) {
+      return;
+    }
+    // Hash collision. Try again in a different node
+    if ((expected & UADDR_OFFSET_MASK) != (offset & UADDR_OFFSET_MASK)) {
+      hash = (hash + 1) & max_index;
+      continue;
+    }
+    // Our offset was in the bucket, but the flags didn't match.
+    // We're done if the value in the node had the round trip flag set.
+    if (expected & UADDR_OFFSET_ROUND_TRIP_FLAG) {
+      return;
+    }
+    // We wanted the round trip flag, but the value in the bucket didn't set it.
+    // Do a second swap to try to set it.
+    if (offsets[hash].compare_exchange_strong(expected, offset)) {
+      // It worked. We're done.
+      return;
+    }
+    if (expected == offset) {
+      // expected was the offset without the flag. After the swap it has the
+      // the flag. This means that some other thread set the flag, so
+      // we're done.
+      return;
+    }
+    // Something about the offset changed. We need to go around again, because:
+    //   our peer processed the old entry
+    //   another thread may have stolen the node while we were distracted
+  }
+}
+
+std::unique_ptr<vsoc::RegionWorker> vsoc::RegionView::StartWorker() {
+    std::unique_ptr<vsoc::RegionWorker> worker(
+            new vsoc::RegionWorker(this /* region */, control_));
+
+    worker->start();
+    return worker;
+}
+
+int vsoc::RegionView::WaitForSignal(std::atomic<uint32_t>* uaddr,
+                                     uint32_t expected_value) {
+  return control_->WaitForSignal(pointer_to_region_offset(uaddr),
+                                 expected_value);
+}
diff --git a/common/vsoc/lib/region_view.h b/common/vsoc/lib/region_view.h
new file mode 100644
index 0000000..055f721
--- /dev/null
+++ b/common/vsoc/lib/region_view.h
@@ -0,0 +1,214 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Object that represents a region on the Host
+
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <atomic>
+#include <cstdint>
+
+#include <functional>
+#include <map>
+#include <thread>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/lib/lock_guard.h"
+#include "common/vsoc/lib/region_control.h"
+#include "common/vsoc/lib/region_signaling_interface.h"
+#include "common/vsoc/shm/base.h"
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+
+class RegionControl;
+class RegionView;
+
+/**
+ * Represents a task that is tied to a RegionView.
+ *
+ * This is currently used for the task that forwards futexes across the
+ * shared memory window.
+ */
+class RegionWorker {
+ public:
+  RegionWorker(RegionView* region, std::shared_ptr<RegionControl> control);
+  ~RegionWorker();
+
+  void start();
+
+  void Work();
+
+ protected:
+  std::shared_ptr<RegionControl> control_;
+  RegionView* region_;
+  std::thread thread_;
+  volatile bool stopping_;
+};
+
+/**
+ * Base class to access a mapped region in VSoC shared memory.
+ * This class holds the methods that depends on the region's memory having an
+ * address. The RegionControl class holds the methods that can be invoked
+ * without mapping the region.
+ */
+class RegionView : public RegionSignalingInterface {
+ public:
+  virtual ~RegionView();
+
+#if defined(CUTTLEFISH_HOST)
+  bool Open(const char* region_name, const char* domain);
+#else
+  bool Open(const char* region_name);
+#endif
+
+  // Returns a pointer to the table that will be scanned for signals
+  const vsoc_signal_table_layout& incoming_signal_table();
+
+  // Returns a pointer to the table that will be used to post signals
+  const vsoc_signal_table_layout& outgoing_signal_table();
+
+  // Returns true iff an interrupt is queued in the signal table
+  bool HasIncomingInterrupt() {
+    return *region_offset_to_pointer<std::atomic<uint32_t>>(
+        incoming_signal_table().interrupt_signalled_offset);
+  }
+
+  // Wake any threads waiting for an interrupt. This is generally used during
+  // shutdown.
+  void InterruptSelf() { control_->InterruptSelf(); }
+
+  // Interrupt our peer if an interrupt is not already on the way.
+  // Returns true if the interrupt was sent, false if an interrupt was already
+  // pending.
+  bool MaybeInterruptPeer();
+
+  // Scan in the incoming signal table, issuing futex calls for any posted
+  // signals and then reposting them to the peer if they were round-trip
+  // signals.
+  //
+  //   signal_handler: An action to perform on every offset signalled by our
+  //   peer, usually a FUTEX_WAKE call, but can be customized for other
+  //   purposes.
+  void ProcessSignalsFromPeer(
+      std::function<void(uint32_t)> signal_handler);
+
+  // Post a signal to the guest, the host, or both.
+  // See futex(2) FUTEX_WAKE for details.
+  //
+  //   sides_to_signal: controls where the signal is sent
+  //
+  //   signal_addr: the memory location to signal. Must be within the region.
+  void SendSignal(layout::Sides sides_to_signal,
+                  std::atomic<uint32_t>* signal_addr);
+
+  // Post a signal to our peer for a specific memeory location.
+  // See futex(2) FUTEX_WAKE for details.
+  //
+  //   signal_addr: the memory location to signal. Must be within the region.
+  //
+  //   round_trip: true if there may be waiters on both sides of the shared
+  //               memory.
+  void SendSignalToPeer(std::atomic<uint32_t>* signal_addr, bool round_trip);
+
+  // Waits until an interrupt appears on this region, then clears the
+  // interrupted flag and returns.
+  void WaitForInterrupt();
+
+  // This implements the following:
+  // if (*signal_addr == last_observed_value)
+  //   wait_for_signal_at(signal_addr);
+  //
+  // Note: the caller still needs to check the value at signal_addr because
+  // this function may return early for reasons that are implementation-defined.
+  // See futex(2) FUTEX_WAIT for details.
+  //
+  //   signal_addr: the memory that will be signaled. Must be within the region.
+  //
+  //   last_observed_value: the value that motivated the calling code to wait.
+  //
+  // The return value is -1 on error. On the guest positive values give the
+  // number of false wakes.
+  int WaitForSignal(std::atomic<uint32_t>* signal_addr,
+                     uint32_t last_observed_value) override;
+
+  // Starts the signal table scanner. This must be invoked by subclasses, which
+  // MUST store the returned unique_ptr as a class member.
+  __attribute__((warn_unused_result))
+  std::unique_ptr<RegionWorker> StartWorker();
+
+  // Returns a pointer to the start of region data that is cast to the given
+  // type.  Initializers that run in the launcher use this to get a typed view
+  // of the region. Most other cases should be handled via TypedRegionView.
+  template <typename LayoutType>
+  LayoutType* GetLayoutPointer() {
+    return this->region_offset_to_pointer<LayoutType>(
+        control_->region_desc().offset_of_region_data);
+  }
+
+  // Helper functions for lock guards. This approach has two advantages:
+  //
+  //   o Callers don't have to be refactored when lock types change
+  //   o The region pointer can be provided automatically
+  template <typename T>
+  LockGuard<T> make_lock_guard(T* lock) {
+    return LockGuard<T>(lock);
+  }
+
+  LockGuard<::vsoc::layout::GuestAndHostLock> make_lock_guard(
+      ::vsoc::layout::GuestAndHostLock* l) {
+    return LockGuard<::vsoc::layout::GuestAndHostLock>(l, this);
+  }
+
+ protected:
+  template <typename T>
+  T* region_offset_to_pointer(uint32_t offset) {
+    return control_->region_offset_to_pointer<T>(offset);
+  }
+
+  template <typename T>
+  const T& region_offset_to_reference(uint32_t offset) const {
+    if (offset > control_->region_size()) {
+      LOG(FATAL) << __FUNCTION__ << ": " << offset << " not in region @"
+                 << region_base_;
+    }
+    return *reinterpret_cast<const T*>(
+        reinterpret_cast<uintptr_t>(region_base_) + offset);
+  }
+
+  // Calculates an offset based on a pointer in the region. Crashes if the
+  // pointer isn't in the region.
+  // This is mostly for the RegionView's internal plumbing. Use TypedRegionView
+  // and RegionLayout to avoid this in most cases.
+  template <typename T>
+  uint32_t pointer_to_region_offset(T* ptr) const {
+    uint32_t rval = reinterpret_cast<uintptr_t>(ptr) -
+                          reinterpret_cast<uintptr_t>(region_base_);
+    if (rval > control_->region_size()) {
+      LOG(FATAL) << __FUNCTION__ << ": " << ptr << " not in region @"
+                 << region_base_;
+    }
+    return rval;
+  }
+
+  std::shared_ptr<RegionControl> control_;
+  void* region_base_{};
+};
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/screen_layout.cpp b/common/vsoc/lib/screen_layout.cpp
new file mode 100644
index 0000000..d302030
--- /dev/null
+++ b/common/vsoc/lib/screen_layout.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/screen_layout.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace screen {
+
+const char* ScreenLayout::region_name = "screen";
+
+}  // namespace screen
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/lib/screen_region_view.cpp b/common/vsoc/lib/screen_region_view.cpp
new file mode 100644
index 0000000..2a22335
--- /dev/null
+++ b/common/vsoc/lib/screen_region_view.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/screen_region_view.h"
+
+#include <memory>
+
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/lib/lock_guard.h"
+
+using vsoc::layout::screen::CompositionStats;
+using vsoc::screen::ScreenRegionView;
+
+const uint8_t* ScreenRegionView::first_buffer() const {
+  // TODO(jemoreira): Add alignments?
+  return &(this->data().buffer[0]);
+}
+
+int ScreenRegionView::number_of_buffers() const {
+  auto offset_of_first_buffer =
+      const_cast<ScreenRegionView*>(this)->pointer_to_region_offset(
+          this->first_buffer());
+  size_t total_buffer_size = control_->region_size() - offset_of_first_buffer;
+  return total_buffer_size / buffer_size();
+}
+
+void* ScreenRegionView::GetBuffer(int buffer_idx) {
+  uint8_t* buffer = const_cast<uint8_t*>(this->first_buffer());
+  return buffer + buffer_idx * this->buffer_size();
+}
+
+// We can use a locking protocol because we decided that the streamer should
+// have more priority than the hwcomposer, so it's OK to block the hwcomposer
+// waiting for the streamer to complete, while the streamer will only block on
+// the hwcomposer when it has ran out of work to do and needs to get more from
+// the hwcomposer.
+void ScreenRegionView::BroadcastNewFrame(int buffer_idx,
+                                         const CompositionStats* stats) {
+  {
+    if (buffer_idx < 0 || buffer_idx >= number_of_buffers()) {
+      LOG(ERROR) << "Attempting to broadcast an invalid buffer index: "
+                 << buffer_idx;
+      return;
+    }
+    auto lock_guard(make_lock_guard(&data()->bcast_lock));
+    data()->seq_num++;
+    data()->buffer_index = static_cast<int32_t>(buffer_idx);
+    if (stats) {
+      data()->stats = *stats;
+    }
+  }
+  // Signaling after releasing the lock may cause spurious wake ups.
+  // Signaling while holding the lock may cause the just-awaken listener to
+  // block immediately trying to acquire the lock.
+  // The former is less costly and slightly less likely to happen.
+  SendSignal(layout::Sides::Both, &data()->seq_num);
+}
+
+int ScreenRegionView::WaitForNewFrameSince(uint32_t* last_seq_num,
+                                           CompositionStats* stats) {
+  static std::unique_ptr<RegionWorker> worker = StartWorker();
+  // It's ok to read seq_num here without holding the lock because the lock will
+  // be acquired immediately after so we'll block if necessary to wait for the
+  // critical section in BroadcastNewFrame to complete.
+  // Also, the call to WaitForSignal receives a pointer to seq_num (so the
+  // compiler should not optimize it out) and includes a memory barrier
+  // (FUTEX_WAIT).
+  while (data()->seq_num == *last_seq_num) {
+    // Don't hold the lock when waiting for a signal, will deadlock.
+    WaitForSignal(&data()->seq_num, *last_seq_num);
+  }
+
+  {
+    auto lock_guard(make_lock_guard(&data()->bcast_lock));
+    *last_seq_num = data()->seq_num;
+    if (stats) {
+      *stats = data()->stats;
+    }
+    return static_cast<int>(data()->buffer_index);
+  }
+}
diff --git a/common/vsoc/lib/screen_region_view.h b/common/vsoc/lib/screen_region_view.h
new file mode 100644
index 0000000..aef4359
--- /dev/null
+++ b/common/vsoc/lib/screen_region_view.h
@@ -0,0 +1,104 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <memory>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/graphics.h"
+#include "common/vsoc/shm/screen_layout.h"
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+namespace screen {
+
+// Provides information related to the device's screen. Allows to query screen
+// properties such as resolution and dpi, as well as subscribe/notify to/of
+// changes on the screen contents. It also holds the contents of the display.
+class ScreenRegionView
+    : public vsoc::TypedRegionView<ScreenRegionView,
+                                   vsoc::layout::screen::ScreenLayout> {
+ public:
+  static int align(int input) {
+    auto constexpr alignment = 16;
+    return (input + alignment - 1) & -alignment;
+  }
+
+  // Screen width in pixels
+  int x_res() const { return data().x_res; }
+
+  // Screen height in pixels
+  int y_res() const { return data().y_res; }
+
+  // Dots per inch
+  int dpi() const { return data().dpi; }
+
+  // Refresh rate in Hertz
+  int refresh_rate_hz() const { return data().refresh_rate_hz; }
+
+  uint32_t pixel_format() const { return kFbPixelFormat; }
+
+  uint32_t bytes_per_pixel() const {
+    return vsoc::PixelFormatProperties<kFbPixelFormat>::bytes_per_pixel;
+  }
+
+  int line_length() const { return align(x_res() * bytes_per_pixel()); }
+
+  size_t buffer_size() const {
+    return (align(x_res() * bytes_per_pixel()) * y_res()) + kSwiftShaderPadding;
+  }
+
+  int number_of_buffers() const;
+
+  // Gets a pointer to the beginning of a buffer. Does not perform any bound
+  // checks on the index.
+  void* GetBuffer(int buffer_idx);
+
+  // Broadcasts a new frame.
+  // buffer_idx is the index of the buffer containing the composed screen, it's
+  // a number in the range [0, number_of_buffers() - 1].
+  // Stats holds performance information of the last composition, can be null.
+  void BroadcastNewFrame(
+      int buffer_idx,
+      const vsoc::layout::screen::CompositionStats* stats = nullptr);
+
+  // Waits for a new frame (one with a different seq_num than last one we saw).
+  // Returns the index of the buffer containing the new frame or a negative
+  // number if there was an error, stores the new sequential number in
+  // *last_seq_num. The frame sequential numbers will be provided by the
+  // hwcomposer and expected to increase monotonically over time (though it's
+  // not a hard requirement), this numbers are guaranteed to be non-zero when a
+  // valid frame is available. Performance statistics are returned through the
+  // stats parameter when it's not null.
+  int WaitForNewFrameSince(
+      uint32_t* last_seq_num,
+      vsoc::layout::screen::CompositionStats* stats = nullptr);
+
+  using Pixel = uint32_t;
+  static constexpr int kSwiftShaderPadding = 4;
+  static constexpr int kRedShift = 0;
+  static constexpr int kGreenShift = 8;
+  static constexpr int kBlueShift = 16;
+  static constexpr int kRedBits = 8;
+  static constexpr int kGreenBits = 8;
+  static constexpr int kBlueBits = 8;
+  static constexpr uint32_t kFbPixelFormat = vsoc::VSOC_PIXEL_FORMAT_RGBA_8888;
+
+ protected:
+  const uint8_t* first_buffer() const;
+};
+}  // namespace screen
+}  // namespace vsoc
diff --git a/common/vsoc/lib/screen_region_view_test.cpp b/common/vsoc/lib/screen_region_view_test.cpp
new file mode 100644
index 0000000..cc02baa
--- /dev/null
+++ b/common/vsoc/lib/screen_region_view_test.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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 "screen_region_view.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include <stdio.h>
+
+using vsoc::screen::ScreenRegionView;
+
+int main() {
+  uint32_t frame_num = 0;
+  int buffer_id = 0;
+
+#if defined(CUTTLEFISH_HOST)
+  auto region = ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
+#else
+  auto region = ScreenRegionView::GetInstance();
+#endif
+  if (!region) {
+    fprintf(stderr, "Error opening region\n");
+    return 1;
+  }
+
+  while (1) {
+    buffer_id = region->WaitForNewFrameSince(&frame_num);
+    printf("Signaled frame_num = %d, buffer_id = %d\n", frame_num, buffer_id);
+  }
+
+  return 0;
+}
diff --git a/common/vsoc/lib/single_sided_signal.h b/common/vsoc/lib/single_sided_signal.h
new file mode 100644
index 0000000..a40c792
--- /dev/null
+++ b/common/vsoc/lib/single_sided_signal.h
@@ -0,0 +1,50 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <unistd.h>
+#include <linux/futex.h>
+#include <sys/syscall.h>
+
+
+// Signaling mechanism that allows threads to signal changes to shared
+// memory and to wait for signals.
+
+namespace vsoc {
+/**
+ * Defines the strategy for signaling among threads on a single kernel.
+ */
+namespace SingleSidedSignal {
+/**
+ * Waits for a signal, assuming the the word at addr matches expected_state.
+ * Will return immediately if the value does not match.
+ * Callers must be equipped to cope with spurious returns.
+ */
+static void AwaitSignal(uint32_t expected_state, uint32_t* uaddr) {
+  syscall(SYS_futex, uaddr, FUTEX_WAIT, expected_state, nullptr, nullptr, 0);
+}
+
+/**
+ * Sends a signal to every thread in AwaitSignal() using the address in
+ * uaddr.
+ */
+static void Signal(std::atomic<uint32_t>* uaddr) {
+  syscall(SYS_futex, reinterpret_cast<int32_t*>(uaddr), FUTEX_WAKE, -1, nullptr,
+          nullptr, 0);
+}
+}  // namespace SingleSidedSignal
+}  // namespace vsoc
diff --git a/common/vsoc/lib/socket_forward_layout.cpp b/common/vsoc/lib/socket_forward_layout.cpp
new file mode 100644
index 0000000..299ef70
--- /dev/null
+++ b/common/vsoc/lib/socket_forward_layout.cpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 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 "common/vsoc/shm/socket_forward_layout.h"
+
+const char* vsoc::layout::socket_forward::SocketForwardLayout::region_name =
+  "socket_forward";
diff --git a/common/vsoc/lib/socket_forward_region_view.cpp b/common/vsoc/lib/socket_forward_region_view.cpp
new file mode 100644
index 0000000..2dbd2e8
--- /dev/null
+++ b/common/vsoc/lib/socket_forward_region_view.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2018 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 <cassert>
+
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/lock_guard.h"
+#include "common/vsoc/lib/socket_forward_region_view.h"
+#include "common/vsoc/shm/lock.h"
+#include "common/vsoc/shm/socket_forward_layout.h"
+
+using vsoc::layout::socket_forward::Queue;
+using vsoc::layout::socket_forward::QueuePair;
+// store the read and write direction as variables to keep the ifdefs and macros
+// in later code to a minimum
+constexpr auto ReadDirection = &QueuePair::
+#ifdef CUTTLEFISH_HOST
+                                   guest_to_host;
+#else
+                                   host_to_guest;
+#endif
+
+constexpr auto WriteDirection = &QueuePair::
+#ifdef CUTTLEFISH_HOST
+                                    host_to_guest;
+#else
+                                    guest_to_host;
+#endif
+
+using vsoc::socket_forward::SocketForwardRegionView;
+
+vsoc::socket_forward::Packet vsoc::socket_forward::Packet::MakeBegin(
+    std::uint16_t port) {
+  auto packet = MakePacket(Header::BEGIN);
+  std::memcpy(packet.payload(), &port, sizeof port);
+  packet.set_payload_length(sizeof port);
+  return packet;
+}
+
+void SocketForwardRegionView::Recv(int connection_id, Packet* packet) {
+  CHECK(packet != nullptr);
+  do {
+    (data()->queues_[connection_id].*ReadDirection)
+        .queue.Read(this, reinterpret_cast<char*>(packet), sizeof *packet);
+  } while (packet->IsBegin());
+  CHECK(!packet->empty()) << "zero-size data message received";
+  CHECK_LE(packet->payload_length(), kMaxPayloadSize) << "invalid size";
+}
+
+bool SocketForwardRegionView::Send(int connection_id, const Packet& packet) {
+  CHECK(!packet.empty());
+  CHECK_LE(packet.payload_length(), kMaxPayloadSize);
+
+  (data()->queues_[connection_id].*WriteDirection)
+      .queue.Write(this, packet.raw_data(), packet.raw_data_length());
+  return true;
+}
+
+int SocketForwardRegionView::IgnoreUntilBegin(int connection_id) {
+  Packet packet{};
+  do {
+    (data()->queues_[connection_id].*ReadDirection)
+        .queue.Read(this, reinterpret_cast<char*>(&packet), sizeof packet);
+  } while (!packet.IsBegin());
+  return packet.port();
+}
+
+constexpr int kNumQueues =
+    static_cast<int>(vsoc::layout::socket_forward::kNumQueues);
+
+void SocketForwardRegionView::CleanUpPreviousConnections() {
+  data()->Recover();
+
+  static constexpr auto kRestartPacket = Packet::MakeRestart();
+  for (int connection_id = 0; connection_id < kNumQueues; ++connection_id) {
+    Send(connection_id, kRestartPacket);
+  }
+}
+
+SocketForwardRegionView::ConnectionViewCollection
+SocketForwardRegionView::AllConnections() {
+  SocketForwardRegionView::ConnectionViewCollection all_queues;
+  for (int connection_id = 0; connection_id < kNumQueues; ++connection_id) {
+    all_queues.emplace_back(this, connection_id);
+  }
+  return all_queues;
+}
+
+// --- Connection ---- //
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::Recv(Packet* packet) {
+  std::unique_lock<std::mutex> guard(receive_thread_data_lock_);
+  while (received_packet_free_) {
+    receive_thread_data_cv_.wait(guard);
+  }
+  CHECK(received_packet_.IsData());
+  *packet = received_packet_;
+  received_packet_free_ = true;
+  receive_thread_data_cv_.notify_one();
+}
+
+bool SocketForwardRegionView::ShmConnectionView::Receiver::GotRecvClosed() const {
+      return received_packet_.IsRecvClosed() || (received_packet_.IsRestart()
+#ifdef CUTTLEFISH_HOST
+                                              && saw_data_
+#endif
+                                              );
+}
+
+bool SocketForwardRegionView::ShmConnectionView::Receiver::ShouldReceiveAnotherPacket() const {
+        return (received_packet_.IsRecvClosed() && !saw_end_) ||
+             (saw_end_ && received_packet_.IsEnd())
+#ifdef CUTTLEFISH_HOST
+             || (received_packet_.IsRestart() && !saw_data_) ||
+             (received_packet_.IsBegin())
+#endif
+             ;
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::ReceivePacket() {
+  view_->region_view()->Recv(view_->connection_id(), &received_packet_);
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::CheckPacketForRecvClosed() {
+      if (GotRecvClosed()) {
+        saw_recv_closed_ = true;
+        view_->MarkOtherSideRecvClosed();
+      }
+#ifdef CUTTLEFISH_HOST
+      if (received_packet_.IsData()) {
+        saw_data_ = true;
+      }
+#endif
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::CheckPacketForEnd() {
+  if (received_packet_.IsEnd() || received_packet_.IsRestart()) {
+    CHECK(!saw_end_ || received_packet_.IsRestart());
+    saw_end_ = true;
+  }
+}
+
+
+bool SocketForwardRegionView::ShmConnectionView::Receiver::ExpectMorePackets() const {
+  return !saw_recv_closed_ || !saw_end_;
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::UpdatePacketAndSignalAvailable() {
+  if (!received_packet_.IsData()) {
+    static constexpr auto kEmptyPacket = Packet::MakeData();
+    received_packet_ = kEmptyPacket;
+  }
+  received_packet_free_ = false;
+  receive_thread_data_cv_.notify_one();
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::Start() {
+  while (ExpectMorePackets()) {
+    std::unique_lock<std::mutex> guard(receive_thread_data_lock_);
+    while (!received_packet_free_) {
+      receive_thread_data_cv_.wait(guard);
+    }
+
+    do {
+      ReceivePacket();
+      CheckPacketForRecvClosed();
+    } while (ShouldReceiveAnotherPacket());
+
+    if (received_packet_.empty()) {
+      LOG(ERROR) << "Received empty packet.";
+    }
+
+    CheckPacketForEnd();
+
+    UpdatePacketAndSignalAvailable();
+  }
+}
+
+auto SocketForwardRegionView::ShmConnectionView::ResetAndConnect()
+    -> ShmSenderReceiverPair {
+  if (receiver_) {
+    receiver_->Join();
+  }
+
+  {
+    std::lock_guard<std::mutex> guard(*other_side_receive_closed_lock_);
+    other_side_receive_closed_ = false;
+  }
+
+#ifdef CUTTLEFISH_HOST
+  region_view()->IgnoreUntilBegin(connection_id());
+  region_view()->Send(connection_id(), Packet::MakeBegin(port_));
+#else
+  region_view()->Send(connection_id(), Packet::MakeBegin(port_));
+  port_ =
+      region_view()->IgnoreUntilBegin(connection_id());
+#endif
+
+  receiver_.reset(new Receiver{this});
+  return {ShmSender{this}, ShmReceiver{this}};
+}
+
+#ifdef CUTTLEFISH_HOST
+auto SocketForwardRegionView::ShmConnectionView::EstablishConnection(int port)
+    -> ShmSenderReceiverPair {
+  port_ = port;
+  return ResetAndConnect();
+}
+#else
+auto SocketForwardRegionView::ShmConnectionView::WaitForNewConnection()
+    -> ShmSenderReceiverPair {
+  port_ = 0;
+  return ResetAndConnect();
+}
+#endif
+
+bool SocketForwardRegionView::ShmConnectionView::Send(const Packet& packet) {
+  if (packet.empty()) {
+    LOG(ERROR) << "Sending empty packet";
+  }
+  if (packet.IsData() && IsOtherSideRecvClosed()) {
+    return false;
+  }
+  return region_view()->Send(connection_id(), packet);
+}
+
+void SocketForwardRegionView::ShmConnectionView::Recv(Packet* packet) {
+  receiver_->Recv(packet);
+}
+
+void SocketForwardRegionView::ShmReceiver::Recv(Packet* packet) {
+  view_->Recv(packet);
+}
+
+bool SocketForwardRegionView::ShmSender::Send(const Packet& packet) {
+  return view_->Send(packet);
+}
diff --git a/common/vsoc/lib/socket_forward_region_view.h b/common/vsoc/lib/socket_forward_region_view.h
new file mode 100644
index 0000000..c41517b
--- /dev/null
+++ b/common/vsoc/lib/socket_forward_region_view.h
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2018 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 <cstdlib>
+#include <utility>
+#include <vector>
+#include <memory>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/socket_forward_layout.h"
+
+namespace vsoc {
+namespace socket_forward {
+
+struct Header {
+  std::uint32_t payload_length;
+  enum MessageType : std::uint32_t {
+    DATA = 0,
+    BEGIN,
+    END,
+    RECV_CLOSED,  // indicate that this side's receive end is closed
+    RESTART,
+  };
+  MessageType message_type;
+};
+
+constexpr std::size_t kMaxPayloadSize =
+    layout::socket_forward::kMaxPacketSize - sizeof(Header);
+
+struct Packet {
+ private:
+  Header header_;
+  using Payload = char[kMaxPayloadSize];
+  Payload payload_data_;
+
+  static constexpr Packet MakePacket(Header::MessageType type) {
+    Packet packet{};
+    packet.header_.message_type = type;
+    return packet;
+  }
+
+ public:
+  // port is only revelant on the host-side.
+  static Packet MakeBegin(std::uint16_t port);
+
+  static constexpr Packet MakeEnd() { return MakePacket(Header::END); }
+
+  static constexpr Packet MakeRecvClosed() {
+    return MakePacket(Header::RECV_CLOSED);
+  }
+
+  static constexpr Packet MakeRestart() { return MakePacket(Header::RESTART); }
+
+  // NOTE payload and payload_length must still be set.
+  static constexpr Packet MakeData() { return MakePacket(Header::DATA); }
+
+  bool empty() const { return IsData() && header_.payload_length == 0; }
+
+  void set_payload_length(std::uint32_t length) {
+    CHECK_LE(length, sizeof payload_data_);
+    header_.payload_length = length;
+  }
+
+  Payload& payload() { return payload_data_; }
+
+  const Payload& payload() const { return payload_data_; }
+
+  constexpr std::uint32_t payload_length() const {
+    return header_.payload_length;
+  }
+
+  constexpr bool IsBegin() const {
+    return header_.message_type == Header::BEGIN;
+  }
+
+  constexpr bool IsEnd() const { return header_.message_type == Header::END; }
+
+  constexpr bool IsData() const { return header_.message_type == Header::DATA; }
+
+  constexpr bool IsRecvClosed() const {
+    return header_.message_type == Header::RECV_CLOSED;
+  }
+
+  constexpr bool IsRestart() const {
+    return header_.message_type == Header::RESTART;
+  }
+
+  constexpr std::uint16_t port() const {
+    CHECK(IsBegin());
+    std::uint16_t port_number{};
+    CHECK_EQ(payload_length(), sizeof port_number);
+    std::memcpy(&port_number, payload(), sizeof port_number);
+    return port_number;
+  }
+
+  char* raw_data() { return reinterpret_cast<char*>(this); }
+
+  const char* raw_data() const { return reinterpret_cast<const char*>(this); }
+
+  constexpr size_t raw_data_length() const {
+    return payload_length() + sizeof header_;
+  }
+};
+
+static_assert(sizeof(Packet) == layout::socket_forward::kMaxPacketSize, "");
+static_assert(std::is_pod<Packet>{}, "");
+
+// Data sent will start with a uint32_t indicating the number of bytes being
+// sent, followed be the data itself
+class SocketForwardRegionView
+    : public TypedRegionView<SocketForwardRegionView,
+                             layout::socket_forward::SocketForwardLayout> {
+ private:
+  // Returns an empty data packet if the other side is closed.
+  void Recv(int connection_id, Packet* packet);
+  // Returns true on success
+  bool Send(int connection_id, const Packet& packet);
+
+  // skip everything in the connection queue until seeing a BEGIN packet.
+  // returns port from begin packet.
+  int IgnoreUntilBegin(int connection_id);
+
+ public:
+  class ShmSender;
+  class ShmReceiver;
+
+  using ShmSenderReceiverPair = std::pair<ShmSender, ShmReceiver>;
+
+  class ShmConnectionView {
+   public:
+    ShmConnectionView(SocketForwardRegionView* region_view, int connection_id)
+        : region_view_{region_view}, connection_id_{connection_id} {}
+
+#ifdef CUTTLEFISH_HOST
+    ShmSenderReceiverPair EstablishConnection(int port);
+#else
+    // Should not be called while there is an active ShmSender or ShmReceiver
+    // for this connection.
+    ShmSenderReceiverPair WaitForNewConnection();
+#endif
+
+    int port() const { return port_; }
+
+    bool Send(const Packet& packet);
+    void Recv(Packet* packet);
+
+    ShmConnectionView(const ShmConnectionView&) = delete;
+    ShmConnectionView& operator=(const ShmConnectionView&) = delete;
+
+    // Moving invalidates all existing ShmSenders and ShmReceiver
+    ShmConnectionView(ShmConnectionView&&) = default;
+    ShmConnectionView& operator=(ShmConnectionView&&) = default;
+    ~ShmConnectionView() = default;
+
+    // NOTE should only be used for debugging/logging purposes.
+    // connection_ids are an implementation detail that are currently useful for
+    // debugging, but may go away in the future.
+    int connection_id() const { return connection_id_; }
+
+   private:
+    SocketForwardRegionView* region_view() const { return region_view_; }
+
+    bool IsOtherSideRecvClosed() {
+      std::lock_guard<std::mutex> guard(*other_side_receive_closed_lock_);
+      return other_side_receive_closed_;
+    }
+
+    void MarkOtherSideRecvClosed() {
+      std::lock_guard<std::mutex> guard(*other_side_receive_closed_lock_);
+      other_side_receive_closed_ = true;
+    }
+
+    void ReceiverThread();
+    ShmSenderReceiverPair ResetAndConnect();
+
+    class Receiver {
+     public:
+      Receiver(ShmConnectionView* view)
+          : view_{view}
+      {
+        receiver_thread_ = std::thread([this] { Start(); });
+      }
+
+      void Recv(Packet* packet);
+
+      void Join() { receiver_thread_.join(); }
+
+      Receiver(const Receiver&) = delete;
+      Receiver& operator=(const Receiver&) = delete;
+
+      ~Receiver() = default;
+     private:
+      void Start();
+      bool GotRecvClosed() const;
+      void ReceivePacket();
+      void CheckPacketForRecvClosed();
+      void CheckPacketForEnd();
+      void UpdatePacketAndSignalAvailable();
+      bool ShouldReceiveAnotherPacket() const;
+      bool ExpectMorePackets() const;
+
+      std::mutex receive_thread_data_lock_;
+      std::condition_variable receive_thread_data_cv_;
+      bool received_packet_free_ = true;
+      Packet received_packet_{};
+
+      ShmConnectionView* view_{};
+      bool saw_recv_closed_ = false;
+      bool saw_end_ = false;
+#ifdef CUTTLEFISH_HOST
+      bool saw_data_ = false;
+#endif
+
+      std::thread receiver_thread_;
+    };
+
+    SocketForwardRegionView* region_view_{};
+    int connection_id_ = -1;
+    int port_ = -1;
+
+    std::unique_ptr<std::mutex> other_side_receive_closed_lock_ =
+        std::unique_ptr<std::mutex>{new std::mutex{}};
+    bool other_side_receive_closed_ = false;
+
+    std::unique_ptr<Receiver> receiver_;
+  };
+
+  class ShmSender {
+   public:
+    explicit ShmSender(ShmConnectionView* view) : view_{view} {}
+
+    ShmSender(const ShmSender&) = delete;
+    ShmSender& operator=(const ShmSender&) = delete;
+
+    ShmSender(ShmSender&&) = default;
+    ShmSender& operator=(ShmSender&&) = default;
+    ~ShmSender() = default;
+
+    // Returns true on success
+    bool Send(const Packet& packet);
+
+   private:
+    struct EndSender {
+      void operator()(ShmConnectionView* view) const {
+        if (view) {
+          view->Send(Packet::MakeEnd());
+        }
+      }
+    };
+
+    // Doesn't actually own the View, responsible for sending the End
+    // indicator and marking the sending side as disconnected.
+    std::unique_ptr<ShmConnectionView, EndSender> view_;
+  };
+
+  class ShmReceiver {
+   public:
+    explicit ShmReceiver(ShmConnectionView* view) : view_{view} {}
+    ShmReceiver(const ShmReceiver&) = delete;
+    ShmReceiver& operator=(const ShmReceiver&) = delete;
+
+    ShmReceiver(ShmReceiver&&) = default;
+    ShmReceiver& operator=(ShmReceiver&&) = default;
+    ~ShmReceiver() = default;
+
+    void Recv(Packet* packet);
+
+   private:
+    struct RecvClosedSender {
+      void operator()(ShmConnectionView* view) const {
+        if (view) {
+          view->Send(Packet::MakeRecvClosed());
+        }
+      }
+    };
+
+    // Doesn't actually own the view, responsible for sending the RecvClosed
+    // indicator
+    std::unique_ptr<ShmConnectionView, RecvClosedSender> view_{};
+  };
+
+  friend ShmConnectionView;
+
+  SocketForwardRegionView() = default;
+  ~SocketForwardRegionView() = default;
+  SocketForwardRegionView(const SocketForwardRegionView&) = delete;
+  SocketForwardRegionView& operator=(const SocketForwardRegionView&) = delete;
+
+  using ConnectionViewCollection = std::vector<ShmConnectionView>;
+  ConnectionViewCollection AllConnections();
+
+  int port(int connection_id);
+  void CleanUpPreviousConnections();
+
+ private:
+#ifndef CUTTLEFISH_HOST
+  std::uint32_t last_seq_number_{};
+#endif
+};
+
+}  // namespace socket_forward
+}  // namespace vsoc
diff --git a/common/vsoc/lib/typed_region_view.h b/common/vsoc/lib/typed_region_view.h
new file mode 100644
index 0000000..ead7412
--- /dev/null
+++ b/common/vsoc/lib/typed_region_view.h
@@ -0,0 +1,104 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Object that represents a region on the Host
+
+#include "common/vsoc/lib/region_view.h"
+
+#include <map>
+#include <mutex>
+#include <string>
+
+namespace vsoc {
+
+/**
+ * This class adds methods that depend on the Region's type.
+ * This may be directly constructed. However, it may be more effective to
+ * subclass it, adding region-specific methods.
+ *
+ * Layout should be VSoC shared memory compatible, defined in common/vsoc/shm,
+ * and should have a constant string region name.
+ */
+template <typename ViewType, typename LayoutType>
+class TypedRegionView : public RegionView {
+ public:
+  using Layout = LayoutType;
+
+  /* Returns a pointer to the region with a type that matches the layout */
+  LayoutType* data() {
+    return this->GetLayoutPointer<LayoutType>();
+  }
+
+  const LayoutType& data() const {
+    return this->region_offset_to_reference<LayoutType>(
+        control_->region_desc().offset_of_region_data);
+  }
+
+ protected:
+#if defined(CUTTLEFISH_HOST)
+  bool Open(const char* domain) {
+    return RegionView::Open(LayoutType::region_name, domain);
+  }
+#else
+  bool Open() {
+    return RegionView::Open(LayoutType::region_name);
+  }
+#endif
+
+ public:
+  // Implementation of the region singletons.
+#if defined(CUTTLEFISH_HOST)
+  static ViewType* GetInstance(const char* domain) {
+    static std::mutex mtx;
+    static std::map<std::string, std::unique_ptr<ViewType>> instances;
+    if (!domain) {
+      return nullptr;
+    }
+    std::lock_guard<std::mutex> lock(mtx);
+    // Get a reference to the actual unique_ptr that's stored in the map, if
+    // there wasn't one it will be default constructed pointing to nullptr.
+    auto& instance = instances[domain];
+    if (!instance) {
+      // Update the referenced pointer with the address of the newly created
+      // region view.
+      instance.reset(new ViewType{});
+      if (!instance->Open(domain)) {
+        instance.reset();
+      }
+    }
+    return instance.get();
+  }
+#else
+  static ViewType* GetInstance() {
+    static std::mutex mtx;
+    static std::unique_ptr<ViewType> instance;
+    std::lock_guard<std::mutex> lock(mtx);
+    if (!instance) {
+      instance.reset(new ViewType{});
+      if (!instance->Open()) {
+        instance.reset();
+      }
+    }
+    return instance.get();
+  }
+#endif
+
+
+};
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/vsoc_audio_message.h b/common/vsoc/lib/vsoc_audio_message.h
new file mode 100644
index 0000000..230c4de
--- /dev/null
+++ b/common/vsoc/lib/vsoc_audio_message.h
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <system/audio.h>
+
+typedef uint32_t size32_t;
+
+struct timespec32 {
+  uint32_t tv_sec;
+  uint32_t tv_nsec;
+
+  timespec32() = default;
+
+  timespec32(const timespec &from)
+      : tv_sec(from.tv_sec),
+        tv_nsec(from.tv_nsec) {
+  }
+};
+
+struct gce_audio_message {
+  static const size32_t kMaxAudioFrameLen = 65536;
+  enum message_t {
+    UNKNOWN = 0,
+    DATA_SAMPLES = 1,
+    OPEN_INPUT_STREAM = 2,
+    OPEN_OUTPUT_STREAM = 3,
+    CLOSE_INPUT_STREAM = 4,
+    CLOSE_OUTPUT_STREAM = 5,
+    CONTROL_PAUSE = 100
+  };
+  // Size of the header + data. Used to frame when we're on TCP.
+  size32_t total_size;
+  // Size of the audio header
+  size32_t header_size;
+  message_t message_type;
+  // Identifier for the stream.
+  uint32_t stream_number;
+  // HAL assigned frame number, starts from 0.
+  int64_t frame_num;
+  // MONOTONIC_TIME when these frames were presented to the HAL.
+  timespec32 time_presented;
+  // Sample rate from the audio configuration.
+  uint32_t frame_rate;
+  // Channel mask from the audio configuration.
+  audio_channel_mask_t channel_mask;
+  // Format from the audio configuration.
+  audio_format_t format;
+  // Size of each frame in bytes.
+  size32_t frame_size;
+  // Number of frames that were presented to the HAL.
+  size32_t num_frames_presented;
+  // Number of frames that the HAL accepted.
+  //   For blocking audio this will be the same as num_frames.
+  //   For non-blocking audio this may be less.
+  size32_t num_frames_accepted;
+  // Count of the number of packets that were dropped because they would
+  // have blocked the HAL or exceeded the maximum message size.
+  size32_t num_packets_dropped;
+  // Count of the number of packets that were shortened to fit within
+  // kMaxAudioFrameLen.
+  size32_t num_packets_shortened;
+  // num_frames_presented (not num_frames_accepted) will follow here.
+
+  gce_audio_message() :
+      total_size(sizeof(gce_audio_message)),
+      header_size(sizeof(gce_audio_message)),
+      message_type(UNKNOWN),
+      stream_number(0),
+      frame_num(0),
+      frame_rate(0),
+      channel_mask(0),
+      format(AUDIO_FORMAT_DEFAULT),
+      frame_size(0),
+      num_frames_presented(0),
+      num_frames_accepted(0),
+      num_packets_dropped(0),
+      num_packets_shortened(0) {
+    time_presented.tv_sec = 0;
+    time_presented.tv_nsec = 0;
+  }
+};
diff --git a/common/vsoc/lib/vsoc_memory.cpp b/common/vsoc/lib/vsoc_memory.cpp
new file mode 100644
index 0000000..017ab13
--- /dev/null
+++ b/common/vsoc/lib/vsoc_memory.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 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 "common/vsoc/lib/vsoc_memory.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <type_traits>
+
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/shm/audio_data_layout.h"
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/e2e_test_region_layout.h"
+#include "common/vsoc/shm/gralloc_layout.h"
+#include "common/vsoc/shm/input_events_layout.h"
+#include "common/vsoc/shm/managed_e2e_test_region_layout.h"
+#include "common/vsoc/shm/screen_layout.h"
+#include "common/vsoc/shm/socket_forward_layout.h"
+
+#include "uapi/vsoc_shm.h"
+
+namespace {
+
+// Takes a vector of objects and returns a vector of pointers to those objects.
+template <typename T, typename R>
+std::vector<R*> GetConstPointers(const std::vector<T>& v) {
+  std::vector<R*> result;
+  result.reserve(v.size());
+  for (auto& element : v) {
+    result.push_back(&element);
+  }
+  return result;
+}
+}  // namespace
+
+namespace vsoc {
+
+namespace {
+
+class VSoCRegionLayoutImpl : public VSoCRegionLayout {
+ public:
+  VSoCRegionLayoutImpl(const char* region_name, size_t layout_size,
+                       int guest_to_host_signal_table_log_size,
+                       int host_to_guest_signal_table_log_size,
+                       const char* managed_by)
+      : region_name_(region_name),
+        layout_size_(layout_size),
+        guest_to_host_signal_table_log_size_(
+            guest_to_host_signal_table_log_size),
+        host_to_guest_signal_table_log_size_(
+            host_to_guest_signal_table_log_size),
+        managed_by_(managed_by) {
+  }
+  VSoCRegionLayoutImpl(const VSoCRegionLayoutImpl&) = default;
+
+  const char* region_name() const override { return region_name_; }
+  const char* managed_by() const override { return managed_by_; }
+
+  size_t layout_size() const override { return layout_size_; }
+  int guest_to_host_signal_table_log_size() const override {
+    return guest_to_host_signal_table_log_size_;
+  }
+  int host_to_guest_signal_table_log_size() const override {
+    return host_to_guest_signal_table_log_size_;
+  }
+
+ private:
+  const char* region_name_{};
+  const size_t layout_size_{};
+  const int guest_to_host_signal_table_log_size_{};
+  const int host_to_guest_signal_table_log_size_{};
+  const char* managed_by_{};
+};
+
+class VSoCMemoryLayoutImpl : public VSoCMemoryLayout {
+ public:
+  explicit VSoCMemoryLayoutImpl(std::vector<VSoCRegionLayoutImpl>&& regions)
+      : regions_(regions), region_idx_by_name_(GetNameToIndexMap(regions)) {
+    for (size_t i = 0; i < regions_.size(); ++i) {
+      // This link could be resolved later, but doing it here disables
+      // managed_by cycles among the regions.
+      if (regions[i].managed_by() &&
+          !region_idx_by_name_.count(regions[i].managed_by())) {
+        LOG(FATAL) << regions[i].region_name()
+                   << " managed by unknown region: " << regions[i].managed_by()
+                   << ". Manager Regions must be declared before the regions "
+                      "they manage";
+      }
+    }
+  }
+
+  ~VSoCMemoryLayoutImpl() = default;
+
+  std::vector<const VSoCRegionLayout*> GetRegions() const {
+    static std::vector<const VSoCRegionLayout*> ret =
+        GetConstPointers<VSoCRegionLayoutImpl, const VSoCRegionLayout>(
+            regions_);
+    return ret;
+  }
+
+  const VSoCRegionLayout* GetRegionByName(
+      const char* region_name) const override {
+    if (!region_idx_by_name_.count(region_name)) {
+      return nullptr;
+    }
+    return &regions_[region_idx_by_name_.at(region_name)];
+  }
+
+ protected:
+  VSoCMemoryLayoutImpl() = delete;
+  VSoCMemoryLayoutImpl(const VSoCMemoryLayoutImpl&) = delete;
+
+  // Helper function to allow the creation of the name to index map in the
+  // constructor and allow the field to be const
+  static std::map<const char*, size_t> GetNameToIndexMap(
+      const std::vector<VSoCRegionLayoutImpl>& regions) {
+    std::map<const char*, size_t> result;
+    for (size_t index = 0; index < regions.size(); ++index) {
+      auto region_name = regions[index].region_name();
+      if (result.count(region_name)) {
+        LOG(FATAL) << region_name << " used for more than one region";
+      }
+      result[region_name] = index;
+    }
+    return result;
+  }
+
+  std::vector<VSoCRegionLayoutImpl> regions_;
+  const std::map<const char*, size_t> region_idx_by_name_;
+};
+
+template <class R>
+VSoCRegionLayoutImpl ValidateAndBuildLayout(int g_to_h_signal_table_log_size,
+                                            int h_to_g_signal_table_log_size,
+                                            const char* managed_by = nullptr) {
+  // Double check that the Layout is a valid shm type.
+  ASSERT_SHM_COMPATIBLE(R);
+  return VSoCRegionLayoutImpl(R::region_name, sizeof(R),
+                              g_to_h_signal_table_log_size,
+                              h_to_g_signal_table_log_size, managed_by);
+}
+
+}  // namespace
+
+VSoCMemoryLayout* VSoCMemoryLayout::Get() {
+  /*******************************************************************
+   * Make sure the first region is not the manager of other regions. *
+   *       This error will only be caught on runtime!!!!!            *
+   *******************************************************************/
+  static VSoCMemoryLayoutImpl layout(
+      {ValidateAndBuildLayout<layout::input_events::InputEventsLayout>(2, 2),
+       ValidateAndBuildLayout<layout::screen::ScreenLayout>(2, 2),
+       ValidateAndBuildLayout<layout::gralloc::GrallocManagerLayout>(2, 2),
+       ValidateAndBuildLayout<layout::gralloc::GrallocBufferLayout>(
+           0, 0,
+           /* managed_by */ layout::gralloc::GrallocManagerLayout::region_name),
+       ValidateAndBuildLayout<layout::socket_forward::SocketForwardLayout>(7,
+                                                                           7),
+       ValidateAndBuildLayout<layout::e2e_test::E2EPrimaryTestRegionLayout>(1,
+                                                                            1),
+       ValidateAndBuildLayout<layout::e2e_test::E2ESecondaryTestRegionLayout>(
+           1, 1),
+       ValidateAndBuildLayout<layout::e2e_test::E2EManagerTestRegionLayout>(1,
+                                                                            1),
+       ValidateAndBuildLayout<layout::e2e_test::E2EManagedTestRegionLayout>(1,
+                                                                            1),
+       ValidateAndBuildLayout<layout::audio_data::AudioDataLayout>(2, 2)});
+
+  // We need this code to compile on both sides to enforce the static checks,
+  // but should only be used host side.
+#if !defined(CUTTLEFISH_HOST)
+  LOG(FATAL) << "Memory layout should not be used guest side, use region "
+                "classes or the vsoc driver directly instead.";
+#endif
+  return &layout;
+}
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/vsoc_memory.h b/common/vsoc/lib/vsoc_memory.h
new file mode 100644
index 0000000..49853f9
--- /dev/null
+++ b/common/vsoc/lib/vsoc_memory.h
@@ -0,0 +1,53 @@
+#pragma once
+/*
+ * Copyright (C) 2018 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 <stdint.h>
+
+#include <vector>
+
+namespace vsoc {
+
+class VSoCRegionLayout {
+ public:
+  virtual const char* region_name() const = 0;
+  virtual const char* managed_by() const = 0;
+
+  virtual size_t layout_size() const = 0;
+  virtual int guest_to_host_signal_table_log_size() const = 0;
+  virtual int host_to_guest_signal_table_log_size() const = 0;
+ protected:
+  VSoCRegionLayout() = default;
+  virtual ~VSoCRegionLayout() = default;
+};
+
+class VSoCMemoryLayout {
+ public:
+  // Returns a pointer to the memory layout singleton.
+  static VSoCMemoryLayout* Get();
+
+  VSoCMemoryLayout(const VSoCMemoryLayout&) = delete;
+  VSoCMemoryLayout(VSoCMemoryLayout&&) = delete;
+
+  virtual std::vector<const VSoCRegionLayout *> GetRegions() const = 0;
+  virtual const VSoCRegionLayout* GetRegionByName(
+      const char* region_name) const = 0;
+ protected:
+  VSoCMemoryLayout() = default;
+  virtual ~VSoCMemoryLayout() = default;
+};
+
+}  // namespace vsoc
diff --git a/common/vsoc/shm/audio_data_layout.h b/common/vsoc/shm/audio_data_layout.h
new file mode 100644
index 0000000..3991ea0
--- /dev/null
+++ b/common/vsoc/shm/audio_data_layout.h
@@ -0,0 +1,38 @@
+#pragma once
+/*
+ * Copyright (C) 2018 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/circqueue.h"
+
+// Memory layout for region carrying audio data from audio HAL to client.
+
+namespace vsoc {
+namespace layout {
+namespace audio_data {
+
+struct AudioDataLayout : public RegionLayout {
+    static const char *const region_name;
+    static constexpr size_t layout_size = 16396;
+
+    // size = 2^14 = 16384, packets are up to 4KB bytes each.
+    CircularPacketQueue<14, 4096> audio_queue;
+};
+ASSERT_SHM_COMPATIBLE(AudioDataLayout);
+
+}  // namespace audio_data
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/base.h b/common/vsoc/shm/base.h
new file mode 100644
index 0000000..f5abec8
--- /dev/null
+++ b/common/vsoc/shm/base.h
@@ -0,0 +1,82 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+#include <type_traits>
+
+// Base macros for all layout structures.
+// ShmTypeValidator provides meaningful information about the type size
+// mismatch in compilation error messages, eg.
+//
+// error:
+//    static_assert failed "Class size changed, update the layout_size field"
+//    static_assert(Current == Expected,
+// note: in instantiation of template class
+//    'ShmTypeValidator<vsoc::layout::myclass::ClassName>'
+//    requested here ASSERT_SHM_COMPATIBLE(ClassName);
+//
+template <typename Type, size_t expected_size = Type::layout_size>
+struct ShmTypeValidator {
+  static_assert(sizeof(Type) == expected_size,
+                "Class size changed, update the layout_size field");
+  static_assert(std::is_trivial<Type>(), "Class uses features that are unsafe");
+  static constexpr bool valid =
+      sizeof(Type) == expected_size && std::is_trivial<Type>();
+};
+
+#define ASSERT_SHM_COMPATIBLE(T)            \
+  static_assert(ShmTypeValidator<T>::valid, \
+                "Compilation error. Please fix above errors and retry.")
+
+namespace vsoc {
+namespace layout {
+
+/**
+ * Memory is shared between Guest and Host kernels. In some cases we need
+ * flag to indicate which side we're on. In those cases we'll use this
+ * simple structure.
+ *
+ * These are carefully formatted to make Guest and Host a bitfield.
+ */
+enum Sides : uint32_t {
+  NoSides = 0,
+  Guest = 1,
+  Host = 2,
+  Both = 3,
+#ifdef CUTTLEFISH_HOST
+  OurSide = Host,
+  Peer = Guest
+#else
+  OurSide = Guest,
+  Peer = Host
+#endif
+};
+// Enums can't have static members, so can't use the macro here.
+  static_assert(ShmTypeValidator<Sides, 4>::valid,
+              "Compilation error. Please fix above errors and retry.");
+
+/**
+ * Base class for all region layout structures.
+ */
+class RegionLayout {
+public:
+  static constexpr size_t layout_size = 1;
+};
+ASSERT_SHM_COMPATIBLE(RegionLayout);
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/circqueue.h b/common/vsoc/shm/circqueue.h
new file mode 100644
index 0000000..05893bf
--- /dev/null
+++ b/common/vsoc/shm/circqueue.h
@@ -0,0 +1,203 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Memory layout for byte-oriented circular queues
+
+#include <atomic>
+#include <cstdint>
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/lock.h"
+
+struct iovec;
+
+namespace vsoc {
+class RegionSignalingInterface;
+namespace layout {
+
+/**
+ * Base classes for all spinlock protected circular queues.
+ * This class should be embedded in the per-region data structure that is used
+ * as the parameter to TypedRegion.
+ */
+template <uint32_t SizeLog2>
+class CircularQueueBase {
+ public:
+  static constexpr size_t layout_size = (1 << SizeLog2) + 12;
+
+ private:
+  CircularQueueBase() = delete;
+  CircularQueueBase(const CircularQueueBase&) = delete;
+  CircularQueueBase& operator=(const CircularQueueBase&) = delete;
+
+ protected:
+  /**
+   * Specifies a part of the queue. Note, the given indexes must be masked
+   * before they can be used against buffer_
+   */
+  struct Range {
+    // Points to the first bytes that is part of the range
+    uint32_t start_idx;
+    // Points to the first byte that is not in the range. This is similar to
+    // the STL end iterator.
+    uint32_t end_idx;
+  };
+  static const uintptr_t BufferSize = (1 << SizeLog2);
+
+  /**
+   * Copy bytes from buffer_in into the part of the queue specified by Range.
+   */
+  void CopyInRange(const char* buffer_in, const Range& t);
+
+  /**
+   * Copy the bytes specified by range to the given buffer. They caller must
+   * ensure that the buffer is large enough to hold the content of the range.
+   */
+  void CopyOutRange(const Range& t, char* buffer_out);
+
+  /**
+   * Wait until data becomes available in the queue. The caller must have
+   * called Lock() before invoking this. The caller must call Unlock()
+   * after this returns.
+   */
+  void WaitForDataLocked(RegionSignalingInterface* r);
+
+  /**
+   * Reserve space in the queue for writing. The caller must have called Lock()
+   * before invoking this. The caller must call Unlock() after this returns.
+   * Indexes pointing to the reserved space will be placed in range.
+   * On success this returns bytes.
+   * On failure a negative errno indicates the problem. -ENOSPC indicates that
+   * bytes > the queue size, -EWOULDBLOCK indicates that the call would block
+   * waiting for space but was requested non bloking.
+   */
+  intptr_t WriteReserveLocked(RegionSignalingInterface* r, size_t bytes,
+                              Range* t, bool non_blocking);
+
+  bool RecoverBase() {
+    return lock_.Recover();
+  }
+
+  // Note: Both of these fields may hold values larger than the buffer size,
+  // they should be interpreted modulo the buffer size. This fact along with the
+  // buffer size being a power of two greatly simplyfies the index calculations.
+  // Advances when a reader has finished with buffer space
+  std::atomic<uint32_t> r_released_;
+  // Advances when buffer space is filled and ready for a reader
+  std::atomic<uint32_t> w_pub_;
+  // Spinlock that protects the region. 0 means unlocked
+  SpinLock lock_;
+  // The actual memory in the buffer
+  char buffer_[BufferSize];
+};
+using CircularQueueBase64k = CircularQueueBase<16>;
+ASSERT_SHM_COMPATIBLE(CircularQueueBase64k);
+
+/**
+ * Byte oriented circular queue. Reads will always return some data, but
+ * may return less data than requested. Writes will always write all of the
+ * data or return an error.
+ */
+template <uint32_t SizeLog2>
+class CircularByteQueue : public CircularQueueBase<SizeLog2> {
+ public:
+  static constexpr size_t layout_size =
+      CircularQueueBase<SizeLog2>::layout_size;
+  /**
+   * Read at most max_size bytes from the qeueue, placing them in buffer_out
+   */
+  intptr_t Read(RegionSignalingInterface* r, char* buffer_out,
+                std::size_t max_size);
+  /**
+   * Write all of the given bytes into the queue. If non_blocking isn't set the
+   * call may block until there is enough available space in the queue. On
+   * success the return value will match bytes. On failure a negative errno is
+   * returned. -ENOSPC: If the queue size is smaller than the number of bytes to
+   * write. -EWOULDBLOCK: If non_blocking is true and there is not enough free
+   * space.
+   */
+  intptr_t Write(RegionSignalingInterface* r, const char* buffer_in,
+                 std::size_t bytes, bool non_blocking = false);
+
+  bool Recover() {
+    return this->RecoverBase();
+  }
+
+ protected:
+  using Range = typename CircularQueueBase<SizeLog2>::Range;
+};
+using CircularByteQueue64k = CircularByteQueue<16>;
+ASSERT_SHM_COMPATIBLE(CircularByteQueue64k);
+
+/**
+ * Packet oriented circular queue. Reads will either return data or an error.
+ * Each return from read corresponds to a call to write and returns all of the
+ * data from that corresponding Write().
+ */
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+class CircularPacketQueue : public CircularQueueBase<SizeLog2> {
+ public:
+  static constexpr size_t layout_size =
+      CircularQueueBase<SizeLog2>::layout_size;
+
+  /**
+   * Read a single packet from the queue, placing its data into buffer_out.
+   * If max_size indicates that buffer_out cannot hold the entire packet
+   * this function will return -ENOSPC.
+   */
+  intptr_t Read(RegionSignalingInterface* r, char* buffer_out,
+                std::size_t max_size);
+
+  /**
+   * Writes [buffer_in, buffer_in + bytes) to the queue.
+   * If the number of bytes to be written exceeds the size of the queue
+   * -ENOSPC will be returned.
+   * If non_blocking is true and there is not enough free space on the queue to
+   * write all the data -EWOULDBLOCK will be returned.
+   */
+  intptr_t Write(RegionSignalingInterface* r, const char* buffer_in,
+                 uint32_t bytes, bool non_blocking = false);
+
+  /**
+   * Writes the data referenced by the given iov scatter/gather array to the
+   * queue.
+   * If the number of bytes to be written exceeds the size of the queue
+   * -ENOSPC will be returned.
+   * If non_blocking is true and there is not enough free space on the queue to
+   * write all the data -EWOULDBLOCK will be returned.
+   */
+  intptr_t Writev(
+          RegionSignalingInterface *r,
+          const iovec *iov,
+          size_t iov_count,
+          bool non_blocking = false);
+
+  bool Recover() {
+    return this->RecoverBase();
+  }
+
+ protected:
+  static_assert(CircularQueueBase<SizeLog2>::BufferSize >= MaxPacketSize,
+                "Buffer is too small to hold the maximum sized packet");
+  using Range = typename CircularQueueBase<SizeLog2>::Range;
+  intptr_t CalculateBufferedSize(size_t payload);
+};
+using CircularPacketQueue64k = CircularPacketQueue<16, 1024>;
+ASSERT_SHM_COMPATIBLE(CircularPacketQueue64k);
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/e2e_test_region_layout.h b/common/vsoc/shm/e2e_test_region_layout.h
new file mode 100644
index 0000000..f3ae615
--- /dev/null
+++ b/common/vsoc/shm/e2e_test_region_layout.h
@@ -0,0 +1,168 @@
+#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 <atomic>
+#include <cstdint>
+#include "common/vsoc/shm/base.h"
+
+// Memory layout for a region that supports end-to-end (E2E) testing of
+// shared memory regions. This verifies that all sorts of things work along the
+// the path:
+//
+//   host libraries <-> ivshmem server <-> kernel <-> guest libraries
+//
+// This is intentionally not a unit test. The primary source of errors along
+// this path is a misunderstanding and/or inconsistency in one of the
+// interfaces. Introducing mocks would allow these errors to go undetected.
+// Another way of looking at it is that the mocks would end up being a
+// a copy-and-paste job, making a series of change-detector tests.
+//
+// These tests are actually run on every device boot to verify that things are
+// ok.
+
+namespace vsoc {
+namespace layout {
+
+namespace e2e_test {
+
+/**
+ * Flags that are used to indicate test status. Some of the latter testing
+ * stages rely on initializion that must be done on the peer.
+ */
+enum E2ETestStage : uint32_t {
+  // No tests have passed
+  E2E_STAGE_NONE = 0,
+  // This side has finished writing its pattern to the region
+  E2E_MEMORY_FILLED = 1,
+  // This side has confirmed that it can see its peer's writes to the region
+  E2E_PEER_MEMORY_READ = 2,
+};
+static_assert(ShmTypeValidator<E2ETestStage, 4>::valid,
+              "Compilation error. Please fix above errors and retry.");
+
+/**
+ * Structure that grants permission to write in the region to either the guest
+ * or the host. This size of these fields is arbitrary.
+ */
+struct E2EMemoryFill {
+  static constexpr size_t layout_size = 64;
+
+  static const std::size_t kOwnedFieldSize = 32;
+
+  // The compiler must not attempt to optimize away reads and writes to the
+  // shared memory window. This is pretty typical when dealing with devices
+  // doing memory mapped I/O.
+  char host_writable[kOwnedFieldSize];
+  char guest_writable[kOwnedFieldSize];
+};
+ASSERT_SHM_COMPATIBLE(E2EMemoryFill);
+
+/**
+ * Structure that grants permission to write in the region to either the guest
+ * or the host. This size of these fields is arbitrary.
+ */
+class E2ETestStageRegister {
+ public:
+  static constexpr size_t layout_size = 4;
+
+  E2ETestStage value() const {
+    return value_;
+  }
+
+  void set_value(E2ETestStage new_value) { value_ = new_value; }
+
+ protected:
+  // The compiler must not attempt to optimize away reads and writes to the
+  // shared memory window. This is pretty typical when dealing with devices
+  // doing memory mapped I/O.
+  E2ETestStage value_;
+};
+ASSERT_SHM_COMPATIBLE(E2ETestStageRegister);
+
+/**
+ * Describes the layout of the regions used for the end-to-end test. There
+ * are multiple regions: primary and secondary, so some details like the region
+ * name must wait until later.
+ */
+class E2ETestRegionLayout : public ::vsoc::layout::RegionLayout {
+ public:
+  static constexpr size_t layout_size = 2 * E2ETestStageRegister::layout_size +
+                                        3 * 4 + E2EMemoryFill::layout_size;
+
+  /**
+   * Computes how many E2EMemoryFill records we need to cover the region.
+   * Covering the entire region during the test ensures that everything is
+   * mapped and coherent between guest and host.
+   */
+  static std::size_t NumFillRecords(std::size_t region_size) {
+    if (region_size < sizeof(E2ETestRegionLayout)) {
+      return 0;
+    }
+    // 1 + ... An array of size 1 is allocated in the E2ETestRegion.
+    // TODO(ghartman): AddressSanitizer may find this sort of thing to be
+    // alarming.
+    return 1 +
+           (region_size - sizeof(E2ETestRegionLayout)) / sizeof(E2EMemoryFill);
+  }
+  // The number of test stages that have completed on the guest
+  // Later host tests will wait on this
+  E2ETestStageRegister guest_status;
+  // The number of test stages that have completed on the host
+  // Later guest tests will wait on this
+  E2ETestStageRegister host_status;
+  // These fields are used to test the signaling mechanism.
+  std::atomic<uint32_t> host_to_guest_signal;
+  std::atomic<uint32_t> guest_to_host_signal;
+  std::atomic<uint32_t> guest_self_register;
+  // There rest of the region will be filled by guest_host_strings.
+  // We actually use more than one of these, but we can't know how many
+  // until we examine the region.
+  E2EMemoryFill data[1];
+};
+ASSERT_SHM_COMPATIBLE(E2ETestRegionLayout);
+
+struct E2EPrimaryTestRegionLayout : public E2ETestRegionLayout {
+  static constexpr size_t layout_size = E2ETestRegionLayout::layout_size;
+
+  static const char* region_name;
+  static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize];
+  static const char host_pattern[E2EMemoryFill::kOwnedFieldSize];
+};
+ASSERT_SHM_COMPATIBLE(E2EPrimaryTestRegionLayout);
+
+struct E2ESecondaryTestRegionLayout : public E2ETestRegionLayout {
+  static constexpr size_t layout_size = E2ETestRegionLayout::layout_size;
+
+  static const char* region_name;
+  static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize];
+  static const char host_pattern[E2EMemoryFill::kOwnedFieldSize];
+};
+ASSERT_SHM_COMPATIBLE(E2ESecondaryTestRegionLayout);
+
+/**
+ * Defines an end-to-end region with a name that should never be configured.
+ */
+struct E2EUnfindableRegionLayout : public E2ETestRegionLayout {
+  static constexpr size_t layout_size = E2ETestRegionLayout::layout_size;
+
+  static const char* region_name;
+};
+ASSERT_SHM_COMPATIBLE(E2EUnfindableRegionLayout);
+
+}  // namespace e2e_test
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/gralloc_layout.h b/common/vsoc/shm/gralloc_layout.h
new file mode 100644
index 0000000..4da740f
--- /dev/null
+++ b/common/vsoc/shm/gralloc_layout.h
@@ -0,0 +1,75 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/graphics.h"
+#include "common/vsoc/shm/lock.h"
+
+// Memory layout for the gralloc manager region.
+
+namespace vsoc {
+namespace layout {
+
+namespace gralloc {
+
+struct BufferEntry {
+  static constexpr size_t layout_size =
+      7 * 4 + PixelFormatRegister::layout_size;
+
+  uint32_t owned_by;
+  uint32_t buffer_begin;
+  uint32_t buffer_end;
+
+  PixelFormatRegister pixel_format;
+  uint32_t stride;
+  uint32_t width;
+  uint32_t height;
+
+  // A size of 28 is causing different layouts when GrallocManagerLayout is
+  // compiled in host and guest sides
+  uint32_t padding;
+
+  uint32_t buffer_size() {
+    return buffer_end - buffer_begin;
+  }
+};
+ASSERT_SHM_COMPATIBLE(BufferEntry);
+
+struct GrallocBufferLayout : public RegionLayout {
+  static constexpr size_t layout_size = 1;
+  static const char* region_name;
+};
+ASSERT_SHM_COMPATIBLE(GrallocBufferLayout);
+
+struct GrallocManagerLayout : public RegionLayout {
+  static constexpr size_t layout_size =
+      8 + GuestLock::layout_size + BufferEntry::layout_size;
+  static const char* region_name;
+  typedef GrallocBufferLayout ManagedRegion;
+
+  uint32_t allocated_buffer_memory;
+  uint32_t buffer_count;
+  // Make sure this isn't the first field
+  GuestLock new_buffer_lock;
+  // Needs to be last field
+  BufferEntry buffers_table[1];
+};
+ASSERT_SHM_COMPATIBLE(GrallocManagerLayout);
+
+} // namespace gralloc
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/graphics.h b/common/vsoc/shm/graphics.h
new file mode 100644
index 0000000..0aa7875
--- /dev/null
+++ b/common/vsoc/shm/graphics.h
@@ -0,0 +1,169 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Memory layout for primitive graphics types.
+
+// The vsoc::layout namespace indicates that these are shared memory structure
+// definitions. The #include's given above are strictly limited, as are the
+// types that can be referenced below.
+
+#include <cstdint>
+
+#include "common/vsoc/shm/base.h"
+
+namespace vsoc {
+
+// The enumerations for VSoC pixel formats are laid out so that hardware to
+// parse bytes per pixel without relying an a exhaustive list of pixel formats.
+// These constants define the fields involved.
+namespace PixelFormatConst {
+  static const uint32_t BytesPerPixelSize = 3;
+  static const uint32_t SubformatSize = 3;
+  static const uint32_t MaxBytesPerPixel = (1 << BytesPerPixelSize);
+  static const uint32_t MaxSubformat = (1 << SubformatSize) - 1;
+};
+
+
+// Builds (statically) a new pixel format enumeration value given constant
+// bytes per pixel.
+template <uint32_t BYTES, uint32_t SUB_FORMAT>
+struct PixelFormatBuilder {
+  static_assert(BYTES > 0, "Too few bytes");
+  static_assert(BYTES <= PixelFormatConst::MaxBytesPerPixel, "Too many bytes");
+  static_assert(SUB_FORMAT <= PixelFormatConst::MaxSubformat,
+                "Too many subformats");
+  static const uint32_t value = ((BYTES - 1) << PixelFormatConst::SubformatSize) | SUB_FORMAT;
+};
+
+template <uint32_t FORMAT>
+struct PixelFormatProperties {
+  // No static asserts since all int32_t values are (technically) valid pixel formats?
+  static const uint32_t bytes_per_pixel = (FORMAT >> PixelFormatConst::SubformatSize) + 1;
+};
+
+// Contains all of the pixel formats currently supported by this VSoC. The
+// enumeration serves multiple purposes:
+//
+//   * The compile will warn (or error) if we switch on PixelFormat and don't
+//     handly all of the cases.
+//
+//   * Code can use PixelFormat to describe paramaters, making APIs a bit more
+//     self-documenting.
+//
+//   * Observant reviewers can verify that the same pixel value is not assigned
+//     to multiple formats. Keep the enums in numerical order below to
+//     make this easier.
+enum PixelFormat : uint32_t {
+  VSOC_PIXEL_FORMAT_UNINITIALIZED = PixelFormatBuilder<1,0>::value,
+  VSOC_PIXEL_FORMAT_BLOB =          PixelFormatBuilder<1,1>::value,
+
+  VSOC_PIXEL_FORMAT_RGB_565 =       PixelFormatBuilder<2,0>::value,
+  VSOC_PIXEL_FORMAT_YV12 =          PixelFormatBuilder<2,1>::value,
+  VSOC_PIXEL_FORMAT_YCbCr_420_888 = PixelFormatBuilder<2,2>::value,
+
+  VSOC_PIXEL_FORMAT_RGB_888 =       PixelFormatBuilder<3,0>::value,
+
+  VSOC_PIXEL_FORMAT_RGBA_8888 =     PixelFormatBuilder<4,0>::value,
+  VSOC_PIXEL_FORMAT_RGBX_8888 =     PixelFormatBuilder<4,1>::value,
+  VSOC_PIXEL_FORMAT_BGRA_8888 =     PixelFormatBuilder<4,2>::value,
+
+  VSOC_PIXEL_FORMAT_RGBA_FP16 =     PixelFormatBuilder<8,0>::value,
+
+  // VSOC_PIXEL_FORMAT_IMPLEMENTATION_DEFINED intentionally left out. The HALs
+  // should choose one of the defined contrete types.
+  //
+  // The following formats are defined in various platform versions, but don't
+  // seem to be used. If we encounter them it's ok to add them to the table.
+  // This does not necessitate a version change.
+  //
+  // The following have been in the framework for a long time:
+  //
+  //   VSOC_PIXEL_FORMAT_YCrCb_420_SP
+  //   VSOC_PIXEL_FORMAT_YCbCr_422_SP
+  //
+  // The following were added in JB_MR2:
+  //
+  //   VSOC_PIXEL_FORMAT_YCbCr_420_888
+  //   VSOC_PIXEL_FORMAT_Y8
+  //   VSOC_PIXEL_FORMAT_Y16
+  //
+  // The following were added in L:
+  //
+  //    VSOC_PIXEL_FORMAT_RAW_OPAQUE
+  //    VSOC_PIXEL_FORMAT_RAW16 (also known as RAW_SENSOR. Define only RAW16)
+  //    VSOC_PIXEL_FORMAT_RAW10
+  //
+  // The following were added in L MR1:
+  //
+  //   VSOC_PIXEL_FORMAT_YCbCr_444_888
+  //   VSOC_PIXEL_FORMAT_YCbCr_422_888
+  //   VSOC_PIXEL_FORMAT_RAW12
+  //   VSOC_PIXEL_FORMAT_FLEX_RGBA_8888
+  //   VSOC_PIXEL_FORMAT_FLEX_RGB_888
+  //
+  // These pixel formats were removed in later framework versions. Implement
+  // only if absolutely necessary.
+  //
+  // Support was dropped in K for:
+  //
+  //   VSOC_PIXEL_FORMAT_RGBA_5551
+  //   VSOC_PIXEL_FORMAT_RGBA_4444
+  //
+  // Supported only in K, L, and LMR1:
+  //
+  //   VSOC_PIXEL_FORMAT_sRGB_X_8888
+  //   VSOC_PIXEL_FORMAT_sRGB_A_8888
+};
+// Enums can't have static members, so can't use the macro here.
+static_assert(ShmTypeValidator<PixelFormat, 4>::valid,
+              "Compilation error. Please fix above errors and retry.");
+
+namespace layout {
+
+// VSoC memory layout for a register that accepts a single pixel format.
+// The value is volatile to ensure that the compiler does not eliminate stores.
+struct PixelFormatRegister {
+  static constexpr size_t layout_size = 4;
+
+  volatile PixelFormat value_;
+};
+ASSERT_SHM_COMPATIBLE(PixelFormatRegister);
+
+// Register layout for a mask giving different PixelFormats. Reserve enough
+// space to allow for future expansion. For example, we may well end with
+// a 12 bit per channel format in the future.
+struct PixelFormatMaskRegister {
+  static constexpr size_t layout_size = 8;
+
+  volatile uint64_t value_;
+
+  bool HasValue(PixelFormat in) {
+    return !!(value_ & (uint64_t(1) << in));
+  }
+};
+ASSERT_SHM_COMPATIBLE(PixelFormatMaskRegister);
+
+// Ensure that the mask is large enough to hold the highest encodable
+// pixel format.
+static_assert(PixelFormatBuilder<
+              PixelFormatConst::MaxBytesPerPixel,
+              PixelFormatConst::MaxSubformat>::value <
+              8 * sizeof(PixelFormatMaskRegister),
+              "Largest pixel format does not fit in mask");
+}  // layout
+}  // vsoc
diff --git a/common/vsoc/shm/input_events_layout.h b/common/vsoc/shm/input_events_layout.h
new file mode 100644
index 0000000..737c903
--- /dev/null
+++ b/common/vsoc/shm/input_events_layout.h
@@ -0,0 +1,46 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/circqueue.h"
+
+// Memory layout for region carrying input events from host to guest
+
+namespace vsoc {
+namespace layout {
+
+namespace input_events {
+
+struct InputEventsLayout : public RegionLayout {
+  static constexpr size_t layout_size =
+      CircularPacketQueue<10, 256>::layout_size +
+      2 * CircularPacketQueue<10, 16>::layout_size;
+
+  static const char* region_name;
+  // Event queues for the different input devices supported. Both the power
+  // button and the keyboard need only generate 2 input events for every
+  // 'hardware' event, so 16 bytes are enough, however when the touchscreen has
+  // multitouch enabled the number of generated events is significantly higher.
+  CircularPacketQueue<10, 256> touch_screen_queue;
+  CircularPacketQueue<10, 16> keyboard_queue;
+  CircularPacketQueue<10, 16> power_button_queue;
+};
+ASSERT_SHM_COMPATIBLE(InputEventsLayout);
+
+}  // namespace input_events
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/lock.h b/common/vsoc/shm/lock.h
new file mode 100644
index 0000000..2049ba8
--- /dev/null
+++ b/common/vsoc/shm/lock.h
@@ -0,0 +1,252 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Memory layout for locks of all types.
+
+// The vsoc::layout namespace indicates that these are shared memory structure
+// definitions. The #include's given above are strictly limited, as are the
+// types that can be referenced below.
+
+// For _mm_pause()
+#if defined(__SSE2__)
+#include <x86intrin.h>
+#define _pause() _mm_pause()
+#elif defined(__arm__) || defined(__aarch64__)
+#include <arm_acle.h>
+#define _pause() __yield()
+#endif
+
+#include <atomic>
+#include <cstdint>
+
+#include "common/vsoc/shm/base.h"
+
+// Host userspace, guest userspace, and the guest kernel must all agree on
+// the relationship between std::atomic and atomic_t. That's hard to do without
+// examining assembly, and we can't really examing atomic_t outside of the
+// kernel tree, but we can at least assert that the host and the guest
+// agree on a size.
+static_assert(sizeof(std::atomic<uint32_t>) == 4, "std::atomic size mismatch");
+
+namespace vsoc {
+
+class RegionView;
+
+namespace layout {
+
+/**
+ * Lock that causes threads to busy loop rather than sleeping.
+ * This lock should never be used when the amount of work in the critical
+ * section cannot be bounded.
+ */
+class SpinLock {
+ public:
+  static constexpr size_t layout_size = 4;
+
+  /**
+   * Acquire the spinlock on the queue. This will effectively block all
+   * readers and writers.
+   */
+  void Lock() {
+    while (1) {
+      uint32_t expected = 0;
+      if (lock_.compare_exchange_strong(expected, Sides::OurSide)) {
+        return;
+      }
+      _pause();
+    }
+  }
+
+  /**
+   * Drop the lock iff it is currently held by this side. Used by
+   * recovery code that cleans up regions in the event of a reboot
+   * (guest side) or a service restart (host side).
+   *
+   * The caller must ensure that there are no other threads on its
+   * side (e.g. guest/host) are using the window.
+   */
+  bool Recover() {
+    uint32_t expected = Sides::OurSide;
+    return lock_.compare_exchange_strong(expected, 0);
+  }
+
+  /**
+   * Release the spinlock.
+   */
+  void Unlock() {
+    lock_ = 0;
+  }
+
+ protected:
+  std::atomic<uint32_t> lock_;
+};
+ASSERT_SHM_COMPATIBLE(SpinLock);
+
+/**
+ * This is a generic synchronization primitive that provides space for the
+ * owner of the lock to write platform-specific information.
+ */
+class WaitingLockBase {
+ public:
+  static constexpr size_t layout_size = 40;
+
+ protected:
+  // Common code to handle locking
+  // Must be called with the kernel's thread id
+  // Returns true if the lock was acquired. In this case the value in
+  // expected_vlaue is undefined.
+  // Returns false if locking failed. The value discovered in the lock word
+  // is returned in expected_value, and should probably be used in a conditional
+  // sleep.
+  bool TryLock(uint32_t tid, uint32_t* expected_value);
+
+  // Common code to handle unlocking.
+  // Must be called with the kernel's thread id
+  // Returns sides that should be signalled or 0
+  Sides UnlockCommon(uint32_t tid);
+
+  // Common code to recover single-sided locks.
+  bool RecoverSingleSided();
+
+  // Non-zero values in this word indicate that the lock is in use.
+  // This is 32 bits for compatibility with futex()
+  std::atomic<uint32_t> lock_uint32_;
+
+  // Pad so we line up with glib's pthread_mutex_t and can share the same queue.
+  // These fields may be redefined at any point in the future. They should not
+  // be used.
+ private:
+// These fields are known to be unused and are provided for compatibility
+// with glibc's locks.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-private-field"
+  uint32_t reserved_1_;
+  char reserved_2_[16];
+  // Provide scratch space for the owner of the lock. The content of this space
+  // is undefined when the lock is acquired. The owner may write to and read
+  // from it while it holds the lock, but must relinquish control before
+  // releasing the lock.
+  //
+  // This is intended to support Linux robust futexes. See the documentation
+  // in the kernel tree:
+  //   Documentation/robust-futex-ABI.txt
+ public:
+  int64_t owner_scratch_[2];
+#pragma clang diagnostic pop
+};
+ASSERT_SHM_COMPATIBLE(WaitingLockBase);
+
+/**
+ * GuestLocks can be acquired and released only on the guest. They reside
+ * in the shared memory window because mutiple guest processes may need
+ * to coordinate activities in certain shared memory regions.
+ *
+ * Representing this as a concrete type allows for some optimizations when
+ * signalling on the lock.
+ */
+class GuestLock : public WaitingLockBase {
+ public:
+  static constexpr size_t layout_size = WaitingLockBase::layout_size;
+
+#ifndef CUTTLEFISH_HOST
+  void Lock();
+  void Unlock();
+  /**
+   * Drop the lock iff it is currently held. Used by
+   * recovery code that cleans up regions in the event of a reboot.
+   *
+   * The caller must ensure that there are no other threads on its
+   * side (e.g. guest/host) are using the window.
+   */
+  bool Recover();
+#endif
+};
+ASSERT_SHM_COMPATIBLE(GuestLock);
+
+/**
+ * HostLocks can be acquired and released only on the host. They reside
+ * in the shared memory window because mutiple host processes may need
+ * to coordinate activities in certain shared memory regions.
+ *
+ * Representing this as a concrete type allows for some optimizations when
+ * signalling on the lock.
+ */
+class HostLock : public WaitingLockBase {
+ public:
+  static constexpr size_t layout_size = WaitingLockBase::layout_size;
+
+#ifdef CUTTLEFISH_HOST
+  void Lock();
+  void Unlock();
+  /**
+   * Drop the lock iff it is currently held. Used by
+   * recovery code that cleans up regions in the event of a daemon
+   * restart.
+   *
+   * The caller must ensure that there are no other threads on its
+   * side (e.g. guest/host) are using the window.
+   */
+  bool Recover();
+#endif
+};
+ASSERT_SHM_COMPATIBLE(HostLock);
+
+/**
+ * GuestAndHostLocks can be acquired and released on either side of the
+ * shared memory window. The locks attempt to enforce fairness by using
+ * a round-trip signal:
+ *
+ *   When a guest releases a lock this code sends a signal to wake the host,
+ *   but not other guest waiters.
+ *
+ *   The wake handler on the host wakes up and local waiters and then reposts
+ *   the signal to the guest.
+ *
+ *   When the guest receives the signal from the host it then wakes ups
+ *   any waiters.
+ *
+ * A similar scenario applies when the host releases a lock with guest waiters.
+ *
+ * Signalling across the shared memory window twice has non-trivial cost.
+ * There are some optimizations in the code to prevent the full round-trip
+ * if the process releasing the lock can confirm that there are no waiters on
+ * the other side.
+ *
+ * Representing this as a concrete type allows for some optimizations when
+ * signalling on the lock.
+ */
+class GuestAndHostLock : public WaitingLockBase {
+ public:
+  static constexpr size_t layout_size = WaitingLockBase::layout_size;
+
+  void Lock(RegionView*);
+  void Unlock(RegionView*);
+  /**
+   * Drop the lock iff it is currently held by this side. Used by
+   * recovery code that cleans up regions in the event of a reboot
+   * (guest side) or a service restart (host side).
+   *
+   * The caller must ensure that there are no other threads on its
+   * side (e.g. guest/host) are using the window.
+   */
+  bool Recover(RegionView*);
+};
+ASSERT_SHM_COMPATIBLE(GuestAndHostLock);
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/managed_e2e_test_region_layout.h b/common/vsoc/shm/managed_e2e_test_region_layout.h
new file mode 100644
index 0000000..dc23f66
--- /dev/null
+++ b/common/vsoc/shm/managed_e2e_test_region_layout.h
@@ -0,0 +1,46 @@
+#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 <cstdint>
+
+#include "common/vsoc/shm/base.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace e2e_test {
+
+struct E2EManagedTestRegionLayout : public RegionLayout {
+  static constexpr size_t layout_size = 4;
+
+  static const char* region_name;
+  uint32_t val;  // Not needed, here only to avoid an empty struct.
+};
+ASSERT_SHM_COMPATIBLE(E2EManagedTestRegionLayout);
+
+struct E2EManagerTestRegionLayout : public RegionLayout {
+  static constexpr size_t layout_size = 4 * 4;
+
+  static const char* region_name;
+  typedef E2EManagedTestRegionLayout ManagedRegion;
+  uint32_t data[4];  // We don't need more than 4 for the tests
+};
+ASSERT_SHM_COMPATIBLE(E2EManagerTestRegionLayout);
+
+}  // namespace e2e_test
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/screen_layout.h b/common/vsoc/shm/screen_layout.h
new file mode 100644
index 0000000..7ad0ca3
--- /dev/null
+++ b/common/vsoc/shm/screen_layout.h
@@ -0,0 +1,76 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/lock.h"
+
+// Memory layout for screen region
+
+namespace vsoc {
+namespace layout {
+
+namespace screen {
+struct TimeSpec {
+  static constexpr size_t layout_size = 16;
+
+  int64_t ts_sec;
+  uint32_t ts_nsec;
+  // Host and guest compilers are giving the structure different sizes without
+  // this field.
+  uint32_t reserved;
+};
+ASSERT_SHM_COMPATIBLE(TimeSpec);
+
+struct CompositionStats {
+  static constexpr size_t layout_size = 4 + 2 * 2 + 5 * TimeSpec::layout_size;
+
+  uint32_t num_prepare_calls;
+  uint16_t num_layers;
+  uint16_t num_hwcomposited_layers;
+  TimeSpec last_vsync;
+  TimeSpec prepare_start;
+  TimeSpec prepare_end;
+  TimeSpec set_start;
+  TimeSpec set_end;
+};
+ASSERT_SHM_COMPATIBLE(CompositionStats);
+
+struct ScreenLayout : public RegionLayout {
+  static constexpr size_t layout_size = 24 + CompositionStats::layout_size;
+  static const char* region_name;
+  // Display properties
+  uint32_t x_res;
+  uint32_t y_res;
+  uint16_t dpi;
+  uint16_t refresh_rate_hz;
+
+  // Protects access to the frame offset, sequential number and stats.
+  // See the region implementation for more details.
+  SpinLock bcast_lock;
+  // The frame sequential number
+  std::atomic<uint32_t> seq_num;
+  // The index of the buffer containing the current frame.
+  int32_t buffer_index;
+  CompositionStats stats;
+  uint8_t buffer[0];
+};
+ASSERT_SHM_COMPATIBLE(ScreenLayout);
+
+}  // namespace screen
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/socket_forward_layout.h b/common/vsoc/shm/socket_forward_layout.h
new file mode 100644
index 0000000..4a9beda
--- /dev/null
+++ b/common/vsoc/shm/socket_forward_layout.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/circqueue.h"
+#include "common/vsoc/shm/lock.h"
+
+// Memory layout for wifi packet exchange region.
+namespace vsoc {
+namespace layout {
+namespace socket_forward {
+
+constexpr std::size_t kMaxPacketSize = 8192;
+constexpr std::size_t kNumQueues = 16;
+
+struct Queue {
+  static constexpr size_t layout_size =
+      CircularPacketQueue<16, kMaxPacketSize>::layout_size;
+
+  CircularPacketQueue<16, kMaxPacketSize> queue;
+
+  bool Recover() { return queue.Recover(); }
+};
+ASSERT_SHM_COMPATIBLE(Queue);
+
+struct QueuePair {
+  static constexpr size_t layout_size = 2 * Queue::layout_size;
+
+  // Traffic originating from host that proceeds towards guest.
+  Queue host_to_guest;
+  // Traffic originating from guest that proceeds towards host.
+  Queue guest_to_host;
+
+  bool Recover() {
+    bool recovered = false;
+    recovered = recovered || host_to_guest.Recover();
+    recovered = recovered || guest_to_host.Recover();
+    return recovered;
+  }
+};
+ASSERT_SHM_COMPATIBLE(QueuePair);
+
+struct SocketForwardLayout : public RegionLayout {
+  static constexpr size_t layout_size = QueuePair::layout_size * kNumQueues;
+
+  bool Recover() {
+    bool recovered = false;
+    for (auto& i : queues_) {
+      bool rval = i.Recover();
+      recovered = recovered || rval;
+    }
+    return recovered;
+  }
+
+  QueuePair queues_[kNumQueues];
+  static const char* region_name;
+};
+
+ASSERT_SHM_COMPATIBLE(SocketForwardLayout);
+
+}  // namespace socket_forward
+}  // namespace layout
+}  // namespace vsoc
diff --git a/guest/commands/vsoc_input_service/Android.bp b/guest/commands/vsoc_input_service/Android.bp
index f2e197f..73ca067 100644
--- a/guest/commands/vsoc_input_service/Android.bp
+++ b/guest/commands/vsoc_input_service/Android.bp
@@ -33,6 +33,7 @@
         "libcuttlefish_fs",
         "libbase",
         "liblog",
+        "vsoc_lib",
     ],
     header_libs: [
         "cuttlefish_glog",
diff --git a/guest/commands/vsoc_input_service/vsoc_input_service.cpp b/guest/commands/vsoc_input_service/vsoc_input_service.cpp
index 8b75b45..8753735 100644
--- a/guest/commands/vsoc_input_service/vsoc_input_service.cpp
+++ b/guest/commands/vsoc_input_service/vsoc_input_service.cpp
@@ -28,6 +28,7 @@
 
 #include "common/libs/fs/shared_fd.h"
 #include "common/libs/device_config/device_config.h"
+#include "common/vsoc/lib/input_events_region_view.h"
 
 using vsoc::input_events::InputEvent;
 using vsoc_input_service::VirtualDeviceBase;
diff --git a/guest/commands/vsoc_input_service/vsoc_input_service.h b/guest/commands/vsoc_input_service/vsoc_input_service.h
index edf196d..2282b40 100644
--- a/guest/commands/vsoc_input_service/vsoc_input_service.h
+++ b/guest/commands/vsoc_input_service/vsoc_input_service.h
@@ -21,18 +21,6 @@
 #include "virtual_power_button.h"
 #include "virtual_touchscreen.h"
 
-namespace vsoc {
-namespace input_events {
-
-struct InputEvent {
-  uint16_t type;
-  uint16_t code;
-  uint32_t value;
-};
-
-} // namespace input_events
-} // namespace vsoc
-
 namespace vsoc_input_service {
 
 class VSoCInputService {
diff --git a/guest/hals/audio/legacy/Android.mk b/guest/hals/audio/legacy/Android.mk
new file mode 100644
index 0000000..8d4ef0a
--- /dev/null
+++ b/guest/hals/audio/legacy/Android.mk
@@ -0,0 +1,65 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := first
+
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    liblog \
+    libcutils \
+    cuttlefish_auto_resources \
+    libcuttlefish_fs \
+    cuttlefish_time \
+    vsoc_lib
+
+LOCAL_HEADER_LIBRARIES := \
+    libhardware_headers
+
+LOCAL_SRC_FILES := \
+    audio_hal.cpp \
+    vsoc_audio.cpp \
+    vsoc_audio_input_stream.cpp \
+    vsoc_audio_output_stream.cpp
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_kernel \
+    $(VSOC_STLPORT_INCLUDES) \
+    frameworks/native/include/media/hardware \
+    $(call include-path-for, audio)
+
+LOCAL_STATIC_LIBRARIES := \
+    libcutils \
+    libcuttlefish_remoter_framework \
+    $(VSOC_STLPORT_STATIC_LIBS)
+
+LOCAL_CFLAGS := \
+    -Wall -Werror -std=c++17 \
+    $(VSOC_VERSION_CFLAGS)
+
+
+LOCAL_MODULE := audio.primary.cutf_ivsh
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/audio/legacy/audio_hal.cpp b/guest/hals/audio/legacy/audio_hal.cpp
new file mode 100644
index 0000000..a865d46
--- /dev/null
+++ b/guest/hals/audio/legacy/audio_hal.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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/hals/audio/legacy/vsoc_audio.h"
+
+#include "guest/hals/audio/legacy/audio_hal.h"
+
+static hw_module_methods_t hal_module_methods = {
+  .open = cvd::GceAudio::Open,
+};
+
+
+audio_module HAL_MODULE_INFO_SYM = {
+  .common = {
+    .tag = HARDWARE_MODULE_TAG,
+    .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
+    .hal_api_version = HARDWARE_HAL_API_VERSION,
+    .id = AUDIO_HARDWARE_MODULE_ID,
+    .name = "GCE Audio HW HAL",
+    .author = "The Android Open Source Project",
+    .methods = &hal_module_methods,
+  },
+};
diff --git a/guest/hals/audio/legacy/audio_hal.h b/guest/hals/audio/legacy/audio_hal.h
new file mode 100644
index 0000000..db547ff
--- /dev/null
+++ b/guest/hals/audio/legacy/audio_hal.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#define AUDIO_DEBUG 0
+
+#if AUDIO_DEBUG
+#  define D(...) ALOGD(__VA_ARGS__)
+#else
+#  define D(...) ((void)0)
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <log/log.h>
+
+#include <hardware/hardware.h>
+
+#pragma GCC diagnostic push
+#pragma  GCC diagnostic ignored "-Wparentheses"
+#pragma  GCC diagnostic ignored "-Wgnu-designator"
+#include <system/audio.h>
+#pragma GCC diagnostic pop
+
+#include <hardware/audio.h>
diff --git a/guest/hals/audio/legacy/policy/Android.mk b/guest/hals/audio/legacy/policy/Android.mk
new file mode 100644
index 0000000..0b2ef8c
--- /dev/null
+++ b/guest/hals/audio/legacy/policy/Android.mk
@@ -0,0 +1,45 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := first
+
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    libcutils
+
+LOCAL_SRC_FILES := \
+    vsoc_audio_policy_hal.cpp
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    $(VSOC_STLPORT_INCLUDES) \
+    frameworks/native/include/media/hardware \
+    $(call include-path-for, audio)
+
+LOCAL_CFLAGS := -Wall -Werror
+
+LOCAL_MODULE := audio_policy.vsoc
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
new file mode 100644
index 0000000..53c3127
--- /dev/null
+++ b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <system/audio_policy.h>
+#include <hardware/audio_policy.h>
+
+#include "guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h"
+
+namespace cvd {
+
+int GceAudioPolicy::Create(
+    const audio_policy_device* device,
+    audio_policy_service_ops* aps_ops,
+    void* service, audio_policy** ap) {
+  D("%s", __FUNCTION__);
+  audio_policy_device* dev;
+  gce_audio_policy* dap;
+  int ret;
+
+  *ap = NULL;
+
+  if (!service || !aps_ops) {
+    return -EINVAL;
+  }
+
+  dap = (gce_audio_policy*) calloc(1, sizeof(*dap));
+  if (!dap) {
+    return -ENOMEM;
+  }
+
+  dap->policy.set_device_connection_state =
+      &GceAudioPolicy::SetDeviceConnectionState;
+  dap->policy.get_device_connection_state =
+      &GceAudioPolicy::GetDeviceConnectionState;
+  dap->policy.set_phone_state = &GceAudioPolicy::SetPhoneState;
+  dap->policy.set_ringer_mode = &GceAudioPolicy::SetRingerMode;
+  dap->policy.set_force_use = &GceAudioPolicy::SetForceUse;
+  dap->policy.get_force_use = &GceAudioPolicy::GetForceUse;
+  dap->policy.set_can_mute_enforced_audible =
+      &GceAudioPolicy::SetCanMuteEnforcedAudible;
+  dap->policy.init_check = &GceAudioPolicy::InitCheck;
+  dap->policy.get_output = &GceAudioPolicy::GetOutput;
+  dap->policy.start_output = &GceAudioPolicy::StartOutput;
+  dap->policy.stop_output = &GceAudioPolicy::StopOutput;
+  dap->policy.release_output = &GceAudioPolicy::ReleaseOutput;
+  dap->policy.get_input = &GceAudioPolicy::GetInput;
+  dap->policy.start_input = &GceAudioPolicy::StartInput;
+  dap->policy.stop_input = &GceAudioPolicy::StopInput;
+  dap->policy.release_input = &GceAudioPolicy::ReleaseInput;
+  dap->policy.init_stream_volume = &GceAudioPolicy::InitStreamVolume;
+  dap->policy.set_stream_volume_index = &GceAudioPolicy::SetStreamVolumeIndex;
+  dap->policy.get_stream_volume_index = &GceAudioPolicy::GetStreamVolumeIndex;
+  dap->policy.set_stream_volume_index_for_device =
+      &GceAudioPolicy::SetStreamVolumeIndexForDevice;
+  dap->policy.get_stream_volume_index_for_device =
+      &GceAudioPolicy::GetStreamVolumeIndexForDevice;
+  dap->policy.get_strategy_for_stream = &GceAudioPolicy::GetStrategyForStream;
+  dap->policy.get_devices_for_stream = &GceAudioPolicy::GetDevicesForStream;
+  dap->policy.get_output_for_effect = &GceAudioPolicy::GetOutputForEffect;
+  dap->policy.register_effect = &GceAudioPolicy::RegisterEffect;
+  dap->policy.unregister_effect = &GceAudioPolicy::UnregisterEffect;
+  dap->policy.set_effect_enabled = &GceAudioPolicy::SetEffectEnabled;
+  dap->policy.is_stream_active = &GceAudioPolicy::IsStreamActive;
+  dap->policy.dump = &GceAudioPolicy::Dump;
+#ifdef ENABLE_OFFLOAD
+  dap->policy.is_offload_supported = &GceAudioPolicy::IsOffloadSupported;
+#endif
+
+  dap->service = service;
+  dap->aps_ops = aps_ops;
+
+  *ap = &dap->policy;
+  return 0;
+}
+
+
+int GceAudioPolicy::Destroy(const audio_policy_device* ap_dev,
+                            audio_policy* ap) {
+  D("%s", __FUNCTION__);
+  free(ap);
+  return 0;
+}
+
+
+int GceAudioPolicy::Close(hw_device_t* device) {
+  D("%s", __FUNCTION__);
+  free(device);
+  return 0;
+}
+
+
+int GceAudioPolicy::Open(
+    const hw_module_t* module, const char* name, hw_device_t** device) {
+  D("%s", __FUNCTION__);
+  audio_policy_device* dev;
+
+  *device = NULL;
+
+  if (strcmp(name, AUDIO_POLICY_INTERFACE) != 0) {
+    return -EINVAL;
+  }
+
+  dev = (audio_policy_device*) calloc(1, sizeof(*dev));
+  if (!dev) {
+    return -ENOMEM;
+  }
+
+  dev->common.tag = HARDWARE_DEVICE_TAG;
+  dev->common.version = 0;
+  dev->common.module = (hw_module_t*) module;
+  dev->common.close = &GceAudioPolicy::Close;
+  dev->create_audio_policy = &GceAudioPolicy::Create;
+  dev->destroy_audio_policy = &GceAudioPolicy::Destroy;
+
+  *device = &dev->common;
+
+  return 0;
+}
+
+}
+
+static hw_module_methods_t gce_audio_policy_module_methods = {
+  .open = &cvd::GceAudioPolicy::Open,
+};
+
+
+audio_policy_module HAL_MODULE_INFO_SYM = {
+  .common = {
+    .tag           = HARDWARE_MODULE_TAG,
+    .version_major = 1,
+    .version_minor = 0,
+    .id            = AUDIO_POLICY_HARDWARE_MODULE_ID,
+    .name          = "GCE Audio Policy HAL",
+    .author        = "The Android Open Source Project",
+    .methods       = &gce_audio_policy_module_methods,
+  },
+};
diff --git a/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h
new file mode 100644
index 0000000..00ea3d1
--- /dev/null
+++ b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <errno.h>
+#include <string.h>
+#include <log/log.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio.h>
+
+#define AUDIO_DEBUG 1
+
+#if AUDIO_DEBUG
+#  define D(...) ALOGD(__VA_ARGS__)
+#else
+#  define D(...) ((void)0)
+#endif
+
+#define LOG_TAG "GceAudioPolicy"
+
+namespace avd {
+
+struct gce_audio_policy {
+  audio_policy policy;
+
+  audio_policy_service_ops* aps_ops;
+  void* service;
+};
+
+
+class GceAudioPolicy {
+ public:
+  GceAudioPolicy() {}
+  ~GceAudioPolicy() {}
+
+  static int Open(
+      const hw_module_t* module, const char* name, hw_device_t** device);
+  static int Create(const audio_policy_device* device,
+      audio_policy_service_ops* aps_ops, void* service,
+      audio_policy** ap);
+  static int Destroy(const audio_policy_device* ap_dev,
+                     audio_policy* ap);
+  static int Close(hw_device_t* device);
+
+  static int SetDeviceConnectionState(
+      audio_policy* pol, audio_devices_t device,
+      audio_policy_dev_state_t state, const char* device_address) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static audio_policy_dev_state_t GetDeviceConnectionState(
+      const audio_policy* pol, audio_devices_t device,
+      const char* device_address) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
+  }
+
+  static void SetPhoneState(audio_policy* pol, audio_mode_t state) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static void SetRingerMode(audio_policy* pol, uint32_t mode,
+                            uint32_t mask) {
+    ALOGW("%s: deprecated", __FUNCTION__);
+  }
+
+  static void SetForceUse(
+      audio_policy* pol, audio_policy_force_use_t usage,
+    audio_policy_forced_cfg_t config) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static audio_policy_forced_cfg_t GetForceUse(
+      const audio_policy* pol, audio_policy_force_use_t usage) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return AUDIO_POLICY_FORCE_NONE;
+  }
+
+  static void SetCanMuteEnforcedAudible(
+      audio_policy* pol, bool can_mute) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static int InitCheck(const audio_policy* pol) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static audio_io_handle_t GetOutput(
+      audio_policy* pol, audio_stream_type_t stream,
+      uint32_t sampling_rate, audio_format_t format,
+      audio_channel_mask_t channelMask, audio_output_flags_t flags
+#ifdef ENABLE_OFFLOAD
+      , const audio_offload_info_t* info
+#endif
+      ) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static int StartOutput(audio_policy* pol, audio_io_handle_t output,
+                         audio_stream_type_t stream, int session) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int StopOutput(audio_policy* pol, audio_io_handle_t output,
+                        audio_stream_type_t stream, int session) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static void ReleaseOutput(
+      audio_policy* pol, audio_io_handle_t output) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static audio_io_handle_t GetInput(
+      audio_policy* pol, audio_source_t inputSource,
+      uint32_t sampling_rate, audio_format_t format,
+      audio_channel_mask_t channelMask, audio_in_acoustics_t acoustics) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static int StartInput(audio_policy* pol, audio_io_handle_t input) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int StopInput(audio_policy* pol, audio_io_handle_t input) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static void ReleaseInput(
+      audio_policy* pol, audio_io_handle_t input) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static void InitStreamVolume(audio_policy* pol,
+                               audio_stream_type_t stream, int index_min,
+                               int index_max) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static int SetStreamVolumeIndex(audio_policy* pol,
+                                  audio_stream_type_t stream, int index) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int GetStreamVolumeIndex(const audio_policy* pol,
+                                  audio_stream_type_t stream, int* index) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int SetStreamVolumeIndexForDevice(
+      audio_policy* pol, audio_stream_type_t stream,
+      int index, audio_devices_t device) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int GetStreamVolumeIndexForDevice(
+      const audio_policy* pol, audio_stream_type_t stream,
+      int* index, audio_devices_t device) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static uint32_t GetStrategyForStream(const audio_policy* pol,
+                                       audio_stream_type_t stream) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static audio_devices_t GetDevicesForStream(const audio_policy* pol,
+                                             audio_stream_type_t stream) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static audio_io_handle_t GetOutputForEffect(
+      audio_policy* pol, const effect_descriptor_s* desc) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static int RegisterEffect(
+      audio_policy* pol, const effect_descriptor_s* desc,
+      audio_io_handle_t output, uint32_t strategy, int session, int id) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int UnregisterEffect(audio_policy* pol, int id) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int SetEffectEnabled(audio_policy* pol, int id, bool enabled) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static bool IsStreamActive(
+      const audio_policy* pol, audio_stream_type_t stream,
+      uint32_t in_past_ms) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return false;
+  }
+
+  static int Dump(const audio_policy* pol, int fd) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+#ifdef ENABLE_OFFLOAD
+  static bool IsOffloadSupported(const audio_policy* pol,
+                                 const audio_offload_info_t* info) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return false;
+  }
+#endif
+};
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio.cpp b/guest/hals/audio/legacy/vsoc_audio.cpp
new file mode 100644
index 0000000..040e1e7
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio.cpp
@@ -0,0 +1,387 @@
+/*
+ * 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/hals/audio/legacy/audio_hal.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+extern "C" {
+#include <cutils/str_parms.h>
+}
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/fs/shared_select.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "common/libs/threads/thunkers.h"
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "guest/hals/audio/legacy/vsoc_audio.h"
+#include "guest/hals/audio/legacy/vsoc_audio_input_stream.h"
+#include "guest/hals/audio/legacy/vsoc_audio_output_stream.h"
+#include "guest/libs/remoter/remoter_framework_pkt.h"
+
+using cvd::LockGuard;
+using cvd::Mutex;
+
+namespace cvd {
+
+GceAudio::~GceAudio() { }
+
+int GceAudio::Close() {
+  D("GceAudio::%s", __FUNCTION__);
+  {
+    LockGuard<Mutex> guard(lock_);
+    for (std::list<GceAudioOutputStream*>::iterator it = output_list_.begin();
+         it != output_list_.end(); ++it) {
+      delete *it;
+    }
+    for (input_map_t::iterator it = input_map_.begin();
+         it != input_map_.end(); ++it) {
+      delete it->second;
+    }
+  }
+  delete this;
+  return 0;
+}
+
+size_t GceAudio::GetInputBufferSize(const audio_config*) const {
+  return IN_BUFFER_BYTES;
+}
+
+uint32_t GceAudio::GetSupportedDevices() const {
+  return AUDIO_DEVICE_OUT_EARPIECE |
+      AUDIO_DEVICE_OUT_SPEAKER |
+      AUDIO_DEVICE_OUT_DEFAULT |
+      AUDIO_DEVICE_IN_COMMUNICATION |
+      AUDIO_DEVICE_IN_BUILTIN_MIC |
+      AUDIO_DEVICE_IN_WIRED_HEADSET |
+      AUDIO_DEVICE_IN_VOICE_CALL |
+      AUDIO_DEVICE_IN_DEFAULT;
+}
+
+int GceAudio::InitCheck() const {
+  D("GceAudio::%s", __FUNCTION__);
+  return 0;
+}
+
+int GceAudio::SetMicMute(bool state) {
+  D("GceAudio::%s", __FUNCTION__);
+  LockGuard<Mutex> guard(lock_);
+  mic_muted_ = state;
+  return 0;
+}
+
+int GceAudio::GetMicMute(bool *state) const {
+  D("GceAudio::%s", __FUNCTION__);
+  LockGuard<Mutex> guard(lock_);
+  *state = mic_muted_;
+  return 0;
+}
+
+int GceAudio::OpenInputStream(audio_io_handle_t handle,
+                              audio_devices_t devices,
+                              audio_config *config,
+                              audio_stream_in **stream_in,
+                              audio_input_flags_t /*flags*/,
+                              const char * /*address*/,
+                              audio_source_t /*source*/) {
+  GceAudioInputStream* new_stream;
+  int rval = GceAudioInputStream::Open(
+      this, handle, devices, *config, &new_stream);
+  uint32_t stream_number;
+  if (new_stream) {
+    LockGuard<Mutex> guard(lock_);
+    stream_number = next_stream_number_++;
+    input_map_[stream_number] = new_stream;
+  }
+  // This should happen after the lock is released, hence the double check
+  if (new_stream) {
+    SendStreamUpdate(new_stream->GetStreamDescriptor(
+        stream_number, gce_audio_message::OPEN_INPUT_STREAM), MSG_DONTWAIT);
+  }
+  *stream_in = new_stream;
+  return rval;
+}
+
+
+void GceAudio::CloseInputStream(audio_stream_in *stream) {
+  GceAudioInputStream* astream = static_cast<GceAudioInputStream*>(stream);
+  gce_audio_message descriptor;
+  {
+    LockGuard<Mutex> guard(lock_);
+    // TODO(ghartman): This could be optimized if stream knew it's number.
+    for (input_map_t::iterator it = input_map_.begin();
+         it != input_map_.end(); ++it) {
+      if (it->second == stream) {
+        descriptor = it->second->GetStreamDescriptor(
+            it->first, gce_audio_message::CLOSE_INPUT_STREAM);
+        input_map_.erase(it);
+        break;
+      }
+    }
+  }
+  SendStreamUpdate(descriptor, MSG_DONTWAIT);
+  delete astream;
+}
+
+
+int GceAudio::OpenOutputStream(audio_io_handle_t handle,
+                               audio_devices_t devices,
+                               audio_output_flags_t flags,
+                               audio_config *config,
+                               audio_stream_out **stream_out,
+                               const char * /*address*/) {
+  GceAudioOutputStream* new_stream;
+  int rval;
+  {
+    LockGuard<Mutex> guard(lock_);
+    rval = GceAudioOutputStream::Open(
+        this, handle, devices, flags, config, next_stream_number_++,
+        &new_stream);
+    if (new_stream) {
+      output_list_.push_back(new_stream);
+    }
+  }
+  if (new_stream) {
+    SendStreamUpdate(new_stream->GetStreamDescriptor(
+        gce_audio_message::OPEN_OUTPUT_STREAM), MSG_DONTWAIT);
+  }
+  *stream_out = new_stream;
+  return rval;
+}
+
+void GceAudio::CloseOutputStream(audio_stream_out *stream) {
+  GceAudioOutputStream* astream = static_cast<GceAudioOutputStream*>(stream);
+  gce_audio_message close;
+  {
+    LockGuard<Mutex> guard(lock_);
+    output_list_.remove(astream);
+    close = astream->GetStreamDescriptor(
+        gce_audio_message::CLOSE_OUTPUT_STREAM);
+  }
+  SendStreamUpdate(close, MSG_DONTWAIT);
+  delete astream;
+}
+
+int GceAudio::Dump(int fd) const {
+  LockGuard<Mutex> guard(lock_);
+  dprintf(
+      fd,
+      "\nadev_dump:\n"
+      "\tmic_mute: %s\n"
+      "\tnum_outputs: %zu\n"
+      "\tnum_inputs: %zu\n\n",
+      mic_muted_ ? "true": "false",
+      output_list_.size(), input_map_.size());
+
+  for (std::list<GceAudioOutputStream*>::const_iterator it =
+           output_list_.begin();
+       it != output_list_.end(); ++it) {
+    (*it)->common.dump(&(*it)->common, fd);
+  }
+
+  for (input_map_t::const_iterator it = input_map_.begin();
+       it != input_map_.end(); ++it) {
+    (*it).second->common.dump(&(*it).second->common, fd);
+  }
+
+  return 0;
+}
+
+ssize_t GceAudio::SendMsg(const msghdr& msg, int /* flags */) {
+    intptr_t res = audio_data_rv_->data()->audio_queue.Writev(
+            audio_data_rv_,
+            msg.msg_iov,
+            msg.msg_iovlen,
+            true /* non_blocking */);
+
+    if (res < 0) {
+        ALOGV("GceAudio::%s: CircularPacketQueue::Write returned %" PRIiPTR,
+              __FUNCTION__,
+              res);
+    }
+
+    return static_cast<ssize_t>(res);
+}
+
+ssize_t GceAudio::SendStreamUpdate(
+    const gce_audio_message& stream_info, int flags) {
+  msghdr msg;
+  iovec msg_iov[1];
+  msg_iov[0].iov_base = const_cast<gce_audio_message*>(&stream_info);
+  msg_iov[0].iov_len = sizeof(gce_audio_message);
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  msg.msg_iov = msg_iov;
+  msg.msg_iovlen = arraysize(msg_iov);
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+  return SendMsg(msg, flags);
+}
+
+int GceAudio::SetVoiceVolume(float volume) {
+  D("GceAudio::%s: set voice volume %f", __FUNCTION__, volume);
+  voice_volume_ = volume;
+  return 0;
+}
+
+int GceAudio::SetMasterVolume(float volume) {
+  D("GceAudio::%s: set master volume %f", __FUNCTION__, volume);
+  master_volume_ = volume;
+  return 0;
+}
+
+int GceAudio::GetMasterVolume(float* volume) {
+  D("GceAudio::%s: get master volume %f", __FUNCTION__, master_volume_);
+  *volume = master_volume_;
+  return 0;
+}
+
+int GceAudio::SetMasterMute(bool muted) {
+  D("GceAudio::%s: set master muted %d", __FUNCTION__, muted);
+  master_muted_ = muted;
+  return 0;
+}
+
+int GceAudio::GetMasterMute(bool* muted) {
+  D("GceAudio::%s: get master muted %d", __FUNCTION__, master_muted_);
+  *muted = master_muted_;
+  return 0;
+}
+
+int GceAudio::SetMode(audio_mode_t mode) {
+  D("GceAudio::%s: new mode %d", __FUNCTION__, mode);
+  mode_ = mode;
+  return 0;
+}
+
+int GceAudio::Open(const hw_module_t* module, const char* name,
+                   hw_device_t** device) {
+  D("GceAudio::%s", __FUNCTION__);
+
+  if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+    ALOGE("GceAudio::%s: invalid module name %s (expected %s)",
+          __FUNCTION__, name, AUDIO_HARDWARE_INTERFACE);
+    return -EINVAL;
+  }
+
+  GceAudio* rval = new GceAudio;
+
+  rval->audio_data_rv_ = AudioDataRegionView::GetInstance();
+  rval->audio_worker_ = rval->audio_data_rv_->StartWorker();
+
+  rval->common.tag = HARDWARE_DEVICE_TAG;
+  rval->common.version = version_;
+  rval->common.module = const_cast<hw_module_t *>(module);
+  rval->common.close = cvd::thunk<hw_device_t, &GceAudio::Close>;
+
+#if !defined(AUDIO_DEVICE_API_VERSION_2_0)
+  // This HAL entry is supported only on AUDIO_DEVICE_API_VERSION_1_0.
+  // In fact, with version 2.0 the device numbers were orgainized in a
+  // way that makes the return value nonsense.
+  // Skipping the assignment is ok: the memset in the constructor already
+  // put a NULL here.
+  rval->get_supported_devices =
+      cvd::thunk<audio_hw_device, &GceAudio::GetSupportedDevices>;
+#endif
+  rval->init_check = cvd::thunk<audio_hw_device, &GceAudio::InitCheck>;
+
+  rval->set_voice_volume =
+      cvd::thunk<audio_hw_device, &GceAudio::SetVoiceVolume>;
+  rval->set_master_volume =
+      cvd::thunk<audio_hw_device, &GceAudio::SetMasterVolume>;
+  rval->get_master_volume =
+      cvd::thunk<audio_hw_device, &GceAudio::GetMasterVolume>;
+
+#if defined(AUDIO_DEVICE_API_VERSION_2_0)
+  rval->set_master_mute =
+      cvd::thunk<audio_hw_device, &GceAudio::SetMasterMute>;
+  rval->get_master_mute =
+      cvd::thunk<audio_hw_device, &GceAudio::GetMasterMute>;
+#endif
+
+  rval->set_mode = cvd::thunk<audio_hw_device, &GceAudio::SetMode>;
+  rval->set_mic_mute = cvd::thunk<audio_hw_device, &GceAudio::SetMicMute>;
+  rval->get_mic_mute = cvd::thunk<audio_hw_device, &GceAudio::GetMicMute>;
+
+  rval->set_parameters = cvd::thunk<audio_hw_device, &GceAudio::SetParameters>;
+  rval->get_parameters = cvd::thunk<audio_hw_device, &GceAudio::GetParameters>;
+
+  rval->get_input_buffer_size =
+      cvd::thunk<audio_hw_device, &GceAudio::GetInputBufferSize>;
+
+  rval->open_input_stream =
+      cvd::thunk<audio_hw_device, &GceAudio::OpenInputStreamCurrentHAL>;
+  rval->close_input_stream =
+      cvd::thunk<audio_hw_device, &GceAudio::CloseInputStream>;
+
+  rval->open_output_stream =
+      cvd::thunk<audio_hw_device, &GceAudio::OpenOutputStreamCurrentHAL>;
+  rval->close_output_stream =
+      cvd::thunk<audio_hw_device, &GceAudio::CloseOutputStream>;
+
+  rval->dump = cvd::thunk<audio_hw_device, &GceAudio::Dump>;
+
+  *device = &rval->common;
+  return 0;
+}
+
+int GceAudio::SetParameters(const char *kvpairs) {
+  ALOGE("GceAudio::%s: not implemented", __FUNCTION__);
+  if (kvpairs) D("GceAudio::%s: kvpairs %s", __FUNCTION__, kvpairs);
+  return 0;
+}
+
+
+char* GceAudio::GetParameters(const char *keys) const {
+  ALOGE("GceAudio::%s: not implemented", __FUNCTION__);
+  if (keys) D("GceAudio::%s: kvpairs %s", __FUNCTION__, keys);
+  return strdup("");
+}
+
+int GceAudio::SetStreamParameters(
+    struct audio_stream *stream, const char *kv_pairs) {
+  struct str_parms *parms = str_parms_create_str(kv_pairs);
+  if (!parms) {
+    return 0;
+  }
+  int sample_rate;
+  if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
+                        &sample_rate) >= 0) {
+    stream->set_sample_rate(stream, sample_rate);
+  }
+  int format;
+  if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_FORMAT,
+                        &format) >= 0) {
+    stream->set_format(stream, static_cast<audio_format_t>(format));
+  }
+  int routing;
+  if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                        &routing) >= 0) {
+    stream->set_device(stream, static_cast<audio_devices_t>(routing));
+  }
+  if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
+                        &routing) >= 0) {
+    stream->set_device(stream, static_cast<audio_devices_t>(routing));
+  }
+  str_parms_destroy(parms);
+  return 0;
+}
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio.h b/guest/hals/audio/legacy/vsoc_audio.h
new file mode 100644
index 0000000..7b96878
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio.h
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <list>
+#include <map>
+#include <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "common/vsoc/lib/audio_data_region_view.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+#include "guest/hals/audio/legacy/vsoc_audio_input_stream.h"
+
+namespace cvd {
+
+class GceAudioInputStream;
+class GceAudioOutputStream;
+
+class GceAudio : public audio_hw_device {
+ public:
+  // This common code manipulates the parameters of input and output streams.
+  static int SetStreamParameters(struct audio_stream *, const char *);
+
+  ~GceAudio();
+
+  // Non-HAL methods that are part of the GCE implementation.
+  // Most of these are used by the input and output streams.
+
+  // Returns true if the microphone is muted. Used by input streams.
+  bool IsMicrophoneMuted() {
+    cvd::LockGuard<cvd::Mutex> guard(lock_);
+    return mic_muted_;
+  }
+
+  // Send a message to the connected streamer.
+  // Returns:
+  //   0 if there is no streamer.
+  //   >0 if the message was sent.
+  //   -1 if there was an error.
+  ssize_t SendMsg(const msghdr&, int flags);
+
+  // Sends a stream update to the connected streamer.
+  // Stream updates have no frames. Use SendMsg if the message has frames.
+  //   0 if there is no streamer.
+  //   >0 if the message was sent.
+  //   -1 if there was an error.
+  ssize_t SendStreamUpdate(
+      const gce_audio_message& stream_info, int flags);
+
+  // Callbacks for the Android audio_module HAL interface.
+  // Most of the comments below are copied from
+  // libhardware/include/hardware/audio.h
+  //
+  // Where the is a conflict the comments there apply.
+  // By default these methods return 0 on success -<errno> for failure.
+
+  // Opens the device.
+  static int Open(const hw_module_t* module, const char* name,
+                  hw_device_t** device);
+
+  // Closes the device, closing any open input streams and output streams.
+  int Close();
+
+  // Closes the input stream, throwing away any data in the buffer.
+  void CloseInputStream(audio_stream_in* stream);
+
+  // Closes the output stream without waiting for the buffer to clear.
+  void CloseOutputStream(audio_stream_out* stream);
+
+  // Creates an audio patch between several source and sink ports.
+  // The handle is allocated by the HAL and should be unique for this
+  // audio HAL module.
+  // TODO(ghartman): Implement this as part of HAL 3.0
+  //int CreateAudioPatch(unsigned int num_sources,
+  //                     const struct audio_port_config *sources,
+  //                     unsigned int num_sinks,
+  //                     const struct audio_port_config *sinks,
+  //                     audio_patch_handle_t *handle);
+
+  // dumps the state of the audio hardware to the given fd.
+  // This information can be retrieved using the dumpsys utility.
+  int Dump(int fd) const;
+
+  // Fills the list of supported attributes for a given audio port.
+  // As input, "port" contains the information (type, role, address etc...)
+  // needed by the HAL to identify the port.
+  // As output, "port" contains possible attributes (sampling rates, formats,
+  // channel masks, gain controllers...) for this port.
+  // TODO(ghartman): Implement this as part of HAL 3.0
+  // int GetAudioPort(struct audio_port *port);
+
+  // Sets audio port configuration
+  // TODO(ghartman): Implement this as part of HAL 3.0
+  // int SetAudioPortConfig(const struct audio_port_config *config);
+
+  size_t GetInputBufferSize(const audio_config*) const;
+
+  // Gets the current master volume value for the HAL, if the HAL supports
+  // master volume control.  AudioFlinger will query this value from the
+  // primary audio HAL when the service starts and use the value for setting
+  // the initial master volume across all HALs.  HALs which do not support
+  // this method may leave it set to NULL.
+  int GetMasterVolume(float* /*volume*/);
+
+  // Get the current master mute status for the HAL, if the HAL supports
+  // master mute control.  AudioFlinger will query this value from the primary
+  // audio HAL when the service starts and use the value for setting the
+  // initial master mute across all HALs.  HALs which do not support this
+  // method may leave it set to NULL.
+  int GetMasterMute(bool* muted);
+
+  // Gets the audio mute status for the microphone.
+  int GetMicMute(bool* state) const;
+
+  // Retrieves the global audio parameters.
+  // TODO(ghartman): Implement this.
+  char* GetParameters(const char* keys) const;
+
+  // Enumerates what devices are supported by each audio_hw_device
+  // implementation.
+  // Return value is a bitmask of 1 or more values of audio_devices_t
+  // used by audio flinger.
+  // NOTE: audio HAL implementations starting with
+  // AUDIO_DEVICE_API_VERSION_2_0 do not implement this function.
+  // AUDIO_DEVICE_API_VERSION_2_0 was the current version as of JB-MR1
+  // All supported devices should be listed in audio_policy.conf
+  // file and the audio policy manager must choose the appropriate
+  // audio module based on information in this file.
+  uint32_t GetSupportedDevices() const;
+
+  // Checks to see if the audio hardware interface has been initialized.
+  // Always returns 0 to indicate success, but -ENODEV is also allowed to
+  // indicate failure.
+  int InitCheck() const;
+
+  // Creates an additional hardware input stream.
+  // Additional parameters were added in the 3.0 version of the HAL.
+  // These defaults make it easier to implement a cross-branch device.
+  int OpenInputStream(
+      audio_io_handle_t handle,
+      audio_devices_t devices, audio_config *config,
+      audio_stream_in **stream_in,
+      audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+      const char* address = 0,
+      audio_source_t source = AUDIO_SOURCE_DEFAULT);
+
+  // Creates an additional output stream.
+  // The "address" parameter qualifies the "devices" audio device type if
+  // needed. On GCE we ignore it for now because we simulate a single SoC
+  // hw devices.
+  //
+  // The format format depends on the device type:
+  //   Bluetooth devices use the MAC address of the device in the form
+  //     "00:11:22:AA:BB:CC"
+  // USB devices use the ALSA card and device numbers in the form
+  //     "card=X;device=Y"
+  // Other devices may use a number or any other string.
+  int OpenOutputStream(
+      audio_io_handle_t handle,
+      audio_devices_t devices, audio_output_flags_t flags,
+      audio_config* config, audio_stream_out** stream_out,
+      const char* address = 0);
+
+  // Releases an audio patch.
+  // TODO(ghartman): Implement this as part of HAL 3.0
+  //int ReleaseAudioPatch(audio_patch_handle_t handle);
+
+  // Sets the audio mute status for all audio activities.  If any value other
+  // than 0 is returned, the software mixer will emulate this capability.
+  // The GCE implementation always returns 0.
+  int SetMasterMute(bool muted);
+
+  // Sets the audio volume for all audio activities other than voice call.
+  // Range between 0.0 and 1.0. If any value other than 0 is returned,
+  // the software mixer will emulate this capability.
+  // The GCE implementation always returns 0.
+  int SetMasterVolume(float volume);
+
+  // Sets the audio mute status for the microphone.
+  int SetMicMute(bool state);
+
+  // set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode
+  // is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is
+  // playing, and AUDIO_MODE_IN_CALL when a call is in progress.
+  int SetMode(audio_mode_t mode);
+
+  // Sets the global audio parameters.
+  // TODO(ghartman): Create a sensible implementation.
+  int SetParameters(const char* kvpairs);
+
+  // Sets the audio volume of a voice call. Range is between 0.0 and 1.0
+  int SetVoiceVolume(float volume);
+
+
+ private:
+  // HAL 3.0 modifies the signatures of OpenInputStream and OpenOutputStream.
+  // We don't want to fork the implementation, and we don't want #ifdefs all
+  // over the code. The current implementation defines OpenInputStream and
+  // OpenOutputStream with default values for the paramteres that were added,
+  // and then generates a HAL-specific wrapper to be used in the function
+  // table.
+#if defined(AUDIO_DEVICE_API_VERSION_3_0)
+  typedef int OpenInputStreamHAL_t(
+      audio_io_handle_t, audio_devices_t, audio_config*, audio_stream_in**,
+      audio_input_flags_t, const char*, audio_source_t);
+
+  int OpenInputStreamCurrentHAL(
+      audio_io_handle_t a, audio_devices_t b, audio_config* c,
+      audio_stream_in** d, audio_input_flags_t e, const char* f,
+      audio_source_t g) {
+    return OpenInputStream(a, b, c, d, e, f, g);
+  }
+
+  typedef int OpenOutputStreamHAL_t(
+      audio_io_handle_t, audio_devices_t, audio_output_flags_t,
+      audio_config*, audio_stream_out**,
+      const char*);
+
+  int OpenOutputStreamCurrentHAL(
+      audio_io_handle_t a, audio_devices_t b, audio_output_flags_t c,
+      audio_config* d, audio_stream_out** e,
+      const char* f) {
+    return OpenOutputStream(a, b, c, d, e, f);
+  }
+#else
+  typedef int OpenInputStreamHAL_t(
+      audio_io_handle_t, audio_devices_t, audio_config*, audio_stream_in**);
+
+  int OpenInputStreamCurrentHAL(
+      audio_io_handle_t a, audio_devices_t b, audio_config* c,
+      audio_stream_in** d) {
+    return OpenInputStream(a, b, c, d);
+  }
+
+  typedef int OpenOutputStreamHAL_t(
+      audio_io_handle_t, audio_devices_t, audio_output_flags_t,
+      audio_config*, audio_stream_out**);
+
+  int OpenOutputStreamCurrentHAL(
+      audio_io_handle_t a, audio_devices_t b, audio_output_flags_t c,
+      audio_config* d, audio_stream_out** e) {
+    return OpenOutputStream(a, b, c, d, e);
+  }
+#endif
+
+  //TODO(ghartman): Update this when we support 3.0.
+#if defined(AUDIO_DEVICE_API_VERSION_2_0)
+  static const unsigned int version_ = AUDIO_DEVICE_API_VERSION_2_0;
+#else
+  static const unsigned int version_ = AUDIO_DEVICE_API_VERSION_1_0;
+#endif
+
+  using AudioDataRegionView = vsoc::audio_data::AudioDataRegionView;
+  AudioDataRegionView* audio_data_rv_{};
+  std::unique_ptr<vsoc::RegionWorker> audio_worker_;
+
+  // Lock to protect the data below.
+  mutable cvd::Mutex lock_;
+  // State that is managed at the device level.
+  float voice_volume_;
+  float master_volume_;
+  bool master_muted_;
+  bool mic_muted_;
+  audio_mode_t mode_;
+  // There can be multiple input and output streams. This field is used
+  // to assign each one a unique identifier.
+  // TODO(ghartman): This can wrap after 2^32 streams. Ideally we should check
+  // the output_list_ to ensure that the stream number hasn't been assigned.
+  // However, streams don't really appear and disapper that often.
+  // We use the same counter for both input and output streams to make things
+  // a little easier on the client.
+  uint32_t next_stream_number_;
+  // List of the currently active output streams.
+  // Used to clean things up Close()
+  std::list<GceAudioOutputStream *> output_list_;
+  // List of the currently active input streams.
+  // Used to clean things up Close()
+  typedef std::map<uint32_t, GceAudioInputStream *> input_map_t;
+  input_map_t input_map_;
+
+  GceAudio() :
+      audio_hw_device(),
+      voice_volume_(0.0),
+      master_volume_(0.0),
+      master_muted_(false),
+      mic_muted_(false),
+      mode_(AUDIO_MODE_NORMAL),
+      next_stream_number_(1) { }
+};
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp b/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
new file mode 100644
index 0000000..bb9e196
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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 <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstdint>
+#include <cinttypes>
+
+extern "C"{
+#include <cutils/str_parms.h>
+}
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/threads/thunkers.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+#include "guest/hals/audio/legacy/vsoc_audio.h"
+#include "guest/hals/audio/legacy/vsoc_audio_input_stream.h"
+
+namespace cvd {
+
+#if defined(AUDIO_DEVICE_API_VERSION_3_0)
+static inline size_t GceAudioFrameSize(const audio_stream_in* s) {
+  return audio_stream_in_frame_size(s);
+}
+#elif defined(AUDIO_DEVICE_API_VERSION_2_0)
+static inline size_t GceAudioFrameSize(const audio_stream_in* s) {
+
+  return audio_stream_frame_size(&s->common);
+}
+#else
+static inline size_t GceAudioFrameSize(audio_stream_in* s) {
+
+  return audio_stream_frame_size(&s->common);
+}
+#endif
+
+GceAudioInputStream::GceAudioInputStream(
+    cvd::GceAudio* dev, audio_devices_t devices, const audio_config& config)
+    : audio_stream_in(),
+      dev_(dev),
+      config_(config),
+      gain_(0.0),
+      device_(devices) {
+  common.get_sample_rate =
+      cvd::thunk<audio_stream, &GceAudioInputStream::GetSampleRate>;
+  common.set_sample_rate =
+      cvd::thunk<audio_stream, &GceAudioInputStream::SetSampleRate>;
+  common.get_buffer_size =
+      cvd::thunk<audio_stream, &GceAudioInputStream::GetBufferSize>;
+  common.get_channels =
+      cvd::thunk<audio_stream, &GceAudioInputStream::GetChannels>;
+  common.get_device = cvd::thunk<audio_stream, &GceAudioInputStream::GetDevice>;
+  common.set_device = cvd::thunk<audio_stream, &GceAudioInputStream::SetDevice>;
+  common.get_format = cvd::thunk<audio_stream, &GceAudioInputStream::GetFormat>;
+  common.set_format = cvd::thunk<audio_stream, &GceAudioInputStream::SetFormat>;
+  common.standby = cvd::thunk<audio_stream, &GceAudioInputStream::Standby>;
+  common.dump = cvd::thunk<audio_stream, &GceAudioInputStream::Dump>;
+  common.set_parameters = GceAudio::SetStreamParameters;
+  common.get_parameters =
+      cvd::thunk<audio_stream, &GceAudioInputStream::GetParameters>;
+  common.add_audio_effect =
+      cvd::thunk<audio_stream, &GceAudioInputStream::AddAudioEffect>;
+  common.remove_audio_effect = cvd::thunk<audio_stream,
+    &GceAudioInputStream::RemoveAudioEffect>;
+  set_gain = cvd::thunk<audio_stream_in, &GceAudioInputStream::SetGain>;
+  read = cvd::thunk<audio_stream_in,
+    &GceAudioInputStream::Read>;
+  get_input_frames_lost = cvd::thunk<audio_stream_in,
+    &GceAudioInputStream::GetInputFramesLost>;
+  frame_size_ = GceAudioFrameSize(this);
+  buffer_model_.reset(
+      new SimulatedInputBuffer(config_.sample_rate, GetBufferSize() /
+                               frame_size_));
+  reported_lost_frames_ = 0;
+}
+
+gce_audio_message GceAudioInputStream::GetStreamDescriptor(
+    uint32_t stream_number, gce_audio_message::message_t event) {
+  gce_audio_message rval;
+  rval.message_type = event;
+  rval.stream_number = stream_number;
+  rval.frame_num = buffer_model_->GetCurrentItemNum();
+  rval.time_presented =
+      buffer_model_->GetLastUpdatedTime().SinceEpoch().GetTS();
+  rval.frame_rate = config_.sample_rate;
+  rval.channel_mask = config_.channel_mask;
+  rval.format = config_.format;
+  rval.frame_size = frame_size_;
+  return rval;
+}
+
+int GceAudioInputStream::Open(GceAudio* dev,
+                              audio_io_handle_t /*handle*/,
+                              audio_devices_t devices,
+                              const audio_config& config,
+                              GceAudioInputStream** stream_in) {
+  D("GceAudioInputStream::%s", __FUNCTION__);
+  *stream_in = new GceAudioInputStream(dev, devices, config);
+  return 0;
+}
+
+int GceAudioInputStream::SetFormat(audio_format_t format) {
+  config_.format = format;
+  frame_size_ = GceAudioFrameSize(this);
+  return 0;
+}
+
+int GceAudioInputStream::Dump(int fd) const {
+  D("GceAudioInputStream::%s", __FUNCTION__);
+  dprintf(
+      fd,
+      "\tInputSteam Dump:\n"
+      "\t\tsample rate: %u\n"
+      "\t\tbuffer size: %zu\n"
+      "\t\tchannel mask: %08x\n"
+      "\t\tformat: %d\n"
+      "\t\tdevice: %08x\n"
+      "\t\taudio dev: %p\n\n",
+      GetSampleRate(), GetBufferSize(),
+      GetChannels(), GetFormat(), device_, dev_);
+  return 0;
+}
+
+int GceAudioInputStream::SetSampleRate(uint32_t sample_rate) {
+  if (sample_rate != config_.sample_rate) {
+    config_.sample_rate = sample_rate;
+    buffer_model_.reset(
+        new SimulatedInputBuffer(sample_rate, GetBufferSize() / frame_size_));
+    reported_lost_frames_ = 0;
+  }
+  return 0;
+}
+
+char* GceAudioInputStream::GetParameters(const char* keys) const {
+  D("GceAudioInputStream::%s", __FUNCTION__);
+  if (keys) D("GceAudioInputStream::%s keys %s", __FUNCTION__, keys);
+
+  str_parms* query = str_parms_create_str(keys);
+  str_parms* reply = str_parms_create();
+
+  char value[256];
+  int ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING,
+                              value, sizeof(value));
+  char* str;
+  if (ret >= 0) {
+    str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, device_);
+    str = strdup(str_parms_to_str(reply));
+  } else {
+    str = strdup(keys);
+  }
+  str_parms_destroy(query);
+  str_parms_destroy(reply);
+  return str;
+}
+
+
+ssize_t GceAudioInputStream::Read(void* buffer, size_t bytes) {
+  int64_t available = buffer_model_->RemoveFromInputBuffer(
+      bytes / frame_size_, false) * frame_size_;
+  ssize_t rval = available;
+  if ((rval != available) || (rval < 0)) {
+    ALOGE("GceAudioInputStream:%s got bad value from "
+          "RemoveFromInputBuffer %" PRId64, __FUNCTION__, available);
+    return -1;
+  }
+  memset(buffer, 0, rval);
+  return rval;
+}
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio_input_stream.h b/guest/hals/audio/legacy/vsoc_audio_input_stream.h
new file mode 100644
index 0000000..222194c
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio_input_stream.h
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <memory>
+
+#include "common/libs/utils/simulated_buffer.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+
+namespace cvd {
+
+namespace {
+static const int IN_BUFFER_BYTES = 4096;
+}
+
+class GceAudio;
+
+// Defines static callback functions for generic_stream_in HAL interface.
+class GceAudioInputStream : public audio_stream_in {
+ public:
+  // These methods are internal to the GCE audio implementation.
+  // Factory for new input streams.
+  static int Open(
+      GceAudio* dev, audio_io_handle_t handle,
+      audio_devices_t devices, const audio_config& config,
+      GceAudioInputStream** stream_in);
+
+  // Gets a description of this stream
+  gce_audio_message GetStreamDescriptor(
+      uint32_t stream_number, gce_audio_message::message_t event);
+
+  // audio_stream_in implementation. These definitions follow the ones
+  // in hardware/libhardware/include/hardware/audio.h
+
+  // Returns the sampling rate in Hz - eg. 44100.
+  uint32_t GetSampleRate() const { return config_.sample_rate; }
+
+  // Sets the sample rate
+  // no direct calls from JB and later, but called indirectly from
+  // GceAudio::SetStreamParamters when it finds
+  // AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+  int SetSampleRate(uint32_t rate);
+
+  // Returns the size of input/output buffer in bytes for this stream - eg.
+  // 4800.
+  // It should be a multiple of the frame size.  See also get_input_buffer_size
+  size_t GetBufferSize() const {
+    return IN_BUFFER_BYTES;
+  }
+
+  // Returns the channel mask -
+  //   e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+  audio_channel_mask_t GetChannels() const {
+    return config_.channel_mask;
+  }
+
+  // Returns the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+  audio_format_t GetFormat() const {
+    return config_.format;
+  }
+
+  // Sets the audio format
+  // no direct calls from JB and later, but called indirectly from
+  // GceAudio::SetStreamParamters when it finds
+  //   AUDIO_PARAMETER_STREAM_FORMAT
+  int SetFormat(audio_format_t format);
+
+  // Puts the audio hardware input/output into standby mode.
+  // Driver should exit from standby mode at the next I/O operation.
+  // Returns 0 on success and <0 on failure.
+  int Standby() { return 0; }
+
+  // Dumps the state of the audio input/output device
+  int Dump(int fd) const;
+
+  // Returns the set of device(s) which this stream is connected to
+  audio_devices_t GetDevice() const {
+    return device_;
+  }
+
+  // Sets the device this stream is connected to.
+  // no direct calls from JB and later, but called indirectly from
+  // GceAudio::SetStreamParamters when it finds
+  //   AUDIO_PARAMETER_STREAM_ROUTING for both input and output.
+  //   AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by
+  //                                       input streams only.
+  int SetDevice(audio_devices_t device) { device_ = device; return 0; }
+
+  // sets audio stream parameters. The function accepts a list of
+  // parameter key value pairs in the form: key1=value1;key2=value2;...
+  //
+  // Some keys are reserved for standard parameters (See AudioParameter class)
+  //
+  // If the implementation does not accept a parameter change while
+  // the output is active but the parameter is acceptable otherwise, it must
+  // return -ENOSYS.
+  // The audio flinger will put the stream in standby and then change the
+  // parameter value.
+  // Uses GceAudio::SetStreamParameters
+
+  // Returns a pointer to a heap allocated string. The caller is responsible
+  // for freeing the memory for it using free().
+  char* GetParameters(const char* keys) const;
+
+  int AddAudioEffect(effect_handle_t /*effect*/) const {
+    return 0;
+  }
+
+  int RemoveAudioEffect(effect_handle_t /*effect*/) const {
+    return 0;
+  }
+
+  // Input stream specific methods
+
+  // Sets the input gain for the audio driver. This method is for
+  // for future use as of M.
+  int SetGain(float gain) {
+    gain_ = gain;
+    return 0;
+  }
+
+  // Reads audio buffer in from audio driver. Returns number of bytes read, or
+  // a negative android::status_t. If at least one frame was read prior to the error,
+  //  read should return that byte count and then return an error in the
+  // subsequent call.
+  ssize_t Read(void* buffer, size_t bytes);
+
+  // Return the amount of input frames lost in the audio driver since the
+  // last call of this function.
+  // Audio driver is expected to reset the value to 0 and restart counting
+  // upon returning the current value by this function call.
+  // Such loss typically occurs when the user space process is blocked
+  // longer than the capacity of audio driver buffers.
+  //
+  // Unit: the number of input audio frames
+  uint32_t GetInputFramesLost() {
+    int64_t cur_lost_frames = buffer_model_->GetLostInputItems();
+    uint32_t rval = cur_lost_frames - reported_lost_frames_;
+    reported_lost_frames_ = cur_lost_frames;
+    return rval;
+  }
+
+ private:
+  GceAudioInputStream(cvd::GceAudio* dev, audio_devices_t devices,
+                      const audio_config& config);
+  std::unique_ptr<SimulatedInputBuffer> buffer_model_;
+  cvd::GceAudio *dev_;
+  audio_config config_;
+  float gain_;
+  audio_devices_t device_;
+  size_t frame_size_;
+  int64_t reported_lost_frames_;
+};
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp b/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
new file mode 100644
index 0000000..571f95e
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
@@ -0,0 +1,322 @@
+/*
+ * 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 <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory>
+
+#include <cutils/sockets.h>
+extern "C"{
+#include <cutils/str_parms.h>
+}
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/threads/thunkers.h"
+#include "common/libs/time/monotonic_time.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+#include "guest/hals/audio/legacy/vsoc_audio.h"
+#include "guest/hals/audio/legacy/vsoc_audio_output_stream.h"
+#include "guest/libs/remoter/remoter_framework_pkt.h"
+
+#if defined(AUDIO_DEVICE_API_VERSION_3_0)
+static inline size_t GceAudioFrameSize(const audio_stream_out* s) {
+  return audio_stream_out_frame_size(s);
+}
+#elif defined(AUDIO_DEVICE_API_VERSION_2_0)
+static inline size_t GceAudioFrameSize(const audio_stream_out* s) {
+
+  return audio_stream_frame_size(&s->common);
+}
+#else
+static inline size_t GceAudioFrameSize(audio_stream_out* s) {
+
+  return audio_stream_frame_size(&s->common);
+}
+#endif
+
+namespace cvd {
+
+const size_t GceAudioOutputStream::kOutBufferSize;
+const size_t GceAudioOutputStream::kOutLatency;
+
+GceAudioOutputStream::GceAudioOutputStream(GceAudio* dev) :
+    audio_stream_out(),
+    dev_(dev),
+    device_(AUDIO_DEVICE_OUT_DEFAULT),
+    frame_count_(0),
+    left_volume_(0.0),
+    right_volume_(0.0) { }
+
+int GceAudioOutputStream::Dump(int fd) const {
+  D("GceAudioOutputStream::%s", __FUNCTION__);
+  dprintf(
+      fd,
+      "\tout_dump:\n"
+      "\t\tsample rate: %u\n"
+      "\t\tbuffer size: %zu\n"
+      "\t\tchannel mask: %08x\n"
+      "\t\tformat: %d\n"
+      "\t\tdevice: %08x\n"
+      "\t\taudio dev: %p\n\n",
+      GetSampleRate(),
+      GetBufferSize(),
+      GetChannels(),
+      GetFormat(),
+      device_,
+      dev_);
+  return 0;
+}
+
+int GceAudioOutputStream::GetNextWriteTimestamp(int64_t* nstime) const {
+  *nstime = cvd::time::Nanoseconds(
+      buffer_->GetNextOutputBufferItemTime().SinceEpoch()).count();
+  return 0;
+}
+
+namespace {
+struct StrParmsDestroyer {
+  void operator()(str_parms* parms) const {
+    if (parms) {
+      str_parms_destroy(parms);
+    }
+  }
+};
+
+typedef std::unique_ptr<str_parms, StrParmsDestroyer> StrParmsPtr;
+}
+
+int GceAudioOutputStream::SetParameters(const char* kv_pairs) {
+  int err = 0;
+  StrParmsPtr parms(str_parms_create_str(kv_pairs));
+  {
+    int fmt = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_FORMAT, &fmt)
+        == 0) {
+      SetFormat(static_cast<audio_format_t>(fmt));
+    }
+  }
+  {
+    int sample_rate = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
+                          &sample_rate) == 0) {
+      SetSampleRate(static_cast<uint32_t>(sample_rate));
+    }
+  }
+  {
+    int routing = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_ROUTING,
+                          &routing) == 0) {
+      device_ = static_cast<uint32_t>(routing);
+    }
+  }
+  {
+    int channels = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_CHANNELS,
+                          &channels) == 0) {
+      message_header_.channel_mask = static_cast<audio_channel_mask_t>(channels);
+    }
+  }
+  {
+    int frame_count = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_FRAME_COUNT,
+                          &frame_count) == 0) {
+      frame_count_ = static_cast<size_t>(frame_count);
+    }
+  }
+  {
+    int input_source = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
+                          &input_source) == 0){
+      ALOGE("GceAudioOutputStream::%s AUDIO_PARAMETER_STREAM_INPUT_SOURCE"
+            " passed to an output stream", __FUNCTION__);
+      err = -EINVAL;
+    }
+  }
+  return err;
+}
+
+void GceAudioOutputStream::AddIntIfKeyPresent(
+    /*const */ str_parms* query, str_parms* reply, const char* key, int value) {
+  if (str_parms_get_str(query, key, NULL, 0) >= 0) {
+    str_parms_add_int(reply, key, value);
+  }
+}
+
+
+char* GceAudioOutputStream::GetParameters(const char* keys) const {
+  D("GceAudioOutputStream::%s", __FUNCTION__);
+  if (keys) D("%s keys %s", __FUNCTION__, keys);
+
+  StrParmsPtr query(str_parms_create_str(keys));
+  StrParmsPtr reply(str_parms_create());
+
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_FORMAT,
+                     static_cast<int>(GetFormat()));
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
+                     static_cast<int>(GetSampleRate()));
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_ROUTING,
+                     static_cast<int>(device_));
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_CHANNELS,
+                     static_cast<int>(message_header_.channel_mask));
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_FRAME_COUNT,
+                     static_cast<int>(frame_count_));
+
+  char *str = str_parms_to_str(reply.get());
+  return str;
+}
+
+int GceAudioOutputStream::GetRenderPosition(uint32_t* dsp_frames) const {
+  *dsp_frames = buffer_->GetCurrentItemNum();
+  return 0;
+}
+
+ssize_t GceAudioOutputStream::Write(const void* buffer, size_t length) {
+  // We're always the blocking case for now.
+  static const bool blocking = true;
+  message_header_.frame_size = frame_size_;
+  frame_count_ += message_header_.num_frames_presented = length / frame_size_;
+  message_header_.message_type = gce_audio_message::DATA_SAMPLES;
+  // First do a nonblocking add
+  int64_t frames_accepted_without_blocking = buffer_->AddToOutputBuffer(
+      message_header_.num_frames_presented, false);
+  // This seems backward, but adding the items to the buffer first
+  // allows us to calculate the right frame number in the case of underflow.
+  message_header_.frame_num =
+      buffer_->GetNextOutputBufferItemNum() - frames_accepted_without_blocking;
+  message_header_.time_presented =
+      buffer_->GetLastUpdatedTime().SinceEpoch().GetTS();
+  // We want to send the message before blocking. If we're in blocking mode
+  // we will accept all of the frames.
+  if (blocking) {
+    message_header_.num_frames_accepted =
+        message_header_.num_frames_presented;
+  } else {
+    message_header_.num_frames_accepted = frames_accepted_without_blocking;
+  }
+  // Never exceed the maximum packet size, as defined by the interface.
+  // Clip off any frames that we can't transmit and increment the clipped
+  // count.
+  size_t transmitted_frame_size = length;
+  if (length > gce_audio_message::kMaxAudioFrameLen) {
+    transmitted_frame_size = gce_audio_message::kMaxAudioFrameLen;
+    message_header_.num_packets_shortened++;
+  }
+  message_header_.total_size =
+      sizeof(message_header_) + transmitted_frame_size;
+  // Now send the message. Do not block if the receiver isn't ready
+  // If this is a blocking write we will block after we have attempted to
+  // send the data to the receiver.
+  msghdr msg;
+  iovec msg_iov[2];
+  // We need a cast here because iov_base is defined non-const to support
+  // recvmsg et.al.
+  // There is no danger here:sendmsg does not write to the buffer.
+  msg_iov[0].iov_base = &message_header_;
+  msg_iov[0].iov_len = sizeof(message_header_);
+  msg_iov[1].iov_base = const_cast<void*>(buffer);
+  msg_iov[1].iov_len = transmitted_frame_size;
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  msg.msg_iov = msg_iov;
+  msg.msg_iovlen = arraysize(msg_iov);
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+  if (dev_->SendMsg(msg, MSG_DONTWAIT) < 0) {
+    message_header_.num_packets_dropped++;
+  }
+  if (!blocking) {
+    return frames_accepted_without_blocking * frame_size_;
+  }
+  if ((message_header_.num_frames_presented) >
+      static_cast<size_t>(frames_accepted_without_blocking)) {
+    buffer_->AddToOutputBuffer(
+        message_header_.num_frames_presented -
+        frames_accepted_without_blocking, true);
+  }
+  return message_header_.num_frames_presented * frame_size_;
+}
+
+int GceAudioOutputStream::Open(
+    GceAudio* dev, audio_io_handle_t /*handle*/,
+    audio_devices_t devices, audio_output_flags_t /*flags*/,
+    audio_config* config, uint32_t stream_number,
+    GceAudioOutputStream** stream_out) {
+  D("GceAudioOutputStream::%s", __FUNCTION__);
+  *stream_out = NULL;
+  // Deleted by Close(); UniquePtr holds until end of Open().
+  std::unique_ptr<GceAudioOutputStream> out(
+      new GceAudioOutputStream(dev));
+  out->message_header_.stream_number = stream_number;
+  out->message_header_.format = config->format;
+  out->message_header_.channel_mask = config->channel_mask;
+  out->message_header_.frame_rate = config->sample_rate;
+  out->frame_count_ = config->frame_count;
+  out->common.get_sample_rate =
+      cvd::thunk<audio_stream, &GceAudioOutputStream::GetSampleRate>;
+  out->common.set_sample_rate =
+      cvd::thunk<audio_stream, &GceAudioOutputStream::SetSampleRate>;
+  out->common.get_buffer_size =
+      cvd::thunk<audio_stream, &GceAudioOutputStream::GetBufferSize>;
+  out->common.get_channels =
+      cvd::thunk<audio_stream, &GceAudioOutputStream::GetChannels>;
+  out->common.get_format = cvd::thunk<audio_stream, &GceAudioOutputStream::GetFormat>;
+  out->common.set_format = cvd::thunk<audio_stream, &GceAudioOutputStream::SetFormat>;
+  out->common.standby = cvd::thunk<audio_stream, &GceAudioOutputStream::Standby>;
+  out->common.dump = cvd::thunk<audio_stream, &GceAudioOutputStream::Dump>;
+  out->common.get_device = cvd::thunk<audio_stream, &GceAudioOutputStream::GetDevice>;
+  out->common.set_device = cvd::thunk<audio_stream, &GceAudioOutputStream::SetDevice>;
+  out->common.set_parameters =
+      cvd::thunk<audio_stream, &GceAudioOutputStream::SetParameters>;
+  out->common.get_parameters =
+      cvd::thunk<audio_stream, &GceAudioOutputStream::GetParameters>;
+  out->common.add_audio_effect =
+      cvd::thunk<audio_stream, &GceAudioOutputStream::AddAudioEffect>;
+  out->common.remove_audio_effect =
+      cvd::thunk<audio_stream, &GceAudioOutputStream::RemoveAudioEffect>;
+
+  out->get_latency =
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetLatency>;
+  out->set_volume =
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::SetVolume>;
+  out->write =
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::Write>;
+  out->get_render_position =
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetRenderPosition>;
+  out->get_next_write_timestamp =
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetNextWriteTimestamp>;
+  out->device_ = devices;
+  out->frame_size_ = GceAudioFrameSize(out.get());
+
+  int64_t item_capacity =
+      out->frame_size_  == 0 ? 0 : out->GetBufferSize() / out->frame_size_;
+  if (item_capacity == 0) {
+    ALOGE("Attempt to create GceAudioOutputStream with frame_size_ of 0.");
+    return -EINVAL;
+  }
+  out->buffer_.reset(
+      new SimulatedOutputBuffer(
+          config->sample_rate, item_capacity));
+  *stream_out = out.release();
+  return 0;
+}
+
+}  // namespace cvd
diff --git a/guest/hals/audio/legacy/vsoc_audio_output_stream.h b/guest/hals/audio/legacy/vsoc_audio_output_stream.h
new file mode 100644
index 0000000..70ced6e
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio_output_stream.h
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <memory>
+
+#include "common/libs/utils/simulated_buffer.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+
+namespace cvd {
+
+// Defines static callback functions for the audio_stream and audio_stream_out
+// interfaces in  libhardware/include/hardware/audio.h
+//
+// Where the is a conflict the comments there apply.
+// By default these methods return 0 on success -<errno> for failure.
+class GceAudioOutputStream : public audio_stream_out {
+ public:
+  // Factory method for a new output stream.
+  static int Open(GceAudio* dev, audio_io_handle_t handle,
+                  audio_devices_t devices, audio_output_flags_t flags,
+                  audio_config* config, uint32_t stream_number,
+                  GceAudioOutputStream** stream_out);
+
+  gce_audio_message GetStreamDescriptor(
+      gce_audio_message::message_t message_type) const {
+    gce_audio_message rval = message_header_;
+    rval.total_size = sizeof(rval);
+    rval.header_size = sizeof(rval);
+    rval.message_type = message_type;
+    rval.num_frames_presented = 0;
+    rval.num_frames_accepted = 0;
+    return rval;
+  }
+
+  // Method from audio_stream, listed in order of appearance.
+  // TODO(ghartman): Consider moving these if they could be shared with
+  // gce_audio_input_stream.
+
+
+  // Returns the sampling rate in Hz - eg. 44100.
+  uint32_t GetSampleRate() const {
+    return message_header_.frame_rate;
+  }
+
+  // Sets the sample rate
+  //   AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+  int SetSampleRate(uint32_t sample_rate) {
+    if (sample_rate != message_header_.frame_rate) {
+      message_header_.frame_rate = sample_rate;
+      // TODO(ghartman): The output buffer should be quantized at about 192
+      // bytes for better fidelity. Do this by passing
+      // frame_rate * frame_size / 192 and then rescaling the outputs.
+      // Or we could always create a quantized wrapper of the buffer...
+      buffer_.reset(
+          new SimulatedOutputBuffer(
+              sample_rate, GetBufferSize() / frame_size_));
+    }
+    return 0;
+  }
+
+  // Returns the size of input/output buffer in bytes for this stream.
+  // eg. 4800.
+  // It should be a multiple of the frame size.  See also GetInputBufferSize.
+  size_t GetBufferSize() const {
+    return kOutBufferSize;
+  }
+
+  // Returns the channel mask -
+  //  e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+  audio_channel_mask_t GetChannels() const {
+    return message_header_.channel_mask;
+  }
+
+  // Returns the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+  audio_format_t GetFormat() const {
+    return message_header_.format;
+  }
+
+  // Sets the audio format.
+  // Unused as of JB - use set_parameters with key
+  //   AUDIO_PARAMETER_STREAM_FORMAT
+  int SetFormat(audio_format_t format) {
+    message_header_.format = format;
+    return 0;
+  }
+
+  // Puts the audio hardware input/output into standby mode.
+  // Driver should exit from standby mode at the next I/O operation.
+  // Returns 0 on success and <0 on failure.
+  // TODO(ghartman): This should reset some of the frame counts.
+  int Standby() {
+    return 0;
+  }
+
+  // dumps the state of the audio hardware to the given fd.
+  // This information can be retrieved using the dumpsys utility.
+  int Dump(int fd) const;
+
+  // Returns the set of device(s) which this stream is connected to.
+  // TODO(ghartman): Implement this.
+  audio_devices_t GetDevice() const { return device_; }
+
+  // Not directly called from JB forward.
+  // Called indirectly from SetParameters with the key
+  //   AUDIO_PARAMETER_STREAM_ROUTING
+  int SetDevice(audio_devices_t device) { device_ = device; return 0; }
+
+  // Sets audio stream parameters. The function accepts a list of
+  // parameter key value pairs in the form: key1=value1;key2=value2;...
+  //
+  // Some keys are reserved for standard parameters (See AudioParameter class)
+  //
+  // If the implementation does not accept a parameter change while
+  // the output is active but the parameter is acceptable otherwise, it must
+  // return -ENOSYS.
+  //
+  // The audio flinger will put the stream in standby and then change the
+  // parameter value.
+  int SetParameters(const char* kv_pairs);
+
+  // Gets audio stream parameters. The function accepts a list of
+  // keys in the form: key1=value1;key2=value2;...
+  //
+  // Returns a pointer to a heap allocated string. The caller is responsible
+  // for freeing the memory for it using free().
+  // TODO(ghartman): Implement this.
+  char* GetParameters(const char* keys) const;
+
+  // TODO(ghartman): Implement this.
+  int AddAudioEffect(effect_handle_t /*effect*/) const {
+    static unsigned int printed = 0;  // printed every 2^32-th call.
+    ALOGE_IF(!printed++, "%s: not implemented", __FUNCTION__);
+    return 0;
+  }
+
+  // TODO(ghartman): Implement this.
+  int RemoveAudioEffect(effect_handle_t /*effect*/) const {
+    static unsigned int printed = 0;  // printed every 2^32-th call.
+    ALOGE_IF(!printed++, "%s: not implemented", __FUNCTION__);
+    return 0;
+  }
+
+  // Methods defined in audio_stream_out
+
+  // Returns the audio hardware driver estimated latency in milliseconds.
+  // TODO(ghartman): Calculate this based on the format and the quantum.
+  uint32_t GetLatency() const {
+    return kOutLatency;
+  }
+
+  // Use this method in situations where audio mixing is done in the
+  // hardware. This method serves as a direct interface with hardware,
+  // allowing you to directly set the volume as apposed to via the framework.
+  // This method might produce multiple PCM outputs or hardware accelerated
+  // codecs, such as MP3 or AAC.
+  //
+  // Note that GCE simulates hardware mixing.
+  int SetVolume(float left_volume, float right_volume) {
+    left_volume_ = left_volume;
+    right_volume_ = right_volume;
+    return 0;
+  }
+
+  // Write audio buffer to driver. Returns number of bytes written, or a
+  // negative android::status_t. If at least one frame was written successfully prior
+  // to the error the driver will return that successful (short) byte count
+  // and then return an error in the subsequent call.
+  //
+  // If SetCallback() has previously been called to enable non-blocking mode
+  // the Write() is not allowed to block. It must write only the number of
+  // bytes that currently fit in the driver/hardware buffer and then return
+  // this byte count. If this is less than the requested write size the
+  // callback function must be called when more space is available in the
+  // driver/hardware buffer.
+  ssize_t Write(const void* buffer, size_t bytes);
+
+  // Returns the number of audio frames written by the audio dsp to DAC since
+  // the output has exited standby
+  // TODO(ghartman): Implement zeroing this in Standby().
+  int GetRenderPosition(uint32_t* dsp_frames) const;
+
+  // Gets the local time at which the next write to the audio driver will be
+  // presented. The units are microseconds, where the epoch is decided by the
+  // local audio HAL.
+  //
+  // The GCE implementation uses CLOCK_MONOTONIC, which also happens to line
+  // up with LocalTime.
+  int GetNextWriteTimestamp(int64_t*) const;
+
+  // Turns on non-blocking mode and sets the callback function for notifying
+  // completion of non-blocking write and drain.
+  // Calling this function implies that all future Write() and Drain()
+  // must be non-blocking and use the callback to signal completion.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int SetCallback(stream_callback_t callback, void *cookie);
+
+  // Notifies to the audio driver to stop playback however the queued buffers
+  // are retained by the hardware. Useful for implementing pause/resume. Empty
+  // implementation if not supported however should be implemented for hardware
+  // with non-trivial latency. In the pause state audio hardware could still be
+  // using power. User may consider calling suspend after a timeout.
+  //
+  // Implementation of this function is mandatory for offloaded playback.
+  //
+  // TODO(ghartman): Implement this URGENTLY. There is already support in
+  // SimulatedBuffer.
+  // int Pause();
+
+  // Notifies to the audio driver to resume playback following a pause.
+  // Returns error if called without matching pause.
+  //
+  // Implementation of this function is mandatory for offloaded playback.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int Resume();
+
+  // Requests notification when data buffered by the driver/hardware has
+  // been played. If set_callback() has previously been called to enable
+  // non-blocking mode, the drain() must not block, instead it should return
+  // quickly and completion of the drain is notified through the callback.
+  // If set_callback() has not been called, the drain() must block until
+  // completion.
+  //
+  // If type==AUDIO_DRAIN_ALL, the drain completes when all previously written
+  // data has been played.
+  //
+  // If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all
+  // data for the current track has played to allow time for the framework
+  // to perform a gapless track switch.
+  //
+  // Drain must return immediately on stop() and flush() call
+  //
+  // Implementation of this function is mandatory for offloaded playback.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int Drain(audio_drain_type_t type);
+
+  // Notifies to the audio driver to flush the queued data. Stream must already
+  // be paused before calling Flush().
+  //
+  // Implementation of this function is mandatory for offloaded playback.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int Flush();
+
+  // Returns a recent count of the number of audio frames presented to an
+  // external observer.  This excludes frames which have been written but are
+  // still in the pipeline.
+  //
+  // The count is not reset to zero when output enters standby.
+  // Also returns the value of CLOCK_MONOTONIC as of this presentation count.
+  // The returned count is expected to be 'recent',
+  // but does not need to be the most recent possible value.
+  // However, the associated time should correspond to whatever count is
+  // returned.
+  //
+  // Example:  assume that N+M frames have been presented, where M is a
+  // 'small' number.
+  // Then it is permissible to return N instead of N+M,
+  // and the timestamp should correspond to N rather than N+M.
+  // The terms 'recent' and 'small' are not defined.
+  // They reflect the quality of the implementation.
+  //
+  // 3.0 and higher only.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int GetPresentationPosition(uint64_t *frames, struct timespec *timestamp);
+
+ private:
+  // If key is present in query, add key=value; to reply.
+  // query should be pointer to const, but the str_parms functions aren't
+  // const-correct, so neither is this.
+  static void AddIntIfKeyPresent(
+      /*const*/ str_parms* query, str_parms* reply, const char* key, int value);
+
+
+  explicit GceAudioOutputStream(cvd::GceAudio*);
+
+  static const size_t kOutBufferSize = 3840;
+  static const size_t kOutLatency = 2;
+
+  gce_audio_message message_header_;
+  std::unique_ptr<SimulatedOutputBuffer> buffer_;
+  cvd::GceAudio *dev_;
+  audio_devices_t device_;
+  size_t frame_size_;
+  size_t frame_count_;
+  float left_volume_;
+  float right_volume_;
+};
+
+}
diff --git a/guest/hals/gralloc/Android.mk b/guest/hals/gralloc/Android.mk
new file mode 100644
index 0000000..68ffffa
--- /dev/null
+++ b/guest/hals/gralloc/Android.mk
@@ -0,0 +1,63 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := gralloc.cutf_ivsh-future
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+    gralloc.cpp \
+    mapper.cpp
+
+LOCAL_C_INCLUDES += \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_kernel \
+    hardware/libhardware/include \
+    system/core/base/include
+
+LOCAL_CFLAGS := \
+    -DLOG_TAG=\"gralloc_vsoc\" \
+    -Wno-missing-field-initializers \
+    -Wall -Werror \
+    $(VSOC_VERSION_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libcutils \
+    cuttlefish_auto_resources \
+    libcuttlefish_fs \
+    liblog \
+    vsoc_lib
+
+LOCAL_HEADER_LIBRARIES := \
+    libhardware_headers
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+LOCAL_VENDOR_MODULE := true
+
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/hals/gralloc/gralloc.cpp b/guest/hals/gralloc/gralloc.cpp
new file mode 100644
index 0000000..d21d918
--- /dev/null
+++ b/guest/hals/gralloc/gralloc.cpp
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2017 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 <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <log/log.h>
+#include <stdlib.h>
+
+#include "guest/hals/gralloc/gralloc_vsoc_priv.h"
+#include "guest/vsoc/lib/gralloc_region_view.h"
+
+using vsoc::gralloc::GrallocRegionView;
+
+namespace {
+
+static const int kSwiftShaderPadding = 4;
+
+inline void formatToYcbcr(
+    int format, int width, int height, void* base_v, android_ycbcr* ycbcr) {
+  uintptr_t it = reinterpret_cast<uintptr_t>(base_v);
+  // Clear reserved fields;
+  memset(ycbcr, 0, sizeof(*ycbcr));
+  switch (format) {
+    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+      ycbcr->ystride = align(width, 16);
+      ycbcr->cstride = align(ycbcr->ystride / 2, 16);
+      ycbcr->chroma_step = 1;
+      ycbcr->y = reinterpret_cast<void*>(it);
+      it += ycbcr->ystride * height;
+      ycbcr->cr = reinterpret_cast<void*>(it);
+      it += ycbcr->cstride * height / 2;
+      ycbcr->cb = reinterpret_cast<void*>(it);
+      break;
+    default:
+      ALOGE("%s: can't deal with format=0x%x", __FUNCTION__, format);
+  }
+}
+
+inline int formatToBytesPerPixel(int format) {
+  switch (format) {
+    case HAL_PIXEL_FORMAT_RGBA_FP16:
+      return 8;
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+    // The camera 3.0 implementation assumes that IMPLEMENTATION_DEFINED
+    // means HAL_PIXEL_FORMAT_RGBA_8888
+    case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+      return 4;
+    case HAL_PIXEL_FORMAT_RGB_888:
+      return 3;
+    case HAL_PIXEL_FORMAT_RGB_565:
+    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+      return 2;
+    case HAL_PIXEL_FORMAT_BLOB:
+      return 1;
+    default:
+      ALOGE("%s: unknown format=%d", __FUNCTION__, format);
+      return 8;
+  }
+}
+
+inline int formatToBytesPerFrame(int format, int w, int h) {
+  int bytes_per_pixel = formatToBytesPerPixel(format);
+  int w16, h16;
+  int y_size, c_size;
+
+  switch (format) {
+    // BLOB is used to allocate buffers for JPEG formatted data. Bytes per pixel
+    // is 1, the desired buffer size is in w, and h should be 1. We refrain from
+    // adding additional padding, although the caller is likely to round
+    // up to a page size.
+    case HAL_PIXEL_FORMAT_BLOB:
+      return bytes_per_pixel * w * h;
+    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+      android_ycbcr strides;
+      formatToYcbcr(format, w, h, NULL, &strides);
+      y_size = strides.ystride * h;
+      c_size = strides.cstride * h / 2;
+      return (y_size + 2 * c_size + kSwiftShaderPadding);
+    /*case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+    case HAL_PIXEL_FORMAT_RGB_888:
+    case HAL_PIXEL_FORMAT_RGB_565:*/
+    default:
+      w16 = align(w, 16);
+      h16 = align(h, 16);
+      return bytes_per_pixel * w16 * h16 + kSwiftShaderPadding;
+  }
+}
+
+}
+
+/******************************************************************************/
+
+void dump(struct alloc_device_t */*dev*/, char */*buff*/, int /*buff_len*/) {}
+
+/******************************************************************************/
+
+int lock(struct gralloc_module_t const* /*module*/,
+         buffer_handle_t handle,
+         int /*usage*/,
+         int /*l*/,
+         int /*t*/,
+         int /*w*/,
+         int /*h*/,
+         void** vaddr) {
+  if (!vaddr || vsoc_buffer_handle_t::validate(handle)) {
+    return -EINVAL;
+  }
+  // TODO(jemoreira): Check allocation usage flags against requested usage.
+  const vsoc_buffer_handle_t* hnd =
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle);
+  void* mapped = reference_buffer(hnd);
+  if (mapped == NULL) {
+    ALOGE("Unable to reference buffer, %s", __FUNCTION__);
+    return -1;
+  }
+  *vaddr = mapped;
+  return 0;
+}
+
+int unlock(struct gralloc_module_t const* /*module*/, buffer_handle_t handle) {
+  if (vsoc_buffer_handle_t::validate(handle)) {
+    return -EINVAL;
+  }
+  return unreference_buffer(
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+}
+
+int lock_ycbcr(struct gralloc_module_t const* module,
+               buffer_handle_t handle,
+               int usage,
+               int l,
+               int t,
+               int w,
+               int h,
+               struct android_ycbcr* ycbcr) {
+  void* mapped;
+  int retval = lock(module, handle, usage, l, t, w, h, &mapped);
+  if (retval) {
+    return retval;
+  }
+  const vsoc_buffer_handle_t* hnd =
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle);
+  formatToYcbcr(hnd->format, w, h, mapped, ycbcr);
+  return 0;
+}
+
+/******************************************************************************/
+
+static int gralloc_alloc(alloc_device_t* /*dev*/,
+                         int w,
+                         int h,
+                         int format,
+                         int /*usage*/,
+                         buffer_handle_t* pHandle,
+                         int* pStrideInPixels) {
+  int fd = -1;
+
+  int bytes_per_pixel = formatToBytesPerPixel(format);
+  int bytes_per_line;
+  int stride_in_pixels;
+  int size = 0;
+  uint32_t offset = 0;
+  // SwiftShader can't handle RGB_888, so fail fast and hard if we try to create
+  // a gralloc buffer in this format.
+  ALOG_ASSERT(format != HAL_PIXEL_FORMAT_RGB_888);
+  if (format == HAL_PIXEL_FORMAT_YV12) {
+    bytes_per_line = align(bytes_per_pixel * w, 16);
+  } else {
+    bytes_per_line = align(bytes_per_pixel * w, 8);
+  }
+  size = align(size + formatToBytesPerFrame(format, w, h), PAGE_SIZE);
+  size += PAGE_SIZE;
+  fd = GrallocRegionView::GetInstance()->AllocateBuffer(size, &offset);
+  if (fd < 0) {
+    ALOGE("Unable to allocate buffer (%s)", strerror(-fd));
+    return fd;
+  }
+
+  stride_in_pixels = bytes_per_line / bytes_per_pixel;
+  vsoc_buffer_handle_t* hnd = new vsoc_buffer_handle_t(fd,
+                                                       offset,
+                                                       size,
+                                                       format,
+                                                       w, h,
+                                                       stride_in_pixels);
+  void* addr =
+      reference_buffer(reinterpret_cast<const vsoc_buffer_handle_t*>(hnd));
+  if (!addr) {
+    ALOGE("Unable to reference buffer, %s", __FUNCTION__);
+    return -EIO;
+  }
+
+  *pHandle = hnd;
+  *pStrideInPixels = stride_in_pixels;
+
+  return 0;
+}
+
+static int gralloc_free(alloc_device_t* /*dev*/, buffer_handle_t handle) {
+  // No need to do anything else, the buffer will be atomatically deallocated
+  // when the handle is closed.
+  return unreference_buffer(
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+}
+
+static int register_buffer(struct gralloc_module_t const* /*module*/,
+                          buffer_handle_t handle) {
+  if (vsoc_buffer_handle_t::validate(handle)) {
+    return -EINVAL;
+  }
+  void* addr =
+      reference_buffer(reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+  if (!addr) {
+    ALOGE("Unable to reference buffer, %s", __FUNCTION__);
+    return -EIO;
+  }
+  return 0;
+}
+
+int unregister_buffer(struct gralloc_module_t const* /*module*/,
+                     buffer_handle_t handle) {
+  if (vsoc_buffer_handle_t::validate(handle)) {
+    return -EINVAL;
+  }
+  return unreference_buffer(
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+}
+
+/******************************************************************************/
+
+static int gralloc_device_close(struct hw_device_t *dev) {
+  vsoc_alloc_device_t* pdev = reinterpret_cast<vsoc_alloc_device_t*>(dev);
+  if (pdev) {
+    free(pdev);
+  }
+  return 0;
+}
+
+static int gralloc_device_open(
+    const hw_module_t* module, const char* name, hw_device_t** device) {
+    int status = -EINVAL;
+  if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
+    vsoc_alloc_device_t *dev;
+    dev = (vsoc_alloc_device_t*) malloc(sizeof(*dev));
+    LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+
+    /* initialize our state here */
+    memset(dev, 0, sizeof(*dev));
+
+    /* initialize the procs */
+    dev->device.common.tag = HARDWARE_DEVICE_TAG;
+    dev->device.common.version = 0; // TODO(jemoreira): Bump to 0_2 when stable
+    dev->device.common.module = const_cast<hw_module_t*>(module);
+    dev->device.common.close = gralloc_device_close;
+
+    dev->device.alloc   = gralloc_alloc;
+    dev->device.free    = gralloc_free;
+
+    if (!GrallocRegionView::GetInstance()) {
+      LOG_FATAL("Unable to instantiate the gralloc region");
+      free(dev);
+      return -EIO;
+    }
+
+    *device = &dev->device.common;
+    status = 0;
+  }
+  // TODO(jemoreira): Consider opening other type of devices (framebuffer)
+  return status;
+}
+
+/******************************************************************************/
+
+static struct hw_module_methods_t gralloc_module_methods = {
+  .open = gralloc_device_open
+};
+
+struct vsoc_gralloc_module_t HAL_MODULE_INFO_SYM = {
+  .base = {
+    .common = {
+      .tag = HARDWARE_MODULE_TAG,
+      .version_major = GRALLOC_MODULE_API_VERSION_0_2,
+      .version_minor = 0,
+      .id = GRALLOC_HARDWARE_MODULE_ID,
+      .name = "VSoC X86 Graphics Memory Allocator Module",
+      .author = "The Android Open Source Project",
+      .methods = &gralloc_module_methods,
+      .dso = NULL,
+      .reserved = {0},
+    },
+    .registerBuffer = register_buffer,
+    .unregisterBuffer = unregister_buffer,
+    .lock = lock,
+    .unlock = unlock,
+    .perform = NULL,
+    .lock_ycbcr = lock_ycbcr,
+    .getTransportSize = NULL,
+    .validateBufferSize = NULL,
+  },
+};
diff --git a/guest/hals/gralloc/gralloc_vsoc_priv.h b/guest/hals/gralloc/gralloc_vsoc_priv.h
new file mode 100644
index 0000000..1ec5659
--- /dev/null
+++ b/guest/hals/gralloc/gralloc_vsoc_priv.h
@@ -0,0 +1,100 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h>
+#include <log/log.h>
+
+struct vsoc_alloc_device_t {
+  alloc_device_t device;
+};
+
+struct vsoc_gralloc_module_t {
+  gralloc_module_t base;
+};
+
+static_assert(sizeof(int) >= 4, "At least 4 bytes are needed for offsets");
+
+struct vsoc_buffer_handle_t : public native_handle {
+  // File descriptors
+  int fd;
+  // ints
+  int magic;
+  int format;
+  int x_res;
+  int y_res;
+  int stride_in_pixels;
+  int size;
+  // buffer offset in bytes divided by PAGE_SIZE
+  int offset;
+
+  static inline int sNumInts() {
+    return ((sizeof(vsoc_buffer_handle_t) - sizeof(native_handle_t)) /
+                sizeof(int) -
+            sNumFds);
+  }
+  static const int sNumFds = 1;
+  static const int sMagic = 0xc63752f4;
+
+  vsoc_buffer_handle_t(int fd,
+                       int offset,
+                       int size,
+                       int format,
+                       int x_res,
+                       int y_res,
+                       int stride_in_pixels)
+      : fd(fd),
+        magic(sMagic),
+        format(format),
+        x_res(x_res),
+        y_res(y_res),
+        stride_in_pixels(stride_in_pixels),
+        size(size),
+        offset(offset) {
+    version = sizeof(native_handle);
+    numInts = sNumInts();
+    numFds = sNumFds;
+  }
+
+  ~vsoc_buffer_handle_t() {
+    magic = 0;
+  }
+
+  static int validate(const native_handle* handle) {
+    const vsoc_buffer_handle_t* hnd =
+        reinterpret_cast<const vsoc_buffer_handle_t*>(handle);
+    if (!hnd || hnd->version != sizeof(native_handle) ||
+        hnd->numInts != sNumInts() || hnd->numFds != sNumFds ||
+        hnd->magic != sMagic) {
+      ALOGE("Invalid gralloc handle (at %p)", handle);
+      return -EINVAL;
+    }
+    return 0;
+  }
+};
+
+// These functions are to be used to map/unmap gralloc buffers. They are thread
+// safe and ensure that the same buffer never gets mapped twice.
+void* reference_buffer(const vsoc_buffer_handle_t* hnd);
+int unreference_buffer(const vsoc_buffer_handle_t* hnd);
+
+// TODO(jemoreira): Move this to a place where it can be used by the gralloc
+// region as well.
+inline int align(int input, int alignment) {
+  return (input + alignment - 1) & -alignment;
+}
diff --git a/guest/hals/gralloc/legacy/Android.mk b/guest/hals/gralloc/legacy/Android.mk
index ac789fb..4bbc9c0 100644
--- a/guest/hals/gralloc/legacy/Android.mk
+++ b/guest/hals/gralloc/legacy/Android.mk
@@ -18,6 +18,7 @@
 
 VSOC_GRALLOC_COMMON_SRC_FILES := \
     gralloc.cpp \
+    framebuffer.cpp \
     mapper.cpp \
     region_registry.cpp
 
@@ -46,7 +47,8 @@
     libbase \
     liblog \
     libutils \
-    libcutils
+    libcutils \
+    vsoc_lib
 
 LOCAL_VENDOR_MODULE := true
 
diff --git a/guest/hals/gralloc/legacy/framebuffer.cpp b/guest/hals/gralloc/legacy/framebuffer.cpp
new file mode 100644
index 0000000..5a2b7f0
--- /dev/null
+++ b/guest/hals/gralloc/legacy/framebuffer.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 <sys/mman.h>
+
+#include <dlfcn.h>
+
+#include <cutils/ashmem.h>
+#include <log/log.h>
+#include <cutils/properties.h>
+
+#include <sys/system_properties.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/atomic.h>
+
+#if defined(__ANDROID__)
+#include <linux/fb.h>
+#endif
+
+#include "gralloc_vsoc_priv.h"
+#include "region_registry.h"
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "common/vsoc/lib/screen_region_view.h"
+
+using vsoc::screen::ScreenRegionView;
+
+/*****************************************************************************/
+
+struct fb_context_t {
+  framebuffer_device_t  device;
+};
+
+/*****************************************************************************/
+
+static int fb_setSwapInterval(struct framebuffer_device_t* dev, int interval) {
+  if (interval < dev->minSwapInterval || interval > dev->maxSwapInterval) {
+    return -EINVAL;
+  }
+  // FIXME: implement fb_setSwapInterval
+  return 0;
+}
+
+/*
+ * These functions (and probably the entire framebuffer device) are most likely
+ * not used when the hardware composer device is present, however is hard to be
+ * 100% sure.
+ */
+static int fb_setUpdateRect(
+    struct framebuffer_device_t* dev __unused, int l, int t, int w, int h) {
+  if (((w|h) <= 0) || ((l|t)<0)) {
+    return -EINVAL;
+  }
+  // TODO(jemoreira): Find a way to broadcast this with the framebuffer control.
+  return 0;
+}
+
+static int fb_post(struct framebuffer_device_t* dev __unused,
+                   buffer_handle_t buffer_handle) {
+  static int frame_buffer_idx = 0;
+
+  auto screen_view = ScreenRegionView::GetInstance();
+
+  void* frame_buffer = screen_view->GetBuffer(frame_buffer_idx);
+  const private_handle_t* p_handle =
+      reinterpret_cast<const private_handle_t*>(buffer_handle);
+  void* buffer;
+  int retval =
+      reinterpret_cast<gralloc_module_t*>(dev->common.module)
+          ->lock(reinterpret_cast<const gralloc_module_t*>(dev->common.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, screen_view->buffer_size());
+  screen_view->BroadcastNewFrame(frame_buffer_idx);
+
+  frame_buffer_idx = (frame_buffer_idx + 1) % screen_view->number_of_buffers();
+
+  return 0;
+}
+
+/*****************************************************************************/
+
+static int fb_close(struct hw_device_t *dev) {
+  fb_context_t* ctx = (fb_context_t*)dev;
+  if (ctx) {
+    free(ctx);
+  }
+  return 0;
+}
+
+int fb_device_open(
+    hw_module_t const* module, const char* name, hw_device_t** device) {
+  if (strcmp(name, GRALLOC_HARDWARE_FB0) != 0) {
+    return -EINVAL;
+  }
+  /* initialize our state here */
+  fb_context_t* dev = (fb_context_t*) malloc(sizeof(*dev));
+  LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+  memset(dev, 0, sizeof(*dev));
+
+  /* initialize the procs */
+  dev->device.common.tag = HARDWARE_DEVICE_TAG;
+  dev->device.common.version = 0;
+  dev->device.common.module = const_cast<hw_module_t*>(module);
+  dev->device.common.close = fb_close;
+  dev->device.setSwapInterval = fb_setSwapInterval;
+  dev->device.post            = fb_post;
+  dev->device.setUpdateRect   = fb_setUpdateRect;
+
+  auto screen_view = ScreenRegionView::GetInstance();
+
+  int stride =
+    screen_view->line_length() / screen_view->bytes_per_pixel();
+  int format = HAL_PIXEL_FORMAT_RGBX_8888;
+  const_cast<uint32_t&>(dev->device.flags) = 0;
+  const_cast<uint32_t&>(dev->device.width) = screen_view->x_res();
+  const_cast<uint32_t&>(dev->device.height) = screen_view->y_res();
+  const_cast<int&>(dev->device.stride) = stride;
+  const_cast<int&>(dev->device.format) = format;
+  const_cast<float&>(dev->device.xdpi) = screen_view->dpi();
+  const_cast<float&>(dev->device.ydpi) = screen_view->dpi();
+  const_cast<float&>(dev->device.fps) =
+    (screen_view->refresh_rate_hz() * 1000) / 1000.0f;
+  const_cast<int&>(dev->device.minSwapInterval) = 1;
+  const_cast<int&>(dev->device.maxSwapInterval) = 1;
+  *device = &dev->device.common;
+
+  return 0;
+}
diff --git a/guest/hals/gralloc/legacy/gralloc.cpp b/guest/hals/gralloc/legacy/gralloc.cpp
index dbe2838..8a0cbd2 100644
--- a/guest/hals/gralloc/legacy/gralloc.cpp
+++ b/guest/hals/gralloc/legacy/gralloc.cpp
@@ -36,6 +36,7 @@
 #include <hardware/gralloc.h>
 
 #include "common/libs/auto_resources/auto_resources.h"
+#include "common/vsoc/lib/screen_region_view.h"
 #include "gralloc_vsoc_priv.h"
 #include "region_registry.h"
 
@@ -161,7 +162,7 @@
     *device = &dev->device.common;
     status = 0;
   } else {
-    ALOGE("Need to create framebuffer, but it is unsupported");
+    status = fb_device_open(module, name, device);
   }
   return status;
 }
diff --git a/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h b/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h
index 390c654..2544b5a 100644
--- a/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h
+++ b/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h
@@ -29,6 +29,8 @@
 
 #include <linux/fb.h>
 
+#include "common/vsoc/lib/screen_region_view.h"
+
 #ifndef GRALLOC_MODULE_API_VERSION_0_2
 // This structure will be defined in later releases of Android. Declare it
 // here to allow us to structure the code well.
@@ -43,20 +45,6 @@
 };
 #endif
 
-namespace vsoc {
-namespace screen {
-
-struct ScreenRegionView {
-  static int align(int input) {
-    auto constexpr alignment = 16;
-    return (input + alignment - 1) & -alignment;
-  }
-  static constexpr int kSwiftShaderPadding = 4;
-};
-
-}
-}
-
 /*****************************************************************************/
 
 struct private_handle_t;
@@ -309,6 +297,9 @@
   }
 }
 
+int fb_device_open(
+    const hw_module_t* module, const char* name, hw_device_t** device);
+
 int gralloc_lock(
     gralloc_module_t const* module,
     buffer_handle_t handle, int usage,
diff --git a/guest/hals/gralloc/mapper.cpp b/guest/hals/gralloc/mapper.cpp
new file mode 100644
index 0000000..e9ba2eb
--- /dev/null
+++ b/guest/hals/gralloc/mapper.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 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 "gralloc_vsoc_priv.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <cutils/hashmap.h>
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <log/log.h>
+
+namespace {
+
+const size_t g_page_size = sysconf(_SC_PAGESIZE);
+
+struct HmLockGuard {
+  HmLockGuard(Hashmap* map) : map_(map) {
+    hashmapLock(map_);
+  }
+  ~HmLockGuard() {
+    hashmapUnlock(map_);
+  }
+ private:
+  Hashmap* map_;
+};
+
+int offset_hash(void* key) {
+  return *reinterpret_cast<int*>(key);
+}
+
+bool offset_equals(void* key1, void* key2) {
+  return *reinterpret_cast<int*>(key1) ==
+         *reinterpret_cast<int*>(key2);
+}
+
+// Keeps track of how many times a buffer is locked in the current process.
+struct GrallocBuffer {
+  void* vaddr;
+  int ref_count;
+  GrallocBuffer() : vaddr(NULL), ref_count(0) {}
+
+  static Hashmap* mapped_buffers() {
+    static Hashmap* mapped_buffers =
+        hashmapCreate(19, offset_hash, offset_equals);
+    return mapped_buffers;
+  }
+};
+
+}
+
+void* reference_buffer(const vsoc_buffer_handle_t* hnd) {
+  Hashmap* map = GrallocBuffer::mapped_buffers();
+  HmLockGuard lock_guard(map);
+  GrallocBuffer* buffer = reinterpret_cast<GrallocBuffer*>(
+      hashmapGet(map, const_cast<int*>(&hnd->offset)));
+  if (!buffer) {
+    buffer = new GrallocBuffer();
+    hashmapPut(map, const_cast<int*>(&hnd->offset), buffer);
+  }
+
+  if (!buffer->vaddr) {
+    void* mapped =
+      mmap(NULL, hnd->size, PROT_READ | PROT_WRITE, MAP_SHARED, hnd->fd, 0);
+    if (mapped == MAP_FAILED) {
+      ALOGE("Unable to map buffer (offset: %d, size: %d): %s",
+            hnd->offset,
+            hnd->size,
+            strerror(errno));
+      return NULL;
+    }
+    // Set up the guard pages. The last page is always a guard
+    uintptr_t base = uintptr_t(mapped);
+    uintptr_t addr = base + hnd->size - g_page_size;
+    if (mprotect((void*)addr, g_page_size, PROT_NONE) == -1) {
+      ALOGW("Unable to protect last page of buffer (offset: %d, size: %d): %s",
+            hnd->offset,
+            hnd->size,
+            strerror(errno));
+    }
+    buffer->vaddr = mapped;
+  }
+  buffer->ref_count++;
+  return buffer->vaddr;
+}
+
+int unreference_buffer(const vsoc_buffer_handle_t* hnd) {
+  int result = 0;
+  Hashmap* map = GrallocBuffer::mapped_buffers();
+  HmLockGuard lock_guard(map);
+  GrallocBuffer* buffer = reinterpret_cast<GrallocBuffer*>(
+      hashmapGet(map, const_cast<int*>(&hnd->offset)));
+  if (!buffer) {
+    ALOGE("Unreferencing an unknown buffer (offset: %d, size: %d)",
+          hnd->offset,
+          hnd->size);
+    return -EINVAL;
+  }
+  if (buffer->ref_count == 0) {
+    ALOGE("Unbalanced reference/unreference on buffer (offset: %d, size: %d)",
+          hnd->offset,
+          hnd->size);
+    return -EINVAL;
+  }
+  buffer->ref_count--;
+  if (buffer->ref_count == 0) {
+    result = munmap(buffer->vaddr, hnd->size);
+    if (result) {
+      ALOGE("Unable to unmap buffer (offset: %d, size: %d): %s",
+            hnd->offset,
+            hnd->size,
+            strerror(errno));
+    }
+    buffer->vaddr = NULL;
+  }
+  return result;
+}
diff --git a/guest/hals/hwcomposer/vsoc-future/Android.mk b/guest/hals/hwcomposer/vsoc-future/Android.mk
new file mode 100644
index 0000000..bcc104e
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc-future/Android.mk
@@ -0,0 +1,58 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := hwcomposer.cutf_ivsh-future
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+    hwcomposer.cpp
+
+LOCAL_C_INCLUDES += \
+    device/google/cuttlefish_kernel \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_common/guest/hals/hwcomposer \
+    hardware/libhardware/include \
+    system/core/base/include
+
+LOCAL_CFLAGS := \
+    -DLOG_TAG=\"hwcomposer_vsoc\" \
+    -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    liblog \
+    vsoc_lib
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+LOCAL_VENDOR_MODULE := true
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+
+LOCAL_HEADER_LIBRARIES := \
+    libhardware_headers
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp b/guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp
new file mode 100644
index 0000000..f18712c
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2017 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 <pthread.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <log/log.h>
+
+#include "common/vsoc/lib/screen_region_view.h"
+#include "guest/hals/gralloc/gralloc_vsoc_priv.h"
+
+// This file contains just a skeleton hwcomposer, the first step in the
+// multisided vsoc hwcomposer for cuttlefish.
+
+using vsoc::screen::ScreenRegionView;
+
+// TODO(jemoreira): ScreenRegionView may become the HWC region
+namespace {
+
+// Ensures that the layer does not include any inconsistencies
+int SanityCheckLayer(const hwc_layer_1& layer) {
+  // 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 -EINVAL;
+  }
+  // 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 -EINVAL;
+  }
+  const vsoc_buffer_handle_t* p_handle =
+      reinterpret_cast<const vsoc_buffer_handle_t*>(layer.handle);
+  if (!p_handle) {
+    ALOGE("Layer has a NULL buffer handle");
+    return -EINVAL;
+  }
+  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 -EINVAL;
+  }
+  return 0;
+}
+
+struct vsoc_hwc_device {
+  hwc_composer_device_1_t base;
+  const hwc_procs_t* procs;
+  pthread_t vsync_thread;
+  int64_t vsync_base_timestamp;
+  int32_t vsync_period_ns;
+  uint32_t frame_num;
+};
+
+void* vsync_thread(void* arg) {
+  vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(arg);
+  setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+  int64_t base_timestamp = pdev->vsync_base_timestamp;
+  int64_t last_logged = base_timestamp / 1e9;
+  int sent = 0;
+  int last_sent = 0;
+  static const int log_interval = 60;
+  void (*vsync_proc)(const struct hwc_procs*, int, int64_t) = nullptr;
+  bool log_no_procs = true, log_no_vsync = true;
+  while (true) {
+    struct timespec rt;
+    if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+      ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
+            strerror(errno));
+    }
+    int64_t timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+    // Given now's timestamp calculate the time of the next vsync.
+    timestamp += pdev->vsync_period_ns -
+                 (timestamp - base_timestamp) % pdev->vsync_period_ns;
+
+    rt.tv_sec = timestamp / 1e9;
+    rt.tv_nsec = timestamp % static_cast<int32_t>(1e9);
+    int err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &rt, NULL);
+    if (err == -1) {
+      ALOGE("error in vsync thread: %s", strerror(errno));
+      if (errno == EINTR) {
+        continue;
+      }
+    }
+
+    // The vsync thread is started on device open, it may run before the
+    // registerProcs callback has a chance to be called, so we need to make sure
+    // procs is not NULL before dereferencing it.
+    if (pdev && pdev->procs) {
+      vsync_proc = pdev->procs->vsync;
+    } else if (log_no_procs) {
+      log_no_procs = false;
+      ALOGI("procs is not set yet, unable to deliver vsync event");
+    }
+    if (vsync_proc) {
+      vsync_proc(const_cast<hwc_procs_t*>(pdev->procs), 0, timestamp);
+      ++sent;
+    } else if (log_no_vsync) {
+      log_no_vsync = false;
+      ALOGE("vsync callback is null (but procs was already set)");
+    }
+    if (rt.tv_sec - last_logged > log_interval) {
+      ALOGI("Sent %d syncs in %ds", sent - last_sent, log_interval);
+      last_logged = rt.tv_sec;
+      last_sent = sent;
+    }
+  }
+
+  return NULL;
+}
+
+int hwc_prepare(struct hwc_composer_device_1* /*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;
+
+  for (size_t i = 0; i < list->numHwLayers; i++) {
+    if (list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
+      continue;
+    }
+    list->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
+  }
+
+  return 0;
+}
+
+int hwc_set(struct hwc_composer_device_1* 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;
+  if (!dev) {
+    ALOGE("%s: dev is NULL", __FUNCTION__);
+    return -EINVAL;
+  }
+
+  for (size_t i = 0; i < list->numHwLayers; i++) {
+    if (vsoc_buffer_handle_t::validate(list->hwLayers[i].handle)) {
+      return -EINVAL;
+    }
+    if (list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
+      if (SanityCheckLayer(list->hwLayers[i])) {
+        ALOGW("Skipping layer %zu due to failed sanity check", i);
+        continue;
+      }
+      const vsoc_buffer_handle_t* fb_handle =
+          reinterpret_cast<const vsoc_buffer_handle_t*>(
+              list->hwLayers[i].handle);
+      ScreenRegionView::GetInstance()->BroadcastNewFrame(fb_handle->offset);
+      break;
+    }
+  }
+
+  return 0;
+}
+
+int hwc_eventControl(struct hwc_composer_device_1* /*dev*/, int disp, int event,
+                     int /*enabled*/) {
+  if (event == HWC_EVENT_VSYNC || disp != HWC_DISPLAY_PRIMARY) {
+    return 0;
+  }
+  return -EINVAL;
+}
+
+int hwc_blank(struct hwc_composer_device_1* /*dev*/, int disp, int /*blank*/) {
+  if (disp != HWC_DISPLAY_PRIMARY) {
+    return -EINVAL;
+  }
+  return 0;
+}
+
+int hwc_query(struct hwc_composer_device_1* dev, int what, int* value) {
+  vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(dev);
+  switch (what) {
+    case HWC_BACKGROUND_LAYER_SUPPORTED:
+      // we don't support the background layer
+      *value = 0;
+      break;
+    case HWC_VSYNC_PERIOD:
+      *value = pdev->vsync_period_ns;
+      break;
+    case HWC_DISPLAY_TYPES_SUPPORTED:
+      // We only support the primary display
+      *value = HWC_DISPLAY_PRIMARY_BIT;
+      break;
+    default:
+      // unsupported query
+      ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
+      return -EINVAL;
+  }
+  return 0;
+}
+
+void hwc_registerProcs(struct hwc_composer_device_1* dev,
+                       hwc_procs_t const* procs) {
+  reinterpret_cast<vsoc_hwc_device*>(dev)->procs = procs;
+}
+
+void hwc_dump(struct hwc_composer_device_1* /*dev*/, char* /*buff*/,
+              int /*buff_len*/) {}
+
+int hwc_getDisplayConfigs(struct hwc_composer_device_1* /*dev*/, int disp,
+                          uint32_t* configs, size_t* numConfigs) {
+  if (*numConfigs == 0) return 0;
+
+  if (disp == HWC_DISPLAY_PRIMARY) {
+    configs[0] = 0;
+    *numConfigs = 1;
+    return 0;
+  }
+
+  return -EINVAL;
+}
+
+int32_t vsoc_hwc_attribute(uint32_t attribute) {
+  auto screen_view = ScreenRegionView::GetInstance();
+  switch (attribute) {
+    case HWC_DISPLAY_VSYNC_PERIOD:
+      return 1000000000 / screen_view->refresh_rate_hz();
+    case HWC_DISPLAY_WIDTH:
+      return screen_view->x_res();
+    case HWC_DISPLAY_HEIGHT:
+      return screen_view->y_res();
+    case HWC_DISPLAY_DPI_X:
+    case HWC_DISPLAY_DPI_Y:
+      // The number of pixels per thousand inches
+      return screen_view->dpi() * 1000;
+    case HWC_DISPLAY_COLOR_TRANSFORM:
+      // TODO(jemoreira): Add the other color transformations
+      return HAL_COLOR_TRANSFORM_IDENTITY;
+    default:
+      ALOGE("unknown display attribute %u", attribute);
+      return -EINVAL;
+  }
+}
+
+int hwc_getDisplayAttributes(struct hwc_composer_device_1* /*dev*/, int disp,
+                             uint32_t /*config*/, const uint32_t* attributes,
+                             int32_t* values) {
+  if (disp != HWC_DISPLAY_PRIMARY) {
+    ALOGE("Unknown display type %u", disp);
+    return -EINVAL;
+  }
+
+  for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
+    values[i] = vsoc_hwc_attribute(attributes[i]);
+  }
+
+  return 0;
+}
+
+int hwc_close(hw_device_t* device) {
+  vsoc_hwc_device* dev = reinterpret_cast<vsoc_hwc_device*>(device);
+  pthread_kill(dev->vsync_thread, SIGTERM);
+  pthread_join(dev->vsync_thread, NULL);
+  delete dev;
+  return 0;
+}
+
+int hwc_open(const struct hw_module_t* module, const char* name,
+             struct hw_device_t** device) {
+  ALOGI("Opening vsoc hwcomposer device: %s", __FUNCTION__);
+  if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
+    ALOGE("%s called with bad name %s", __FUNCTION__, name);
+    return -EINVAL;
+  }
+
+  vsoc_hwc_device* dev = new vsoc_hwc_device();
+  if (!dev) {
+    ALOGE("%s failed to allocate dev", __FUNCTION__);
+    return -ENOMEM;
+  }
+  memset(dev, 0, sizeof(*dev));
+
+  int refreshRate = 60;
+  dev->vsync_period_ns = 1000000000 / refreshRate;
+  struct timespec rt;
+  if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+    ALOGE("%s:%d error in clock_gettime: %s", __FILE__, __LINE__,
+          strerror(errno));
+  }
+  dev->vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+
+  dev->base.common.tag = HARDWARE_DEVICE_TAG;
+  dev->base.common.version = HWC_DEVICE_API_VERSION_1_1;
+  dev->base.common.module = const_cast<hw_module_t*>(module);
+  dev->base.common.close = hwc_close;
+
+  dev->base.prepare = hwc_prepare;
+  dev->base.set = hwc_set;
+  dev->base.query = hwc_query;
+  dev->base.registerProcs = hwc_registerProcs;
+  dev->base.dump = hwc_dump;
+  dev->base.blank = hwc_blank;
+  dev->base.eventControl = hwc_eventControl;
+  dev->base.getDisplayConfigs = hwc_getDisplayConfigs;
+  dev->base.getDisplayAttributes = hwc_getDisplayAttributes;
+
+  if (!ScreenRegionView::GetInstance()) {
+    ALOGE("Unable to open screen region (%s)", __FUNCTION__);
+    delete dev;
+    return -1;
+  }
+
+  int ret = pthread_create(&dev->vsync_thread, NULL, vsync_thread, dev);
+  if (ret) {
+    ALOGE("failed to start vsync thread: %s", strerror(ret));
+    delete dev;
+  } else {
+    *device = &dev->base.common;
+  }
+
+  return -ret;
+}
+
+struct hw_module_methods_t hwc_module_methods = {hwc_open};
+
+}  // namespace
+
+hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG,
+                                     HWC_MODULE_API_VERSION_0_1,
+                                     HARDWARE_HAL_API_VERSION,
+                                     HWC_HARDWARE_MODULE_ID,
+                                     "Cuttlefish hwcomposer module",
+                                     "Google",
+                                     &hwc_module_methods,
+                                     NULL,
+                                     {0}}};
diff --git a/guest/hals/hwcomposer/vsoc/Android.bp b/guest/hals/hwcomposer/vsoc/Android.bp
new file mode 100644
index 0000000..559a1c1
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc/Android.bp
@@ -0,0 +1,47 @@
+// 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.
+
+
+cc_library_shared {
+    name: "hwcomposer.cutf_ivsh_ashmem",
+    relative_install_path: "hw",
+    defaults: ["cuttlefish_guest_only"],
+    cflags: [
+        "-DGATHER_STATS",
+        // Uncomment the following line to revert to GL compositions
+        // "-DUSE_OLD_HWCOMPOSER",
+    ],
+    srcs: [
+        "vsoc_screen_view.cpp",
+        "hwcomposer.cpp",
+    ],
+    export_include_dirs: ["."],
+    static_libs: [
+        "libyuv_static", 
+        "hwcomposer_common"
+    ],
+    shared_libs: [
+        "cuttlefish_auto_resources",
+        "liblog",
+        "libhardware",
+        "libbase",
+        "libcutils",
+        "libutils",
+        "libsync",
+        "libjpeg",
+        "libcuttlefish_utils",
+        "libcuttlefish_fs",
+        "vsoc_lib",
+    ],
+}
diff --git a/guest/hals/hwcomposer/vsoc/hwcomposer.cpp b/guest/hals/hwcomposer/vsoc/hwcomposer.cpp
new file mode 100644
index 0000000..67713f2
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc/hwcomposer.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "hwc.cutf_cvm"
+
+#include <cutils/properties.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <log/log.h>
+
+#include "guest/hals/hwcomposer/common/hwcomposer.h"
+
+#include "guest/hals/hwcomposer/vsoc/vsoc_screen_view.h"
+
+static int hwc_open(const struct hw_module_t* module, const char* name,
+                    struct hw_device_t** device) {
+  std::unique_ptr<cvd::ScreenView> screen_view(new cvd::VSoCScreenView());
+  if (!screen_view) {
+    ALOGE("Failed to instantiate screen view");
+    return -1;
+  }
+
+  return cvd::cvd_hwc_open(std::move(screen_view), module, name, device);
+}
+
+static struct hw_module_methods_t hwc_module_methods = {
+    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,
+                                     "VSoC hwcomposer module",
+                                     "Google",
+                                     &hwc_module_methods,
+                                     NULL,
+                                     {0}}};
diff --git a/guest/hals/hwcomposer/vsoc/vsoc_screen_view.cpp b/guest/hals/hwcomposer/vsoc/vsoc_screen_view.cpp
new file mode 100644
index 0000000..6ca9ac2
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc/vsoc_screen_view.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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/hals/hwcomposer/vsoc/vsoc_screen_view.h"
+
+#include <sys/time.h>
+
+#include "common/vsoc/lib/screen_region_view.h"
+
+using vsoc::layout::screen::TimeSpec;
+using vsoc::screen::ScreenRegionView;
+
+namespace cvd {
+namespace {
+
+TimeSpec TimeSpecFromSystemStruct(const timespec* spec) {
+  return {static_cast<uint32_t>(spec->tv_sec),
+          static_cast<uint32_t>(spec->tv_nsec), 0};
+}
+
+void VSoCStatsFromCvdStats(vsoc::layout::screen::CompositionStats* vsoc_stats,
+                           const cvd::CompositionStats* stats) {
+  vsoc_stats->num_prepare_calls = stats->num_prepare_calls;
+  vsoc_stats->num_layers = stats->num_layers;
+  vsoc_stats->num_hwcomposited_layers = stats->num_hwcomposited_layers;
+  vsoc_stats->last_vsync = TimeSpecFromSystemStruct(&stats->last_vsync);
+  vsoc_stats->prepare_start = TimeSpecFromSystemStruct(&stats->prepare_start);
+  vsoc_stats->prepare_end = TimeSpecFromSystemStruct(&stats->prepare_end);
+  vsoc_stats->set_start = TimeSpecFromSystemStruct(&stats->set_start);
+  vsoc_stats->set_end = TimeSpecFromSystemStruct(&stats->set_end);
+}
+
+}  // namespace
+
+VSoCScreenView::VSoCScreenView()
+    : region_view_(ScreenRegionView::GetInstance()) {}
+
+VSoCScreenView::~VSoCScreenView() {}
+
+void VSoCScreenView::Broadcast(int buffer_id,
+                               const cvd::CompositionStats* stats) {
+  if (stats) {
+    vsoc::layout::screen::CompositionStats vsoc_stats;
+    VSoCStatsFromCvdStats(&vsoc_stats, stats);
+    region_view_->BroadcastNewFrame(buffer_id, &vsoc_stats);
+  } else {
+    region_view_->BroadcastNewFrame(buffer_id);
+  }
+}
+
+void* VSoCScreenView::GetBuffer(int fb_index) {
+  return region_view_->GetBuffer(fb_index);
+}
+
+int32_t VSoCScreenView::x_res() const { return region_view_->x_res(); }
+
+int32_t VSoCScreenView::y_res() const { return region_view_->y_res(); }
+
+int32_t VSoCScreenView::dpi() const { return region_view_->dpi(); }
+
+int32_t VSoCScreenView::refresh_rate() const {
+  return region_view_->refresh_rate_hz();
+}
+
+int VSoCScreenView::num_buffers() const {
+  return region_view_->number_of_buffers();
+}
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/vsoc/vsoc_screen_view.h b/guest/hals/hwcomposer/vsoc/vsoc_screen_view.h
new file mode 100644
index 0000000..1ecace6
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc/vsoc_screen_view.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/vsoc/lib/screen_region_view.h"
+#include "guest/hals/hwcomposer/common/screen_view.h"
+
+namespace cvd {
+
+class VSoCScreenView : public ScreenView {
+ public:
+  VSoCScreenView();
+  virtual ~VSoCScreenView();
+
+  void Broadcast(int buffer_id,
+                 const CompositionStats* stats = nullptr) override;
+  void* GetBuffer(int fb_index) override;
+
+  int32_t x_res() const override;
+  int32_t y_res() const override;
+  int32_t dpi() const override;
+  int32_t refresh_rate() const override;
+
+  int num_buffers() const override;
+
+ private:
+  vsoc::screen::ScreenRegionView* region_view_;
+};
+
+}  // namespace cvd
diff --git a/guest/vsoc/lib/Android.bp b/guest/vsoc/lib/Android.bp
new file mode 100644
index 0000000..d534a5d
--- /dev/null
+++ b/guest/vsoc/lib/Android.bp
@@ -0,0 +1,77 @@
+//
+// 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.
+
+cc_binary {
+    name: "vsoc_driver_test",
+    srcs: [
+        "vsoc_driver_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "cuttlefish_auto_resources",
+        "libcuttlefish_fs",
+        "libbase",
+    ],
+    static_libs: [
+        "libgtest",
+    ],
+    cflags: [
+        "-DGTEST_OS_LINUX_ANDROID",
+        "-DGTEST_HAS_STD_STRING",
+    ],
+    defaults: ["cuttlefish_guest_only"]
+}
+
+cc_binary {
+    name: "vsoc_guest_region_e2e_test",
+    srcs: [
+        "guest_region_e2e_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "cuttlefish_auto_resources",
+        "libcuttlefish_fs",
+        "libbase",
+    ],
+    static_libs: [
+        "libgtest",
+    ],
+    cflags: [
+        "-DGTEST_OS_LINUX_ANDROID",
+        "-DGTEST_HAS_STD_STRING",
+    ],
+    defaults: ["cuttlefish_guest_only"]
+}
+
+cc_binary {
+    name: "vsoc_managed_region_e2e_test",
+    srcs: [
+        "managed_region_e2e_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "cuttlefish_auto_resources",
+        "libcuttlefish_fs",
+        "libbase",
+    ],
+    static_libs: [
+        "libgtest",
+    ],
+    cflags: [
+        "-DGTEST_OS_LINUX_ANDROID",
+        "-DGTEST_HAS_STD_STRING",
+    ],
+    defaults: ["cuttlefish_guest_only"]
+}
diff --git a/guest/vsoc/lib/e2e_test_common.h b/guest/vsoc/lib/e2e_test_common.h
new file mode 100644
index 0000000..521f4c6
--- /dev/null
+++ b/guest/vsoc/lib/e2e_test_common.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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 <android-base/logging.h>
+
+#define DEATH_TEST_MESSAGE "abort converted to exit of 2 during death test"
+
+static inline void disable_tombstones() {
+  // We don't want a tombstone, and we're already in the child, so we modify the
+  // behavior of LOG(ABORT) to print the well known message and do an
+  // error-based exit.
+  android::base::SetAborter([](const char*) {
+    fputs(DEATH_TEST_MESSAGE, stderr);
+    fflush(stderr);
+    exit(2);
+  });
+}
diff --git a/guest/vsoc/lib/gralloc_region_view.cpp b/guest/vsoc/lib/gralloc_region_view.cpp
new file mode 100644
index 0000000..2c42f57
--- /dev/null
+++ b/guest/vsoc/lib/gralloc_region_view.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 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/vsoc/lib/gralloc_region_view.h"
+
+#include <common/vsoc/lib/lock_guard.h>
+#include <log/log.h>
+#include <sys/types.h>
+#include <uapi/vsoc_shm.h>
+#include <atomic>
+
+using vsoc::gralloc::GrallocRegionView;
+using vsoc::layout::gralloc::BufferEntry;
+using vsoc::layout::gralloc::GrallocBufferLayout;
+using vsoc::layout::gralloc::GrallocManagerLayout;
+
+namespace {
+template <typename T>
+inline T gralloc_align(T val) {
+  return (val + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+}
+
+// Use the thread id to identify the original creator of a buffer.
+inline uint32_t gralloc_owned_value() {
+  return gettid();
+}
+
+}  // namespace
+
+bool GrallocRegionView::Open() {
+  if (!vsoc::ManagerRegionView<GrallocRegionView, GrallocManagerLayout>::Open()) {
+    return false;
+  }
+  std::shared_ptr<vsoc::RegionControl> managed_region =
+      vsoc::RegionControl::Open(
+          GrallocManagerLayout::ManagedRegion::region_name);
+  if (!managed_region) {
+    LOG_FATAL("Unable to open managed region");
+    return false;
+  }
+  offset_of_buffer_memory_ = gralloc_align<uint32_t>(
+      managed_region->region_desc().offset_of_region_data);
+  total_buffer_memory_ =
+      managed_region->region_size() - offset_of_buffer_memory_;
+
+  // TODO(jemoreira): Handle the case of unexpected values in the region.
+  return true;
+}
+
+int GrallocRegionView::AllocateBuffer(size_t size, uint32_t* begin_offset) {
+  size = gralloc_align<size_t>(size);
+  // Cache the value of buffer_count in shared memory.
+  uint32_t buffer_count_local = 0;
+  {
+    vsoc::LockGuard<vsoc::layout::GuestLock>(&data()->new_buffer_lock);
+    buffer_count_local = data()->buffer_count;
+  }
+  // Find a free buffer entry of the appropriate size.
+  for (uint32_t idx = 0; idx < buffer_count_local; ++idx) {
+    BufferEntry& entry = data()->buffers_table[idx];
+    if (entry.owned_by == VSOC_REGION_FREE && entry.buffer_size() == size) {
+      int fd = control_->CreateFdScopedPermission(
+          GrallocManagerLayout::ManagedRegion::region_name,
+          pointer_to_region_offset(&entry.owned_by),
+          gralloc_owned_value(),
+          entry.buffer_begin,
+          entry.buffer_end);
+      if (fd >= 0) {
+        if (begin_offset) {
+          *begin_offset = entry.buffer_begin;
+        }
+        return fd;
+      }
+    }
+  }
+
+  // We couldn't find any suitable buffer, create one
+  {
+    vsoc::LockGuard<vsoc::layout::GuestLock>(&data()->new_buffer_lock);
+    // don't use the cached value here!!!
+    uint32_t idx = data()->buffer_count;
+    if (pointer_to_region_offset(&data()->buffers_table[idx + 1]) >
+        control_->region_size()) {
+      ALOGE(
+          "Out of memory in gralloc_manager (total: %d, used: %d, "
+          "requested: %d)",
+          static_cast<int>(control_->region_size()),
+          static_cast<int>(
+              pointer_to_region_offset(&data()->buffers_table[idx])),
+          static_cast<int>(sizeof(data()->buffers_table[idx])));
+      return -ENOMEM;
+    }
+    if (total_buffer_memory_ - data()->allocated_buffer_memory < size) {
+      ALOGE(
+          "Out of memory in gralloc_memory (total: %d, used: %d, requested: %d)",
+          static_cast<int>(total_buffer_memory_),
+          static_cast<int>(data()->allocated_buffer_memory),
+          static_cast<int>(size));
+      return -ENOMEM;
+    }
+    // Initialize the buffer entry and acquire ownership
+    // Do it before increasing buffer_count so that another thread looking for
+    // free entries doesn't find this one
+    BufferEntry& new_entry = data()->buffers_table[idx];
+    new_entry.buffer_begin =
+        offset_of_buffer_memory_ + data()->allocated_buffer_memory;
+    data()->allocated_buffer_memory += size;
+    new_entry.buffer_end = new_entry.buffer_begin + size;
+    int fd = control_->CreateFdScopedPermission(
+        GrallocManagerLayout::ManagedRegion::region_name,
+        pointer_to_region_offset(&new_entry.owned_by),
+        gralloc_owned_value(),
+        new_entry.buffer_begin,
+        new_entry.buffer_end);
+    if (fd < 0) {
+      LOG_FATAL(
+          "Unexpected error while creating fd scoped permission over "
+          "uncontested memory: %s",
+          strerror(-fd));
+      return fd;
+    }
+    // Increment buffer_count now that the entry can't be taken from us
+    data()->buffer_count++;
+    if (begin_offset) {
+      *begin_offset = new_entry.buffer_begin;
+    }
+    return fd;
+  }
+}
diff --git a/guest/vsoc/lib/gralloc_region_view.h b/guest/vsoc/lib/gralloc_region_view.h
new file mode 100644
index 0000000..ef5553f
--- /dev/null
+++ b/guest/vsoc/lib/gralloc_region_view.h
@@ -0,0 +1,52 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <common/vsoc/shm/gralloc_layout.h>
+#include <guest/vsoc/lib/manager_region_view.h>
+#include <memory>
+#include <stdlib.h>
+
+namespace vsoc {
+namespace gralloc {
+
+class GrallocRegionView : public vsoc::ManagerRegionView<
+                          GrallocRegionView,
+                          vsoc::layout::gralloc::GrallocManagerLayout> {
+ public:
+  friend TypedRegionView<
+      GrallocRegionView, vsoc::layout::gralloc::GrallocManagerLayout>;
+  GrallocRegionView() = default;
+  // Allocates a gralloc buffer of (at least) the specified size. Returns a file
+  // descriptor that exposes the buffer when mmapped from 0 to (the page
+  // aligned) size (and fails to mmap anything outside of that range) or a
+  // negative number in case of error (e.g not enough free memory left).
+  // TODO(jemoreira): Include debug info like stride, width, height, etc
+  int AllocateBuffer(size_t size, uint32_t* begin_offset = nullptr);
+
+ protected:
+  GrallocRegionView(const GrallocRegionView&) = delete;
+  GrallocRegionView & operator=(const GrallocRegionView&) = delete;
+
+  bool Open();
+
+  uint32_t offset_of_buffer_memory_{};
+  uint32_t total_buffer_memory_{};
+};
+
+} // namespace gralloc
+} // namespace vsoc
diff --git a/guest/vsoc/lib/guest_lock.cpp b/guest/vsoc/lib/guest_lock.cpp
new file mode 100644
index 0000000..247b48b
--- /dev/null
+++ b/guest/vsoc/lib/guest_lock.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+#include "common/vsoc/lib/compat.h"
+#include "common/vsoc/lib/single_sided_signal.h"
+
+namespace vsoc {
+namespace layout {
+
+void GuestLock::Lock() {
+  uint32_t tid = gettid();
+  uint32_t expected_value;
+  uint32_t* uaddr = reinterpret_cast<uint32_t*>(&lock_uint32_);
+
+  while (1) {
+    if (TryLock(tid, &expected_value)) {
+      return;
+    }
+    SingleSidedSignal::AwaitSignal(expected_value, uaddr);
+  }
+}
+
+void GuestLock::Unlock() {
+  Sides sides_to_signal = UnlockCommon(gettid());
+  if (sides_to_signal != Sides::NoSides) {
+    SingleSidedSignal::Signal(&lock_uint32_);
+  }
+}
+
+bool GuestLock::Recover() {
+  return RecoverSingleSided();
+}
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/guest/vsoc/lib/guest_region_e2e_test.cpp b/guest/vsoc/lib/guest_region_e2e_test.cpp
new file mode 100644
index 0000000..5fd644a
--- /dev/null
+++ b/guest/vsoc/lib/guest_region_e2e_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/*
+ * End-to-end test to ensure that mapping of vsoc regions works on the guest.
+ */
+
+#include "guest/vsoc/lib/e2e_test_common.h"
+#include "common/vsoc/lib/e2e_test_region_view.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+template <typename View>
+void DeathTestView() {
+  disable_tombstones();
+  // View::GetInstance should never return.
+  EXPECT_FALSE(!!View::GetInstance());
+}
+
+// Here is a summary of the two regions interrupt and write test:
+// 1. Write our strings to the first region
+// 2. Ensure that our peer hasn't signalled the second region. That would
+//    indicate that it didn't wait for our interrupt.
+// 3. Send the interrupt on the first region
+// 4. Wait for our peer's interrupt on the first region
+// 5. Confirm that we can see our peer's writes in the first region
+// 6. Initialize our strings in the second region
+// 7. Send an interrupt on the second region to our peer
+// 8. Wait for our peer's interrupt on the second region
+// 9. Confirm that we can see our peer's writes in the second region
+// 10. Repeat the process for signaling.
+// 11. Confirm that no interrupt is pending in the first region
+// 12. Confirm that no interrupt is pending in the second region
+
+template <typename View>
+void SetGuestStrings(View* in) {
+  size_t num_data = in->string_size();
+  EXPECT_LE(2U, num_data);
+  for (size_t i = 0; i < num_data; ++i) {
+    EXPECT_TRUE(!in->guest_string(i)[0] ||
+                !strcmp(in->guest_string(i), View::Layout::guest_pattern));
+    in->set_guest_string(i, View::Layout::guest_pattern);
+    EXPECT_STREQ(in->guest_string(i), View::Layout::guest_pattern);
+  }
+}
+
+template <typename View>
+void CheckPeerStrings(View* in) {
+  size_t num_data = in->string_size();
+  EXPECT_LE(2U, num_data);
+  for (size_t i = 0; i < num_data; ++i) {
+    EXPECT_STREQ(View::Layout::host_pattern, in->host_string(i));
+  }
+}
+
+TEST(RegionTest, BasicPeerTests) {
+  auto primary = vsoc::E2EPrimaryRegionView::GetInstance();
+  auto secondary = vsoc::E2ESecondaryRegionView::GetInstance();
+  ASSERT_TRUE(!!primary);
+  ASSERT_TRUE(!!secondary);
+  LOG(INFO) << "Regions are open";
+  SetGuestStrings(primary);
+  LOG(INFO) << "Primary guest strings are set";
+  EXPECT_FALSE(secondary->HasIncomingInterrupt());
+  LOG(INFO) << "Verified no early second interrupt";
+  EXPECT_TRUE(primary->MaybeInterruptPeer());
+  LOG(INFO) << "Interrupt sent. Waiting for first interrupt from peer";
+  primary->WaitForInterrupt();
+  LOG(INFO) << "First interrupt received";
+  CheckPeerStrings(primary);
+  LOG(INFO) << "Verified peer's primary strings";
+  SetGuestStrings(secondary);
+  LOG(INFO) << "Secondary guest strings are set";
+  EXPECT_TRUE(secondary->MaybeInterruptPeer());
+  LOG(INFO) << "Second interrupt sent";
+  secondary->WaitForInterrupt();
+  LOG(INFO) << "Second interrupt received";
+  CheckPeerStrings(secondary);
+  LOG(INFO) << "Verified peer's secondary strings";
+
+  // Test signals
+  EXPECT_FALSE(secondary->HasIncomingInterrupt());
+  LOG(INFO) << "Verified no early second signal";
+  primary->SendSignal(vsoc::layout::Sides::Peer,
+                      &primary->data()->guest_to_host_signal);
+  LOG(INFO) << "Signal sent. Waiting for first signal from peer";
+  primary->WaitForInterrupt();
+  int count = 0;  // counts the number of signals received.
+  primary->ProcessSignalsFromPeer(
+      [&primary, &count](uint32_t offset) {
+        ++count;
+        EXPECT_TRUE(offset == primary->host_to_guest_signal_offset());
+      });
+  EXPECT_TRUE(count == 1);
+  LOG(INFO) << "Signal received on primary region";
+  secondary->SendSignal(vsoc::layout::Sides::Peer,
+                        &secondary->data()->guest_to_host_signal);
+  LOG(INFO) << "Signal sent. Waiting for second signal from peer";
+  secondary->WaitForInterrupt();
+  count = 0;
+  secondary->ProcessSignalsFromPeer(
+      [&secondary, &count](uint32_t offset) {
+        ++count;
+        EXPECT_TRUE(offset == secondary->host_to_guest_signal_offset());
+      });
+  EXPECT_TRUE(count == 1);
+  LOG(INFO) << "Signal received on secondary region";
+
+  EXPECT_FALSE(primary->HasIncomingInterrupt());
+  EXPECT_FALSE(secondary->HasIncomingInterrupt());
+  LOG(INFO) << "PASS: BasicPeerTests";
+}
+
+TEST(RegionTest, MissingRegionDeathTest) {
+  // EXPECT_DEATH creates a child for the test, so we do it out here.
+  // DeathTestGuestRegion will actually do the deadly call after ensuring
+  // that we don't create an unwanted tombstone.
+  EXPECT_EXIT(DeathTestView<vsoc::E2EUnfindableRegionView>(),
+              testing::ExitedWithCode(2),
+              ".*" DEATH_TEST_MESSAGE ".*");
+}
+
+int main(int argc, char** argv) {
+  if (argc == 2) {
+    // gtest tries to leave temporary files in the current directory, so make the
+    // current directory something that we control.
+    if (chdir(argv[1]) != 0) {
+      abort();
+    }
+  }
+  android::base::InitLogging(argv);
+  testing::InitGoogleTest(&argc, argv);
+  int rval = RUN_ALL_TESTS();
+  if (!rval) {
+    auto region = vsoc::E2EPrimaryRegionView::GetInstance();
+    region->guest_status(vsoc::layout::e2e_test::E2E_MEMORY_FILLED);
+    LOG(INFO) << "stage_1_guest_region_e2e_tests PASSED";
+  } else {
+    LOG(ERROR) << "stage_1_guest_region_e2e_tests FAILED";
+  }
+  return rval;
+}
diff --git a/guest/vsoc/lib/managed_region_e2e_test.cpp b/guest/vsoc/lib/managed_region_e2e_test.cpp
new file mode 100644
index 0000000..390a6b7
--- /dev/null
+++ b/guest/vsoc/lib/managed_region_e2e_test.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/managed_e2e_test_region_layout.h"
+#include "guest/vsoc/lib/e2e_test_common.h"
+#include "guest/vsoc/lib/manager_region_view.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+using vsoc::layout::e2e_test::E2EManagedTestRegionLayout;
+using vsoc::layout::e2e_test::E2EManagerTestRegionLayout;
+
+// Region view classes to allow calling the Open() function from the test.
+class E2EManagedTestRegionView
+    : public vsoc::TypedRegionView<
+        E2EManagedTestRegionView,
+        E2EManagedTestRegionLayout> {
+ public:
+  using vsoc::TypedRegionView<
+      E2EManagedTestRegionView, E2EManagedTestRegionLayout>::Open;
+};
+class E2EManagerTestRegionView
+    : public vsoc::ManagerRegionView<
+        E2EManagerTestRegionView,
+        E2EManagerTestRegionLayout> {
+ public:
+  using vsoc::ManagerRegionView<
+      E2EManagerTestRegionView, E2EManagerTestRegionLayout>::Open;
+};
+
+class ManagedRegionTest {
+ public:
+  void testManagedRegionFailMap() {
+    E2EManagedTestRegionView managed_region;
+    disable_tombstones();
+    // managed_region.Open should never return.
+    EXPECT_FALSE(managed_region.Open());
+  }
+
+  void testManagedRegionMap() {
+    EXPECT_TRUE(manager_region_.Open());
+
+    // Maps correctly with permission
+    const uint32_t owned_value = 65, begin_offset = 4096, end_offset = 8192;
+    int perm_fd = manager_region_.CreateFdScopedPermission(
+        &manager_region_.data()->data[0], owned_value, begin_offset,
+        end_offset);
+    EXPECT_TRUE(perm_fd >= 0);
+    fd_scoped_permission perm;
+    ASSERT_TRUE(ioctl(perm_fd, VSOC_GET_FD_SCOPED_PERMISSION, &perm) == 0);
+    void* mapped_ptr = mmap(NULL, perm.end_offset - perm.begin_offset,
+                            PROT_WRITE | PROT_READ, MAP_SHARED, perm_fd, 0);
+    EXPECT_FALSE(mapped_ptr == MAP_FAILED);
+
+    // Owned value gets written
+    EXPECT_TRUE(manager_region_.data()->data[0] == owned_value);
+
+    // Data written to the mapped memory stays there after unmap
+    std::string str = "managed by e2e_manager";
+    strcpy(reinterpret_cast<char*>(mapped_ptr), str.c_str());
+    EXPECT_TRUE(munmap(mapped_ptr, end_offset - begin_offset) == 0);
+    mapped_ptr = mmap(NULL, end_offset - begin_offset, PROT_WRITE | PROT_READ,
+                      MAP_SHARED, perm_fd, 0);
+    EXPECT_FALSE(mapped_ptr == MAP_FAILED);
+    EXPECT_TRUE(strcmp(reinterpret_cast<char*>(mapped_ptr), str.c_str()) == 0);
+
+    // Create permission elsewhere in the region, map same offset and length,
+    // ensure data isn't there
+    EXPECT_TRUE(munmap(mapped_ptr, end_offset - begin_offset) == 0);
+    close(perm_fd);
+    EXPECT_TRUE(manager_region_.data()->data[0] == 0);
+    perm_fd = manager_region_.CreateFdScopedPermission(
+        &manager_region_.data()->data[1], owned_value, begin_offset + 4096,
+        end_offset + 4096);
+    EXPECT_TRUE(perm_fd >= 0);
+    mapped_ptr = mmap(NULL, end_offset - begin_offset, PROT_WRITE | PROT_READ,
+                      MAP_SHARED, perm_fd, 0);
+    EXPECT_FALSE(mapped_ptr == MAP_FAILED);
+    EXPECT_FALSE(strcmp(reinterpret_cast<char*>(mapped_ptr), str.c_str()) == 0);
+  }
+  ManagedRegionTest() {}
+
+ private:
+  E2EManagerTestRegionView manager_region_;
+};
+
+TEST(ManagedRegionTest, ManagedRegionFailMap) {
+  ManagedRegionTest test;
+  EXPECT_EXIT(test.testManagedRegionFailMap(), testing::ExitedWithCode(2),
+              ".*" DEATH_TEST_MESSAGE ".*");
+}
+
+TEST(ManagedRegionTest, ManagedRegionMap) {
+  ManagedRegionTest test;
+  test.testManagedRegionMap();
+}
+
+int main(int argc, char** argv) {
+  if (argc == 2) {
+    // gtest tries to leave temporary files in the current directory, so make the
+    // current directory something that we control.
+    if (chdir(argv[1]) != 0) {
+      abort();
+    }
+  }
+  android::base::InitLogging(argv);
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/guest/vsoc/lib/manager_region_view.h b/guest/vsoc/lib/manager_region_view.h
new file mode 100644
index 0000000..c2a873d
--- /dev/null
+++ b/guest/vsoc/lib/manager_region_view.h
@@ -0,0 +1,58 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/typed_region_view.h"
+
+namespace vsoc {
+
+/**
+ * Adds methods to create file descriptor scoped permissions. Just like
+ * TypedRegionView it can be directly constructed or subclassed.
+ *
+ * The Layout type must (in addition to requirements for TypedRegionView) also
+ * provide a nested type for the layout of the managed region.
+ */
+template <typename View, typename Layout>
+class ManagerRegionView : public TypedRegionView<View, Layout> {
+ public:
+  ManagerRegionView() = default;
+  /**
+   * Creates a fd scoped permission on the managed region.
+   *
+   * The managed_region_fd is in/out parameter that can be a not yet open file
+   * descriptor. If the fd is not open yet it will open the managed region
+   * device and then create the permission. If the function returns EBUSY
+   * (meaning that we lost the race to acquire the memory) the same fd can (and
+   * is expected to) be used in a subsequent call to create a permission on
+   * another memory location.
+   *
+   * On success returns an open fd with the requested permission asociated to
+   * it. If another thread/process acquired ownership of *owner_ptr before this
+   * one returns -EBUSY, returns a different negative number otherwise.
+   */
+  int CreateFdScopedPermission(uint32_t* owner_ptr, uint32_t owned_val,
+                               uint32_t begin_offset,
+                               uint32_t end_offset) {
+    return this->control_->CreateFdScopedPermission(
+        Layout::ManagedRegion::region_name,
+        this->pointer_to_region_offset(owner_ptr), owned_val, begin_offset,
+        end_offset);
+  }
+};
+
+}  // namespace vsoc
diff --git a/guest/vsoc/lib/region_control.cpp b/guest/vsoc/lib/region_control.cpp
new file mode 100644
index 0000000..1fcbd13
--- /dev/null
+++ b/guest/vsoc/lib/region_control.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <uapi/vsoc_shm.h>
+
+using cvd::SharedFD;
+
+namespace {
+class GuestRegionControl : public vsoc::RegionControl {
+ public:
+  explicit GuestRegionControl(const SharedFD& region_fd,
+                              const vsoc_device_region& desc)
+      : region_fd_{region_fd} {
+    region_desc_ = desc;
+  }
+  virtual bool InterruptPeer() override;
+  virtual void InterruptSelf() override;
+  virtual void WaitForInterrupt() override;
+  virtual void* Map() override;
+  virtual int SignalSelf(uint32_t offset) override;
+  virtual int WaitForSignal(uint32_t offset, uint32_t expected_value) override;
+
+ protected:
+  int CreateFdScopedPermission(const char* managed_region_name,
+                               uint32_t owner_offset, uint32_t owned_val,
+                               uint32_t begin_offset,
+                               uint32_t end_offset) override;
+  cvd::SharedFD region_fd_;
+};
+
+std::string device_path_from_name(const char* region_name) {
+  return std::string("/dev/") + region_name;
+}
+
+bool GuestRegionControl::InterruptPeer() {
+  int rval = region_fd_->Ioctl(VSOC_SEND_INTERRUPT_TO_HOST, 0);
+  if ((rval == -1) && (errno != EBUSY)) {
+    LOG(INFO) << __FUNCTION__ << ": ioctl failed (" << strerror(errno) << ")";
+  }
+  return !rval;
+}
+
+void GuestRegionControl::InterruptSelf() {
+  region_fd_->Ioctl(VSOC_SELF_INTERRUPT, 0);
+}
+
+void GuestRegionControl::WaitForInterrupt() {
+  region_fd_->Ioctl(VSOC_WAIT_FOR_INCOMING_INTERRUPT, 0);
+}
+
+int GuestRegionControl::SignalSelf(uint32_t offset) {
+  return region_fd_->Ioctl(VSOC_COND_WAKE, reinterpret_cast<void*>(offset));
+}
+
+int GuestRegionControl::WaitForSignal(uint32_t offset,
+                                      uint32_t expected_value) {
+  struct vsoc_cond_wait wait;
+  wait.offset = offset;
+  wait.value = expected_value;
+  wait.wake_time_sec = 0;
+  wait.wake_time_nsec = 0;
+  wait.wait_type = VSOC_WAIT_IF_EQUAL;
+  wait.wakes = 1000;
+  wait.reserved_1 = 0;
+  int rval = region_fd_->Ioctl(VSOC_COND_WAIT, &wait);
+  if (rval == -1) {
+    return rval;
+  }
+  // Clamp the number of wakes if it overflows an integer.
+  rval = wait.wakes;
+  if (rval >= 0) {
+    return rval;
+  }
+  return INT_MAX;
+}
+
+int GuestRegionControl::CreateFdScopedPermission(
+    const char* managed_region_name, uint32_t owner_offset,
+    uint32_t owned_value, uint32_t begin_offset,
+    uint32_t end_offset) {
+  if (!region_fd_->IsOpen()) {
+    LOG(FATAL) << "Can't create permission before opening controller region";
+    return -EINVAL;
+  }
+  int managed_region_fd =
+      open(device_path_from_name(managed_region_name).c_str(), O_RDWR);
+  if (managed_region_fd < 0) {
+    int errno_ = errno;
+    LOG(FATAL) << "Can't open managed region: " << managed_region_name;
+    return -errno_;
+  }
+
+  fd_scoped_permission_arg perm;
+  perm.perm.begin_offset = begin_offset;
+  perm.perm.end_offset = end_offset;
+  perm.perm.owned_value = owned_value;
+  perm.perm.owner_offset = owner_offset;
+  perm.managed_region_fd = managed_region_fd;
+  LOG(INFO) << "owner offset: " << perm.perm.owner_offset;
+  int retval = region_fd_->Ioctl(VSOC_CREATE_FD_SCOPED_PERMISSION, &perm);
+  if (retval) {
+    int errno_ = errno;
+    close(managed_region_fd);
+    if (errno_ != EBUSY) {
+      LOG(FATAL) << "Unable to create fd scoped permission ("
+                 << strerror(errno_) << ")";
+    }
+    return -errno_;
+  }
+  return managed_region_fd;
+}
+
+void* GuestRegionControl::Map() {
+  region_base_ =
+      region_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE, MAP_SHARED, 0);
+  if (region_base_ == MAP_FAILED) {
+    LOG(FATAL) << "mmap failed (" << region_fd_->StrError() << ")";
+    region_base_ = nullptr;
+  }
+  return region_base_;
+}
+}  // namespace
+
+// domain is here to ensure that this method has the same signature as the
+// method on host regions.
+std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open(
+    const char* region_name) {
+  std::string path = device_path_from_name(region_name);
+  SharedFD fd = SharedFD::Open(path.c_str(), O_RDWR);
+  if (!fd->IsOpen()) {
+    LOG(FATAL) << "Unable to open region " << region_name << " ("
+               << fd->StrError() << ")";
+    return nullptr;
+  }
+  vsoc_device_region desc;
+  if (fd->Ioctl(VSOC_DESCRIBE_REGION, &desc)) {
+    LOG(FATAL) << "Unable to obtain region descriptor (" << fd->StrError()
+               << ")";
+    return nullptr;
+  }
+  return std::shared_ptr<vsoc::RegionControl>(new GuestRegionControl(fd, desc));
+}
diff --git a/guest/vsoc/lib/region_view.cpp b/guest/vsoc/lib/region_view.cpp
new file mode 100644
index 0000000..db1bec0
--- /dev/null
+++ b/guest/vsoc/lib/region_view.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h"
+#include "common/vsoc/lib/region_control.h"
+
+const vsoc_signal_table_layout& vsoc::RegionView::incoming_signal_table() {
+  return control_->region_desc().host_to_guest_signal_table;
+}
+
+// Returns a pointer to the table that will be used to post signals
+const vsoc_signal_table_layout& vsoc::RegionView::outgoing_signal_table() {
+  return control_->region_desc().guest_to_host_signal_table;
+}
diff --git a/guest/vsoc/lib/vsoc_driver_test.cpp b/guest/vsoc/lib/vsoc_driver_test.cpp
new file mode 100644
index 0000000..ff20ecb
--- /dev/null
+++ b/guest/vsoc/lib/vsoc_driver_test.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+/*
+ * Stand-alone tests for the ioctls in the vsoc driver.
+ */
+
+#include "uapi/vsoc_shm.h"
+#include <atomic>
+#include <stdint.h>
+#include "common/vsoc/lib/e2e_test_region_view.h"
+#include "guest/vsoc/lib/manager_region_view.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+void BasicWaitForSignal(vsoc::E2EPrimaryRegionView* region,
+                        uint32_t expected_start,
+                        uint32_t expected_finish) {
+  ASSERT_EQ(expected_start, region->read_guest_self_register());
+  int rval = region->wait_guest_self_register(expected_start);
+  EXPECT_LE(0, rval);
+  EXPECT_GT(5, rval);
+  EXPECT_EQ(expected_finish, region->read_guest_self_register());
+}
+
+TEST(FutexTest, BasicFutexTests) {
+  constexpr uint32_t INITIAL_SIGNAL = 0;
+  constexpr uint32_t SILENT_UPDATE_SIGNAL = 1;
+  constexpr uint32_t WAKE_SIGNAL = 2;
+  auto region = vsoc::E2EPrimaryRegionView::GetInstance();
+  ASSERT_TRUE(region != NULL);
+  LOG(INFO) << "Regions are open";
+  region->write_guest_self_register(INITIAL_SIGNAL);
+  std::thread waiter(BasicWaitForSignal, region, INITIAL_SIGNAL, WAKE_SIGNAL);
+  sleep(1);
+  LOG(INFO) << "Still waiting. Trying to wake wrong address";
+  region->signal_guest_to_host_register();
+  sleep(1);
+  LOG(INFO) << "Still waiting. Trying to wake without update";
+  region->signal_guest_self_register();
+  sleep(1);
+  LOG(INFO) << "Still waiting. Trying to wake without signal";
+  region->write_guest_self_register(SILENT_UPDATE_SIGNAL);
+  sleep(1);
+  LOG(INFO) << "Still waiting. Trying to wake with signal";
+  region->write_guest_self_register(WAKE_SIGNAL);
+  region->signal_guest_self_register();
+  waiter.join();
+  LOG(INFO) << "Wake worked";
+  LOG(INFO) << "PASS: BasicPeerTests";
+}
+
+int main(int argc, char* argv[]) {
+  android::base::InitLogging(argv);
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/host/commands/Android.bp b/host/commands/Android.bp
index c200611..a6f059d 100644
--- a/host/commands/Android.bp
+++ b/host/commands/Android.bp
@@ -19,6 +19,7 @@
     "launch",
     "stop_cvd",
     "ivserver",
+    "record_audio",
     "virtual_usb_manager",
     "kernel_log_monitor",
     "config_server",
diff --git a/host/commands/assemble_cvd/Android.bp b/host/commands/assemble_cvd/Android.bp
index 75d85d0..c5ac4c9 100644
--- a/host/commands/assemble_cvd/Android.bp
+++ b/host/commands/assemble_cvd/Android.bp
@@ -44,6 +44,7 @@
     ],
     shared_libs: [
         "cdisk_spec",
+        "vsoc_lib",
         "libcuttlefish_fs",
         "libcuttlefish_utils",
         "cuttlefish_auto_resources",
@@ -56,6 +57,7 @@
     static_libs: [
         "libcuttlefish_host_config",
         "libcuttlefish_vm_manager",
+        "libivserver",
         "libgflags",
         "libxml2",
         "libjsoncpp",
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 09e76e6..5ae5a91 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -9,6 +9,7 @@
 
 #include "common/libs/utils/environment.h"
 #include "common/libs/utils/files.h"
+#include "common/vsoc/lib/vsoc_memory.h"
 #include "host/commands/assemble_cvd/boot_image_unpacker.h"
 #include "host/commands/assemble_cvd/data_image.h"
 #include "host/commands/assemble_cvd/image_aggregator.h"
@@ -112,6 +113,11 @@
 DEFINE_string(vnc_server_binary,
               vsoc::DefaultHostArtifactsPath("bin/vnc_server"),
               "Location of the vnc server binary.");
+DEFINE_bool(start_stream_audio, false,
+            "Whether to start the stream audio process.");
+DEFINE_string(stream_audio_binary,
+              vsoc::DefaultHostArtifactsPath("bin/stream_audio"),
+              "Location of the stream_audio binary.");
 DEFINE_string(virtual_usb_manager_binary,
               vsoc::DefaultHostArtifactsPath("bin/virtual_usb_manager"),
               "Location of the virtual usb manager binary.");
@@ -123,6 +129,8 @@
               "Location of the ivshmem server binary.");
 DEFINE_int32(vnc_server_port, GetPerInstanceDefault(6444),
              "The port on which the vnc server should listen");
+DEFINE_int32(stream_audio_port, GetPerInstanceDefault(7444),
+             "The port on which stream_audio should listen.");
 DEFINE_string(socket_forward_proxy_binary,
               vsoc::DefaultHostArtifactsPath("bin/socket_forward_proxy"),
               "Location of the socket_forward_proxy binary.");
@@ -273,6 +281,7 @@
     const cvd::BootImageUnpacker& boot_image_unpacker,
     const cvd::FetcherConfig& fetcher_config) {
   vsoc::CuttlefishConfig tmp_config_obj;
+  auto& memory_layout = *vsoc::VSoCMemoryLayout::Get();
   // Set this first so that calls to PerInstancePath below are correct
   tmp_config_obj.set_instance_dir(FLAGS_instance_dir);
   if (!vm_manager::VmManager::IsValidName(FLAGS_vm_manager)) {
@@ -435,7 +444,7 @@
       tmp_config_obj.PerInstanceInternalPath("ivshmem_socket_qemu"));
   tmp_config_obj.set_ivshmem_client_socket_path(
       tmp_config_obj.PerInstanceInternalPath("ivshmem_socket_client"));
-  tmp_config_obj.set_ivshmem_vector_count(0);
+  tmp_config_obj.set_ivshmem_vector_count(memory_layout.GetRegions().size());
 
   if (tmp_config_obj.adb_mode().count(vsoc::AdbMode::Usb) > 0) {
     tmp_config_obj.set_usb_v1_socket_name(
@@ -478,6 +487,10 @@
   tmp_config_obj.set_vnc_server_binary(FLAGS_vnc_server_binary);
   tmp_config_obj.set_vnc_server_port(FLAGS_vnc_server_port);
 
+  tmp_config_obj.set_enable_stream_audio(FLAGS_start_stream_audio);
+  tmp_config_obj.set_stream_audio_binary(FLAGS_stream_audio_binary);
+  tmp_config_obj.set_stream_audio_port(FLAGS_stream_audio_port);
+
   tmp_config_obj.set_restart_subprocesses(FLAGS_restart_subprocesses);
   tmp_config_obj.set_run_adb_connector(FLAGS_run_adb_connector);
   tmp_config_obj.set_adb_connector_binary(FLAGS_adb_connector_binary);
diff --git a/host/commands/ivserver/Android.bp b/host/commands/ivserver/Android.bp
new file mode 100644
index 0000000..4fc47a9
--- /dev/null
+++ b/host/commands/ivserver/Android.bp
@@ -0,0 +1,83 @@
+//
+// Copyright (C) 2017 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.
+
+cc_library_host_static {
+    name: "libivserver",
+    srcs: [
+        "hald_client.cc",
+        "ivserver.cc",
+        "options.cc",
+        "qemu_client.cc",
+        "vsocsharedmem.cc",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libbase",
+        "vsoc_lib",
+    ],
+    static_libs: [
+        "libjsoncpp",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
+
+cc_test_host {
+    name: "hald_client_test",
+    srcs: [
+        "hald_client_test.cc",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+        "vsoc_lib",
+    ],
+    static_libs: [
+        "libivserver",
+        "libjsoncpp",
+        "libgmock",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
+
+cc_binary_host {
+    name: "ivserver",
+    srcs: [
+        "main.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libbase",
+        "libcuttlefish_fs",
+        "libcuttlefish_utils",
+        "cuttlefish_auto_resources",
+        "liblog",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libivserver",
+        "libgflags",
+        "libjsoncpp",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/commands/ivserver/hald_client.cc b/host/commands/ivserver/hald_client.cc
new file mode 100644
index 0000000..98a83f6
--- /dev/null
+++ b/host/commands/ivserver/hald_client.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/commands/ivserver/hald_client.h"
+
+#include <string>
+
+#include <glog/logging.h>
+
+namespace ivserver {
+namespace {
+// The protocol between host-clients and the ivserver could change.
+// Clients should verify what version they are talking to during the handshake.
+const uint32_t kHaldClientProtocolVersion = 0;
+}  // anonymous namespace
+
+std::unique_ptr<HaldClient> HaldClient::New(const VSoCSharedMemory& shmem,
+                                            const cvd::SharedFD& clientfd) {
+  std::unique_ptr<HaldClient> res;
+
+  if (!clientfd->IsOpen()) {
+    LOG(WARNING) << "Invalid socket passed to HaldClient: "
+                 << clientfd->StrError();
+    return res;
+  }
+
+  res.reset(new HaldClient(clientfd));
+  if (!res->PerformHandshake(shmem)) {
+    LOG(ERROR) << "HalD handshake failed. Dropping connection.";
+    res.reset();
+  }
+
+  return res;
+}
+
+HaldClient::HaldClient(const cvd::SharedFD& client_socket)
+    : client_socket_(client_socket) {}
+
+bool HaldClient::PerformHandshake(const VSoCSharedMemory& shared_mem) {
+  int rval =
+      client_socket_->Send(&kHaldClientProtocolVersion,
+                           sizeof(kHaldClientProtocolVersion), MSG_NOSIGNAL);
+  if (rval != sizeof(kHaldClientProtocolVersion)) {
+    LOG(ERROR) << "failed to send protocol version: "
+               << client_socket_->StrError();
+    return false;
+  }
+
+  int16_t region_name_len;
+  if (client_socket_->Recv(&region_name_len, sizeof(region_name_len),
+                           MSG_NOSIGNAL) != sizeof(region_name_len)) {
+    LOG(ERROR) << "Error receiving region name length: "
+               << client_socket_->StrError();
+    return false;
+  }
+
+  if (region_name_len <= 0 ||
+      region_name_len > VSOC_DEVICE_NAME_SZ) {
+    LOG(ERROR) << "Invalid region length received: " << region_name_len;
+    return false;
+  }
+
+  std::vector<char> region_name_data(region_name_len);
+  rval = client_socket_->Recv(region_name_data.data(), region_name_len,
+                              MSG_NOSIGNAL);
+  if (rval != region_name_len) {
+    LOG(ERROR) << "Incomplete region name length received. Want: "
+               << region_name_len << ", got: " << rval;
+    return false;
+  }
+
+  std::string region_name(region_name_data.begin(), region_name_data.end());
+  LOG(INFO) << "New HALD requesting region: " << region_name;
+
+  // Send Host, Guest and SharedMemory FDs associated with this region.
+  cvd::SharedFD guest_to_host_efd;
+  cvd::SharedFD host_to_guest_efd;
+
+  if (!shared_mem.GetEventFdPairForRegion(region_name, &guest_to_host_efd,
+                                          &host_to_guest_efd)) {
+    LOG(ERROR) << "Region " << region_name << " was not found.";
+    return false;
+  }
+
+  if (!guest_to_host_efd->IsOpen()) {
+    LOG(ERROR) << "Host channel is not open; last known error: "
+               << guest_to_host_efd->StrError();
+    return false;
+  }
+
+  if (!host_to_guest_efd->IsOpen()) {
+    LOG(ERROR) << "Guest channel is not open; last known error: "
+               << host_to_guest_efd->StrError();
+    return false;
+  }
+
+  // TODO(ender): delete this once no longer necessary. Currently, absence of
+  // payload makes RecvMsgAndFDs hang forever.
+  uint64_t control_data = 0;
+  struct iovec vec {
+    &control_data, sizeof(control_data)
+  };
+  cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+  cvd::SharedFD fds[3] = {guest_to_host_efd, host_to_guest_efd,
+                          shared_mem.SharedMemFD()};
+  rval = client_socket_->SendMsgAndFDs<3>(hdr, MSG_NOSIGNAL, fds);
+  if (rval == -1) {
+    LOG(ERROR) << "failed to send Host FD: " << client_socket_->StrError();
+    return false;
+  }
+
+  LOG(INFO) << "HALD managing region: " << region_name << " connected.";
+  return true;
+}
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/hald_client.h b/host/commands/ivserver/hald_client.h
new file mode 100644
index 0000000..3b0fe2b
--- /dev/null
+++ b/host/commands/ivserver/hald_client.h
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <inttypes.h>
+#include <unistd.h>
+#include <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/commands/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+
+// Handles a HAL deamon client connection & handshake.
+class HaldClient final {
+ public:
+  static std::unique_ptr<HaldClient> New(const VSoCSharedMemory &shared_mem,
+                                         const cvd::SharedFD &client_fd);
+
+ private:
+  cvd::SharedFD client_socket_;
+
+  // Initialize new instance of HAL Client.
+  HaldClient(const cvd::SharedFD &client_fd);
+
+  // Perform handshake with HAL Client.
+  // If the region is not found approprate status (-1) is sent.
+  // Note that for every new client connected a unique ClientConnection object
+  // will be created and after the handshake it will be destroyed.
+  bool PerformHandshake(const VSoCSharedMemory &shared_mem);
+
+  HaldClient(const HaldClient &) = delete;
+  HaldClient &operator=(const HaldClient &) = delete;
+};
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/hald_client_test.cc b/host/commands/ivserver/hald_client_test.cc
new file mode 100644
index 0000000..ee65530
--- /dev/null
+++ b/host/commands/ivserver/hald_client_test.cc
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 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 <stdlib.h>
+#include <memory>
+#include <string>
+#include <thread>
+
+#include <glog/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/commands/ivserver/hald_client.h"
+#include "host/commands/ivserver/vsocsharedmem_mock.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SetArgPointee;
+
+namespace ivserver {
+namespace test {
+
+class HaldClientTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    std::string socket_location;
+    ASSERT_TRUE(GetTempLocation(&socket_location))
+        << "Could not create temp file";
+    LOG(INFO) << "Temp file location: " << socket_location;
+
+    hald_server_socket_ = cvd::SharedFD::SocketLocalServer(
+        socket_location.c_str(), false, SOCK_STREAM, 0666);
+    test_socket_ = cvd::SharedFD::SocketLocalClient(socket_location.c_str(),
+                                                    false, SOCK_STREAM);
+    hald_socket_ =
+        cvd::SharedFD::Accept(*hald_server_socket_, nullptr, nullptr);
+
+    EXPECT_TRUE(hald_server_socket_->IsOpen());
+    EXPECT_TRUE(test_socket_->IsOpen());
+    EXPECT_TRUE(hald_socket_->IsOpen());
+  }
+
+  bool GetTempLocation(std::string* target) {
+    char temp_location[] = "/tmp/hald-client-test-XXXXXX";
+    mktemp(temp_location);
+    *target = temp_location;
+    if (target->size()) {
+      cleanup_files_.emplace_back(*target);
+      return true;
+    }
+    return false;
+  }
+
+  void TearDown() override {
+    hald_socket_->Close();
+    test_socket_->Close();
+    hald_server_socket_->Close();
+
+    for (const std::string& file : cleanup_files_) {
+      unlink(file.c_str());
+    }
+  }
+
+ protected:
+  ::testing::NiceMock<VSoCSharedMemoryMock> vsoc_;
+
+  cvd::SharedFD hald_server_socket_;
+  cvd::SharedFD hald_socket_;
+  cvd::SharedFD test_socket_;
+
+ private:
+  std::vector<std::string> cleanup_files_;
+};
+
+TEST_F(HaldClientTest, HandshakeTerminatedByHald) {
+  std::thread thread(
+      [this]() {
+        auto client(HaldClient::New(vsoc_, hald_socket_));
+        EXPECT_FALSE(client)
+            << "Handshake should not complete when client terminates early.";
+      });
+
+  test_socket_->Close();
+  thread.join();
+}
+
+TEST_F(HaldClientTest, HandshakeTerminatedByInvalidRegionSize) {
+  uint16_t sizes[] = {0, VSOC_DEVICE_NAME_SZ + 1, 0xffff};
+
+  for (uint16_t size : sizes) {
+    std::thread thread([this, size]() {
+      auto client(HaldClient::New(vsoc_, hald_socket_));
+      EXPECT_FALSE(client) << "Handle should not be created when size is "
+                           << size;
+    });
+
+    int32_t proto_version;
+    EXPECT_EQ(sizeof(proto_version),
+              static_cast<size_t>(test_socket_->Recv(
+                  &proto_version, sizeof(proto_version), MSG_NOSIGNAL)));
+
+    test_socket_->Send(&size, sizeof(size), MSG_NOSIGNAL);
+    thread.join();
+  }
+}
+
+TEST_F(HaldClientTest, FullSaneHandshake) {
+  std::string temp;
+  ASSERT_TRUE(GetTempLocation(&temp));
+  cvd::SharedFD host_fd(
+      cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+  EXPECT_TRUE(host_fd->IsOpen());
+
+  ASSERT_TRUE(GetTempLocation(&temp));
+  cvd::SharedFD guest_fd(
+      cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+  EXPECT_TRUE(guest_fd->IsOpen());
+
+  ASSERT_TRUE(GetTempLocation(&temp));
+  cvd::SharedFD shmem_fd(
+      cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+  EXPECT_TRUE(shmem_fd->IsOpen());
+
+  const std::string test_location("testing");
+  EXPECT_CALL(vsoc_, GetEventFdPairForRegion(test_location, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(host_fd), SetArgPointee<2>(guest_fd),
+                      Return(true)));
+  EXPECT_CALL(vsoc_, SharedMemFD()).WillOnce(ReturnRef(shmem_fd));
+
+  std::thread thread([this]() {
+    auto client(HaldClient::New(vsoc_, hald_socket_));
+    EXPECT_TRUE(client);
+  });
+
+  int32_t proto_version;
+  EXPECT_EQ(sizeof(proto_version),
+            static_cast<size_t>(test_socket_->Recv(
+                &proto_version, sizeof(proto_version), MSG_NOSIGNAL)));
+
+  uint16_t size = test_location.size();
+  EXPECT_EQ(sizeof(size), static_cast<size_t>(test_socket_->Send(
+                              &size, sizeof(size), MSG_NOSIGNAL)));
+  EXPECT_EQ(size, static_cast<size_t>(test_socket_->Send(test_location.data(),
+                                                         size, MSG_NOSIGNAL)));
+
+  // TODO(ender): delete this once no longer necessary. Currently, absence of
+  // payload makes RecvMsgAndFDs hang forever.
+  uint64_t control_data;
+  struct iovec vec {
+    &control_data, sizeof(control_data)
+  };
+  cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+  cvd::SharedFD fds[3];
+
+  EXPECT_GT(test_socket_->RecvMsgAndFDs<3>(hdr, MSG_NOSIGNAL, &fds), 0);
+  EXPECT_TRUE(fds[0]->IsOpen());
+  EXPECT_TRUE(fds[1]->IsOpen());
+  EXPECT_TRUE(fds[2]->IsOpen());
+
+  thread.join();
+}
+
+}  // namespace test
+}  // namespace ivserver
diff --git a/host/commands/ivserver/ivserver.cc b/host/commands/ivserver/ivserver.cc
new file mode 100644
index 0000000..ca274cc
--- /dev/null
+++ b/host/commands/ivserver/ivserver.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/commands/ivserver/ivserver.h"
+
+#include <sys/select.h>
+#include <algorithm>
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_select.h"
+#include "host/commands/ivserver/hald_client.h"
+#include "host/commands/ivserver/qemu_client.h"
+
+namespace ivserver {
+
+IVServer::IVServer(const IVServerOptions &options, int qemu_channel_fd,
+                   int client_channel_fd)
+    : vsoc_shmem_(VSoCSharedMemory::New(options.shm_file_path)) {
+  if (qemu_channel_fd > 0) {
+    qemu_channel_ = cvd::SharedFD::Dup(qemu_channel_fd);
+  } else {
+    LOG_IF(WARNING, unlink(options.qemu_socket_path.c_str()) == 0)
+        << "Removed existing unix socket: " << options.qemu_socket_path
+        << ". We can't confirm yet whether another instance is running.";
+    qemu_channel_ = cvd::SharedFD::SocketLocalServer(
+        options.qemu_socket_path.c_str(), false, SOCK_STREAM, 0666);
+  }
+  LOG_IF(FATAL, !qemu_channel_->IsOpen())
+      << "Could not create QEmu channel: " << qemu_channel_->StrError();
+
+  if (client_channel_fd > 0) {
+    client_channel_ = cvd::SharedFD::Dup(client_channel_fd);
+  } else {
+    LOG_IF(WARNING, unlink(options.client_socket_path.c_str()) == 0)
+        << "Removed existing unix socket: " << options.client_socket_path
+        << ". We can't confirm yet whether another instance is running.";
+    client_channel_ = cvd::SharedFD::SocketLocalServer(
+        options.client_socket_path.c_str(), false, SOCK_STREAM, 0666);
+  }
+  LOG_IF(FATAL, !client_channel_->IsOpen())
+      << "Could not create Client channel: " << client_channel_->StrError();
+}
+
+void IVServer::Serve() {
+  while (true) {
+    cvd::SharedFDSet rset;
+    rset.Set(qemu_channel_);
+    rset.Set(client_channel_);
+    cvd::Select(&rset, nullptr, nullptr, nullptr);
+
+    if (rset.IsSet(qemu_channel_)) {
+      HandleNewQemuConnection();
+    }
+
+    if (rset.IsSet(client_channel_)) {
+      HandleNewClientConnection();
+    }
+  }
+
+  LOG(FATAL) << "Control reached out of event loop";
+}
+
+void IVServer::HandleNewClientConnection() {
+  std::unique_ptr<HaldClient> res = HaldClient::New(
+      *vsoc_shmem_, cvd::SharedFD::Accept(*client_channel_, nullptr, nullptr));
+  if (!res) {
+    LOG(WARNING) << "Rejecting unsuccessful HALD connection.";
+  }
+}
+
+void IVServer::HandleNewQemuConnection() {
+  std::unique_ptr<QemuClient> res = QemuClient::New(
+      *vsoc_shmem_, cvd::SharedFD::Accept(*qemu_channel_, nullptr, nullptr));
+
+  if (!res) {
+    LOG(WARNING) << "Could not accept new QEmu client.";
+  }
+}
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/ivserver.h b/host/commands/ivserver/ivserver.h
new file mode 100644
index 0000000..d28d14b
--- /dev/null
+++ b/host/commands/ivserver/ivserver.h
@@ -0,0 +1,51 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/commands/ivserver/options.h"
+#include "host/commands/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+
+// This class is responsible for orchestrating the setup and then serving
+// new connections.
+class IVServer final {
+ public:
+  // The qemu_channel_fd and client_channel_fd are the server sockets. If
+  // non-positive values are provided the server will create those sockets
+  // itself.
+  IVServer(const IVServerOptions &options, int qemu_channel_fd,
+           int client_channel_fd);
+  IVServer(const IVServer &) = delete;
+  IVServer& operator=(const IVServer&) = delete;
+
+  // Serves incoming client and qemu connection.
+  // This method should never return.
+  void Serve();
+
+ private:
+  void HandleNewClientConnection();
+  void HandleNewQemuConnection();
+
+  std::unique_ptr<VSoCSharedMemory> vsoc_shmem_;
+  cvd::SharedFD qemu_channel_;
+  cvd::SharedFD client_channel_;
+};
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/main.cpp b/host/commands/ivserver/main.cpp
new file mode 100644
index 0000000..16aa747
--- /dev/null
+++ b/host/commands/ivserver/main.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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 <unistd.h>
+
+#include <thread>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/commands/ivserver/ivserver.h"
+#include "host/commands/ivserver/options.h"
+
+DEFINE_int32(
+    qemu_socket_fd, -1,
+    "A file descriptor to use as the server Qemu connects to. If not specified "
+    "a unix socket will be created in the default location.");
+DEFINE_int32(
+    client_socket_fd, -1,
+    "A file descriptor to use as the server clients connects to. If not "
+    "specified a unix socket will be created in the default location.");
+
+int main(int argc, char* argv[]) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  auto config = vsoc::CuttlefishConfig::Get();
+  if (!config) {
+    LOG(ERROR) << "Unable to get cuttlefish config";
+    return 1;
+  }
+
+  ivserver::IVServer server(
+      ivserver::IVServerOptions(config->mempath(),
+                                config->ivshmem_qemu_socket_path(),
+                                vsoc::GetDomain()),
+      FLAGS_qemu_socket_fd, FLAGS_client_socket_fd);
+
+  // Close the file descriptors as they have been dupped by now.
+  if (FLAGS_qemu_socket_fd > 0) {
+    close(FLAGS_qemu_socket_fd);
+  }
+  if (FLAGS_client_socket_fd > 0) {
+    close(FLAGS_client_socket_fd);
+  }
+
+  server.Serve();
+
+  return 0;
+}
diff --git a/host/commands/ivserver/options.cc b/host/commands/ivserver/options.cc
new file mode 100644
index 0000000..0f19397
--- /dev/null
+++ b/host/commands/ivserver/options.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/commands/ivserver/options.h"
+
+namespace ivserver {
+
+IVServerOptions::IVServerOptions(const std::string &shm_file_path,
+                                 const std::string &qemu_socket_path,
+                                 const std::string &client_socket_path)
+    : shm_file_path(shm_file_path),
+      qemu_socket_path(qemu_socket_path),
+      client_socket_path(client_socket_path) {}
+
+std::ostream &operator<<(std::ostream &out, const IVServerOptions &options) {
+  out << "\nshm_file: " << options.shm_file_path
+      << "\nqemu_socket_path: " << options.qemu_socket_path
+      << "\nclient_socket_path: " << options.client_socket_path << std::endl;
+
+  return out;
+}
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/options.h b/host/commands/ivserver/options.h
new file mode 100644
index 0000000..40b3246
--- /dev/null
+++ b/host/commands/ivserver/options.h
@@ -0,0 +1,47 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <inttypes.h>
+#include <iostream>
+#include <string>
+
+namespace ivserver {
+
+const uint16_t kIVServerMajorVersion = 1;
+const uint16_t kIVServerMinorVersion = 0;
+const uint32_t kIVServerDefaultShmSizeInMiB = 4;
+
+//
+// structure that contains the various options to start the server.
+//
+struct IVServerOptions final {
+  IVServerOptions(const std::string &shm_file_path,
+                  const std::string &qemu_socket_path,
+                  const std::string &client_socket_path);
+
+  //
+  // We still need a friend here
+  //
+  friend std::ostream &operator<<(std::ostream &out,
+                                  const IVServerOptions &opts);
+
+  const std::string shm_file_path;
+  const std::string qemu_socket_path;
+  const std::string client_socket_path;
+};
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/qemu_client.cc b/host/commands/ivserver/qemu_client.cc
new file mode 100644
index 0000000..660c915
--- /dev/null
+++ b/host/commands/ivserver/qemu_client.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/commands/ivserver/qemu_client.h"
+
+#include <glog/logging.h>
+
+namespace ivserver {
+
+std::unique_ptr<QemuClient> QemuClient::New(const VSoCSharedMemory& shmem,
+                                            const cvd::SharedFD& socket) {
+  std::unique_ptr<QemuClient> res;
+  if (!socket->IsOpen()) {
+    LOG(WARNING) << "Invalid socket passed to QemuClient: "
+                 << socket->StrError();
+    return res;
+  }
+
+  res.reset(new QemuClient(std::move(socket)));
+  if (!res->PerformHandshake(shmem)) {
+    LOG(ERROR) << "Qemu handshake failed. Dropping connection.";
+    res.reset();
+  }
+
+  return res;
+}
+
+QemuClient::QemuClient(cvd::SharedFD socket) : client_socket_(socket) {}
+
+// Once the QemuClient object is constructed, invoking the following
+// method will perform the actual handshake with a QEMU instance.
+bool QemuClient::PerformHandshake(const VSoCSharedMemory& shmem) {
+  LOG(INFO) << "New QEmu client connected.";
+  // 1. The protocol version number, currently zero.  The client should
+  //    close the connection on receipt of versions it can't handle.
+  int64_t msg = QemuConstants::kIvshMemProtocolVersion;
+  int rval = client_socket_->Send(&msg, sizeof(msg), MSG_NOSIGNAL);
+  if (rval != sizeof(msg)) {
+    LOG(ERROR) << "Failed to send protocol version: "
+               << client_socket_->StrError();
+    return false;
+  }
+
+  // 2. The client's ID.  This is unique among all clients of this server.
+  //    IDs must be between 0 and 65535, because the Doorbell register
+  //    provides only 16 bits for them.
+  msg = QemuConstants::kGuestID;
+  rval = client_socket_->Send(&msg, sizeof(msg), MSG_NOSIGNAL);
+  if (rval != sizeof(msg)) {
+    LOG(ERROR) << "Failed to send VM Id: " << client_socket_->StrError();
+    return false;
+  }
+
+  // 3. Connect notifications for existing other clients, if any.  This is
+  //    a peer ID (number between 0 and 65535 other than the client's ID),
+  //    repeated N times.  Each repetition is accompanied by one file
+  //    descriptor.  These are for interrupting the peer with that ID using
+  //    vector 0,..,N-1, in order.  If the client is configured for fewer
+  //    vectors, it closes the extra file descriptors.  If it is configured
+  //    for more, the extra vectors remain unconnected.
+  for (const auto& region_data : shmem.Regions()) {
+    if (!SendSocketInfo(kHostID, region_data.host_fd)) {
+      LOG(ERROR) << "Failed to send Host Side FD for region "
+                 << region_data.device_name << ": " << client_socket_->StrError();
+      return false;
+    }
+  }
+
+  // 4. Interrupt setup.  This is the client's own ID, repeated N times.
+  //    Each repetition is accompanied by one file descriptor.  These are
+  //    for receiving interrupts from peers using vector 0,..,N-1, in
+  //    order.  If the client is configured for fewer vectors, it closes
+  //    the extra file descriptors.  If it is configured for more, the
+  //    extra vectors remain unconnected.
+  for (const auto& region_data : shmem.Regions()) {
+    if (!SendSocketInfo(kGuestID, region_data.guest_fd)) {
+      LOG(ERROR) << "Failed to send Guest Side FD for region "
+                 << region_data.device_name << ": " << client_socket_->StrError();
+      return false;
+    }
+  }
+
+  // 5. The number -1, accompanied by the file descriptor for the shared
+  //    memory.
+  if (!SendSocketInfo(kSharedMem, shmem.SharedMemFD())) {
+    LOG(ERROR) << "Failed to send Shared Memory socket: "
+               << client_socket_->StrError();
+    return false;
+  }
+
+
+  LOG(INFO) << "QEmu handshake completed.";
+  return true;
+}
+
+bool QemuClient::SendSocketInfo(QemuConstants message,
+                                const cvd::SharedFD& socket) {
+  struct iovec vec {
+    &message, sizeof(message)
+  };
+  cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+  cvd::SharedFD fds[] = {socket};
+  int rval = client_socket_->SendMsgAndFDs(hdr, 0, fds);
+  if (rval == -1) {
+    LOG(ERROR) << "failed to send shared_mem_fd: "
+               << client_socket_->StrError();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/qemu_client.h b/host/commands/ivserver/qemu_client.h
new file mode 100644
index 0000000..31cd211
--- /dev/null
+++ b/host/commands/ivserver/qemu_client.h
@@ -0,0 +1,69 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/commands/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+
+// QemuClient manages individual QEmu connections using protocol specified
+// in documentation file distributed as part of QEmu 2.8 package under:
+// docs/specs/ivshmem-spec.txt
+// Alternatively, please point your browser to the following URL:
+// https://github.com/qemu/qemu/blob/stable-2.8/docs/specs/ivshmem-spec.txt
+class QemuClient final {
+ public:
+  static std::unique_ptr<QemuClient> New(const VSoCSharedMemory &shmem,
+                                         const cvd::SharedFD &connection);
+
+  cvd::SharedFD client_socket() const { return client_socket_; }
+
+ private:
+  enum QemuConstants : int64_t {
+    kIvshMemProtocolVersion = 0,
+    // Marker for the shared memory file
+    kSharedMem = -1,
+    // HostID is in fact a Peer ID and can take multiple values, depending on
+    // how many subsystems we would like Guest to talk to.
+    kHostID = 0,
+    // GuestID is a unique form of Peer ID (see above), that identifies newly
+    // created quest in IvSharedMem world.
+    kGuestID = 1
+  };
+
+  static_assert(QemuConstants::kHostID != QemuConstants::kGuestID,
+                "Guest and host should have different IDs");
+
+  cvd::SharedFD client_socket_;
+
+  // Initialize new instance of QemuClient.
+  QemuClient(cvd::SharedFD qemu_listener_socket);
+
+  // Once the QemuClient object is constructed, invoking the following
+  // method will perform the actual handshake with a QEMU instance.
+  bool PerformHandshake(const VSoCSharedMemory &shmem_fd);
+
+  // Send socket data to Qemu.
+  bool SendSocketInfo(QemuConstants message, const cvd::SharedFD &socket);
+
+  QemuClient(const QemuClient &) = delete;
+  QemuClient &operator=(const QemuClient &) = delete;
+};
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/shm_layout.txt b/host/commands/ivserver/shm_layout.txt
new file mode 100644
index 0000000..39d66a7
--- /dev/null
+++ b/host/commands/ivserver/shm_layout.txt
@@ -0,0 +1,88 @@
+(Please      use      a      smaller        font        and/or       appropriate     terminal   width  to      see    this     line without wrapping)
+
+
+                                              REGION DESCRIPTORS
+                                              (starts at page 0)
+                                       +------------------------------+
+                                       |     major_version: u16       |
+                                       +------------------------------+
+                                       |     minor_version: u16       |
+                                       +------------------------------+
+        (size of shared memory)        |         size: u32            |
+                                       +------------------------------+
+          (number of regions)          |     region_count: u32        |
+                                       +------------------------------+
+  (offset of region descriptor data)   | vsoc_region_desc_offset: u32 |o-----+
+                                       +------------------------------+      |
+                                       |                              |      |
+                                       | ..          ..            .. |      |
+                                       |                              |      |
+      (region 0 descriptor start)      +------------------------------+ <----+
+                                       |    current_version: u16      |
+                                       +------------------------------+
+                                       |  min_compatible_version: u16 |
+                                       +------------------------------+
+       (start offset of region 0)      |  region_begin_offset: u32    |o--------------------+
+                                       +------------------------------+                     |
+       (end offset   of region 0)      |  region_end_offset: u32      |o----------------------------------------------------------------------+
+                                       +------------------------------+                     |                                                 |
+                                       |  offset_of_region_data: u32  |o-----+              |                                                 |
+                                       +------------------------------+      |              |                                                 |
+                                       | guest_to_host_signal_table   |      |              |                                                 |
+                                       |______________________________|      |              |                                                 |
+                                        +----------------------------+       |              |                                                 |
+                                        |    num_nodes_lg2: u32      |       |              |                                                 |
+                                        +----------------------------+       |              |                                                 |
+                                        |futex_uaddr_table_offset:u32|o------------------+  |                                                 |
+                                        +----------------------------+       |           |  |                                                 |
+                                        | intr_signaled_offset: u32  |o----------------+ |  |                                                 |
+                                        +----------------------------+       |         | |  |                                                 |
+                                       +------------------------------+      |         | |  |                                                 |
+                                       | host_to_guest_signal_table   |      |         | |  |                                                 |
+                                       |______________________________|      |         | |  |                                                 |
+                                        +----------------------------+       |         | |  |                                                 |
+                                        |    num_nodes_lg2: u32      |       |         | |  |                                                 |
+                                        +----------------------------+       |         | |  |                                                 |
+                                        |futex_uaddr_table_offset:u32|o--------------+ | |  |                                                 |
+                                        +----------------------------+       |       | | |  \/      REGION AREA (page aligned)                |
+                                        | intr_signaled_offset: u32  |o------|-----+ | | |  +---------------------------------------+         |
+                                        +----------------------------+       |     | | | |  |  region area before start of guest to |         |
+                                       +------------------------------+      |     | | | |  |  host signal table.                   |         |
+                                       |    device_name: char[16]     |      |     | | | |  |     (currently does not exist)        |         |
+         (region 0 descriptor end)     +------------------------------+      |     | | | +->+---------------------------------------+         |
+       (region 1 descriptor start)     |                              |      |     | | |    |        uaddr_offset_0 : u32           |         |
+                                       |  current_version: u16        |      |     | | |    +---------------------------------------+         |
+                                       +------------------------------+      |     | | |    |        uaddr_offset_1 : u32           |         |
+                                       |  min_compatible_version: u16 |      |     | | |    +---------------------------------------+         |
+                                       +------------------------------+      |     | | |    |     ...      ..       ...             |         |
+                                       |  region_begin_offset: u32    |      |     | | |    +---------------------------------------+         |
+                                       +------------------------------+      |     | | |    | uaddr_offset_(2^num_nodes_lg2 - 1):u32|         |
+                                       |  region_end_offset: u32      |      |     | | +--->+---------------------------------------+         |
+                                       +------------------------------+      |     | |      |     (interrupt_signaled_area) : u32   |         |
+                                       |  offset_of_region_data: u32  |      |     | +----->+---------------------------------------+         |
+                                       +------------------------------+      |     |        |        uaddr_offset_0 : u32           |         |
+                                       | guest_to_host_signal_table   |      |     |        +---------------------------------------+         |
+                                       +------------------------------+      |     |        |     ...      ..       ...             |         |
+                                       | host_to_guest_signal_table   |      |     |        +---------------------------------------+         |
+                                       +------------------------------+      |     |        | uaddr_offset_(2^num_nodes_lg2 - 1):u32|         |
+                                       |    device_name: char[16]     |      |     +------->+---------------------------------------+         |
+          (region 1 descriptor end)    +------------------------------+      |              |     (interrupt_signaled_area) : u32   |         |
+                                       ...           ...            ...      +------------->+---------------------------------------+         |
+                                       +------------------------------+                     |                                       |         |
+                                       |    current_version: u16      |                     |                                       |         |
+                                       +------------------------------+                     |                                       |         |
+                                       |  min_compatible_version: u16 |                     |         region specific data          |         |
+                                       +------------------------------+                     | (defined by region specific agreement |         |
+                                       |  region_begin_offset: u32    |                     |  between HAL and host-side process)   |         |
+                                       +------------------------------+                     |                                       |         |
+                                       |  region_end_offset: u32      |                     |                                       |         |
+                                       +------------------------------+                     |                                       |         |
+                                       |  offset_of_region_data: u32  |                     +---------------------------------------+ <-------+
+                                       +------------------------------+
+                                       | guest_to_host_signal_table   |
+                                       +------------------------------+
+                                       | host_to_guest_signal_table   |
+                                       +------------------------------+
+                                       |    device_name: char[16]     |
+                                       +------------------------------+
+                                          END OF REGION DESCRIPTORS
diff --git a/host/commands/ivserver/vsocsharedmem.cc b/host/commands/ivserver/vsocsharedmem.cc
new file mode 100644
index 0000000..eb448d8
--- /dev/null
+++ b/host/commands/ivserver/vsocsharedmem.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/commands/ivserver/vsocsharedmem.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/eventfd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <algorithm>
+#include <tuple>
+
+#include <glog/logging.h>
+
+#include "common/vsoc/lib/vsoc_memory.h"
+#include "uapi/vsoc_shm.h"
+
+namespace ivserver {
+namespace {
+
+class VSoCSharedMemoryImpl : public VSoCSharedMemory {
+ public:
+  VSoCSharedMemoryImpl(const std::map<std::string, size_t> &name_to_region_idx,
+                       const std::vector<Region> &regions,
+                       const std::string &path);
+
+  bool GetEventFdPairForRegion(const std::string &region_name,
+                               cvd::SharedFD *guest_to_host,
+                               cvd::SharedFD *host_to_guest) const override;
+
+  const cvd::SharedFD &SharedMemFD() const override;
+
+  const std::vector<Region> &Regions() const override;
+
+ private:
+  void CreateLayout();
+
+  cvd::SharedFD shared_mem_fd_;
+  const std::map<std::string, size_t> region_name_to_index_;
+  const std::vector<Region> region_data_;
+
+  VSoCSharedMemoryImpl(const VSoCSharedMemoryImpl &) = delete;
+  VSoCSharedMemoryImpl &operator=(const VSoCSharedMemoryImpl &other) = delete;
+};
+
+VSoCSharedMemoryImpl::VSoCSharedMemoryImpl(
+    const std::map<std::string, size_t> &name_to_region_idx,
+    const std::vector<Region> &regions, const std::string &path)
+    : shared_mem_fd_(cvd::SharedFD::Open(path.c_str(), O_RDWR)),
+      region_name_to_index_{name_to_region_idx},
+      region_data_{regions} {
+  LOG_IF(FATAL, !shared_mem_fd_->IsOpen())
+      << "Error in creating shared_memory file: " << shared_mem_fd_->StrError();
+}
+
+const cvd::SharedFD &VSoCSharedMemoryImpl::SharedMemFD() const {
+  return shared_mem_fd_;
+}
+
+const std::vector<VSoCSharedMemory::Region> &VSoCSharedMemoryImpl::Regions()
+    const {
+  return region_data_;
+}
+
+bool VSoCSharedMemoryImpl::GetEventFdPairForRegion(
+    const std::string &region_name, cvd::SharedFD *guest_to_host,
+    cvd::SharedFD *host_to_guest) const {
+  auto it = region_name_to_index_.find(region_name);
+  if (it == region_name_to_index_.end()) return false;
+
+  *guest_to_host = region_data_[it->second].host_fd;
+  *host_to_guest = region_data_[it->second].guest_fd;
+  return true;
+}
+
+}  // anonymous namespace
+
+std::unique_ptr<VSoCSharedMemory> VSoCSharedMemory::New(
+    const std::string &path) {
+  auto device_layout = vsoc::VSoCMemoryLayout::Get();
+
+  std::map<std::string, size_t> name_to_region_idx;
+  std::vector<Region> regions;
+  regions.reserve(device_layout->GetRegions().size());
+
+  for (auto region_spec : device_layout->GetRegions()) {
+    auto device_name = region_spec->region_name();
+
+    // Create one pair of eventfds for this region. Note that the guest to host
+    // eventfd is non-blocking, whereas the host to guest eventfd is blocking.
+    // This is in anticipation of blocking semantics for the host side locks.
+    auto host_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
+    if (!host_fd->IsOpen()) {
+      LOG(ERROR) << "Failed to create host eventfd for " << device_name << ": "
+                 << host_fd->StrError();
+      return nullptr;
+    }
+    auto guest_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
+    if (!guest_fd->IsOpen()) {
+      LOG(ERROR) << "Failed to create guest eventfd for " << device_name << ": "
+                 << guest_fd->StrError();
+      return nullptr;
+    }
+
+    auto region_idx = regions.size();
+    name_to_region_idx[device_name] = region_idx;
+    regions.emplace_back(device_name, host_fd, guest_fd);
+  }
+
+  return std::unique_ptr<VSoCSharedMemory>(
+      new VSoCSharedMemoryImpl(name_to_region_idx, regions, path));
+}
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/vsocsharedmem.h b/host/commands/ivserver/vsocsharedmem.h
new file mode 100644
index 0000000..613c6a0
--- /dev/null
+++ b/host/commands/ivserver/vsocsharedmem.h
@@ -0,0 +1,59 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <inttypes.h>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "common/libs/fs/shared_fd.h"
+#include "uapi/vsoc_shm.h"
+
+namespace ivserver {
+
+class VSoCSharedMemory {
+ public:
+  // Region describes a VSoCSharedMem region.
+  struct Region {
+    Region() = default;
+    explicit Region(const char *device_name, const cvd::SharedFD &host_fd,
+                    const cvd::SharedFD &guest_fd)
+        : device_name(device_name), host_fd(host_fd), guest_fd(guest_fd) {}
+    const char *device_name;
+    cvd::SharedFD host_fd;
+    cvd::SharedFD guest_fd;
+  };
+
+  VSoCSharedMemory() = default;
+  virtual ~VSoCSharedMemory() = default;
+
+  static std::unique_ptr<VSoCSharedMemory> New(const std::string &name);
+
+  virtual bool GetEventFdPairForRegion(const std::string &region_name,
+                                       cvd::SharedFD *guest_to_host,
+                                       cvd::SharedFD *host_to_guest) const = 0;
+
+  virtual const cvd::SharedFD &SharedMemFD() const = 0;
+  virtual const std::vector<Region> &Regions() const = 0;
+
+ private:
+  VSoCSharedMemory(const VSoCSharedMemory &) = delete;
+  VSoCSharedMemory &operator=(const VSoCSharedMemory &other) = delete;
+};
+
+}  // namespace ivserver
diff --git a/host/commands/ivserver/vsocsharedmem_mock.h b/host/commands/ivserver/vsocsharedmem_mock.h
new file mode 100644
index 0000000..4ed1f16
--- /dev/null
+++ b/host/commands/ivserver/vsocsharedmem_mock.h
@@ -0,0 +1,33 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <gmock/gmock.h>
+
+#include "host/commands/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+namespace test {
+class VSoCSharedMemoryMock : public VSoCSharedMemory {
+ public:
+  MOCK_CONST_METHOD3(GetEventFdPairForRegion,
+                     bool(const std::string&, cvd::SharedFD*, cvd::SharedFD*));
+  MOCK_CONST_METHOD0(SharedMemFD, const cvd::SharedFD&());
+  MOCK_CONST_METHOD0(Regions,
+                     const std::vector<VSoCSharedMemory::Region>&());
+};
+}  // namespace test
+}  // namespace ivserver
diff --git a/host/commands/launch/Android.bp b/host/commands/launch/Android.bp
index 6dd6426..e2db615 100644
--- a/host/commands/launch/Android.bp
+++ b/host/commands/launch/Android.bp
@@ -24,6 +24,7 @@
         "cuttlefish_glog",
     ],
     shared_libs: [
+        "vsoc_lib",
         "libcuttlefish_fs",
         "libcuttlefish_utils",
         "cuttlefish_auto_resources",
@@ -33,6 +34,7 @@
     static_libs: [
         "libcuttlefish_host_config",
         "libcuttlefish_vm_manager",
+        "libivserver",
         "libgflags",
         "libxml2",
         "libjsoncpp",
diff --git a/host/commands/record_audio/Android.bp b/host/commands/record_audio/Android.bp
new file mode 100644
index 0000000..13f47ef
--- /dev/null
+++ b/host/commands/record_audio/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2018 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.
+
+cc_binary_host {
+    name: "record_audio",
+    srcs: [
+        "main.cc",
+    ],
+    include_dirs: [
+        "frameworks/av/cmds/stagefright",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcuttlefish_utils",
+        "vsoc_lib",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libjsoncpp",
+        "libgflags",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/commands/record_audio/main.cc b/host/commands/record_audio/main.cc
new file mode 100644
index 0000000..3c024b8
--- /dev/null
+++ b/host/commands/record_audio/main.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 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 "common/vsoc/lib/audio_data_region_view.h"
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+#include "WaveWriter.h"
+
+#include <android-base/logging.h>
+#include <gflags/gflags.h>
+#include <iostream>
+#include <signal.h>
+
+using AudioDataRegionView = vsoc::audio_data::AudioDataRegionView;
+using WaveWriter = android::WaveWriter;
+
+DEFINE_string(output_file, "", "Location of the output audio file.");
+DEFINE_bool(verbose, false, "Enable verbose logging.");
+
+volatile bool gDone = false;
+static void SigIntHandler(int /* sig */) {
+  gDone = true;
+}
+
+int main(int argc, char **argv) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  LOG_IF(FATAL, FLAGS_output_file.empty())
+      << "--output_file must be specified.";
+
+  AudioDataRegionView *audio_data_rv =
+      AudioDataRegionView::GetInstance(vsoc::GetDomain().c_str());
+
+  auto worker = audio_data_rv->StartWorker();
+
+  std::unique_ptr<WaveWriter> writer;
+  int64_t frameCount = 0LL;
+
+  // The configuration the writer is setup for.
+  gce_audio_message writer_hdr;
+
+  uint8_t buffer[4096];
+
+  gDone = false;
+
+  struct sigaction act;
+  sigemptyset(&act.sa_mask);
+  act.sa_flags = 0;
+  act.sa_handler = SigIntHandler;
+
+  struct sigaction oact;
+  sigaction(SIGINT, &act, &oact);
+
+  while (!gDone) {
+    intptr_t res = audio_data_rv->data()->audio_queue.Read(
+            audio_data_rv,
+            reinterpret_cast<char *>(buffer),
+            sizeof(buffer));
+
+    if (res < 0) {
+      std::cerr << "CircularPacketQueue::Read returned " << res << std::endl;
+        continue;
+    }
+
+    CHECK_GE(static_cast<size_t>(res), sizeof(gce_audio_message));
+
+    gce_audio_message hdr;
+    std::memcpy(&hdr, buffer, sizeof(gce_audio_message));
+
+    if (hdr.message_type != gce_audio_message::DATA_SAMPLES) {
+        continue;
+    }
+
+    const size_t payloadSize = res - sizeof(gce_audio_message);
+
+    if (FLAGS_verbose) {
+      std::cerr
+          << "stream "
+          << hdr.stream_number
+          << ", frame "
+          << hdr.frame_num
+          << ", rate "
+          << hdr.frame_rate
+          << ", channel_mask "
+          << hdr.channel_mask
+          << ", format "
+          << hdr.format
+          << ", payload_size "
+          << payloadSize
+          << std::endl;
+    }
+
+    if (!writer) {
+      const size_t numChannels = hdr.frame_size / sizeof(int16_t);
+
+      writer.reset(
+          new WaveWriter(FLAGS_output_file.c_str(), numChannels, hdr.frame_rate));
+
+      frameCount = hdr.frame_num;
+      writer_hdr = hdr;
+    } else if (writer_hdr.frame_size != hdr.frame_size
+        || writer_hdr.frame_rate != hdr.frame_rate
+        || writer_hdr.stream_number != hdr.stream_number) {
+      std::cerr << "Audio configuration changed. Aborting." << std::endl;
+      break;
+    }
+
+    int64_t framesMissing = hdr.frame_num - frameCount;
+    if (framesMissing > 0) {
+      // TODO(andih): Insert silence here, if necessary.
+    }
+
+    frameCount = hdr.frame_num;
+
+    writer->Append(&buffer[sizeof(gce_audio_message)], payloadSize);
+  }
+
+  std::cout << "DONE" << std::endl;
+
+  return 0;
+}
+
diff --git a/host/commands/run_cvd/Android.bp b/host/commands/run_cvd/Android.bp
index a8621e9..29238c9 100644
--- a/host/commands/run_cvd/Android.bp
+++ b/host/commands/run_cvd/Android.bp
@@ -19,11 +19,14 @@
         "launch.cc",
         "process_monitor.cc",
         "main.cc",
+        "screen_region_handler.cc",
+        "vsoc_shared_memory.cc",
     ],
     header_libs: [
         "cuttlefish_glog",
     ],
     shared_libs: [
+        "vsoc_lib",
         "libcuttlefish_fs",
         "libcuttlefish_utils",
         "cuttlefish_auto_resources",
@@ -33,6 +36,7 @@
     static_libs: [
         "libcuttlefish_host_config",
         "libcuttlefish_vm_manager",
+        "libivserver",
         "libgflags",
         "libxml2",
         "libjsoncpp",
diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc
index e4eb858..5c42e59 100644
--- a/host/commands/run_cvd/launch.cc
+++ b/host/commands/run_cvd/launch.cc
@@ -8,8 +8,10 @@
 #include "common/libs/fs/shared_fd.h"
 #include "common/libs/utils/files.h"
 #include "common/libs/utils/size_utils.h"
+#include "common/vsoc/shm/screen_layout.h"
 #include "host/commands/run_cvd/runner_defs.h"
 #include "host/commands/run_cvd/pre_launch_initializers.h"
+#include "host/commands/run_cvd/vsoc_shared_memory.h"
 #include "host/libs/vm_manager/crosvm_manager.h"
 #include "host/libs/vm_manager/qemu_manager.h"
 
@@ -18,6 +20,20 @@
 
 namespace {
 
+cvd::SharedFD CreateIvServerUnixSocket(const std::string& path) {
+  return cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM,
+                                          0666);
+}
+
+std::string GetGuestPortArg() {
+  constexpr int kEmulatorPort = 5555;
+  return std::string{"--guest_ports="} + std::to_string(kEmulatorPort);
+}
+
+std::string GetHostPortArg(const vsoc::CuttlefishConfig& config) {
+  return std::string{"--host_ports="} + std::to_string(config.host_port());
+}
+
 std::string GetAdbConnectorTcpArg(const vsoc::CuttlefishConfig& config) {
   return std::string{"127.0.0.1:"} + std::to_string(config.host_port());
 }
@@ -32,6 +48,10 @@
   return config.adb_mode().count(mode) > 0;
 }
 
+bool AdbTunnelEnabled(const vsoc::CuttlefishConfig& config) {
+  return AdbModeEnabled(config, vsoc::AdbMode::Tunnel);
+}
+
 bool AdbVsockTunnelEnabled(const vsoc::CuttlefishConfig& config) {
   return config.vsock_guest_cid() > 2
       && AdbModeEnabled(config, vsoc::AdbMode::VsockTunnel);
@@ -43,9 +63,11 @@
 }
 
 bool AdbTcpConnectorEnabled(const vsoc::CuttlefishConfig& config) {
+  bool tunnel = AdbTunnelEnabled(config);
   bool vsock_tunnel = AdbVsockTunnelEnabled(config);
   bool vsock_half_tunnel = AdbVsockHalfTunnelEnabled(config);
-  return config.run_adb_connector() && (vsock_tunnel || vsock_half_tunnel);
+  return config.run_adb_connector()
+      && (tunnel || vsock_tunnel || vsock_half_tunnel);
 }
 
 bool AdbVsockConnectorEnabled(const vsoc::CuttlefishConfig& config) {
@@ -71,6 +93,32 @@
   return config.logcat_mode() == cvd::kLogcatVsockMode;
 }
 
+cvd::Command GetIvServerCommand(const vsoc::CuttlefishConfig& config) {
+  // Resize screen region
+  auto actual_width = cvd::AlignToPowerOf2(config.x_res() * 4, 4);// align to 16
+  uint32_t screen_buffers_size =
+      config.num_screen_buffers() *
+      cvd::AlignToPageSize(actual_width * config.y_res() + 16 /* padding */);
+  screen_buffers_size +=
+      (config.num_screen_buffers() - 1) * 4096; /* Guard pages */
+
+  // TODO(b/79170615) Resize gralloc region too.
+
+  vsoc::CreateSharedMemoryFile(
+      config.mempath(),
+      {{vsoc::layout::screen::ScreenLayout::region_name, screen_buffers_size}});
+
+
+  cvd::Command ivserver(config.ivserver_binary());
+  ivserver.AddParameter(
+      "-qemu_socket_fd=",
+      CreateIvServerUnixSocket(config.ivshmem_qemu_socket_path()));
+  ivserver.AddParameter(
+      "-client_socket_fd=",
+      CreateIvServerUnixSocket(config.ivshmem_client_socket_path()));
+  return ivserver;
+}
+
 std::vector<cvd::SharedFD> LaunchKernelLogMonitor(
     const vsoc::CuttlefishConfig& config,
     cvd::ProcessMonitor* process_monitor,
@@ -266,6 +314,17 @@
   return false;
 }
 
+void LaunchStreamAudioIfEnabled(const vsoc::CuttlefishConfig& config,
+                                cvd::ProcessMonitor* process_monitor,
+                                std::function<bool(MonitorEntry*)> callback) {
+  if (config.enable_stream_audio()) {
+    auto port_options = "-port=" + std::to_string(config.stream_audio_port());
+    cvd::Command stream_audio(config.stream_audio_binary());
+    stream_audio.AddParameter(port_options);
+    process_monitor->StartSubprocess(std::move(stream_audio), callback);
+  }
+}
+
 void LaunchAdbConnectorIfEnabled(cvd::ProcessMonitor* process_monitor,
                                  const vsoc::CuttlefishConfig& config,
                                  cvd::SharedFD adbd_events_pipe) {
@@ -292,6 +351,17 @@
   }
 }
 
+void LaunchSocketForwardProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
+                                 const vsoc::CuttlefishConfig& config) {
+  if (AdbTunnelEnabled(config)) {
+    cvd::Command adb_tunnel(config.socket_forward_proxy_binary());
+    adb_tunnel.AddParameter(GetGuestPortArg());
+    adb_tunnel.AddParameter(GetHostPortArg(config));
+    process_monitor->StartSubprocess(std::move(adb_tunnel),
+                                     GetOnSubprocessExitCallback(config));
+  }
+}
+
 void LaunchSocketVsockProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
                                  const vsoc::CuttlefishConfig& config) {
   if (AdbVsockTunnelEnabled(config)) {
@@ -315,3 +385,14 @@
                                      GetOnSubprocessExitCallback(config));
   }
 }
+
+void LaunchIvServerIfEnabled(cvd::ProcessMonitor* process_monitor,
+                             const vsoc::CuttlefishConfig& config) {
+  if (config.enable_ivserver()) {
+    process_monitor->StartSubprocess(GetIvServerCommand(config),
+                                     GetOnSubprocessExitCallback(config));
+
+    // Initialize the regions that require so before the VM starts.
+    PreLaunchInitializers::Initialize(config);
+  }
+}
diff --git a/host/commands/run_cvd/launch.h b/host/commands/run_cvd/launch.h
index 4db6e12..714b5bd 100644
--- a/host/commands/run_cvd/launch.h
+++ b/host/commands/run_cvd/launch.h
@@ -6,6 +6,7 @@
 #include "host/commands/run_cvd/process_monitor.h"
 #include "host/libs/config/cuttlefish_config.h"
 
+cvd::Command GetIvServerCommand(const vsoc::CuttlefishConfig& config);
 std::vector <cvd::SharedFD> LaunchKernelLogMonitor(
     const vsoc::CuttlefishConfig& config,
     cvd::ProcessMonitor* process_monitor,
@@ -19,10 +20,17 @@
 bool LaunchVNCServerIfEnabled(const vsoc::CuttlefishConfig& config,
                               cvd::ProcessMonitor* process_monitor,
                               std::function<bool(cvd::MonitorEntry*)> callback);
+void LaunchStreamAudioIfEnabled(const vsoc::CuttlefishConfig& config,
+                                cvd::ProcessMonitor* process_monitor,
+                                std::function<bool(cvd::MonitorEntry*)> callback);
 void LaunchAdbConnectorIfEnabled(cvd::ProcessMonitor* process_monitor,
                                  const vsoc::CuttlefishConfig& config,
                                  cvd::SharedFD adbd_events_pipe);
+void LaunchSocketForwardProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
+                                 const vsoc::CuttlefishConfig& config);
 void LaunchSocketVsockProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
                                  const vsoc::CuttlefishConfig& config);
+void LaunchIvServerIfEnabled(cvd::ProcessMonitor* process_monitor,
+                             const vsoc::CuttlefishConfig& config);
 void LaunchTombstoneReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
                                       cvd::ProcessMonitor* process_monitor);
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index a4ed8cc..dd5ed4d 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -46,9 +46,12 @@
 #include "common/libs/utils/files.h"
 #include "common/libs/utils/subprocess.h"
 #include "common/libs/utils/size_utils.h"
+#include "common/vsoc/lib/vsoc_memory.h"
+#include "common/vsoc/shm/screen_layout.h"
 #include "host/commands/run_cvd/launch.h"
 #include "host/commands/run_cvd/runner_defs.h"
 #include "host/commands/run_cvd/process_monitor.h"
+#include "host/commands/run_cvd/vsoc_shared_memory.h"
 #include "host/libs/config/cuttlefish_config.h"
 #include "host/commands/kernel_log_monitor/kernel_log_server.h"
 #include <host/libs/vm_manager/crosvm_manager.h>
@@ -452,6 +455,8 @@
   LaunchTombstoneReceiverIfEnabled(*config, &process_monitor);
 
   LaunchUsbServerIfEnabled(*config, &process_monitor);
+
+  LaunchIvServerIfEnabled(&process_monitor, *config);
   // Launch the e2e tests after the ivserver is ready
   LaunchE2eTestIfEnabled(&process_monitor, boot_state_machine, *config);
 
@@ -469,7 +474,10 @@
   }
 
   // Start other host processes
+  LaunchSocketForwardProxyIfEnabled(&process_monitor, *config);
   LaunchSocketVsockProxyIfEnabled(&process_monitor, *config);
+  LaunchStreamAudioIfEnabled(*config, &process_monitor,
+                             GetOnSubprocessExitCallback(*config));
   LaunchAdbConnectorIfEnabled(&process_monitor, *config, adbd_events_pipe);
 
   ServerLoop(launcher_monitor_socket, &process_monitor); // Should not return
diff --git a/host/commands/run_cvd/screen_region_handler.cc b/host/commands/run_cvd/screen_region_handler.cc
new file mode 100644
index 0000000..9be969e
--- /dev/null
+++ b/host/commands/run_cvd/screen_region_handler.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 <glog/logging.h>
+
+#include "common/vsoc/lib/screen_region_view.h"
+#include "host/commands/run_cvd/pre_launch_initializers.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+void InitializeScreenRegion(const vsoc::CuttlefishConfig& config) {
+  auto region =
+      vsoc::screen::ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
+  if (!region) {
+    LOG(FATAL) << "Screen region was not found";
+    return;
+  }
+  auto dest = region->data();
+  dest->x_res = config.x_res();
+  dest->y_res = config.y_res();
+  dest->dpi = config.dpi();
+  dest->refresh_rate_hz = config.refresh_rate_hz();
+}
diff --git a/host/commands/run_cvd/vsoc_shared_memory.cc b/host/commands/run_cvd/vsoc_shared_memory.cc
new file mode 100644
index 0000000..0278cad
--- /dev/null
+++ b/host/commands/run_cvd/vsoc_shared_memory.cc
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/commands/run_cvd/vsoc_shared_memory.h"
+
+#include <unistd.h>
+
+#include <map>
+#include <vector>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/size_utils.h"
+#include "common/vsoc/lib/vsoc_memory.h"
+#include "glog/logging.h"
+
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+
+namespace {
+
+uint32_t OffsetOfRegionData(const VSoCRegionLayout& layout) {
+  uint32_t offset = 0;
+  // Signal tables
+  offset +=
+      (1 << layout.guest_to_host_signal_table_log_size()) * sizeof(uint32_t);
+  offset +=
+      (1 << layout.host_to_guest_signal_table_log_size()) * sizeof(uint32_t);
+  // Interrup signals
+  offset += 2 * sizeof(uint32_t);
+  return offset;
+}
+
+struct VSoCRegionAllocator {
+  const VSoCRegionLayout* region_layout;
+  uint32_t begin_offset;
+  uint32_t region_size;
+
+  VSoCRegionAllocator(const VSoCRegionLayout& layout, uint32_t offset,
+                      uint32_t requested_layout_increase = 0)
+      : region_layout(&layout),
+        begin_offset(offset),
+        region_size(cvd::AlignToPageSize(OffsetOfRegionData(layout) +
+                                         layout.layout_size() +
+                                         requested_layout_increase)) {}
+};
+
+// Writes a region's signal table layout to shared memory. Returns the region
+// offset of free memory after the table and interrupt signaled word.
+uint32_t WriteSignalTableDescription(vsoc_signal_table_layout* layout,
+                                     uint32_t offset, int log_size) {
+  layout->num_nodes_lg2 = log_size;
+  // First the signal table
+  layout->futex_uaddr_table_offset = offset;
+  offset += (1 << log_size) * sizeof(uint32_t);
+  // Then the interrupt signaled word
+  layout->interrupt_signalled_offset = offset;
+  offset += sizeof(uint32_t);
+  return offset;
+}
+
+// Writes a region's layout description to shared memory
+void WriteRegionDescription(vsoc_device_region* shmem_region_desc,
+                            const VSoCRegionAllocator& allocator) {
+  // Region versions are deprecated, write some sensible value
+  shmem_region_desc->current_version = 0;
+  shmem_region_desc->min_compatible_version = 0;
+
+  shmem_region_desc->region_begin_offset = allocator.begin_offset;
+  shmem_region_desc->region_end_offset =
+      allocator.begin_offset + allocator.region_size;
+  shmem_region_desc->offset_of_region_data =
+      OffsetOfRegionData(*allocator.region_layout);
+  strncpy(shmem_region_desc->device_name,
+          allocator.region_layout->region_name(), VSOC_DEVICE_NAME_SZ - 1);
+  shmem_region_desc->device_name[VSOC_DEVICE_NAME_SZ - 1] = '\0';
+  // Guest to host signal table at the beginning of the region
+  uint32_t offset = 0;
+  offset = WriteSignalTableDescription(
+      &shmem_region_desc->guest_to_host_signal_table, offset,
+      allocator.region_layout->guest_to_host_signal_table_log_size());
+  // Host to guest signal table right after
+  offset = WriteSignalTableDescription(
+      &shmem_region_desc->host_to_guest_signal_table, offset,
+      allocator.region_layout->host_to_guest_signal_table_log_size());
+  // Double check that the region metadata does not collide with the data
+  if (offset > shmem_region_desc->offset_of_region_data) {
+    LOG(FATAL) << "Error: Offset of region data too small (is "
+               << shmem_region_desc->offset_of_region_data << " should be "
+               << offset << " ) for region "
+               << allocator.region_layout->region_name() << ". This is a bug";
+  }
+}
+
+void WriteLayout(void* shared_memory,
+                 const std::vector<VSoCRegionAllocator>& allocators,
+                 uint32_t file_size) {
+  // Device header
+  static_assert(CURRENT_VSOC_LAYOUT_MAJOR_VERSION == 2,
+                "Region layout code must be updated");
+  auto header = reinterpret_cast<vsoc_shm_layout_descriptor*>(shared_memory);
+  header->major_version = CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
+  header->minor_version = CURRENT_VSOC_LAYOUT_MINOR_VERSION;
+  header->size = file_size;
+  header->region_count = allocators.size();
+
+  std::map<std::string, size_t> region_idx_by_name;
+  for (size_t idx = 0; idx < allocators.size(); ++idx) {
+    region_idx_by_name[allocators[idx].region_layout->region_name()] = idx;
+  }
+
+  // Region descriptions go right after the layout descriptor
+  header->vsoc_region_desc_offset = sizeof(vsoc_shm_layout_descriptor);
+  auto region_descriptions = reinterpret_cast<vsoc_device_region*>(header + 1);
+  for (size_t idx = 0; idx < allocators.size(); ++idx) {
+    auto shmem_region_desc = &region_descriptions[idx];
+    const auto& region = *allocators[idx].region_layout;
+    WriteRegionDescription(shmem_region_desc, allocators[idx]);
+    // Handle managed_by links
+    if (region.managed_by()) {
+      auto manager_idx = region_idx_by_name.at(region.managed_by());
+      if (manager_idx == VSOC_REGION_WHOLE) {
+        LOG(FATAL) << "Region '" << region.region_name() << "' has owner "
+                   << region.managed_by() << " with index " << manager_idx
+                   << " which is the default value for regions without an "
+                      "owner. Choose a different region to be at index "
+                   << manager_idx
+                   << ", make sure the chosen region is NOT the owner of any "
+                      "other region";
+      }
+      shmem_region_desc->managed_by = manager_idx;
+    } else {
+      shmem_region_desc->managed_by = VSOC_REGION_WHOLE;
+    }
+  }
+}
+}  // namespace
+
+void CreateSharedMemoryFile(
+    const std::string& path,
+    const std::map<std::string, uint32_t>& layout_increases) {
+  // TODO(ender): Lock the file after creation and check lock status upon second
+  // execution attempt instead of throwing an error.
+  LOG_IF(WARNING, unlink(path.c_str()) == 0)
+      << "Removed existing instance of " << path
+      << ". We currently don't know if another instance of daemon is running";
+  auto shared_mem_fd = cvd::SharedFD::Open(
+      path.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+  LOG_IF(FATAL, !shared_mem_fd->IsOpen())
+      << "Error in creating shared_memory file: " << shared_mem_fd->StrError();
+
+  auto region_layouts = VSoCMemoryLayout::Get()->GetRegions();
+  std::vector<VSoCRegionAllocator> allocators;
+  uint32_t file_size =
+      cvd::AlignToPageSize(sizeof(vsoc_shm_layout_descriptor) +
+                           region_layouts.size() * sizeof(vsoc_device_region));
+  for (auto layout : region_layouts) {
+    allocators.emplace_back(*layout, file_size /* offset */,
+                            layout_increases.count(layout->region_name())
+                                ? layout_increases.at(layout->region_name())
+                                : 0);
+    file_size += allocators.back().region_size;
+  }
+  file_size = cvd::RoundUpToNextPowerOf2(file_size);
+
+  int truncate_res = shared_mem_fd->Truncate(file_size);
+  LOG_IF(FATAL, truncate_res == -1)
+      << "Error in sizing up the shared memory file: "
+      << shared_mem_fd->StrError();
+
+  void* mmap_addr =
+      shared_mem_fd->Mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, 0);
+  LOG_IF(FATAL, mmap_addr == MAP_FAILED)
+      << "Error mmaping file: " << strerror(errno);
+  WriteLayout(mmap_addr, allocators, file_size);
+  munmap(mmap_addr, file_size);
+}
+
+}  // namespace vsoc
diff --git a/host/commands/run_cvd/vsoc_shared_memory.h b/host/commands/run_cvd/vsoc_shared_memory.h
new file mode 100644
index 0000000..94c03a1
--- /dev/null
+++ b/host/commands/run_cvd/vsoc_shared_memory.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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 <map>
+#include <string>
+
+namespace vsoc {
+
+// Creates the shared memory file and initializes it with with the region
+// information. The size of each region is determined to be the smallest
+// multiple of page size that can accommodate the header and the layout. The
+// size of the layout can be increased by specifyng it on the
+// layout_increases_by_region_name parameter.
+void CreateSharedMemoryFile(
+    const std::string& path,
+    const std::map<std::string, uint32_t>& layout_increases_by_region_name = {});
+
+}  // namespace vsoc
diff --git a/host/commands/virtual_usb_manager/Android.bp b/host/commands/virtual_usb_manager/Android.bp
index 40c8c74..9c86b70 100644
--- a/host/commands/virtual_usb_manager/Android.bp
+++ b/host/commands/virtual_usb_manager/Android.bp
@@ -34,6 +34,7 @@
         "cuttlefish_glog",
     ],
     shared_libs: [
+        "vsoc_lib",
         "libcuttlefish_fs",
         "libcuttlefish_utils",
         "cuttlefish_auto_resources",
diff --git a/host/frontend/Android.bp b/host/frontend/Android.bp
index 8d78a9f..5a053e7 100644
--- a/host/frontend/Android.bp
+++ b/host/frontend/Android.bp
@@ -14,6 +14,7 @@
 // limitations under the License.
 
 subdirs = [
+    "stream_audio",
     "vnc_server",
     "adb_connector",
 ]
diff --git a/host/frontend/stream_audio/Android.bp b/host/frontend/stream_audio/Android.bp
new file mode 100644
index 0000000..8f07536
--- /dev/null
+++ b/host/frontend/stream_audio/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2018 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.
+
+cc_binary_host {
+    name: "stream_audio",
+    srcs: [
+        "main.cpp",
+    ],
+    shared_libs: [
+        "cuttlefish_tcp_socket",
+        "libbase",
+        "libcuttlefish_utils",
+        "libcuttlefish_fs",
+        "libopus",
+        "vsoc_lib",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libjsoncpp",
+        "libgflags",
+        "libopuscpp",
+    ],
+    cpp_std: "c++17",
+    enabled: false,
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/frontend/stream_audio/main.cpp b/host/frontend/stream_audio/main.cpp
new file mode 100644
index 0000000..f63e315
--- /dev/null
+++ b/host/frontend/stream_audio/main.cpp
@@ -0,0 +1,270 @@
+/*
+ *
+ * Copyright (C) 2018 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.
+ */
+
+// For each client that connects initially a header is sent with the following,
+// in this order, all as uint16_t in network-byte-order:
+//  number of channels, frame rate
+//
+// Following, audio packets are sent as a uint32_t length (network byte order)
+// indicating the number of bytes
+// followed by the (opus) frame_size as a uint32_t
+// followed by <length> bytes.
+
+#include "common/libs/tcp_socket/tcp_socket.h"
+#include "common/vsoc/lib/audio_data_region_view.h"
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "host/frontend/stream_audio/opuscpp/opus_wrapper.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+#include <android-base/logging.h>
+#include <gflags/gflags.h>
+
+#include <arpa/inet.h>
+
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+using vsoc::audio_data::AudioDataRegionView;
+
+DEFINE_int32(port, 0, "port on which to serve audio.");
+
+namespace {
+
+// Read audio frames from the AudioDataRegionView
+class AudioStreamer {
+ public:
+  cvd::Message MakeAudioDescriptionHeader() const {
+    std::unique_lock guard(buffer_lock_);
+    while (!audio_buffer_) {
+      buffer_cv_.wait(guard);
+    }
+
+    const size_t num_channels = header_.frame_size / sizeof(opus_int16);
+    return cvd::CreateMessage(static_cast<std::uint16_t>(num_channels),
+                              static_cast<std::uint16_t>(header_.frame_rate));
+  }
+
+  std::uint32_t frame_rate() const {
+    std::unique_lock guard(buffer_lock_);
+    while (!audio_buffer_) {
+      buffer_cv_.wait(guard);
+    }
+    return header_.frame_rate;
+  }
+
+  std::uint32_t num_channels() const {
+    std::unique_lock guard(buffer_lock_);
+    while (!audio_buffer_) {
+      buffer_cv_.wait(guard);
+    }
+    return header_.frame_size / sizeof(opus_int16);
+  }
+
+  // Returns the frame id and audio frame
+  std::tuple<std::int64_t, std::shared_ptr<const cvd::Message>> audio_buffer(
+      std::int64_t previous_frame_num) const {
+    std::unique_lock guard(buffer_lock_);
+    while (header_.frame_num <= previous_frame_num) {
+      buffer_cv_.wait(guard);
+    }
+
+    return {header_.frame_num, audio_buffer_};
+  }
+
+  void Update() {
+    auto audio_data_rv =
+        AudioDataRegionView::GetInstance(vsoc::GetDomain().c_str());
+    auto worker = audio_data_rv->StartWorker();
+    std::vector<char> new_buffer;
+
+    while (true) {
+      new_buffer.resize(new_buffer.capacity());
+
+      auto [new_header, payload_size, audio_data] =
+          NextAudioMessage(audio_data_rv, &new_buffer);
+
+      LOG(DEBUG) << "stream " << new_header.stream_number << ", frame "
+                 << new_header.frame_num << ", rate " << new_header.frame_rate
+                 << ", channel_mask " << new_header.channel_mask << ", format "
+                 << new_header.format << ", payload_size " << payload_size
+                 << '\n';
+
+      {
+        std::lock_guard guard(buffer_lock_);
+        CheckAudioConfigurationIsSame(new_header);
+        header_ = new_header;
+        audio_buffer_ = std::make_shared<const cvd::Message>(
+            audio_data, audio_data + payload_size);
+      }
+      buffer_cv_.notify_all();
+    }
+  }
+
+ private:
+  struct AudioMessage {
+    gce_audio_message header;
+    std::size_t payload_size;
+    const std::uint8_t* payload_data;
+  };
+
+  void ReadAudioMessage(AudioDataRegionView* audio_data_rv,
+                        std::vector<char>* buffer) const {
+    while (true) {
+      auto read_size = audio_data_rv->data()->audio_queue.Read(
+          audio_data_rv, buffer->data(), buffer->size());
+      if (read_size == -ENOSPC) {
+        DoubleSize(buffer);
+      } else if (read_size < 0) {
+        LOG(ERROR) << "CircularPacketQueue::Read returned " << read_size;
+      } else {
+        buffer->resize(read_size);
+        return;
+      }
+    }
+  }
+
+  void DoubleSize(std::vector<char>* buffer) const {
+    if (buffer->empty()) {
+      buffer->resize(1);
+    } else {
+      buffer->resize(buffer->size() * 2);
+    }
+  }
+
+  gce_audio_message GetHeaderFromBuffer(const std::vector<char>& buffer) const {
+    gce_audio_message new_header{};
+    CHECK_GE(buffer.size(), sizeof new_header);
+
+    std::memcpy(&new_header, buffer.data(), sizeof new_header);
+    CHECK_GT(new_header.stream_number, 0u);
+    return new_header;
+  }
+
+  std::tuple<std::size_t, const std::uint8_t*> GetPayloadFromBuffer(
+      const std::vector<char>& buffer) const {
+    const auto payload_size = buffer.size() - sizeof(gce_audio_message);
+    const auto* audio_data =
+        reinterpret_cast<const std::uint8_t*>(buffer.data()) +
+        sizeof(gce_audio_message);
+    return {payload_size, audio_data};
+  }
+
+  AudioMessage NextAudioMessage(AudioDataRegionView* audio_data_rv,
+                                std::vector<char>* buffer) const {
+    while (true) {
+      ReadAudioMessage(audio_data_rv, buffer);
+      auto header = GetHeaderFromBuffer(*buffer);
+      if (header.message_type == gce_audio_message::DATA_SAMPLES) {
+        auto [payload_size, payload_data] = GetPayloadFromBuffer(*buffer);
+        return {header, payload_size, payload_data};
+      }
+    }
+  }
+
+  void CheckAudioConfigurationIsSame(
+      const gce_audio_message& new_header) const {
+    if (audio_buffer_) {
+      CHECK_EQ(header_.frame_size, new_header.frame_size)
+          << "audio frame_size changed";
+      CHECK_EQ(header_.frame_rate, new_header.frame_rate)
+          << "audio frame_rate changed";
+      CHECK_EQ(header_.stream_number, new_header.stream_number)
+          << "audio stream_number changed";
+    }
+  }
+
+  std::shared_ptr<const cvd::Message> audio_buffer_{};
+  gce_audio_message header_{};
+  mutable std::mutex buffer_lock_;
+  mutable std::condition_variable buffer_cv_;
+};
+
+void HandleClient(AudioStreamer* audio_streamer,
+                  cvd::ClientSocket client_socket) {
+  auto num_channels = audio_streamer->num_channels();
+  opus::Encoder enc(audio_streamer->frame_rate(),
+                    audio_streamer->num_channels(), OPUS_APPLICATION_AUDIO);
+  CHECK(enc.valid()) << "Could not construct Encoder. Maybe bad frame_rate ("
+                     << audio_streamer->frame_rate() <<") or num_channels ("
+                     << audio_streamer->num_channels() << ")?";
+
+  auto header = audio_streamer->MakeAudioDescriptionHeader();
+  client_socket.SendNoSignal(header);
+  std::int64_t previous_frame_num = 0;
+
+  while (!client_socket.closed()) {
+    CHECK(enc.valid()) << "encoder in invalid state";
+    auto [frame_num, audio_data] =
+        audio_streamer->audio_buffer(previous_frame_num);
+    previous_frame_num = frame_num;
+
+    std::vector<opus_int16> pcm(audio_data->size() / sizeof(opus_int16));
+    std::memcpy(pcm.data(), audio_data->data(), audio_data->size());
+    // in opus terms "frame_size" is the number of unencoded samples per frame
+    const std::uint32_t frame_size = pcm.size() / num_channels;
+    auto encoded = enc.Encode(pcm, frame_size);
+    for (auto&& p : encoded) {
+      auto length_message =
+          cvd::CreateMessage(static_cast<std::uint32_t>(p.size()));
+      client_socket.SendNoSignal(length_message);
+      client_socket.SendNoSignal(cvd::CreateMessage(frame_size));
+      client_socket.SendNoSignal(p);
+    }
+  }
+}
+
+[[noreturn]] void AudioStreamerUpdateLoop(AudioStreamer* audio_streamer) {
+  while (true) {
+    audio_streamer->Update();
+  }
+}
+
+[[noreturn]] void MainLoop() {
+  AudioStreamer audio_streamer;
+  std::thread audio_streamer_update_thread;
+  auto server = cvd::ServerSocket(FLAGS_port);
+  while (true) {
+    LOG(INFO) << "waiting for client connection";
+    auto client = server.Accept();
+    LOG(INFO) << "client socket accepted";
+    if (!audio_streamer_update_thread.joinable()) {
+      audio_streamer_update_thread =
+          std::thread{AudioStreamerUpdateLoop, &audio_streamer};
+    }
+    std::thread(HandleClient, &audio_streamer, std::move(client)).detach();
+  }
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  gflags::SetUsageMessage(" ");
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  if (FLAGS_port <= 0) {
+    std::cerr << "--port must be specified.\n";
+    return 1;
+  }
+  MainLoop();
+}
diff --git a/host/frontend/stream_audio/opuscpp/Android.bp b/host/frontend/stream_audio/opuscpp/Android.bp
new file mode 100644
index 0000000..9ad7a52
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/Android.bp
@@ -0,0 +1,28 @@
+//
+// 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.
+
+cc_library_host_static {
+    name: "libopuscpp",
+    srcs: [
+        "opus_wrapper.cc",
+    ],
+    shared_libs: [
+        "libbase",
+        "libopus",
+    ],
+    cpp_std: "c++17",
+    enabled: false,
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/frontend/stream_audio/opuscpp/opus_wrapper.cc b/host/frontend/stream_audio/opuscpp/opus_wrapper.cc
new file mode 100644
index 0000000..538bee8
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/opus_wrapper.cc
@@ -0,0 +1,177 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+//     https://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.
+
+// https://github.com/google/opuscpp
+
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include "host/frontend/stream_audio/opuscpp/opus_wrapper.h"
+
+std::string opus::ErrorToString(int error) {
+  switch (error) {
+    case OPUS_OK:
+      return "OK";
+    case OPUS_BAD_ARG:
+      return "One or more invalid/out of range arguments.";
+    case OPUS_BUFFER_TOO_SMALL:
+      return "The mode struct passed is invalid.";
+    case OPUS_INTERNAL_ERROR:
+      return "An internal error was detected.";
+    case OPUS_INVALID_PACKET:
+      return "The compressed data passed is corrupted.";
+    case OPUS_UNIMPLEMENTED:
+      return "Invalid/unsupported request number.";
+    case OPUS_INVALID_STATE:
+      return "An encoder or decoder structure is invalid or already freed.";
+    default:
+      return "Unknown error code: " + std::to_string(error);
+  }
+}
+
+void opus::internal::OpusDestroyer::operator()(OpusEncoder* encoder) const
+    noexcept {
+  opus_encoder_destroy(encoder);
+}
+
+void opus::internal::OpusDestroyer::operator()(OpusDecoder* decoder) const
+    noexcept {
+  opus_decoder_destroy(decoder);
+}
+
+opus::Encoder::Encoder(opus_int32 sample_rate, int num_channels,
+                       int application, int expected_loss_percent)
+    : num_channels_{num_channels} {
+  int error{};
+  encoder_.reset(
+      opus_encoder_create(sample_rate, num_channels, application, &error));
+  valid_ = error == OPUS_OK;
+  if (!valid()) {
+    LOG(INFO) << "Could not construct encoder. Error: " << ErrorToString(error);
+    return;
+  }
+  if (expected_loss_percent > 0) {
+    LOG(INFO) << "Enabling FEC in the encoder.";
+    Ctl(OPUS_SET_INBAND_FEC(1));
+    Ctl(OPUS_SET_PACKET_LOSS_PERC(expected_loss_percent));
+  }
+}
+
+bool opus::Encoder::ResetState() {
+  valid_ = Ctl(OPUS_RESET_STATE) == OPUS_OK;
+  return valid_;
+}
+
+bool opus::Encoder::SetBitrate(int bitrate) {
+  valid_ = Ctl(OPUS_SET_BITRATE(bitrate)) == OPUS_OK;
+  return valid_;
+}
+
+bool opus::Encoder::SetVariableBitrate(int vbr) {
+  valid_ = Ctl(OPUS_SET_VBR(vbr)) == OPUS_OK;
+  return valid_;
+}
+
+bool opus::Encoder::SetComplexity(int complexity) {
+  valid_ = Ctl(OPUS_SET_COMPLEXITY(complexity)) == OPUS_OK;
+  return valid_;
+}
+
+int opus::Encoder::GetLookahead() {
+  opus_int32 skip{};
+  valid_ = Ctl(OPUS_GET_LOOKAHEAD(&skip)) == OPUS_OK;
+  return skip;
+}
+
+std::vector<std::vector<unsigned char>> opus::Encoder::Encode(
+    const std::vector<opus_int16>& pcm, int frame_size) {
+  constexpr auto sample_size = sizeof(pcm[0]);
+  const auto frame_length = frame_size * num_channels_ * sample_size;
+  auto data_length = pcm.size() * sample_size;
+  if (data_length % frame_length != 0u) {
+    LOG(WARNING) << "PCM samples contain an incomplete frame. Ignoring the "
+                    "incomplete frame.";
+    data_length -= (data_length % frame_length);
+  }
+
+  std::vector<std::vector<unsigned char>> encoded;
+  for (std::size_t i{}; i < data_length; i += frame_length) {
+    encoded.push_back(EncodeFrame(pcm.begin() + (i / sample_size), frame_size));
+  }
+  return encoded;
+}
+
+std::vector<unsigned char> opus::Encoder::EncodeFrame(
+    const std::vector<opus_int16>::const_iterator& frame_start,
+    int frame_size) {
+  const auto frame_length = (frame_size * num_channels_ * sizeof(*frame_start));
+  std::vector<unsigned char> encoded(frame_length);
+  auto num_bytes = opus_encode(encoder_.get(), &*frame_start, frame_size,
+                               encoded.data(), encoded.size());
+  if (num_bytes < 0) {
+    LOG(ERROR) << "Encode error: " << opus::ErrorToString(num_bytes);
+    return {};
+  }
+  encoded.resize(num_bytes);
+  return encoded;
+}
+
+opus::Decoder::Decoder(opus_uint32 sample_rate, int num_channels)
+    : num_channels_(num_channels) {
+  int error{};
+  decoder_.reset(opus_decoder_create(sample_rate, num_channels, &error));
+  valid_ = error == OPUS_OK;
+}
+
+std::vector<opus_int16> opus::Decoder::Decode(
+    const std::vector<std::vector<unsigned char>>& packets, int frame_size,
+    bool decode_fec) {
+  std::vector<opus_int16> decoded;
+  for (const auto& enc : packets) {
+    auto just_decoded = Decode(enc, frame_size, decode_fec);
+    decoded.insert(std::end(decoded), std::begin(just_decoded),
+                   std::end(just_decoded));
+  }
+  return decoded;
+}
+
+std::vector<opus_int16> opus::Decoder::Decode(
+    const std::vector<unsigned char>& packet, int frame_size, bool decode_fec) {
+  const auto frame_length = (frame_size * num_channels_ * sizeof(opus_int16));
+  std::vector<opus_int16> decoded(frame_length);
+  auto num_samples = opus_decode(decoder_.get(), packet.data(), packet.size(),
+                                 decoded.data(), frame_size, decode_fec);
+  if (num_samples < 0) {
+    LOG(ERROR) << "Decode error: " << opus::ErrorToString(num_samples);
+    return {};
+  }
+  decoded.resize(num_samples * num_channels_);
+  return decoded;
+}
+
+std::vector<opus_int16> opus::Decoder::DecodeDummy(int frame_size) {
+  const auto frame_length = (frame_size * num_channels_ * sizeof(opus_int16));
+  std::vector<opus_int16> decoded(frame_length);
+  auto num_samples =
+      opus_decode(decoder_.get(), nullptr, 0, decoded.data(), frame_size, true);
+  if (num_samples < 0) {
+    LOG(ERROR) << "Decode error: " << opus::ErrorToString(num_samples);
+    return {};
+  }
+  decoded.resize(num_samples * num_channels_);
+  return decoded;
+}
diff --git a/host/frontend/stream_audio/opuscpp/opus_wrapper.h b/host/frontend/stream_audio/opuscpp/opus_wrapper.h
new file mode 100644
index 0000000..07e932e
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/opus_wrapper.h
@@ -0,0 +1,133 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+//     https://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.
+
+// https://github.com/google/opuscpp
+
+#ifndef OPUSCPP_OPUS_WRAPPER_H_
+#define OPUSCPP_OPUS_WRAPPER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "opus.h"
+
+namespace opus {
+
+std::string ErrorToString(int error);
+
+namespace internal {
+// Deleter for OpusEncoders and OpusDecoders
+struct OpusDestroyer {
+  void operator()(OpusEncoder* encoder) const noexcept;
+  void operator()(OpusDecoder* decoder) const noexcept;
+};
+template <typename T>
+using opus_uptr = std::unique_ptr<T, OpusDestroyer>;
+}  // namespace internal
+
+class Encoder {
+ public:
+  // see documentation at:
+  // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__encoder.html#gaa89264fd93c9da70362a0c9b96b9ca88
+  // Fs corresponds to sample_rate
+  //
+  // If expected_loss_percent is positive, FEC will be enabled
+  Encoder(opus_int32 sample_rate, int num_channels, int application,
+          int expected_loss_percent = 0);
+
+  // Resets internal state of encoder. This should be called between encoding
+  // different streams so that back-to-back decoding and one-at-a-time decoding
+  // give the same result. Returns true on success.
+  bool ResetState();
+
+  // Sets the desired bitrate. Rates from 500 to 512000 are meaningful as well
+  // as the special values OPUS_AUTO and OPUS_BITRATE_MAX. If this method
+  // is not called, the default value of OPUS_AUTO is used.
+  // Returns true on success.
+  bool SetBitrate(int bitrate);
+
+  // Enables or disables variable bitrate in the encoder. By default, variable
+  // bitrate is enabled. Returns true on success.
+  bool SetVariableBitrate(int vbr);
+
+  // Sets the computational complexity of the encoder, in the range of 0 to 10,
+  // inclusive, with 10 being the highest complexity. Returns true on success.
+  bool SetComplexity(int complexity);
+
+  // Gets the total samples of delay added by the entire codec. This value
+  // is the minimum amount of 'preskip' that has to be specified in an
+  // ogg-stream that encapsulates the encoded audio.
+  int GetLookahead();
+
+  // Takes audio data and encodes it. Returns a sequence of encoded packets.
+  // pcm.size() must be divisible by frame_size * (number of channels);
+  // pcm must not contain any incomplete packets.
+  // see documentation for pcm and frame_size at:
+  // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__encoder.html#gad2d6bf6a9ffb6674879d7605ed073e25
+  std::vector<std::vector<unsigned char>> Encode(
+      const std::vector<opus_int16>& pcm, int frame_size);
+
+  int valid() const { return valid_; }
+
+ private:
+  std::vector<unsigned char> EncodeFrame(
+      const std::vector<opus_int16>::const_iterator& frame_start,
+      int frame_size);
+
+  template <typename... Ts>
+  int Ctl(int request, Ts... args) const {
+    return opus_encoder_ctl(encoder_.get(), request, args...);
+  }
+
+  int num_channels_{};
+  bool valid_{};
+  internal::opus_uptr<OpusEncoder> encoder_;
+};
+
+class Decoder {
+ public:
+  // see documentation at:
+  // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html#ga753f6fe0b699c81cfd47d70c8e15a0bd
+  // Fs corresponds to sample_rate
+  Decoder(opus_uint32 sample_rate, int num_channels);
+
+  // Takes a sequence of encoded packets and decodes them. Returns the decoded
+  // audio.
+  // see documentation at:
+  // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html#ga7d1111f64c36027ddcb81799df9b3fc9
+  std::vector<opus_int16> Decode(
+      const std::vector<std::vector<unsigned char>>& packets, int frame_size,
+      bool decode_fec);
+
+  int valid() const { return valid_; }
+
+  // Takes an encoded packet and decodes it. Returns the decoded audio
+  // see documentation at:
+  // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html#ga7d1111f64c36027ddcb81799df9b3fc9
+  std::vector<opus_int16> Decode(const std::vector<unsigned char>& packet,
+                                 int frame_size, bool decode_fec);
+
+  // Generates a dummy frame by passing nullptr to the underlying opus decode.
+  std::vector<opus_int16> DecodeDummy(int frame_size);
+
+ private:
+  int num_channels_{};
+  bool valid_{};
+  internal::opus_uptr<OpusDecoder> decoder_;
+};
+
+}  // namespace opus
+
+#endif
diff --git a/host/frontend/vnc_server/Android.bp b/host/frontend/vnc_server/Android.bp
index 210f21d..fae5a17 100644
--- a/host/frontend/vnc_server/Android.bp
+++ b/host/frontend/vnc_server/Android.bp
@@ -30,6 +30,7 @@
         "cuttlefish_glog",
     ],
     shared_libs: [
+        "vsoc_lib",
         "libcuttlefish_fs",
         "libcuttlefish_utils",
         "cuttlefish_tcp_socket",
diff --git a/host/frontend/vnc_server/screen_connector.cpp b/host/frontend/vnc_server/screen_connector.cpp
index 35cf22f..53cb389 100644
--- a/host/frontend/vnc_server/screen_connector.cpp
+++ b/host/frontend/vnc_server/screen_connector.cpp
@@ -18,11 +18,11 @@
 
 #include <atomic>
 #include <condition_variable>
-#include <thread>
 
-#include <glog/logging.h>
 #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, "");
@@ -31,6 +31,23 @@
 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 {
@@ -123,7 +140,12 @@
 }  // namespace
 
 ScreenConnector* ScreenConnector::Get() {
-  return new SocketBasedScreenConnector();
+  auto config = vsoc::CuttlefishConfig::Get();
+  if (config->enable_ivserver()) {
+    return new VSoCScreenConnector();
+  } else {
+    return new SocketBasedScreenConnector();
+  }
 }
 
 }  // namespace vnc
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index 96702e8..7a94454 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -20,6 +20,7 @@
 #include "host/libs/config/cuttlefish_config.h"
 
 using cvd::vnc::SimulatedHWComposer;
+using vsoc::screen::ScreenRegionView;
 
 SimulatedHWComposer::SimulatedHWComposer(BlackBoard* bb)
     :
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
index fb91409..31c0328 100644
--- a/host/frontend/vnc_server/virtual_inputs.cpp
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -16,19 +16,18 @@
 
 #include "host/frontend/vnc_server/virtual_inputs.h"
 #include <gflags/gflags.h>
-#include <glog/logging.h>
 #include <linux/input.h>
 #include <linux/uinput.h>
 
 #include <cstdint>
 #include <mutex>
-#include <thread>
 #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");
@@ -246,6 +245,37 @@
 
 }  // namespace
 
+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";
+    }
+  }
+
+  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()
diff --git a/host/frontend/vnc_server/virtual_inputs.h b/host/frontend/vnc_server/virtual_inputs.h
index 7aca3eb..f92693b 100644
--- a/host/frontend/vnc_server/virtual_inputs.h
+++ b/host/frontend/vnc_server/virtual_inputs.h
@@ -21,6 +21,8 @@
 #include <map>
 #include <mutex>
 
+#include "common/vsoc/lib/input_events_region_view.h"
+
 namespace cvd {
 namespace vnc {
 
diff --git a/host/frontend/vnc_server/vnc_client_connection.cpp b/host/frontend/vnc_server/vnc_client_connection.cpp
index b81bb88..a8faf47 100644
--- a/host/frontend/vnc_server/vnc_client_connection.cpp
+++ b/host/frontend/vnc_server/vnc_client_connection.cpp
@@ -42,17 +42,7 @@
 using cvd::vnc::Stripe;
 using cvd::vnc::StripePtrVec;
 using cvd::vnc::VncClientConnection;
-
-struct ScreenRegionView {
-  using Pixel = uint32_t;
-  static constexpr int kSwiftShaderPadding = 4;
-  static constexpr int kRedShift = 0;
-  static constexpr int kGreenShift = 8;
-  static constexpr int kBlueShift = 16;
-  static constexpr int kRedBits = 8;
-  static constexpr int kGreenBits = 8;
-  static constexpr int kBlueBits = 8;
-};
+using vsoc::screen::ScreenRegionView;
 
 DEFINE_bool(debug_client, false, "Turn on detailed logging for the client");
 
diff --git a/host/frontend/vnc_server/vnc_utils.h b/host/frontend/vnc_server/vnc_utils.h
index db88131..0953c4e 100644
--- a/host/frontend/vnc_server/vnc_utils.h
+++ b/host/frontend/vnc_server/vnc_utils.h
@@ -23,6 +23,7 @@
 
 #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"
 
 namespace cvd {
@@ -64,7 +65,7 @@
 };
 
 inline constexpr int BytesPerPixel() {
-  return sizeof(uint32_t);
+  return sizeof(vsoc::screen::ScreenRegionView::Pixel);
 }
 
 // The width of the screen regardless of orientation. Does not change.
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 6f66fb3..17dcb6e 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -134,6 +134,10 @@
 const char* kVncServerBinary = "vnc_server_binary";
 const char* kVncServerPort = "vnc_server_port";
 
+const char* kEnableStreamAudio = "enable_stream_audio";
+const char* kStreamAudioBinary = "stream_audio_binary";
+const char* kStreamAudioPort = "stream_audio_port";
+
 const char* kRestartSubprocesses = "restart_subprocesses";
 const char* kRunAdbConnector = "run_adb_connector";
 const char* kAdbConnectorBinary = "adb_connector_binary";
@@ -559,7 +563,9 @@
 
 static AdbMode stringToAdbMode(std::string mode) {
   std::transform(mode.begin(), mode.end(), mode.begin(), ::tolower);
-  if (mode == "vsock_tunnel") {
+  if (mode == "tunnel") {
+    return AdbMode::Tunnel;
+  } else if (mode == "vsock_tunnel") {
     return AdbMode::VsockTunnel;
   } else if (mode == "vsock_half_tunnel") {
     return AdbMode::VsockHalfTunnel;
@@ -606,10 +612,11 @@
 
 std::string CuttlefishConfig::adb_device_name() const {
   // TODO(schuffelen): Deal with duplication between here and launch.cc
+  bool tunnelMode = adb_mode().count(AdbMode::Tunnel) > 0;
   bool vsockTunnel = adb_mode().count(AdbMode::VsockTunnel) > 0;
   bool vsockHalfProxy = adb_mode().count(AdbMode::VsockHalfTunnel) > 0;
   bool nativeVsock = adb_mode().count(AdbMode::NativeVsock) > 0;
-  if (vsockTunnel || vsockHalfProxy || nativeVsock) {
+  if (tunnelMode || vsockTunnel || vsockHalfProxy || nativeVsock) {
     return adb_ip_and_port();
   } else if (adb_mode().count(AdbMode::Usb) > 0) {
     return serial_number();
@@ -701,6 +708,31 @@
   (*dictionary_)[kVncServerPort] = vnc_server_port;
 }
 
+bool CuttlefishConfig::enable_stream_audio() const {
+  return (*dictionary_)[kEnableStreamAudio].asBool();
+}
+
+void CuttlefishConfig::set_enable_stream_audio(bool enable_stream_audio) {
+  (*dictionary_)[kEnableStreamAudio] = enable_stream_audio;
+}
+
+std::string CuttlefishConfig::stream_audio_binary() const {
+  return (*dictionary_)[kStreamAudioBinary].asString();
+}
+
+void CuttlefishConfig::set_stream_audio_binary(
+    const std::string& stream_audio_binary) {
+  (*dictionary_)[kStreamAudioBinary] = stream_audio_binary;
+}
+
+int CuttlefishConfig::stream_audio_port() const {
+  return (*dictionary_)[kStreamAudioPort].asInt();
+}
+
+void CuttlefishConfig::set_stream_audio_port(int stream_audio_port) {
+  (*dictionary_)[kStreamAudioPort] = stream_audio_port;
+}
+
 bool CuttlefishConfig::restart_subprocesses() const {
   return (*dictionary_)[kRestartSubprocesses].asBool();
 }
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 4ec49e7..fc479ec 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -39,6 +39,7 @@
 
 
 enum class AdbMode {
+  Tunnel,
   VsockTunnel,
   VsockHalfTunnel,
   NativeVsock,
@@ -275,6 +276,15 @@
   void set_vnc_server_binary(const std::string& vnc_server_binary);
   std::string vnc_server_binary() const;
 
+  void set_enable_stream_audio(bool enable_stream_audio);
+  bool enable_stream_audio() const;
+
+  void set_stream_audio_port(int stream_audio_port);
+  int stream_audio_port() const;
+
+  void set_stream_audio_binary(const std::string& stream_audio_binary);
+  std::string stream_audio_binary() const;
+
   void set_restart_subprocesses(bool restart_subprocesses);
   bool restart_subprocesses() const;
 
diff --git a/host/vsoc/lib/gralloc_buffer_region_view.cpp b/host/vsoc/lib/gralloc_buffer_region_view.cpp
new file mode 100644
index 0000000..db302cb
--- /dev/null
+++ b/host/vsoc/lib/gralloc_buffer_region_view.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 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/vsoc/lib/gralloc_buffer_region_view.h"
+
+#include <memory>
+#include <mutex>
+#include "glog/logging.h"
+
+using vsoc::gralloc::GrallocBufferRegionView;
+
+uint8_t* GrallocBufferRegionView::OffsetToBufferPtr(uint32_t offset) {
+  if (offset <= control_->region_desc().offset_of_region_data ||
+      offset >= control_->region_size()) {
+    LOG(FATAL)
+        << "Attempted to access a gralloc buffer outside region data, offset: "
+        << offset;
+    return nullptr;
+  }
+  return region_offset_to_pointer<uint8_t>(offset);
+}
diff --git a/host/vsoc/lib/gralloc_buffer_region_view.h b/host/vsoc/lib/gralloc_buffer_region_view.h
new file mode 100644
index 0000000..b132a0e
--- /dev/null
+++ b/host/vsoc/lib/gralloc_buffer_region_view.h
@@ -0,0 +1,47 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/gralloc_layout.h"
+
+#include <string>
+
+namespace vsoc {
+namespace gralloc {
+
+// Allows access to the gralloc buffer region from host side. It needs to be a
+// different class than the one on guest side because of the required
+// interactions with the kernel on the guest.
+// Initially this class only returns a pointer to a buffer in memory given a
+// region offset, which is enough for now since it's only used by the hwcomposer
+// (which gets all other information from the guest side hwcomposer) and by the
+// VNC server (which uses only the frame buffer and gets the information it
+// needs from the framebuffer region).
+class GrallocBufferRegionView
+    : vsoc::TypedRegionView<
+        GrallocBufferRegionView,
+        vsoc::layout::gralloc::GrallocBufferLayout> {
+   public:
+  GrallocBufferRegionView() = default;
+  GrallocBufferRegionView(const GrallocBufferRegionView&) = delete;
+  GrallocBufferRegionView& operator=(const GrallocBufferRegionView&) = delete;
+
+  uint8_t* OffsetToBufferPtr(uint32_t offset);
+};
+
+}  // namespace gralloc
+}  // namespace vsoc
diff --git a/host/vsoc/lib/host_lock.cpp b/host/vsoc/lib/host_lock.cpp
new file mode 100644
index 0000000..895a429
--- /dev/null
+++ b/host/vsoc/lib/host_lock.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+#include "sys/types.h"
+
+#include "common/vsoc/lib/compat.h"
+#include "common/vsoc/lib/single_sided_signal.h"
+
+namespace vsoc {
+namespace layout {
+
+void HostLock::Lock() {
+  uint32_t tid = gettid();
+  uint32_t expected_value;
+  uint32_t* uaddr = reinterpret_cast<uint32_t*>(&lock_uint32_);
+
+  while (1) {
+    if (TryLock(tid, &expected_value)) {
+      return;
+    }
+    SingleSidedSignal::AwaitSignal(expected_value, uaddr);
+  }
+}
+
+void HostLock::Unlock() {
+  Sides sides_to_signal = UnlockCommon(gettid());
+  if (sides_to_signal != Sides::NoSides) {
+    SingleSidedSignal::Signal(&lock_uint32_);
+  }
+}
+
+bool HostLock::Recover() {
+  return RecoverSingleSided();
+}
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/host/vsoc/lib/host_region_e2e_test.cpp b/host/vsoc/lib/host_region_e2e_test.cpp
new file mode 100644
index 0000000..1366838
--- /dev/null
+++ b/host/vsoc/lib/host_region_e2e_test.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+/*
+ * End-to-end test to ensure that mapping of vsoc regions works on the host.
+ */
+
+#include <gtest/gtest.h>
+#include "common/vsoc/lib/e2e_test_region_view.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+// Here is a summary of the two regions interrupt and write test:
+// 1. Write our strings to the first region
+// 2. Ensure that our peer hasn't signalled the second region. That would
+//    indicate that it didn't wait for our interrupt.
+// 3. Send the interrupt on the first region
+// 4. Wait for our peer's interrupt on the first region
+// 5. Confirm that we can see our peer's writes in the first region
+// 6. Initialize our strings in the second region
+// 7. Send an interrupt on the second region to our peer
+// 8. Wait for our peer's interrupt on the second region
+// 9. Confirm that we can see our peer's writes in the second region
+// 10. Repeat the process for signaling.
+// 11. Confirm that no interrupt is pending in the first region
+// 12. Confirm that no interrupt is pending in the second region
+
+template <typename View>
+void SetHostStrings(View* in) {
+  size_t num_data = in->string_size();
+  EXPECT_LE(static_cast<size_t>(2), num_data);
+  for (size_t i = 0; i < num_data; ++i) {
+    EXPECT_TRUE(!in->host_string(i)[0] ||
+                !strcmp(in->host_string(i), View::Layout::host_pattern));
+    in->set_host_string(i, View::Layout::host_pattern);
+    EXPECT_STREQ(in->host_string(i), View::Layout::host_pattern);
+  }
+}
+
+template <typename View>
+void CheckPeerStrings(View* in) {
+  size_t num_data = in->string_size();
+  EXPECT_LE(static_cast<size_t>(2), num_data);
+  for (size_t i = 0; i < num_data; ++i) {
+    EXPECT_STREQ(View::Layout::guest_pattern, in->guest_string(i));
+  }
+}
+
+TEST(RegionTest, PeerTests) {
+  auto primary =
+      vsoc::E2EPrimaryRegionView::GetInstance(vsoc::GetDomain().c_str());
+  ASSERT_TRUE(!!primary);
+  auto secondary =
+      vsoc::E2ESecondaryRegionView::GetInstance(vsoc::GetDomain().c_str());
+  ASSERT_TRUE(!!secondary);
+  LOG(INFO) << "Regions are open";
+  SetHostStrings(primary);
+  EXPECT_FALSE(secondary->HasIncomingInterrupt());
+  EXPECT_TRUE(primary->MaybeInterruptPeer());
+  LOG(INFO) << "Waiting for first interrupt from peer";
+  primary->WaitForInterrupt();
+  LOG(INFO) << "First interrupt received";
+  CheckPeerStrings(primary);
+  SetHostStrings(secondary);
+  EXPECT_TRUE(secondary->MaybeInterruptPeer());
+  LOG(INFO) << "Waiting for second interrupt from peer";
+  secondary->WaitForInterrupt();
+  LOG(INFO) << "Second interrupt received";
+  CheckPeerStrings(secondary);
+
+  // Test signals
+  EXPECT_FALSE(secondary->HasIncomingInterrupt());
+  LOG(INFO) << "Verified no early second signal";
+  primary->SendSignal(vsoc::layout::Sides::Peer,
+                      &primary->data()->host_to_guest_signal);
+  LOG(INFO) << "Signal sent. Waiting for first signal from peer";
+  primary->WaitForInterrupt();
+  int count = 0;  // counts the number of signals received.
+  primary->ProcessSignalsFromPeer(
+      [&primary, &count](uint32_t offset) {
+        ++count;
+        EXPECT_EQ(primary->guest_to_host_signal_offset(), offset);
+      });
+  EXPECT_EQ(1, count);
+  LOG(INFO) << "Signal received on primary region";
+  secondary->SendSignal(vsoc::layout::Sides::Peer,
+                        &secondary->data()->host_to_guest_signal);
+  LOG(INFO) << "Signal sent. Waiting for second signal from peer";
+  secondary->WaitForInterrupt();
+  count = 0;
+  secondary->ProcessSignalsFromPeer(
+      [secondary, &count](uint32_t offset) {
+        ++count;
+        EXPECT_EQ(secondary->guest_to_host_signal_offset(), offset);
+      });
+  EXPECT_EQ(1, count);
+  LOG(INFO) << "Signal received on secondary region";
+
+  EXPECT_FALSE(primary->HasIncomingInterrupt());
+  EXPECT_FALSE(secondary->HasIncomingInterrupt());
+}
+
+TEST(RegionTest, MissingRegionCausesDeath) {
+  EXPECT_DEATH(
+      vsoc::E2EUnfindableRegionView::GetInstance(vsoc::GetDomain().c_str()),
+      ".*");
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  int rval = RUN_ALL_TESTS();
+  if (!rval) {
+    auto region =
+        vsoc::E2EPrimaryRegionView::GetInstance(vsoc::GetDomain().c_str());
+    region->host_status(vsoc::layout::e2e_test::E2E_MEMORY_FILLED);
+  }
+  return rval;
+}
diff --git a/host/vsoc/lib/region_control.cpp b/host/vsoc/lib/region_control.cpp
new file mode 100644
index 0000000..491f600
--- /dev/null
+++ b/host/vsoc/lib/region_control.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h"
+
+#define LOG_TAG "vsoc: region_host"
+
+#include <stdio.h>
+#include <string.h>
+#include <linux/futex.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+
+using cvd::SharedFD;
+
+namespace {
+
+class HostRegionControl : public vsoc::RegionControl {
+ public:
+  HostRegionControl(const char* region_name,
+                    const SharedFD& incoming_interrupt_fd,
+                    const SharedFD& outgoing_interrupt_fd,
+                    const SharedFD& shared_memory_fd)
+      : region_name_{region_name},
+        incoming_interrupt_fd_{incoming_interrupt_fd},
+        outgoing_interrupt_fd_{outgoing_interrupt_fd},
+        shared_memory_fd_{shared_memory_fd} {}
+
+  int CreateFdScopedPermission(const char* /*managed_region_name*/,
+                               uint32_t /*owner_offset*/,
+                               uint32_t /*owned_val*/,
+                               uint32_t /*begin_offset*/,
+                               uint32_t /*end_offset*/) override {
+    return -1;
+  }
+
+  bool InitializeRegion();
+
+  virtual bool InterruptPeer() override {
+    uint64_t one = 1;
+    ssize_t rval = outgoing_interrupt_fd_->Write(&one, sizeof(one));
+    if (rval != sizeof(one)) {
+      LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))";
+      return false;
+    }
+    return true;
+  }
+
+  // Wake the local signal table scanner. Primarily used during shutdown
+  virtual void InterruptSelf() override {
+    uint64_t one = 1;
+    ssize_t rval = incoming_interrupt_fd_->Write(&one, sizeof(one));
+    if (rval != sizeof(one)) {
+      LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))";
+    }
+  }
+
+  virtual void WaitForInterrupt() override {
+    // Check then act isn't a problem here: the other side does
+    // the following things in exactly this order:
+    //   1. exchanges 1 with interrupt_signalled
+    //   2. if interrupt_signalled was 0 it increments the eventfd
+    // eventfd increments are persistent, so if interrupt_signalled was set
+    // back to 1 while we are going to sleep the sleep will return
+    // immediately.
+    uint64_t missed{};
+    cvd::SharedFDSet readset;
+    readset.Set(incoming_interrupt_fd_);
+    cvd::Select(&readset, NULL, NULL, NULL);
+    ssize_t rval = incoming_interrupt_fd_->Read(&missed, sizeof(missed));
+    if (rval != sizeof(missed)) {
+      LOG(FATAL) << __FUNCTION__ << ": rval (" << rval
+                 << ") != sizeof(missed)), are there more than one threads "
+                    "waiting for interrupts?";
+    }
+    if (!missed) {
+      LOG(FATAL) << __FUNCTION__ << ": woke with 0 interrupts";
+    }
+  }
+
+  virtual void* Map() override {
+    if (region_base_) {
+      return region_base_;
+    }
+    // Now actually map the region
+    region_base_ =
+        shared_memory_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE,
+                                MAP_SHARED, region_desc_.region_begin_offset);
+    if (region_base_ == MAP_FAILED) {
+      LOG(FATAL) << "mmap failed for offset "
+                 << region_desc_.region_begin_offset << " ("
+                 << shared_memory_fd_->StrError() << ")";
+      region_base_ = nullptr;
+    }
+    return region_base_;
+  }
+
+
+  virtual int SignalSelf(uint32_t offset) override {
+    return syscall(SYS_futex, region_offset_to_pointer<int32_t*>(offset),
+                   FUTEX_WAKE, -1, nullptr, nullptr, 0);
+  }
+
+  virtual int WaitForSignal(uint32_t offset, uint32_t expected_value) override {
+    return syscall(SYS_futex, region_offset_to_pointer<int32_t*>(offset),
+                   FUTEX_WAIT, expected_value, nullptr, nullptr, 0);
+  }
+
+ protected:
+  const char* region_name_{};
+  cvd::SharedFD incoming_interrupt_fd_;
+  cvd::SharedFD outgoing_interrupt_fd_;
+  cvd::SharedFD shared_memory_fd_;
+};
+
+// Default path to the ivshmem_server socket. This can vary when we're
+// launching multiple CVDs.
+constexpr int kMaxSupportedProtocolVersion = 0;
+
+bool HostRegionControl::InitializeRegion() {
+  size_t region_name_len = strlen(region_name_);
+  if (region_name_len >= VSOC_DEVICE_NAME_SZ) {
+    LOG(FATAL) << "Region name length (" << region_name_len << ") not < "
+               << VSOC_DEVICE_NAME_SZ;
+    return false;
+  }
+  vsoc_shm_layout_descriptor layout;
+  ssize_t rval = shared_memory_fd_->Pread(&layout, sizeof(layout), 0);
+  if (rval != sizeof(layout)) {
+    LOG(FATAL) << "Unable to read layout, rval=" << rval << " ("
+               << shared_memory_fd_->StrError() << ")";
+    return false;
+  }
+  if (layout.major_version != CURRENT_VSOC_LAYOUT_MAJOR_VERSION) {
+    LOG(FATAL) << "Incompatible major version: saw " << layout.major_version
+               << " wanted " << CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
+  }
+  std::vector<vsoc_device_region> descriptors;
+  descriptors.resize(layout.region_count);
+  ssize_t wanted = sizeof(vsoc_device_region) * layout.region_count;
+  rval = shared_memory_fd_->Pread(descriptors.data(), wanted,
+                                  layout.vsoc_region_desc_offset);
+  if (rval != wanted) {
+    LOG(FATAL) << "Unable to read region descriptors, rval=" << rval << " ("
+               << shared_memory_fd_->StrError() << ")";
+    return false;
+  }
+  for (const auto& desc : descriptors) {
+    if (!strcmp(region_name_, desc.device_name)) {
+      region_desc_ = desc;
+      return true;
+    }
+  }
+
+  std::ostringstream buf;
+  for (const auto& desc : descriptors) {
+    buf << " " << desc.device_name;
+  }
+  LOG(FATAL) << "Region name of " << region_name_
+             << " not found among:" << buf.str();
+  return false;
+}
+}  // namespace
+
+std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open(
+    const char* region_name, const char* domain) {
+  AutoFreeBuffer msg;
+
+  SharedFD region_server =
+      SharedFD::SocketLocalClient(domain, false, SOCK_STREAM);
+  if (!region_server->IsOpen()) {
+    LOG(FATAL) << "Could not contact ivshmem_server ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+
+  // Check server protocol version.
+  uint32_t protocol_version;
+  ssize_t bytes = region_server->Recv(&protocol_version,
+                                      sizeof(protocol_version), MSG_NOSIGNAL);
+  if (bytes != sizeof(protocol_version)) {
+    LOG(FATAL) << "Failed to recv protocol version; res=" << bytes << " ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+
+  if (protocol_version > kMaxSupportedProtocolVersion) {
+    LOG(FATAL) << "Unsupported protocol version " << protocol_version
+               << "; max supported version is " << kMaxSupportedProtocolVersion;
+    return nullptr;
+  }
+
+  // Send requested region.
+  int16_t size = strlen(region_name);
+  bytes = region_server->Send(&size, sizeof(size), MSG_NOSIGNAL);
+  if (bytes != sizeof(size)) {
+    LOG(FATAL) << "Failed to send region name length; res=" << bytes << " ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+
+  bytes = region_server->Send(region_name, size, MSG_NOSIGNAL);
+  if (bytes != size) {
+    LOG(FATAL) << "Failed to send region name; res=" << bytes << " ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+
+  // Receive control sockets.
+  uint64_t control_data;
+  struct iovec iov {
+    &control_data, sizeof(control_data)
+  };
+  cvd::InbandMessageHeader hdr{};
+  hdr.msg_iov = &iov;
+  hdr.msg_iovlen = 1;
+  SharedFD fds[3];
+  bytes = region_server->RecvMsgAndFDs(hdr, 0, &fds);
+  if (bytes != sizeof(control_data)) {
+    LOG(FATAL) << "Failed to complete handshake; res=" << bytes << " ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+  HostRegionControl* rval =
+      new HostRegionControl(region_name, fds[0], fds[1], fds[2]);
+  if (!rval) {
+    return nullptr;
+  }
+  // Search for the region header
+  if (!rval->InitializeRegion()) {
+    // We already logged, so we can just bail out.
+    return nullptr;
+  }
+  return std::shared_ptr<RegionControl>(rval);
+}
diff --git a/host/vsoc/lib/region_view.cpp b/host/vsoc/lib/region_view.cpp
new file mode 100644
index 0000000..975b7cd
--- /dev/null
+++ b/host/vsoc/lib/region_view.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h"
+#include "common/vsoc/lib/region_control.h"
+
+const vsoc_signal_table_layout& vsoc::RegionView::incoming_signal_table() {
+  return control_->region_desc().guest_to_host_signal_table;
+}
+
+// Returns a pointer to the table that will be used to post signals
+const vsoc_signal_table_layout& vsoc::RegionView::outgoing_signal_table() {
+  return control_->region_desc().host_to_guest_signal_table;
+}