Merge remote-tracking branch 'aosp/cuttlefish-testing' into master

Bug: 129429729
Test: TH
Change-Id: Iec596ffa78a0b07c7d1952eb501f703f93d462a0
diff --git a/Android.bp b/Android.bp
index be209a0..a604f8c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,6 +40,7 @@
     header_libs: [
         "cuttlefish_common_headers",
         "cuttlefish_kernel_headers",
+        "cuttlefish_shared_config",
     ],
     target: {
         host: {
@@ -56,19 +57,6 @@
     vendor: true,
 }
 
-// ARM code should not touch the VSoC window on an x86 CPU.
-cc_defaults {
-    name: "cuttlefish_native_isa",
-    target: {
-        android_arm: {
-            enabled: false,
-        },
-        android_arm64: {
-            enabled: false,
-        },
-    },
-}
-
 cc_defaults {
     name: "cuttlefish_guest_only",
     defaults: ["cuttlefish_base"],
@@ -97,16 +85,13 @@
         "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/ril_layout.cpp",
-        "common/vsoc/lib/ril_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",
-        "common/vsoc/lib/wifi_exchange_layout.cpp",
-        "common/vsoc/lib/wifi_exchange_view.cpp",
     ],
     header_libs: ["cuttlefish_glog"],
     shared_libs: [
@@ -133,7 +118,7 @@
             ],
         },
     },
-    defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"],
+    defaults: ["cuttlefish_host_and_guest"],
 }
 
 cc_test_host {
diff --git a/Android.mk b/Android.mk
index c38d99b..fa659d7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -11,7 +11,7 @@
 # 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.
-ifneq ($(filter vsoc_x86 vsoc_x86_64, $(TARGET_DEVICE)),)
+ifneq ($(filter vsoc_arm vsoc_arm64 vsoc_x86 vsoc_x86_64, $(TARGET_DEVICE)),)
 LOCAL_PATH:= $(call my-dir)
 include $(call first-makefiles-under,$(LOCAL_PATH))
 endif
diff --git a/OWNERS b/OWNERS
index 5c0d0bd..3f32871 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,7 @@
+astrachan@google.com
 ghartman@google.com
 jemoreira@google.com
-haining@google.com
 malchev@google.com
 schuffelen@google.com
+muntsinger@google.com
+rammuthiah@google.com
\ No newline at end of file
diff --git a/common/frontend/Android.bp b/common/frontend/Android.bp
index 8021915..a7a4de5 100644
--- a/common/frontend/Android.bp
+++ b/common/frontend/Android.bp
@@ -15,4 +15,5 @@
 
 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
index 81ba9c4..74ef53a 100644
--- a/common/frontend/socket_forward_proxy/Android.bp
+++ b/common/frontend/socket_forward_proxy/Android.bp
@@ -41,5 +41,5 @@
             ],
         },
     },
-    defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"]
+    defaults: ["cuttlefish_host_and_guest"]
 }
diff --git a/common/frontend/socket_vsock_proxy/Android.bp b/common/frontend/socket_vsock_proxy/Android.bp
new file mode 100644
index 0000000..0a2f572
--- /dev/null
+++ b/common/frontend/socket_vsock_proxy/Android.bp
@@ -0,0 +1,44 @@
+//
+// 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_vsock_proxy",
+    srcs: [
+        "main.cpp",
+    ],
+    shared_libs: [
+        "cuttlefish_auto_resources",
+        "libbase",
+        "libcuttlefish_fs",
+        "libcuttlefish_utils",
+        "libcuttlefish_strings",
+        "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_vsock_proxy/main.cpp b/common/frontend/socket_vsock_proxy/main.cpp
new file mode 100644
index 0000000..ac421a3
--- /dev/null
+++ b/common/frontend/socket_vsock_proxy/main.cpp
@@ -0,0 +1,232 @@
+/*
+ * 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 <set>
+#include <thread>
+#include <glog/logging.h>
+#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
+
+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");
+DEFINE_uint32(vsock_guest_cid, 0, "Guest identifier");
+
+namespace {
+// Sends packets, Shutdown(SHUT_WR) on destruction
+class SocketSender {
+ public:
+  explicit SocketSender(cvd::SharedFD socket) : socket_{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_{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 SocketToVsock(SocketReceiver socket_receiver,
+                   SocketSender vsock_sender) {
+  while (true) {
+    auto packet = Packet::MakeData();
+    socket_receiver.Recv(&packet);
+    if (packet.empty() || vsock_sender.SendAll(packet) < 0) {
+      break;
+    }
+  }
+  LOG(INFO) << "Socket to vsock exiting";
+}
+
+void VsockToSocket(SocketSender socket_sender,
+                   SocketReceiver vsock_receiver) {
+  auto packet = Packet::MakeData();
+  while (true) {
+    vsock_receiver.Recv(&packet);
+    CHECK(packet.IsData());
+    if (packet.empty()) {
+      break;
+    }
+    if (socket_sender.SendAll(packet) < 0) {
+      break;
+    }
+  }
+  LOG(INFO) << "Vsock 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(cvd::SharedFD vsock,
+                      cvd::SharedFD socket) {
+  auto socket_to_vsock =
+      std::thread(SocketToVsock, SocketReceiver{socket}, SocketSender{vsock});
+  VsockToSocket(SocketSender{socket}, SocketReceiver{vsock});
+  socket_to_vsock.join();
+}
+
+#ifdef CUTTLEFISH_HOST
+[[noreturn]] void host() {
+  LOG(INFO) << "starting server on " << FLAGS_tcp_port << " for vsock port "
+            << FLAGS_vsock_port;
+  auto server = cvd::SharedFD::SocketLocalServer(FLAGS_tcp_port, SOCK_STREAM);
+  CHECK(server->IsOpen()) << "Could not start server on " << FLAGS_tcp_port;
+  LOG(INFO) << "Accepting client connections";
+  int last_failure_reason = 0;
+  while (true) {
+    auto client_socket = cvd::SharedFD::Accept(*server);
+    CHECK(client_socket->IsOpen()) << "error creating client socket";
+    cvd::SharedFD vsock_socket = cvd::SharedFD::VsockClient(
+        FLAGS_vsock_guest_cid, FLAGS_vsock_port, SOCK_STREAM);
+    if (vsock_socket->IsOpen()) {
+      last_failure_reason = 0;
+      LOG(INFO) << "Connected to vsock:" << FLAGS_vsock_guest_cid << ":"
+                << FLAGS_vsock_port;
+    } else {
+      // Don't log if the previous connection failed with the same error
+      if (last_failure_reason != vsock_socket->GetErrno()) {
+        last_failure_reason = vsock_socket->GetErrno();
+        LOG(ERROR) << "Unable to connect to vsock server: "
+                   << vsock_socket->StrError();
+      }
+      continue;
+    }
+    auto thread = std::thread(HandleConnection, std::move(vsock_socket),
+                              std::move(client_socket));
+    thread.detach();
+  }
+}
+
+#else
+cvd::SharedFD OpenSocketConnection() {
+  while (true) {
+    auto sock = cvd::SharedFD::SocketLocalClient(FLAGS_tcp_port, SOCK_STREAM);
+    if (sock->IsOpen()) {
+      return sock;
+    }
+    LOG(WARNING) << "could not connect on port " << FLAGS_tcp_port
+                 << ". sleeping for 1 second";
+    sleep(1);
+  }
+}
+
+bool socketErrorIsRecoverable(int error) {
+  std::set<int> unrecoverable{EACCES, EAFNOSUPPORT, EINVAL, EPROTONOSUPPORT};
+  return unrecoverable.find(error) == unrecoverable.end();
+}
+
+[[noreturn]] static void SleepForever() {
+  while (true) {
+    sleep(std::numeric_limits<unsigned int>::max());
+  }
+}
+
+[[noreturn]] void guest() {
+  LOG(INFO) << "Starting guest mainloop";
+  LOG(INFO) << "starting server on " << FLAGS_vsock_port;
+  cvd::SharedFD vsock;
+  do {
+    vsock = cvd::SharedFD::VsockServer(FLAGS_vsock_port, SOCK_STREAM);
+    if (!vsock->IsOpen() && !socketErrorIsRecoverable(vsock->GetErrno())) {
+      LOG(ERROR) << "Could not open vsock socket: " << vsock->StrError();
+      SleepForever();
+    }
+  } while (!vsock->IsOpen());
+  CHECK(vsock->IsOpen()) << "Could not start server on " << FLAGS_vsock_port;
+  while (true) {
+    LOG(INFO) << "waiting for vsock connection";
+    auto vsock_client = cvd::SharedFD::Accept(*vsock);
+    CHECK(vsock_client->IsOpen()) << "error creating vsock socket";
+    LOG(INFO) << "vsock socket accepted";
+    auto client = OpenSocketConnection();
+    CHECK(client->IsOpen()) << "error connecting to guest client";
+    auto thread = std::thread(HandleConnection, std::move(vsock_client),
+                              std::move(client));
+    thread.detach();
+  }
+}
+
+#endif
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  CHECK(FLAGS_tcp_port != 0) << "Must specify -tcp_port flag";
+  CHECK(FLAGS_vsock_port != 0) << "Must specify -vsock_port flag";
+#ifdef CUTTLEFISH_HOST
+  CHECK(FLAGS_vsock_guest_cid != 0) << "Must specify -vsock_guest_cid flag";
+  host();
+#else
+  guest();
+#endif
+}
diff --git a/common/libs/constants/ril.h b/common/libs/constants/ril.h
new file mode 100644
index 0000000..b9c3e8a
--- /dev/null
+++ b/common/libs/constants/ril.h
@@ -0,0 +1,31 @@
+#pragma once
+/*
+ * 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.
+ */
+
+#if defined(CUTTLEFISH_HOST)
+#define CF_PROPERTY_PREFIX "androidboot"
+#else
+#define CF_PROPERTY_PREFIX "ro.boot"
+#endif
+
+#define CUTTLEFISH_RIL_ADDR_PROPERTY CF_PROPERTY_PREFIX ".cuttlefish_ril_addr"
+#define CUTTLEFISH_RIL_GATEWAY_PROPERTY \
+  CF_PROPERTY_PREFIX ".cuttlefish_ril_gateway"
+#define CUTTLEFISH_RIL_DNS_PROPERTY CF_PROPERTY_PREFIX ".cuttlefish_ril_dns"
+#define CUTTLEFISH_RIL_BROADCAST_PROPERTY \
+  CF_PROPERTY_PREFIX ".cuttlefish_ril_broadcast"
+#define CUTTLEFISH_RIL_PREFIXLEN_PROPERTY \
+  CF_PROPERTY_PREFIX ".cuttlefish_ril_prefixlen"
diff --git a/common/libs/fs/shared_fd.cpp b/common/libs/fs/shared_fd.cpp
index 93d2027..6ca2826 100644
--- a/common/libs/fs/shared_fd.cpp
+++ b/common/libs/fs/shared_fd.cpp
@@ -253,8 +253,8 @@
       new FileInstance(epoll_create1(flags), errno));
 }
 
-inline bool SharedFD::SocketPair(int domain, int type, int protocol,
-                                 SharedFD* fd0, SharedFD* fd1) {
+bool SharedFD::SocketPair(int domain, int type, int protocol,
+                          SharedFD* fd0, SharedFD* fd1) {
   int fds[2];
   int rval = socketpair(domain, type, protocol, fds);
   if (rval != -1) {
@@ -287,6 +287,10 @@
   }
 }
 
+SharedFD SharedFD::ErrorFD(int error) {
+  return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(-1, error)));
+}
+
 SharedFD SharedFD::SocketLocalClient(const char* name, bool abstract,
                                      int in_type) {
   struct sockaddr_un addr;
@@ -297,8 +301,7 @@
     return rval;
   }
   if (rval->Connect(reinterpret_cast<sockaddr*>(&addr), addrlen) == -1) {
-    return SharedFD(
-        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+    return SharedFD::ErrorFD(rval->GetErrno());
   }
   return rval;
 }
@@ -314,8 +317,7 @@
   }
   if (rval->Connect(reinterpret_cast<const sockaddr*>(&addr),
                     sizeof addr) < 0) {
-    return SharedFD(
-        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+    return SharedFD::ErrorFD(rval->GetErrno());
   }
   return rval;
 }
@@ -333,19 +335,16 @@
   int n = 1;
   if (rval->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) {
     LOG(ERROR) << "SetSockOpt failed " << rval->StrError();
-    return SharedFD(
-        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+    return SharedFD::ErrorFD(rval->GetErrno());
   }
   if(rval->Bind(reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
     LOG(ERROR) << "Bind failed " << rval->StrError();
-    return SharedFD(
-        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+    return SharedFD::ErrorFD(rval->GetErrno());
   }
   if (type == SOCK_STREAM) {
     if (rval->Listen(4) < 0) {
       LOG(ERROR) << "Listen failed " << rval->StrError();
-      return SharedFD(std::shared_ptr<FileInstance>(
-          new FileInstance(-1, rval->GetErrno())));
+      return SharedFD::ErrorFD(rval->GetErrno());
     }
   }
   return rval;
@@ -368,13 +367,11 @@
   int n = 1;
   if (rval->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) {
     LOG(ERROR) << "SetSockOpt failed " << rval->StrError();
-    return SharedFD(
-        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+    return SharedFD::ErrorFD(rval->GetErrno());
   }
   if (rval->Bind(reinterpret_cast<sockaddr*>(&addr), addrlen) == -1) {
     LOG(ERROR) << "Bind failed; name=" << name << ": " << rval->StrError();
-    return SharedFD(
-        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+    return SharedFD::ErrorFD(rval->GetErrno());
   }
 
   /* Only the bottom bits are really the socket type; there are flags too. */
@@ -385,8 +382,7 @@
     // Follows the default from socket_local_server
     if (rval->Listen(1) == -1) {
       LOG(ERROR) << "Listen failed: " << rval->StrError();
-      return SharedFD(std::shared_ptr<FileInstance>(
-          new FileInstance(-1, rval->GetErrno())));
+      return SharedFD::ErrorFD(rval->GetErrno());
     }
   }
 
@@ -399,4 +395,43 @@
   return rval;
 }
 
+SharedFD SharedFD::VsockServer(unsigned int port, int type) {
+  auto vsock = cvd::SharedFD::Socket(AF_VSOCK, type, 0);
+  if (!vsock->IsOpen()) {
+    return vsock;
+  }
+  sockaddr_vm addr{};
+  addr.svm_family = AF_VSOCK;
+  addr.svm_port = port;
+  addr.svm_cid = VMADDR_CID_ANY;
+  auto casted_addr = reinterpret_cast<sockaddr*>(&addr);
+  if (vsock->Bind(casted_addr, sizeof(addr)) == -1) {
+    LOG(ERROR) << "Bind failed (" << vsock->StrError() << ")";
+    return SharedFD::ErrorFD(vsock->GetErrno());
+  }
+  if (type == SOCK_STREAM) {
+    if (vsock->Listen(4) < 0) {
+      LOG(ERROR) << "Listen failed (" << vsock->StrError() << ")";
+      return SharedFD::ErrorFD(vsock->GetErrno());
+    }
+  }
+  return vsock;
+}
+
+SharedFD SharedFD::VsockClient(unsigned int cid, unsigned int port, int type) {
+  auto vsock = cvd::SharedFD::Socket(AF_VSOCK, type, 0);
+  if (!vsock->IsOpen()) {
+    return vsock;
+  }
+  sockaddr_vm addr{};
+  addr.svm_family = AF_VSOCK;
+  addr.svm_port = port;
+  addr.svm_cid = cid;
+  auto casted_addr = reinterpret_cast<sockaddr*>(&addr);
+  if (vsock->Connect(casted_addr, sizeof(addr)) == -1) {
+    return SharedFD::ErrorFD(vsock->GetErrno());
+  }
+  return vsock;
+}
+
 }  // namespace cvd
diff --git a/common/libs/fs/shared_fd.h b/common/libs/fs/shared_fd.h
index 33afee2..b811f4a 100644
--- a/common/libs/fs/shared_fd.h
+++ b/common/libs/fs/shared_fd.h
@@ -40,6 +40,7 @@
 #include <unistd.h>
 
 #include "common/libs/auto_resources/auto_resources.h"
+#include "vm_sockets.h"
 
 /**
  * Classes to to enable safe access to files.
@@ -155,6 +156,8 @@
   static SharedFD SocketLocalServer(int port, int type);
   static SharedFD SocketSeqPacketServer(const char* name, mode_t mode);
   static SharedFD SocketSeqPacketClient(const char* name);
+  static SharedFD VsockServer(unsigned int port, int type);
+  static SharedFD VsockClient(unsigned int cid, unsigned int port, int type);
   static SharedFD TimerFD(int clock, int flags);
 
   bool operator==(const SharedFD& rhs) const { return value_ == rhs.value_; }
@@ -176,6 +179,8 @@
   cvd::FileInstance& operator*() { return *value_; }
 
  private:
+  static SharedFD ErrorFD(int error);
+
   std::shared_ptr<FileInstance> value_;
 };
 
@@ -531,7 +536,7 @@
 /* Methods that need both a fully defined SharedFD and a fully defined
    FileInstance. */
 
-SharedFD::SharedFD() : value_(FileInstance::ClosedInstance()) {}
+inline SharedFD::SharedFD() : value_(FileInstance::ClosedInstance()) {}
 
 }  // namespace cvd
 
diff --git a/common/libs/fs/shared_select.h b/common/libs/fs/shared_select.h
index 20bfb5a..d103f1d 100644
--- a/common/libs/fs/shared_select.h
+++ b/common/libs/fs/shared_select.h
@@ -48,7 +48,7 @@
   }
 
   void Clr(const SharedFD& in) {
-    value_.insert(in);
+    value_.erase(in);
   }
 
   bool IsSet(const SharedFD& in) const {
diff --git a/common/libs/fs/vm_sockets.h b/common/libs/fs/vm_sockets.h
new file mode 100644
index 0000000..151f676
--- /dev/null
+++ b/common/libs/fs/vm_sockets.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_VM_SOCKETS_H
+#define _UAPI_VM_SOCKETS_H
+#include <linux/socket.h>
+#define SO_VM_SOCKETS_BUFFER_SIZE 0
+#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
+#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
+#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
+#define SO_VM_SOCKETS_TRUSTED 5
+#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
+#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
+#define VMADDR_CID_ANY - 1U
+#define VMADDR_PORT_ANY - 1U
+#define VMADDR_CID_HYPERVISOR 0
+#define VMADDR_CID_RESERVED 1
+#define VMADDR_CID_HOST 2
+#define VM_SOCKETS_INVALID_VERSION - 1U
+#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v) & 0xFF000000) >> 24)
+#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v) & 0x00FF0000) >> 16)
+#define VM_SOCKETS_VERSION_MINOR(_v) (((_v) & 0x0000FFFF))
+struct sockaddr_vm {
+  __kernel_sa_family_t svm_family;
+  unsigned short svm_reserved1;
+  unsigned int svm_port;
+  unsigned int svm_cid;
+  unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) - sizeof(unsigned int) - sizeof(unsigned int)];
+};
+#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+#endif
diff --git a/common/libs/tcp_socket/tcp_socket.cpp b/common/libs/tcp_socket/tcp_socket.cpp
index dc244b5..64da7d7 100644
--- a/common/libs/tcp_socket/tcp_socket.cpp
+++ b/common/libs/tcp_socket/tcp_socket.cpp
@@ -28,9 +28,9 @@
 using cvd::ServerSocket;
 
 ClientSocket::ClientSocket(int port)
-  : fd_(SharedFD::SocketLocalClient(port, SOCK_STREAM)) {}
+    : fd_(SharedFD::SocketLocalClient(port, SOCK_STREAM)) {}
 
-cvd::Message ClientSocket::RecvAny(size_t length) {
+cvd::Message ClientSocket::RecvAny(std::size_t length) {
   Message buf(length);
   auto read_count = fd_->Read(buf.data(), buf.size());
   if (read_count < 0) {
@@ -45,7 +45,7 @@
   return other_side_closed_;
 }
 
-cvd::Message ClientSocket::Recv(size_t length) {
+cvd::Message ClientSocket::Recv(std::size_t length) {
   Message buf(length);
   ssize_t total_read = 0;
   while (total_read < static_cast<ssize_t>(length)) {
@@ -66,12 +66,14 @@
   return buf;
 }
 
-ssize_t ClientSocket::Send(const uint8_t* data, std::size_t size) {
+ssize_t ClientSocket::SendNoSignal(const uint8_t* data, std::size_t size) {
   std::lock_guard<std::mutex> lock(send_lock_);
   ssize_t written{};
   while (written < static_cast<ssize_t>(size)) {
-    if (!fd_->IsOpen()) { LOG(ERROR) << "fd_ is closed"; }
-    auto just_written = fd_->Write(data + written, size - written);
+    if (!fd_->IsOpen()) {
+      LOG(ERROR) << "fd_ is closed";
+    }
+    auto just_written = fd_->Send(data + written, size - written, MSG_NOSIGNAL);
     if (just_written <= 0) {
       LOG(INFO) << "Couldn't write to client: " << strerror(errno);
       {
@@ -85,8 +87,8 @@
   return written;
 }
 
-ssize_t ClientSocket::Send(const Message& message) {
-  return Send(&message[0], message.size());
+ssize_t ClientSocket::SendNoSignal(const Message& message) {
+  return SendNoSignal(&message[0], message.size());
 }
 
 ServerSocket::ServerSocket(int port)
@@ -103,3 +105,29 @@
   }
   return ClientSocket{client};
 }
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::uint8_t b) {
+  msg->push_back(b);
+}
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::uint16_t s) {
+  const std::uint16_t n = htons(s);
+  auto p = reinterpret_cast<const std::uint8_t*>(&n);
+  msg->insert(msg->end(), p, p + sizeof n);
+}
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::uint32_t w) {
+  const std::uint32_t n = htonl(w);
+  auto p = reinterpret_cast<const std::uint8_t*>(&n);
+  msg->insert(msg->end(), p, p + sizeof n);
+}
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::int32_t w) {
+  std::uint32_t u{};
+  std::memcpy(&u, &w, sizeof u);
+  AppendInNetworkByteOrder(msg, u);
+}
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::string& str) {
+  msg->insert(msg->end(), str.begin(), str.end());
+}
diff --git a/common/libs/tcp_socket/tcp_socket.h b/common/libs/tcp_socket/tcp_socket.h
index 889d09b..e484f48 100644
--- a/common/libs/tcp_socket/tcp_socket.h
+++ b/common/libs/tcp_socket/tcp_socket.h
@@ -49,13 +49,14 @@
   Message Recv(std::size_t length);
   // RecvAny will receive whatever is available.
   // An empty message returned indicates error or close.
-  Message RecvAny(size_t length);
-  ssize_t Send(const std::uint8_t* data, std::size_t size);
-  ssize_t Send(const Message& message);
+  Message RecvAny(std::size_t length);
+  // Sends are called with MSG_NOSIGNAL to suppress SIGPIPE
+  ssize_t SendNoSignal(const std::uint8_t* data, std::size_t size);
+  ssize_t SendNoSignal(const Message& message);
 
   template <std::size_t N>
-  ssize_t Send(const std::uint8_t (&data)[N]) {
-    return Send(data, N);
+  ssize_t SendNoSignal(const std::uint8_t (&data)[N]) {
+    return SendNoSignal(data, N);
   }
 
   bool closed() const;
@@ -83,4 +84,25 @@
   cvd::SharedFD fd_;
 };
 
+void AppendInNetworkByteOrder(Message* msg, const std::uint8_t b);
+void AppendInNetworkByteOrder(Message* msg, const std::uint16_t s);
+void AppendInNetworkByteOrder(Message* msg, const std::uint32_t w);
+void AppendInNetworkByteOrder(Message* msg, const std::int32_t w);
+void AppendInNetworkByteOrder(Message* msg, const std::string& str);
+
+inline void AppendToMessage(Message*) {}
+
+template <typename T, typename... Ts>
+void AppendToMessage(Message* msg, T v, Ts... vals) {
+  AppendInNetworkByteOrder(msg, v);
+  AppendToMessage(msg, vals...);
+}
+
+template <typename... Ts>
+Message CreateMessage(Ts... vals) {
+  Message m;
+  AppendToMessage(&m, vals...);
+  return m;
+}
+
 }  // namespace cvd
diff --git a/common/libs/threads/Android.bp b/common/libs/threads/Android.bp
index dece9ae..7c89060 100644
--- a/common/libs/threads/Android.bp
+++ b/common/libs/threads/Android.bp
@@ -22,5 +22,6 @@
         "cuttlefish_time",
         "libbase",
     ],
+    cpp_std: "c++17",
     defaults: ["cuttlefish_host_only"],
 }
diff --git a/common/libs/threads/cuttlefish_thread_test.cpp b/common/libs/threads/cuttlefish_thread_test.cpp
index 513262e..e63d60c 100644
--- a/common/libs/threads/cuttlefish_thread_test.cpp
+++ b/common/libs/threads/cuttlefish_thread_test.cpp
@@ -52,10 +52,8 @@
 
   void Run() {
     {
-      ScopedThread thread_a(
-          MutexTestThunker<void*()>::call<&MutexTest::FastThread>, this);
-      ScopedThread thread_b(
-          MutexTestThunker<void*()>::call<&MutexTest::SlowThread>, this);
+      ScopedThread thread_a(cvd::thunk<void, &MutexTest::FastThread>, this);
+      ScopedThread thread_b(cvd::thunk<void, &MutexTest::SlowThread>, this);
     }
     LOG(INFO) << "MutexTest: completed at stage "
               << stage_
@@ -64,9 +62,6 @@
   }
 
 protected:
-  template <typename F> struct MutexTestThunker :
-  ThunkerBase<void, MutexTest, F>{};
-
   void* FastThread() {
     mutex_.Lock();
     CHECK(busy_ == NULL);
@@ -111,11 +106,11 @@
   void Run() {
     {
       ScopedThread thread_s(
-          Thunker<void*()>::call<&NotifyOneTest::SignalThread>, this);
+          cvd::thunk<void, &NotifyOneTest::SignalThread>, this);
       ScopedThread thread_w1(
-          Thunker<void*()>::call<&NotifyOneTest::WaitThread>, this);
+          cvd::thunk<void, &NotifyOneTest::WaitThread>, this);
       ScopedThread thread_w2(
-          Thunker<void*()>::call<&NotifyOneTest::WaitThread>, this);
+          cvd::thunk<void, &NotifyOneTest::WaitThread>, this);
     }
     LOG(INFO) << "NotifyOneTest: completed, signalled "
               << signalled_
@@ -124,9 +119,6 @@
   }
 
 protected:
-  template <typename F> struct Thunker :
-  ThunkerBase<void, NotifyOneTest, F>{};
-
   void* SignalThread() {
     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
     mutex_.Lock();
@@ -164,20 +156,17 @@
   void Run() {
     {
       ScopedThread thread_s(
-          Thunker<void*()>::call<&NotifyAllTest::SignalThread>, this);
+          cvd::thunk<void, &NotifyAllTest::SignalThread>, this);
       ScopedThread thread_w1(
-          Thunker<void*()>::call<&NotifyAllTest::WaitThread>, this);
+          cvd::thunk<void, &NotifyAllTest::WaitThread>, this);
       ScopedThread thread_w2(
-          Thunker<void*()>::call<&NotifyAllTest::WaitThread>, this);
+          cvd::thunk<void, &NotifyAllTest::WaitThread>, this);
     }
     printf("NotifyAllTest: completed, signalled %d (%s)\n",
            signalled_, (signalled_ == 2) ? "PASSED" : "FAILED");
   }
 
 protected:
-  template <typename F> struct Thunker :
-  ThunkerBase<void, NotifyAllTest, F>{};
-
   void* SignalThread() {
     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
     mutex_.Lock();
@@ -211,18 +200,15 @@
     start_ = MonotonicTimePoint::Now();
     {
       ScopedThread thread_s(
-          Thunker<void*()>::call<&WaitUntilTest::SignalThread>, this);
+          cvd::thunk<void, &WaitUntilTest::SignalThread>, this);
       ScopedThread thread_w2(
-          Thunker<void*()>::call<&WaitUntilTest::WaitThread>, this);
+          cvd::thunk<void, &WaitUntilTest::WaitThread>, this);
     }
     printf("WaitUntilTest: completed, stage %d (%s)\n",
            stage_, (stage_ == FINISHED) ? "PASSED" : "FAILED");
   }
 
 protected:
-  template <typename F> struct Thunker :
-  ThunkerBase<void, WaitUntilTest, F>{};
-
   void* SignalThread() {
     SleepUntil(start_ + Milliseconds(200));
     mutex_.Lock();
diff --git a/common/libs/threads/thunkers.h b/common/libs/threads/thunkers.h
index e496839..bb97174 100644
--- a/common/libs/threads/thunkers.h
+++ b/common/libs/threads/thunkers.h
@@ -16,148 +16,39 @@
 #ifndef CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_THUNKERS_H_
 #define CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_THUNKERS_H_
 
-template <typename HalType, typename Impl, typename F> struct ThunkerBase;
+namespace cvd {
+namespace internal {
 
-/* Handle varying number of arguments with a bunch of specializations.
- * The C++11 dream is:
- *
- * template <typename HalType, typename Impl, typename R, typename... Args>
- * struct ThunkerBase<HalType, Impl, R(Args...)> {
- *   template <R (Impl::*MemFn)(Args...)>
- *   static R call(HalType* in, Args... args) {
- *     return (reinterpret_cast<Impl*>(in)->*MemFn)(args...);
- *   }
- * };
- */
+template <typename HalType, typename F>
+struct ThunkerImpl;
 
-template <typename HalType, typename Impl, typename R>
-struct ThunkerBase<HalType, Impl, R()> {
-  template <R (Impl::*MemFn)()>
-  static R call(HalType* in) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)();
-  }
-
-  template <R (Impl::*MemFn)() const>
-  static R call(const HalType* in) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)();
+template <typename HalType, typename Impl, typename R, typename... Args>
+struct ThunkerImpl<HalType, R (Impl::*)(Args...)> {
+  template <R (Impl::*MemFn)(Args...)>
+  static R call(HalType* in, Args... args) {
+    return (reinterpret_cast<Impl*>(in)->*MemFn)(args...);
   }
 };
 
-template <typename HalType, typename Impl, typename R, typename T1>
-struct ThunkerBase<HalType, Impl, R(T1)> {
-  template <R (Impl::*MemFn)(T1)>
-  static R call(HalType* in, T1 t1) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)(t1);
-  }
-
-  template <R (Impl::*MemFn)(T1) const>
-  static R call(const HalType* in, T1 t1) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1);
+template <typename HalType, typename Impl, typename R, typename... Args>
+struct ThunkerImpl<HalType, R (Impl::*)(Args...) const> {
+  template <R (Impl::*MemFn)(Args...) const>
+  static R call(const HalType* in, Args... args) {
+    return (reinterpret_cast<const Impl*>(in)->*MemFn)(args...);
   }
 };
 
-template <typename HalType, typename Impl, typename R, typename T1, typename T2>
-struct ThunkerBase<HalType, Impl, R(T1, T2)> {
-  template <R (Impl::*MemFn)(T1, T2)>
-  static R call(HalType* in, T1 t1, T2 t2) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2);
-  }
-
-  template <R (Impl::*MemFn)(T1, T2) const>
-  static R call(const HalType* in, T1 t1, T2 t2) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2);
-  }
+template <typename HalType, auto MemFunc>
+struct Thunker {
+  static constexpr auto call =
+      ThunkerImpl<HalType, decltype(MemFunc)>::template call<MemFunc>;
 };
 
-template <typename HalType, typename Impl, typename R, typename T1,
-          typename T2, typename T3>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3)> {
-  template <R (Impl::*MemFn)(T1, T2, T3)>
-  static R call(HalType* in, T1 t1, T2 t2, T3 t3) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3);
-  }
+}  // namespace internal
 
-  template <R (Impl::*MemFn)(T1, T2, T3) const>
-  static R call(const HalType* in, T1 t1, T2 t2, T3 t3) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2, t3);
-  }
-};
+template <typename HalType, auto MemFunc>
+constexpr auto thunk = internal::Thunker<HalType, MemFunc>::call;
 
-template <typename HalType, typename Impl, typename R, typename T1,
-          typename T2, typename T3, typename T4>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4)> {
-  template <R (Impl::*MemFn)(T1, T2, T3, T4)>
-  static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3, t4);
-  }
+}  // namespace cvd
 
-  template <R (Impl::*MemFn)(T1, T2, T3, T4) const>
-  static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2, t3, t4);
-  }
-};
-
-template <typename HalType, typename Impl, typename R, typename T1,
-          typename T2, typename T3, typename T4, typename T5>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4, T5)> {
-  template <R (Impl::*MemFn)(T1, T2, T3, T4, T5)>
-  static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5);
-  }
-
-  template <R (Impl::*MemFn)(T1, T2, T3, T4, T5) const>
-  static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5);
-  }
-};
-
-template <typename HalType, typename Impl, typename R, typename T1,
-          typename T2, typename T3, typename T4, typename T5, typename T6>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4, T5, T6)> {
-  template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6)>
-  static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5, t6);
-  }
-
-  template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6) const>
-  static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5, t6);
-  }
-};
-
-template <typename HalType, typename Impl, typename R, typename T1,
-          typename T2, typename T3, typename T4, typename T5, typename T6,
-          typename T7>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4, T5, T6, T7)> {
-  template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6, T7)>
-  static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5, t6, t7);
-  }
-
-  template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6, T7) const>
-  static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6,
-                T7 t7) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)(
-        t1, t2, t3, t4, t5, t6, t7);
-  }
-};
-
-template <typename HalType, typename Impl, typename R, typename T1,
-          typename T2, typename T3, typename T4, typename T5, typename T6,
-          typename T7, typename T8>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4, T5, T6, T7, T8)> {
-  template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6, T7, T8)>
-  static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7,
-                T8 t8) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)(
-        t1, t2, t3, t4, t5, t6, t7, t8);
-  }
-
-  template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6, T7, T8) const>
-  static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6,
-                T7 t7, T8 t8) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)(
-        t1, t2, t3, t4, t5, t6, t7, t8);
-  }
-};
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_THUNKERS_H_
+#endif
diff --git a/common/libs/utils/Android.bp b/common/libs/utils/Android.bp
index 4785fad..1551515 100644
--- a/common/libs/utils/Android.bp
+++ b/common/libs/utils/Android.bp
@@ -21,6 +21,7 @@
         "size_utils.cpp",
         "files.cpp",
         "users.cpp",
+        "network.cpp",
     ],
     header_libs: [
         "cuttlefish_glog",
@@ -28,6 +29,15 @@
     shared_libs: [
         "libbase",
         "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
     ],
     defaults: ["cuttlefish_host_and_guest"],
 }
+
+cc_test {
+    name: "cuttlefish_simulated_buffer_test",
+    srcs: ["simulated_buffer_test.cpp"],
+    shared_libs: ["libcuttlefish_utils"],
+    gtest: true,
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/common/libs/utils/files.cpp b/common/libs/utils/files.cpp
index 654d971..432e8da 100644
--- a/common/libs/utils/files.cpp
+++ b/common/libs/utils/files.cpp
@@ -20,6 +20,7 @@
 
 #include <array>
 #include <climits>
+#include <cstdio>
 #include <cstdlib>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -27,6 +28,11 @@
 
 namespace cvd {
 
+bool FileExists(const std::string& path) {
+  struct stat st;
+  return stat(path.c_str(), &st) == 0;
+}
+
 bool FileHasContent(const std::string& path) {
   return FileSize(path) > 0;
 }
@@ -67,4 +73,9 @@
   return st.st_size;
 }
 
+bool RemoveFile(const std::string& file) {
+  LOG(INFO) << "Removing " << file;
+  return remove(file.c_str()) == 0;
+}
+
 }  // namespace cvd
diff --git a/common/libs/utils/files.h b/common/libs/utils/files.h
index ce689ac..d05501d 100644
--- a/common/libs/utils/files.h
+++ b/common/libs/utils/files.h
@@ -20,9 +20,11 @@
 #include <string>
 
 namespace cvd {
+bool FileExists(const std::string& path);
 bool FileHasContent(const std::string& path);
 bool DirectoryExists(const std::string& path);
 off_t FileSize(const std::string& path);
+bool RemoveFile(const std::string& file);
 
 // The returned value may contain .. or . if these are present in the path
 // argument.
diff --git a/common/libs/utils/network.cpp b/common/libs/utils/network.cpp
new file mode 100644
index 0000000..f4943ed
--- /dev/null
+++ b/common/libs/utils/network.cpp
@@ -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.
+ */
+
+#include "common/libs/utils/network.h"
+
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <string.h>
+
+#include "common/libs/glog/logging.h"
+
+namespace cvd {
+namespace {
+// This should be the size of virtio_net_hdr_v1, from linux/virtio_net.h, but
+// the version of that header that ships with android in Pie does not include
+// that struct (it was added in Q).
+// This is what that struct looks like:
+// struct virtio_net_hdr_v1 {
+// u8 flags;
+// u8 gso_type;
+// u16 hdr_len;
+// u16 gso_size;
+// u16 csum_start;
+// u16 csum_offset;
+// u16 num_buffers;
+// };
+static constexpr int SIZE_OF_VIRTIO_NET_HDR_V1 = 12;
+}  // namespace
+
+SharedFD OpenTapInterface(const std::string& interface_name) {
+  constexpr auto TUNTAP_DEV = "/dev/net/tun";
+
+  auto tap_fd = SharedFD::Open(TUNTAP_DEV, O_RDWR | O_NONBLOCK);
+  if (!tap_fd->IsOpen()) {
+    LOG(ERROR) << "Unable to open tun device: " << tap_fd->StrError();
+    return tap_fd;
+  }
+
+  struct ifreq ifr;
+  memset(&ifr, 0, sizeof(ifr));
+  ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
+  strncpy(ifr.ifr_name, interface_name.c_str(), IFNAMSIZ);
+
+  int err = tap_fd->Ioctl(TUNSETIFF, &ifr);
+  if (err < 0) {
+    LOG(ERROR) << "Unable to connect to " << interface_name
+               << " tap interface: " << tap_fd->StrError();
+    tap_fd->Close();
+    return cvd::SharedFD();
+  }
+
+  // The interface's configuration may have been modified or just not set
+  // correctly on creation. While qemu checks this and enforces the right
+  // configuration, crosvm does not, so it needs to be set before it's passed to
+  // it.
+  tap_fd->Ioctl(TUNSETOFFLOAD,
+                reinterpret_cast<void*>(TUN_F_CSUM | TUN_F_UFO | TUN_F_TSO4 |
+                                        TUN_F_TSO6));
+  int len = SIZE_OF_VIRTIO_NET_HDR_V1;
+  tap_fd->Ioctl(TUNSETVNETHDRSZ, &len);
+
+  return tap_fd;
+}
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/common/libs/utils/network.h
similarity index 62%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to common/libs/utils/network.h
index b6a037b..7b856e4 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/common/libs/utils/network.h
@@ -1,6 +1,5 @@
-#pragma once
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,11 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
-#include "hwcomposer_common.h"
+#include <string>
+
+#include "common/libs/fs/shared_fd.h"
 
 namespace cvd {
-
-bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
-
-}  // namespace cvd
+// Creates, or connects to if it already exists, a tap network interface. The
+// user needs CAP_NET_ADMIN to create such interfaces or be the owner to connect
+// to one.
+SharedFD OpenTapInterface(const std::string& interface_name);
+}
\ No newline at end of file
diff --git a/guest/hals/audio/simulated_buffer.h b/common/libs/utils/simulated_buffer.h
similarity index 100%
rename from guest/hals/audio/simulated_buffer.h
rename to common/libs/utils/simulated_buffer.h
diff --git a/common/libs/utils/simulated_buffer_test.cpp b/common/libs/utils/simulated_buffer_test.cpp
new file mode 100644
index 0000000..c3fbe49
--- /dev/null
+++ b/common/libs/utils/simulated_buffer_test.cpp
@@ -0,0 +1,442 @@
+/*
+ * 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 "common/libs/utils/simulated_buffer.h"
+#include <gtest/gtest.h>
+
+using cvd::time::MonotonicTimePoint;
+using cvd::time::MonotonicTimePointFactory;
+using cvd::time::Seconds;
+using cvd::time::Milliseconds;
+using cvd::time::Nanoseconds;
+using cvd::time::kNanosecondsPerSecond;
+
+class MockTimepointFactory : public MonotonicTimePointFactory {
+ public:
+  virtual void FetchCurrentTime(MonotonicTimePoint* dest) const override {
+    *dest = system_time_;
+  }
+
+  void SetTime(const MonotonicTimePoint& in) {
+    system_time_ = in;
+  }
+
+ protected:
+  MonotonicTimePoint system_time_;
+};
+
+template <typename T> class MockSimulatedBuffer : public T {
+ public:
+  MockSimulatedBuffer(
+      int64_t sample_rate,
+      int64_t capacity,
+      MockTimepointFactory* factory) :
+      T(sample_rate, capacity, factory),
+      factory_(factory) { }
+
+  void FetchCurrentTime(MonotonicTimePoint* dest) const {
+    return factory_->FetchCurrentTime(dest);
+  }
+
+  void SleepUntilTime(const MonotonicTimePoint& tick) {
+    factory_->SetTime(tick);
+  }
+
+ protected:
+  // Save a redundant pointer to avoid downcasting
+  MockTimepointFactory* factory_;
+};
+
+static const int64_t kItemRate = 48000;
+static const int64_t kBufferCapacity = 4800;
+
+class SimulatedBufferTest : public ::testing::Test {
+ public:
+  MockTimepointFactory clock;
+  MockSimulatedBuffer<SimulatedBufferBase> buffer;
+
+  SimulatedBufferTest() : buffer(kItemRate, kBufferCapacity, &clock) { }
+};
+
+TEST_F(SimulatedBufferTest, TimeMocking) {
+  // Ensure that the mocked clock starts at the epoch.
+  MonotonicTimePoint epoch_time;
+  MonotonicTimePoint actual_time;
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(epoch_time, actual_time);
+
+  // Ensure that sleeping works
+  MonotonicTimePoint test_time = actual_time + Seconds(10);
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+
+  // Try one more sleep to make sure that time moves forward
+  test_time += Seconds(5);
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+}
+
+TEST_F(SimulatedBufferTest, ItemScaling) {
+  // Make certain that we start at item 0
+  EXPECT_EQ(0, buffer.GetCurrentItemNum());
+
+  // Make certain that the expected number of items appear in 1 second
+  MonotonicTimePoint actual_time;
+  buffer.FetchCurrentTime(&actual_time);
+  MonotonicTimePoint test_time = actual_time + Seconds(1);
+  buffer.SleepUntilTime(test_time);
+  EXPECT_EQ(kItemRate, buffer.GetCurrentItemNum());
+
+  // Sleep an additional 10 seconds to make certain that the item numbers
+  // increment
+  test_time += Seconds(10);
+  buffer.SleepUntilTime(test_time);
+  EXPECT_EQ(11 * kItemRate, buffer.GetCurrentItemNum());
+
+  // Make certain that partial seconds work
+  test_time += Milliseconds(1500);
+  buffer.SleepUntilTime(test_time);
+  EXPECT_EQ(12.5 * kItemRate, buffer.GetCurrentItemNum());
+
+  // Make certain that we don't get new items when paused
+  buffer.SetPaused(true);
+  test_time += Seconds(10);
+  buffer.SleepUntilTime(test_time);
+  EXPECT_EQ(12.5 * kItemRate, buffer.GetCurrentItemNum());
+
+  // Make certain that we start getting items when pausing stops
+  buffer.SetPaused(false);
+  test_time += Milliseconds(500);
+  buffer.SleepUntilTime(test_time);
+  EXPECT_EQ(13 * kItemRate, buffer.GetCurrentItemNum());
+}
+
+TEST_F(SimulatedBufferTest, ItemSleeping) {
+  // See if sleeping on an time causes the right amount of time to pass
+  EXPECT_EQ(0, buffer.GetCurrentItemNum());
+  MonotonicTimePoint base_time;
+  buffer.FetchCurrentTime(&base_time);
+
+  // Wait for 1500ms worth of samples
+  buffer.SleepUntilItem(kItemRate * 1500 / 1000);
+  EXPECT_EQ(kItemRate * 1500 / 1000, buffer.GetCurrentItemNum());
+  MonotonicTimePoint actual_time;
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(1500, Milliseconds(actual_time - base_time).count());
+
+  // Now wait again for more samples
+  buffer.SleepUntilItem(kItemRate * 2500 / 1000);
+  EXPECT_EQ(kItemRate * 2500 / 1000, buffer.GetCurrentItemNum());
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(2500, Milliseconds(actual_time - base_time).count());
+}
+
+class OutputBufferTest : public ::testing::Test {
+ public:
+  MockTimepointFactory clock;
+  MockSimulatedBuffer<SimulatedOutputBuffer> buffer;
+
+  OutputBufferTest() : buffer(kItemRate, kBufferCapacity, &clock) { }
+};
+
+TEST_F(OutputBufferTest, NonBlockingQueueing) {
+  int64_t half_buffer = kBufferCapacity / 2;
+  EXPECT_EQ(0, buffer.GetCurrentItemNum());
+
+  // Filling half of the buffer should not block
+  MonotonicTimePoint test_time;
+  buffer.FetchCurrentTime(&test_time);
+  EXPECT_EQ(half_buffer, buffer.AddToOutputBuffer(half_buffer, false));
+  MonotonicTimePoint actual_time;
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(half_buffer, buffer.GetOutputBufferSize());
+
+  // Filling all but one entry of the buffer should not block
+  EXPECT_EQ(half_buffer - 1,
+            buffer.AddToOutputBuffer(half_buffer - 1, false));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity - 1, buffer.GetOutputBufferSize());
+
+  // Filling the entire buffer should not block
+  EXPECT_EQ(1, buffer.AddToOutputBuffer(half_buffer, false));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(actual_time, test_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // The buffer should reject additional data but not block
+  EXPECT_EQ(0, buffer.AddToOutputBuffer(half_buffer, false));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // One quarter of the buffer should drain in the expected time
+  Nanoseconds quarter_drain_time(
+      kBufferCapacity / 4 * kNanosecondsPerSecond / kItemRate);
+  test_time += quarter_drain_time;
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(actual_time, test_time);
+  EXPECT_EQ(kBufferCapacity * 3 / 4, buffer.GetOutputBufferSize());
+
+  // The buffer should now accept new data without blocking
+  EXPECT_EQ(kBufferCapacity / 4,
+            buffer.AddToOutputBuffer(half_buffer, false));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // Now that the buffer is full it should reject additional data but
+  // not block
+  EXPECT_EQ(0, buffer.AddToOutputBuffer(half_buffer, false));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // Wait for 3/4 of the buffer to drain
+  test_time += Nanoseconds(3 * quarter_drain_time.count());
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity / 4, buffer.GetOutputBufferSize());
+
+  // The entire buffer should drain on schedule
+  test_time += Nanoseconds(quarter_drain_time.count() - 1);
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(1, buffer.GetOutputBufferSize());
+  test_time += Nanoseconds(1);
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(0, buffer.GetOutputBufferSize());
+
+  // It should be possible to fill the buffer in a single shot
+  EXPECT_EQ(kBufferCapacity,
+            buffer.AddToOutputBuffer(kBufferCapacity, false));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // The buffer shouldn't accept additional data but shouldn't block
+  EXPECT_EQ(0, buffer.AddToOutputBuffer(1, false));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // The buffer should underflow sanely
+  test_time += Nanoseconds(6 * quarter_drain_time.count());
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(0, buffer.GetOutputBufferSize());
+
+  // The underflow shouldn't increase the buffer's capacity
+  EXPECT_EQ(kBufferCapacity,
+            buffer.AddToOutputBuffer(kBufferCapacity + 1, false));
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+}
+
+TEST_F(OutputBufferTest, BlockingQueueing) {
+  int64_t half_buffer = kBufferCapacity / 2;
+
+  // Check the initial setup
+  EXPECT_EQ(0, buffer.GetCurrentItemNum());
+  MonotonicTimePoint test_time;
+  buffer.FetchCurrentTime(&test_time);
+
+  // Filling half the buffer works without blocking
+  EXPECT_EQ(half_buffer, buffer.AddToOutputBuffer(half_buffer, true));
+  MonotonicTimePoint actual_time;
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(half_buffer, buffer.GetOutputBufferSize());
+
+  // Filling all but one entry of the buffer also works without blocking
+  EXPECT_EQ(half_buffer - 1,
+            buffer.AddToOutputBuffer(half_buffer - 1, true));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity - 1, buffer.GetOutputBufferSize());
+
+  // Putting the last sample into the buffer doesn't block
+  EXPECT_EQ(1, buffer.AddToOutputBuffer(1, true));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // Putting more data into the buffer causes blocking
+  EXPECT_EQ(half_buffer, buffer.AddToOutputBuffer(half_buffer, true));
+  Nanoseconds half_drain_time(
+      ((kBufferCapacity / 2) * kNanosecondsPerSecond + kItemRate - 1) /
+      kItemRate);
+  Nanoseconds quarter_drain_time(half_drain_time.count() / 2);
+  test_time += half_drain_time;
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // The buffer drains as expected
+  test_time += quarter_drain_time;
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity * 3 / 4, buffer.GetOutputBufferSize());
+
+  // Overfilling the drained buffer also causes blocking
+  EXPECT_EQ(half_buffer, buffer.AddToOutputBuffer(half_buffer, true));
+  test_time += quarter_drain_time;
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // The buffer drains on schedule
+  test_time += Nanoseconds(half_drain_time.count() * 2 - 1);
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(1, buffer.GetOutputBufferSize());
+  test_time += Nanoseconds(1);
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(0, buffer.GetOutputBufferSize());
+
+  // It's possible to fill the entire output buffer in 1 shot without blocking
+  EXPECT_EQ(kBufferCapacity,
+            buffer.AddToOutputBuffer(kBufferCapacity, true));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+  // Adding a single extra sample causes some blocking
+  EXPECT_EQ(1, buffer.AddToOutputBuffer(1, true));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_LT(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+}
+
+class InputBufferTest : public ::testing::Test {
+ public:
+  MockTimepointFactory clock;
+  MockSimulatedBuffer<SimulatedInputBuffer> buffer;
+
+  InputBufferTest() : buffer(kItemRate, kBufferCapacity, &clock) { }
+};
+
+TEST_F(InputBufferTest, NonBlockingInput) {
+  Nanoseconds quarter_fill_time(kBufferCapacity / 4 * kNanosecondsPerSecond /
+                                kItemRate);
+  // Verify that the buffer starts empty
+  EXPECT_EQ(0, buffer.GetCurrentItemNum());
+  MonotonicTimePoint actual_time;
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(0, buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+  EXPECT_EQ(0, buffer.GetLostInputItems());
+
+  // Wait for 1/4 of the buffer to fill
+  MonotonicTimePoint test_time = actual_time + quarter_fill_time;
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(0, buffer.GetLostInputItems());
+
+  // Verify that we can read the samples in two groups
+  EXPECT_EQ(kBufferCapacity / 8,
+            buffer.RemoveFromInputBuffer(kBufferCapacity / 8, false));
+  EXPECT_EQ(kBufferCapacity / 8,
+            buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+
+  // Verify that there are no samples left and that we did not block
+  EXPECT_EQ(0, buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+
+  // Verify that the buffer fills on schedule
+  test_time += Nanoseconds(4 * quarter_fill_time.count() - 1);
+  buffer.SleepUntilTime(test_time);
+  EXPECT_EQ(kBufferCapacity - 1,
+            buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+  test_time += Nanoseconds(1);
+  buffer.SleepUntilTime(test_time);
+  EXPECT_EQ(1, buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(0, buffer.GetLostInputItems());
+
+  // Verify that the buffer overflows as expected
+  test_time += Nanoseconds(5 * quarter_fill_time.count());
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity / 4, buffer.GetLostInputItems());
+  EXPECT_EQ(0, buffer.GetLostInputItems());
+
+  EXPECT_EQ(kBufferCapacity,
+            buffer.RemoveFromInputBuffer(2 * kBufferCapacity, false));
+  EXPECT_EQ(0, buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+}
+
+TEST_F(InputBufferTest, BlockingInput) {
+  Nanoseconds quarter_fill_time(kBufferCapacity / 4 * kNanosecondsPerSecond /
+                                kItemRate);
+  // Verify that the buffer starts empty
+  EXPECT_EQ(0, buffer.GetCurrentItemNum());
+  MonotonicTimePoint actual_time;
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(0, buffer.GetLostInputItems());
+
+  // Wait for 1/4 of the buffer to fill
+  MonotonicTimePoint test_time = actual_time + quarter_fill_time;
+  EXPECT_EQ(kBufferCapacity / 4,
+            buffer.RemoveFromInputBuffer(kBufferCapacity / 4, true));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(0, buffer.GetLostInputItems());
+
+  // Verify that the buffer fills on schedule
+  test_time += Nanoseconds(4 * quarter_fill_time.count());
+  EXPECT_EQ(kBufferCapacity,
+            buffer.RemoveFromInputBuffer(kBufferCapacity, true));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(0, buffer.GetLostInputItems());
+
+  // Verify that the buffer overflows as expected
+  test_time += Nanoseconds(5 * quarter_fill_time.count());
+  buffer.SleepUntilTime(test_time);
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+  EXPECT_EQ(kBufferCapacity / 4, buffer.GetLostInputItems());
+  EXPECT_EQ(0, buffer.GetLostInputItems());
+  EXPECT_EQ(kBufferCapacity,
+            buffer.RemoveFromInputBuffer(kBufferCapacity, true));
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+
+  // Verify that reads bigger than the buffer work as expected
+  test_time += Nanoseconds(8 * quarter_fill_time.count());
+  EXPECT_EQ(kBufferCapacity * 2,
+            buffer.RemoveFromInputBuffer(kBufferCapacity * 2, true));
+  EXPECT_EQ(0, buffer.GetLostInputItems());
+  buffer.FetchCurrentTime(&actual_time);
+  EXPECT_EQ(test_time, actual_time);
+}
diff --git a/common/libs/utils/subprocess.cpp b/common/libs/utils/subprocess.cpp
index 7f7b961..773a56c 100644
--- a/common/libs/utils/subprocess.cpp
+++ b/common/libs/utils/subprocess.cpp
@@ -16,17 +16,77 @@
 
 #include "common/libs/utils/subprocess.h"
 
+#include <errno.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <map>
+#include <set>
+
 #include <glog/logging.h>
 namespace {
 
-pid_t subprocess_impl(const char* const* command, const char* const* envp) {
+// If a redirected-to file descriptor was already closed, it's possible that
+// some inherited file descriptor duped to this file descriptor and the redirect
+// would override that. This function makes sure that doesn't happen.
+bool validate_redirects(
+    const std::map<cvd::Subprocess::StdIOChannel, int>& redirects,
+    const std::map<cvd::SharedFD, int>& inherited_fds) {
+  // Add the redirected IO channels to a set as integers. This allows converting
+  // the enum values into integers instead of the other way around.
+  std::set<int> int_redirects;
+  for (const auto& entry: redirects) {
+    int_redirects.insert(static_cast<int>(entry.first));
+  }
+  for (const auto& entry: inherited_fds) {
+    auto dupped_fd = entry.second;
+    if (int_redirects.count(dupped_fd)) {
+      LOG(ERROR) << "Requested redirect of fd(" << dupped_fd
+                 << ") conflicts with inherited FD.";
+      return false;
+    }
+  }
+  return true;
+}
+
+void do_redirects(const std::map<cvd::Subprocess::StdIOChannel, int>& redirects) {
+  for (const auto& entry: redirects) {
+    auto std_channel = static_cast<int>(entry.first);
+    auto fd = entry.second;
+    TEMP_FAILURE_RETRY(dup2(fd, std_channel));
+  }
+}
+
+cvd::Subprocess subprocess_impl(
+    const char* const* command,
+    const char* const* envp,
+    const std::map<cvd::Subprocess::StdIOChannel, int>& redirects,
+    const std::map<cvd::SharedFD, int>& inherited_fds,
+    bool with_control_socket) {
+  // The parent socket will get closed on the child on the call to exec, the
+  // child socket will be closed on the parent when this function returns and no
+  // references to the fd are left
+  cvd::SharedFD parent_socket, child_socket;
+  if (with_control_socket) {
+    if (!cvd::SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &parent_socket,
+                                   &child_socket)) {
+      LOG(ERROR) << "Unable to create control socket pair: " << strerror(errno);
+      return cvd::Subprocess(-1, {});
+    }
+    // Remove FD_CLOEXEC from the child socket, ensure the parent has it
+    child_socket->Fcntl(F_SETFD, 0);
+    parent_socket->Fcntl(F_SETFD, FD_CLOEXEC);
+  }
+
+  if (!validate_redirects(redirects, inherited_fds)) {
+    return cvd::Subprocess(-1, {});
+  }
+
   pid_t pid = fork();
   if (!pid) {
+    do_redirects(redirects);
     int rval;
     // If envp is NULL, the current process's environment is used as the
     // environment of the child process. To force an empty emvironment for
@@ -51,7 +111,7 @@
   while (command[i]) {
     LOG(INFO) << command[i++];
   }
-  return pid;
+  return cvd::Subprocess(pid, parent_socket);
 }
 
 std::vector<const char*> ToCharPointers(
@@ -66,6 +126,27 @@
 }  // namespace
 namespace cvd {
 
+Subprocess::Subprocess(Subprocess&& subprocess)
+    : pid_(subprocess.pid_),
+      started_(subprocess.started_),
+      control_socket_(subprocess.control_socket_) {
+  // Make sure the moved object no longer controls this subprocess
+  subprocess.pid_ = -1;
+  subprocess.started_ = false;
+  subprocess.control_socket_ = SharedFD();
+}
+
+Subprocess& Subprocess::operator=(Subprocess&& other) {
+  pid_ = other.pid_;
+  started_ = other.started_;
+  control_socket_ = other.control_socket_;
+
+  other.pid_ = -1;
+  other.started_ = false;
+  other.control_socket_ = SharedFD();
+  return *this;
+}
+
 int Subprocess::Wait() {
   if (pid_ < 0) {
     LOG(ERROR)
@@ -108,9 +189,14 @@
 }
 
 Command::~Command() {
+  // Close all inherited file descriptors
   for(const auto& entry: inherited_fds_) {
     close(entry.second);
   }
+  // Close all redirected file descriptors
+  for (const auto& entry: redirects_) {
+    close(entry.second);
+  }
 }
 
 bool Command::BuildParameter(std::stringstream* stream, SharedFD shared_fd) {
@@ -128,13 +214,33 @@
   return true;
 }
 
-Subprocess Command::Start() const {
+bool Command::RedirectStdIO(cvd::Subprocess::StdIOChannel channel,
+                            cvd::SharedFD shared_fd){
+  if (!shared_fd->IsOpen()) {
+    return false;
+  }
+  if (redirects_.count(channel)) {
+    LOG(ERROR) << "Attempted multiple redirections of fd: "
+               << static_cast<int>(channel);
+    return false;
+  }
+  auto dup_fd = shared_fd->UNMANAGED_Dup();
+  if (dup_fd < 0) {
+    return false;
+  }
+  redirects_[channel] = dup_fd;
+  return true;
+}
+
+Subprocess Command::Start(bool with_control_socket) const {
   auto cmd = ToCharPointers(command_);
   if (use_parent_env_) {
-    return Subprocess(subprocess_impl(cmd.data(), NULL));
+    return subprocess_impl(cmd.data(), nullptr, redirects_, inherited_fds_,
+                           with_control_socket);
   } else {
     auto envp = ToCharPointers(env_);
-    return Subprocess(subprocess_impl(cmd.data(), envp.data()));
+    return subprocess_impl(cmd.data(), envp.data(), redirects_, inherited_fds_,
+                           with_control_socket);
   }
 }
 
diff --git a/common/libs/utils/subprocess.h b/common/libs/utils/subprocess.h
index 814325b..a7485c2 100644
--- a/common/libs/utils/subprocess.h
+++ b/common/libs/utils/subprocess.h
@@ -29,9 +29,19 @@
 // It's an error to wait twice for the same subprocess.
 class Subprocess {
  public:
-  Subprocess(pid_t pid) : pid_(pid), started_(pid > 0) {}
-  Subprocess(Subprocess&&) = default;
+  enum class StdIOChannel {
+    kStdIn = 0,
+    kStdOut = 1,
+    kStdErr = 2,
+  };
+
+  Subprocess(pid_t pid, SharedFD control)
+      : pid_(pid), started_(pid > 0), control_socket_(control) {}
+  // The default implementation won't do because we need to reset the pid of the
+  // moved object.
+  Subprocess(Subprocess&&);
   ~Subprocess() = default;
+  Subprocess& operator=(Subprocess&&);
   // Waits for the subprocess to complete. Returns zero if completed
   // successfully, non-zero otherwise.
   int Wait();
@@ -41,6 +51,9 @@
   // fork() succeeded or not, it says nothing about exec or successful
   // completion of the command, that's what Wait is for.
   bool Started() const {return started_;}
+  SharedFD control_socket() {
+    return control_socket_;
+  }
 
  private:
   // Copy is disabled to avoid waiting twice for the same pid (the first wait
@@ -49,7 +62,8 @@
   Subprocess(const Subprocess&) = delete;
   Subprocess& operator=(const Subprocess&) = delete;
   pid_t pid_ = -1;
-  bool started_= false;
+  bool started_ = false;
+  SharedFD control_socket_;
 };
 
 // An executable command. Multiple subprocesses can be started from the same
@@ -102,13 +116,25 @@
     }
     return false;
   }
-  // Starts execution of the command. This method can be called multiple times,
-  // effectively staring multiple (possibly concurrent) instances.
-  Subprocess Start() const;
 
+  // Redirects the standard IO of the command.
+  bool RedirectStdIO(Subprocess::StdIOChannel channel, cvd::SharedFD shared_fd);
+
+  // Starts execution of the command. This method can be called multiple times,
+  // effectively staring multiple (possibly concurrent) instances. If
+  // with_control_socket is true the returned Subprocess instance will have a
+  // sharedFD that enables communication with the child process.
+  Subprocess Start(bool with_control_socket = false) const;
+
+  std::string GetShortName() const {
+    // This is safe because the constructor guarantees the name of the binary to
+    // be at index 0 on the vector
+    return command_[0];
+  }
  private:
   std::vector<std::string> command_;
   std::map<cvd::SharedFD, int> inherited_fds_{};
+  std::map<Subprocess::StdIOChannel, int> redirects_{};
   bool use_parent_env_ = true;
   std::vector<std::string> env_{};
 };
diff --git a/common/vsoc/lib/e2e_test_region_layout.cpp b/common/vsoc/lib/e2e_test_region_layout.cpp
index 67a5435..9a787bd 100644
--- a/common/vsoc/lib/e2e_test_region_layout.cpp
+++ b/common/vsoc/lib/e2e_test_region_layout.cpp
@@ -38,10 +38,6 @@
 
 const char* E2EUnfindableRegionLayout::region_name = "e2e_must_not_exist";
 
-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/wifi_exchange_layout.cpp b/common/vsoc/lib/managed_e2e_test_region_layout.cpp
similarity index 73%
rename from common/vsoc/lib/wifi_exchange_layout.cpp
rename to common/vsoc/lib/managed_e2e_test_region_layout.cpp
index 12e9577..1cc794b 100644
--- a/common/vsoc/lib/wifi_exchange_layout.cpp
+++ b/common/vsoc/lib/managed_e2e_test_region_layout.cpp
@@ -13,12 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "common/vsoc/shm/wifi_exchange_layout.h"
+
+#include "common/vsoc/shm/managed_e2e_test_region_layout.h"
 
 namespace vsoc {
 namespace layout {
-namespace wifi {
-const char* WifiExchangeLayout::region_name = "wifi_exchange";
-}  // namespace wifi
+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/ril_layout.cpp b/common/vsoc/lib/ril_layout.cpp
deleted file mode 100644
index 89f45f1..0000000
--- a/common/vsoc/lib/ril_layout.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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/ril_layout.h"
-
-namespace vsoc {
-namespace layout {
-namespace ril {
-
-const char* RilLayout::region_name = "ril";
-}
-}  // namespace layout
-}  // namespace vsoc
diff --git a/common/vsoc/lib/ril_region_view.cpp b/common/vsoc/lib/ril_region_view.cpp
deleted file mode 100644
index ad19ac3..0000000
--- a/common/vsoc/lib/ril_region_view.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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 <arpa/inet.h>
-
-#include <mutex>
-
-#include "common/vsoc/lib/ril_region_view.h"
-
-namespace vsoc {
-namespace ril {
-
-const char* RilRegionView::address_and_prefix_length() const {
-  static char buffer[sizeof(data().ipaddr) + 3]{};  // <ipaddr>/dd
-  if (buffer[0] == '\0') {
-    snprintf(buffer, sizeof(buffer), "%s/%d", data().ipaddr, data().prefixlen);
-  }
-  return &buffer[0];
-}
-
-}  // namespace ril
-}  // namespace vsoc
diff --git a/common/vsoc/lib/ril_region_view.h b/common/vsoc/lib/ril_region_view.h
deleted file mode 100644
index 0f7fe84..0000000
--- a/common/vsoc/lib/ril_region_view.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#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/ril_layout.h"
-
-namespace vsoc {
-namespace ril {
-class RilRegionView
-    : public vsoc::TypedRegionView<
-        RilRegionView,
-        vsoc::layout::ril::RilLayout> {
- public:
-  // returns a string with '<ip>/<prefix_len>' like this: 192.168.99.2/30
-  const char* address_and_prefix_length() const;
-};
-}  // namespace ril
-}  // namespace vsoc
diff --git a/common/vsoc/lib/vsoc_memory.cpp b/common/vsoc/lib/vsoc_memory.cpp
index 807b2d9..017ab13 100644
--- a/common/vsoc/lib/vsoc_memory.cpp
+++ b/common/vsoc/lib/vsoc_memory.cpp
@@ -29,10 +29,9 @@
 #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/ril_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 "common/vsoc/shm/wifi_exchange_layout.h"
 
 #include "uapi/vsoc_shm.h"
 
@@ -173,8 +172,6 @@
            /* managed_by */ layout::gralloc::GrallocManagerLayout::region_name),
        ValidateAndBuildLayout<layout::socket_forward::SocketForwardLayout>(7,
                                                                            7),
-       ValidateAndBuildLayout<layout::wifi::WifiExchangeLayout>(2, 2),
-       ValidateAndBuildLayout<layout::ril::RilLayout>(2, 2),
        ValidateAndBuildLayout<layout::e2e_test::E2EPrimaryTestRegionLayout>(1,
                                                                             1),
        ValidateAndBuildLayout<layout::e2e_test::E2ESecondaryTestRegionLayout>(
diff --git a/common/vsoc/lib/wifi_exchange_view.cpp b/common/vsoc/lib/wifi_exchange_view.cpp
deleted file mode 100644
index 23dcd09..0000000
--- a/common/vsoc/lib/wifi_exchange_view.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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/wifi_exchange_view.h"
-
-#include <algorithm>
-#include <string>
-
-#include <linux/if_ether.h>
-#include "common/vsoc/lib/circqueue_impl.h"
-
-namespace vsoc {
-namespace wifi {
-
-intptr_t WifiExchangeView::Send(const void* buffer, intptr_t length) {
-#ifdef CUTTLEFISH_HOST
-  return data()->guest_ingress.Write(this, static_cast<const char*>(buffer),
-                                     length);
-#else
-  return data()->guest_egress.Write(this, static_cast<const char*>(buffer),
-                                    length);
-#endif
-}
-
-intptr_t WifiExchangeView::Recv(void* buffer, intptr_t max_length) {
-#ifdef CUTTLEFISH_HOST
-  return data()->guest_egress.Read(this, static_cast<char*>(buffer),
-                                   max_length);
-#else
-  return data()->guest_ingress.Read(this, static_cast<char*>(buffer),
-                                    max_length);
-#endif
-}
-
-void WifiExchangeView::SetGuestMACAddress(
-    const WifiExchangeView::MacAddress& mac_address) {
-  std::copy(std::begin(mac_address),
-            std::end(mac_address),
-            std::begin(data()->guest_mac_address));
-}
-
-WifiExchangeView::MacAddress WifiExchangeView::GetGuestMACAddress() {
-  WifiExchangeView::MacAddress ret;
-  std::copy(std::begin(data()->guest_mac_address),
-            std::end(data()->guest_mac_address),
-            std::begin(ret));
-  return ret;
-}
-
-void WifiExchangeView::SetHostMACAddress(
-    const WifiExchangeView::MacAddress& mac_address) {
-  std::copy(std::begin(mac_address),
-            std::end(mac_address),
-            std::begin(data()->host_mac_address));
-}
-
-WifiExchangeView::MacAddress WifiExchangeView::GetHostMACAddress() {
-  WifiExchangeView::MacAddress ret;
-  std::copy(std::begin(data()->host_mac_address),
-            std::end(data()->host_mac_address),
-            std::begin(ret));
-  return ret;
-}
-
-// static
-bool WifiExchangeView::ParseMACAddress(const std::string& s,
-                                       WifiExchangeView::MacAddress* mac) {
-  char dummy;
-  // This is likely to always be true, but better safe than sorry
-  static_assert(std::tuple_size<WifiExchangeView::MacAddress>::value == 6,
-                "Mac address size has changed");
-  if (sscanf(s.c_str(),
-             "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx%c",
-             &(*mac)[0],
-             &(*mac)[1],
-             &(*mac)[2],
-             &(*mac)[3],
-             &(*mac)[4],
-             &(*mac)[5],
-             &dummy) != 6) {
-    return false;
-  }
-  return true;
-}
-
-// static
-std::string WifiExchangeView::MacAddressToString(
-    const WifiExchangeView::MacAddress& mac) {
-  char buffer[3 * mac.size()];
-  // This is likely to always be true, but better safe than sorry
-  static_assert(std::tuple_size<WifiExchangeView::MacAddress>::value == 6,
-                "Mac address size has changed");
-  snprintf(buffer,
-           sizeof(buffer),
-           "%02x:%02x:%02x:%02x:%02x:%02x",
-           mac[0],
-           mac[1],
-           mac[2],
-           mac[3],
-           mac[4],
-           mac[5]);
-  return std::string(buffer);
-}
-
-}  // namespace wifi
-}  // namespace vsoc
diff --git a/common/vsoc/lib/wifi_exchange_view.h b/common/vsoc/lib/wifi_exchange_view.h
deleted file mode 100644
index 5230a8e..0000000
--- a/common/vsoc/lib/wifi_exchange_view.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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 <array>
-#include <memory>
-
-#include "common/vsoc/lib/typed_region_view.h"
-#include "common/vsoc/shm/wifi_exchange_layout.h"
-#include "uapi/vsoc_shm.h"
-
-namespace vsoc {
-namespace wifi {
-
-class WifiExchangeView
-    : public vsoc::TypedRegionView<
-        WifiExchangeView,
-        vsoc::layout::wifi::WifiExchangeLayout> {
- public:
-  using MacAddress = std::array<
-      uint8_t,
-      sizeof(vsoc::layout::wifi::WifiExchangeLayout::guest_mac_address)>;
-
-  // Send netlink packet to peer.
-  // returns true, if operation was successful.
-  intptr_t Send(const void* buffer, intptr_t length);
-
-  // Receive netlink packet from peer.
-  // Returns number of bytes read, or negative value, if failed.
-  intptr_t Recv(void* buffer, intptr_t max_length);
-
-  // Set guest MAC address.
-  void SetGuestMACAddress(const MacAddress& mac_address);
-  MacAddress GetGuestMACAddress();
-
-  // Set host MAC address.
-  void SetHostMACAddress(const MacAddress& mac_address);
-  MacAddress GetHostMACAddress();
-
-  void SetConfigReady();
-  void WaitConfigReady();
-
-  static bool ParseMACAddress(const std::string &s, MacAddress *mac);
-  static std::string MacAddressToString(const MacAddress& mac);
-};
-
-}  // namespace wifi
-}  // namespace vsoc
diff --git a/common/vsoc/shm/e2e_test_region_layout.h b/common/vsoc/shm/e2e_test_region_layout.h
index 2c2670b..f3ae615 100644
--- a/common/vsoc/shm/e2e_test_region_layout.h
+++ b/common/vsoc/shm/e2e_test_region_layout.h
@@ -43,7 +43,7 @@
  * 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 {
+enum E2ETestStage : uint32_t {
   // No tests have passed
   E2E_STAGE_NONE = 0,
   // This side has finished writing its pattern to the region
@@ -163,23 +163,6 @@
 };
 ASSERT_SHM_COMPATIBLE(E2EUnfindableRegionLayout);
 
-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/lock.h b/common/vsoc/shm/lock.h
index cc86add..2049ba8 100644
--- a/common/vsoc/shm/lock.h
+++ b/common/vsoc/shm/lock.h
@@ -23,7 +23,13 @@
 // 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>
@@ -62,7 +68,7 @@
       if (lock_.compare_exchange_strong(expected, Sides::OurSide)) {
         return;
       }
-      _mm_pause();
+      _pause();
     }
   }
 
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/ril_layout.h b/common/vsoc/shm/ril_layout.h
deleted file mode 100644
index 33348e2..0000000
--- a/common/vsoc/shm/ril_layout.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#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"
-
-// Memory layout for the ril hal region
-
-namespace vsoc {
-namespace layout {
-namespace ril {
-
-struct RilLayout : public RegionLayout {
-  static constexpr size_t layout_size = 4 * 16 + 4;
-  static const char* region_name;
-
-  char ipaddr[16]; // xxx.xxx.xxx.xxx\0 = 16 bytes
-  char gateway[16];
-  char dns[16];
-  char broadcast[16];
-  uint32_t prefixlen;
-};
-ASSERT_SHM_COMPATIBLE(RilLayout);
-}  // namespace ril
-}  // namespace layout
-}  // namespace vsoc
diff --git a/common/vsoc/shm/wifi_exchange_layout.h b/common/vsoc/shm/wifi_exchange_layout.h
deleted file mode 100644
index df4659e..0000000
--- a/common/vsoc/shm/wifi_exchange_layout.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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 "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 wifi {
-
-struct WifiExchangeLayout : public RegionLayout {
-  static constexpr size_t layout_size = 2 * CircularPacketQueue<16, 8192>::layout_size + 12;
-
-  // Traffic originating from host that proceeds towards guest.
-  CircularPacketQueue<16, 8192> guest_ingress;
-  // Traffic originating from guest that proceeds towards host.
-  CircularPacketQueue<16, 8192> guest_egress;
-
-  // Desired MAC address for guest device.
-  uint8_t guest_mac_address[6];
-  // MAC address of host device.
-  uint8_t host_mac_address[6];
-
-  static const char* region_name;
-};
-
-ASSERT_SHM_COMPATIBLE(WifiExchangeLayout);
-
-}  // namespace wifi
-}  // namespace layout
-}  // namespace vsoc
diff --git a/guest/Android.bp b/guest/Android.bp
index 28eec2c..ba07c2e 100644
--- a/guest/Android.bp
+++ b/guest/Android.bp
@@ -16,4 +16,5 @@
 subdirs = [
     "commands",
     "hals/health",
+    "hals/hwcomposer",
 ]
diff --git a/guest/commands/Android.bp b/guest/commands/Android.bp
index 8264fcc..5c7c3c2 100644
--- a/guest/commands/Android.bp
+++ b/guest/commands/Android.bp
@@ -14,4 +14,5 @@
 // limitations under the License.
 
 subdirs = [
+  "vsock_logcat",
 ]
diff --git a/guest/commands/rename_netiface/Android.bp b/guest/commands/rename_netiface/Android.bp
new file mode 100644
index 0000000..b93a465
--- /dev/null
+++ b/guest/commands/rename_netiface/Android.bp
@@ -0,0 +1,26 @@
+//
+// 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: "rename_netiface",
+    srcs: [
+        "main.cpp",
+    ],
+    shared_libs: [
+        "cuttlefish_net",
+    ],
+    defaults: ["cuttlefish_guest_only"]
+}
diff --git a/guest/commands/rename_netiface/Android.mk b/guest/commands/rename_netiface/Android.mk
deleted file mode 100644
index 67e97f4..0000000
--- a/guest/commands/rename_netiface/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := rename_netiface
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := main.cpp
-LOCAL_SHARED_LIBRARIES := cuttlefish_net
-LOCAL_C_INCLUDES := device/google/cuttlefish_common
-LOCAL_MULTILIB := first
-LOCAL_VENDOR_MODULE := true
-
-include $(BUILD_EXECUTABLE)
diff --git a/guest/commands/setup_wifi/Android.bp b/guest/commands/setup_wifi/Android.bp
new file mode 100644
index 0000000..ec89852
--- /dev/null
+++ b/guest/commands/setup_wifi/Android.bp
@@ -0,0 +1,29 @@
+//
+// 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: "setup_wifi",
+    srcs: [
+        "main.cpp",
+    ],
+    shared_libs: [
+        "cuttlefish_net",
+        "cuttlefish_auto_resources",
+        "libbase",
+        "liblog",
+    ],
+    defaults: ["cuttlefish_guest_only"]
+}
diff --git a/guest/commands/setup_wifi/Android.mk b/guest/commands/setup_wifi/Android.mk
deleted file mode 100644
index b920483..0000000
--- a/guest/commands/setup_wifi/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := setup_wifi
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := main.cpp
-LOCAL_SHARED_LIBRARIES := cuttlefish_net cuttlefish_auto_resources libbase liblog
-LOCAL_C_INCLUDES := device/google/cuttlefish_common
-LOCAL_MULTILIB := first
-LOCAL_VENDOR_MODULE := true
-
-include $(BUILD_EXECUTABLE)
diff --git a/guest/commands/setup_wifi/main.cpp b/guest/commands/setup_wifi/main.cpp
index cd60e99..e3840ff 100644
--- a/guest/commands/setup_wifi/main.cpp
+++ b/guest/commands/setup_wifi/main.cpp
@@ -93,7 +93,7 @@
   return 0;
 }
 
-int main(int argc, char** argv) {
+int main() {
   int renamed_eth0 = RenameNetwork("eth0", "buried_eth0");
   if (renamed_eth0 != 0) {
     return renamed_eth0;
diff --git a/guest/commands/vsock_logcat/Android.bp b/guest/commands/vsock_logcat/Android.bp
new file mode 100644
index 0000000..52575d7
--- /dev/null
+++ b/guest/commands/vsock_logcat/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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: "vsock_logcat",
+    srcs: [
+        "main.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libcuttlefish_fs",
+        "libcuttlefish_utils",
+        "liblog",
+    ],
+    static_libs: [
+        "libgflags",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    defaults: ["cuttlefish_guest_only"]
+}
diff --git a/guest/commands/vsock_logcat/main.cpp b/guest/commands/vsock_logcat/main.cpp
new file mode 100644
index 0000000..51d97e3
--- /dev/null
+++ b/guest/commands/vsock_logcat/main.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "vsock_logcat"
+
+#include <string.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <cutils/properties.h>
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/files.h"
+#include "common/libs/utils/subprocess.h"
+
+DEFINE_uint32(port, property_get_int32("ro.boot.vsock_logcat_port", 0),
+              "VSOCK port to send logcat output to");
+DEFINE_uint32(cid, 2, "VSOCK CID to send logcat output to");
+DEFINE_string(pipe_name, "/dev/cf_logcat_pipe",
+              "The path for the named pipe logcat will write to");
+
+namespace {
+
+constexpr char kLogcatExitMsg[] = "\nDetected exit of logcat process\n\n";
+
+class ServiceStatus {
+ public:
+  static const char* kServiceStatusProperty;
+  static const char* kStatusStarted;
+  static const char* kStatusFailed;
+
+  ServiceStatus() {
+    // This can fail if the property isn't set (the first time it runs), so
+    // ignore the result.
+    property_get(kServiceStatusProperty, status_, kStatusStarted);
+  }
+
+  bool Set(const char* status) {
+    auto ret = property_set(kServiceStatusProperty, status);
+
+    if (ret == 0) {
+      strcpy(status_, status);
+      return true;
+    }
+    return false;
+  }
+
+  const char* Get() { return status_; }
+
+ private:
+  char status_[PROP_VALUE_MAX];
+};
+
+const char* ServiceStatus::kServiceStatusProperty = "vendor.vsock_logcat_status";
+const char* ServiceStatus::kStatusStarted = "started";
+const char* ServiceStatus::kStatusFailed = "failed";
+
+void LogFailed(const std::string& msg, ServiceStatus* status) {
+  // Only log if status is not failed, ensuring it logs once per fail.
+  if (strcmp(status->Get(), ServiceStatus::kStatusFailed) != 0) {
+    LOG(ERROR) << msg;
+    std::ofstream kmsg;
+    kmsg.open("/dev/kmsg");
+    kmsg << LOG_TAG << ": " << msg;
+    kmsg << "";
+    kmsg.close();
+  }
+  auto ret = status->Set(ServiceStatus::kStatusFailed);
+  if (!ret) {
+    LOG(ERROR) << "Unable to set value of property: "
+               << ServiceStatus::kServiceStatusProperty;
+  }
+}
+}  // namespace
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  CHECK(FLAGS_port != 0) << "Port flag is required";
+
+  ServiceStatus status;
+
+  auto log_fd = cvd::SharedFD::VsockClient(FLAGS_cid, FLAGS_port, SOCK_STREAM);
+  if (!log_fd->IsOpen()) {
+    std::ostringstream msg;
+    msg << "Unable to connect to vsock:" << FLAGS_cid << ":" << FLAGS_port
+        << ": " << log_fd->StrError();
+    LogFailed(msg.str(), &status);
+    return 1;
+  }
+  auto ret = status.Set(ServiceStatus::kStatusStarted);
+  if (!ret) {
+    LOG(ERROR) << "Unable to set value of property: "
+               << ServiceStatus::kServiceStatusProperty;
+  }
+
+  if (cvd::FileExists(FLAGS_pipe_name)) {
+    LOG(WARNING) << "The file " << FLAGS_pipe_name << " already exists. Deleting...";
+    cvd::RemoveFile(FLAGS_pipe_name);
+  }
+  auto pipe = mkfifo(FLAGS_pipe_name.c_str(), 0600);
+  if (pipe != 0) {
+    LOG(FATAL) << "unable to create pipe: " << strerror(errno);
+  }
+  property_set("vendor.ser.cf-logcat", FLAGS_pipe_name.c_str());
+  while (1) {
+    auto conn = cvd::SharedFD::Open(FLAGS_pipe_name.c_str(), O_RDONLY);
+    while (conn->IsOpen()) {
+      char buff[4096];
+      auto read = conn->Read(buff, sizeof(buff));
+      if (read) {
+        log_fd->Write(buff, read);
+      } else {
+        conn->Close();
+      }
+    }
+    log_fd->Write(kLogcatExitMsg, sizeof(kLogcatExitMsg) - 1);
+  }
+}
diff --git a/guest/hals/audio/Android.bp b/guest/hals/audio/Android.bp
new file mode 100644
index 0000000..93a7455
--- /dev/null
+++ b/guest/hals/audio/Android.bp
@@ -0,0 +1,23 @@
+// 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: "audio.primary.cutf",
+    relative_install_path: "hw",
+    defaults: ["cuttlefish_guest_only"],
+    vendor: true,
+    srcs: ["audio_hw.c"],
+    cflags: ["-Wno-unused-parameter"],
+    shared_libs: ["libcutils", "libhardware", "liblog", "libtinyalsa"],
+}
diff --git a/guest/hals/audio/audio_hw.c b/guest/hals/audio/audio_hw.c
new file mode 100644
index 0000000..058d620
--- /dev/null
+++ b/guest/hals/audio/audio_hw.c
@@ -0,0 +1,1634 @@
+/*
+ * Copyright (C) 2012 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.
+ *
+ * This code was forked from device/generic/goldfish/audio/audio_hw.c
+ *
+ * At the time of forking, the code was identical except that a fallback
+ * to a legacy HAL which does not use ALSA was removed, and the dependency
+ * on libdl was also removed.
+ */
+
+#define LOG_TAG "audio_hw_generic"
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <cutils/str_parms.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio.h>
+#include <tinyalsa/asoundlib.h>
+
+#define PCM_CARD 0
+#define PCM_DEVICE 0
+
+
+#define OUT_PERIOD_MS 15
+#define OUT_PERIOD_COUNT 4
+
+#define IN_PERIOD_MS 15
+#define IN_PERIOD_COUNT 4
+
+struct generic_audio_device {
+    struct audio_hw_device device; // Constant after init
+    pthread_mutex_t lock;
+    bool mic_mute;                 // Proteced by this->lock
+    struct mixer* mixer;           // Proteced by this->lock
+};
+
+/* If not NULL, this is a pointer to the fallback module.
+ * This really is the original goldfish audio device /dev/eac which we will use
+ * if no alsa devices are detected.
+ */
+static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state);
+static int adev_get_microphones(const audio_hw_device_t *dev,
+                                struct audio_microphone_characteristic_t *mic_array,
+                                size_t *mic_count);
+
+
+typedef struct audio_vbuffer {
+    pthread_mutex_t lock;
+    uint8_t *  data;
+    size_t     frame_size;
+    size_t     frame_count;
+    size_t     head;
+    size_t     tail;
+    size_t     live;
+} audio_vbuffer_t;
+
+static int audio_vbuffer_init (audio_vbuffer_t * audio_vbuffer, size_t frame_count,
+                              size_t frame_size) {
+    if (!audio_vbuffer) {
+        return -EINVAL;
+    }
+    audio_vbuffer->frame_size = frame_size;
+    audio_vbuffer->frame_count = frame_count;
+    size_t bytes = frame_count * frame_size;
+    audio_vbuffer->data = calloc(bytes, 1);
+    if (!audio_vbuffer->data) {
+        return -ENOMEM;
+    }
+    audio_vbuffer->head = 0;
+    audio_vbuffer->tail = 0;
+    audio_vbuffer->live = 0;
+    pthread_mutex_init (&audio_vbuffer->lock, (const pthread_mutexattr_t *) NULL);
+    return 0;
+}
+
+static int audio_vbuffer_destroy (audio_vbuffer_t * audio_vbuffer) {
+    if (!audio_vbuffer) {
+        return -EINVAL;
+    }
+    free(audio_vbuffer->data);
+    pthread_mutex_destroy(&audio_vbuffer->lock);
+    return 0;
+}
+
+static int audio_vbuffer_live (audio_vbuffer_t * audio_vbuffer) {
+    if (!audio_vbuffer) {
+        return -EINVAL;
+    }
+    pthread_mutex_lock (&audio_vbuffer->lock);
+    int live = audio_vbuffer->live;
+    pthread_mutex_unlock (&audio_vbuffer->lock);
+    return live;
+}
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+static size_t audio_vbuffer_write (audio_vbuffer_t * audio_vbuffer, const void * buffer, size_t frame_count) {
+    size_t frames_written = 0;
+    pthread_mutex_lock (&audio_vbuffer->lock);
+
+    while (frame_count != 0) {
+        int frames = 0;
+        if (audio_vbuffer->live == 0 || audio_vbuffer->head > audio_vbuffer->tail) {
+            frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->head);
+        } else if (audio_vbuffer->head < audio_vbuffer->tail) {
+            frames = MIN(frame_count, audio_vbuffer->tail - (audio_vbuffer->head));
+        } else {
+            // Full
+            break;
+        }
+        memcpy(&audio_vbuffer->data[audio_vbuffer->head*audio_vbuffer->frame_size],
+               &((uint8_t*)buffer)[frames_written*audio_vbuffer->frame_size],
+               frames*audio_vbuffer->frame_size);
+        audio_vbuffer->live += frames;
+        frames_written += frames;
+        frame_count -= frames;
+        audio_vbuffer->head = (audio_vbuffer->head + frames) % audio_vbuffer->frame_count;
+    }
+
+    pthread_mutex_unlock (&audio_vbuffer->lock);
+    return frames_written;
+}
+
+static size_t audio_vbuffer_read (audio_vbuffer_t * audio_vbuffer, void * buffer, size_t frame_count) {
+    size_t frames_read = 0;
+    pthread_mutex_lock (&audio_vbuffer->lock);
+
+    while (frame_count != 0) {
+        int frames = 0;
+        if (audio_vbuffer->live == audio_vbuffer->frame_count ||
+            audio_vbuffer->tail > audio_vbuffer->head) {
+            frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->tail);
+        } else if (audio_vbuffer->tail < audio_vbuffer->head) {
+            frames = MIN(frame_count, audio_vbuffer->head - audio_vbuffer->tail);
+        } else {
+            break;
+        }
+        memcpy(&((uint8_t*)buffer)[frames_read*audio_vbuffer->frame_size],
+               &audio_vbuffer->data[audio_vbuffer->tail*audio_vbuffer->frame_size],
+               frames*audio_vbuffer->frame_size);
+        audio_vbuffer->live -= frames;
+        frames_read += frames;
+        frame_count -= frames;
+        audio_vbuffer->tail = (audio_vbuffer->tail + frames) % audio_vbuffer->frame_count;
+    }
+
+    pthread_mutex_unlock (&audio_vbuffer->lock);
+    return frames_read;
+}
+
+struct generic_stream_out {
+    struct audio_stream_out stream;   // Constant after init
+    pthread_mutex_t lock;
+    struct generic_audio_device *dev; // Constant after init
+    audio_devices_t device;           // Protected by this->lock
+    struct audio_config req_config;   // Constant after init
+    struct pcm_config pcm_config;     // Constant after init
+    audio_vbuffer_t buffer;           // Constant after init
+
+    // Time & Position Keeping
+    bool standby;                      // Protected by this->lock
+    uint64_t underrun_position;        // Protected by this->lock
+    struct timespec underrun_time;     // Protected by this->lock
+    uint64_t last_write_time_us;       // Protected by this->lock
+    uint64_t frames_total_buffered;    // Protected by this->lock
+    uint64_t frames_written;           // Protected by this->lock
+    uint64_t frames_rendered;          // Protected by this->lock
+
+    // Worker
+    pthread_t worker_thread;          // Constant after init
+    pthread_cond_t worker_wake;       // Protected by this->lock
+    bool worker_standby;              // Protected by this->lock
+    bool worker_exit;                 // Protected by this->lock
+};
+
+struct generic_stream_in {
+    struct audio_stream_in stream;    // Constant after init
+    pthread_mutex_t lock;
+    struct generic_audio_device *dev; // Constant after init
+    audio_devices_t device;           // Protected by this->lock
+    struct audio_config req_config;   // Constant after init
+    struct pcm *pcm;                  // Protected by this->lock
+    struct pcm_config pcm_config;     // Constant after init
+    int16_t *stereo_to_mono_buf;      // Protected by this->lock
+    size_t stereo_to_mono_buf_size;   // Protected by this->lock
+    audio_vbuffer_t buffer;           // Protected by this->lock
+
+    // Time & Position Keeping
+    bool standby;                     // Protected by this->lock
+    int64_t standby_position;         // Protected by this->lock
+    struct timespec standby_exit_time;// Protected by this->lock
+    int64_t standby_frames_read;      // Protected by this->lock
+
+    // Worker
+    pthread_t worker_thread;          // Constant after init
+    pthread_cond_t worker_wake;       // Protected by this->lock
+    bool worker_standby;              // Protected by this->lock
+    bool worker_exit;                 // Protected by this->lock
+};
+
+static struct pcm_config pcm_config_out = {
+    .channels = 2,
+    .rate = 0,
+    .period_size = 0,
+    .period_count = OUT_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+};
+
+static struct pcm_config pcm_config_in = {
+    .channels = 2,
+    .rate = 0,
+    .period_size = 0,
+    .period_count = IN_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+};
+
+static pthread_mutex_t adev_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static unsigned int audio_device_ref_count = 0;
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    return out->req_config.sample_rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+    return -ENOSYS;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    int size = out->pcm_config.period_size *
+                audio_stream_out_frame_size(&out->stream);
+
+    return size;
+}
+
+static audio_channel_mask_t out_get_channels(const struct audio_stream *stream)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    return out->req_config.channel_mask;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+
+    return out->req_config.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+    return -ENOSYS;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    pthread_mutex_lock(&out->lock);
+    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",
+                out_get_sample_rate(stream),
+                out_get_buffer_size(stream),
+                out_get_channels(stream),
+                out_get_format(stream),
+                out->device,
+                out->dev);
+    pthread_mutex_unlock(&out->lock);
+    return 0;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    struct str_parms *parms;
+    char value[32];
+    int ret = -ENOSYS;
+    int success;
+    long val;
+    char *end;
+
+    if (kvpairs == NULL || kvpairs[0] == 0) {
+        return 0;
+    }
+    pthread_mutex_lock(&out->lock);
+    if (out->standby) {
+        parms = str_parms_create_str(kvpairs);
+        success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                                value, sizeof(value));
+        if (success >= 0) {
+            errno = 0;
+            val = strtol(value, &end, 10);
+            if (errno == 0 && (end != NULL) && (*end == '\0') && ((int)val == val)) {
+                out->device = (int)val;
+                ret = 0;
+            }
+        }
+
+        // NO op for AUDIO_PARAMETER_DEVICE_CONNECT and AUDIO_PARAMETER_DEVICE_DISCONNECT
+        success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT,
+                                value, sizeof(value));
+        if (success >= 0) {
+            ret = 0;
+        }
+        success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT,
+                                value, sizeof(value));
+        if (success >= 0) {
+            ret = 0;
+        }
+
+        if (ret != 0) {
+            ALOGD("%s Unsupported parameter %s", __FUNCTION__, kvpairs);
+        }
+
+        str_parms_destroy(parms);
+    }
+    pthread_mutex_unlock(&out->lock);
+    return ret;
+}
+
+static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    struct str_parms *query = str_parms_create_str(keys);
+    char *str = NULL;
+    char value[256];
+    struct str_parms *reply = str_parms_create();
+    int ret;
+    bool get = false;
+
+    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+    if (ret >= 0) {
+        pthread_mutex_lock(&out->lock);
+        str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, out->device);
+        pthread_mutex_unlock(&out->lock);
+        get = true;
+    }
+
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
+        value[0] = 0;
+        strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
+        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
+        get = true;
+    }
+
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) {
+        value[0] = 0;
+        strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
+        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value);
+        get = true;
+    }
+
+    if (get) {
+        str = strdup(str_parms_to_str(reply));
+    }
+    else {
+        ALOGD("%s Unsupported paramter: %s", __FUNCTION__, keys);
+    }
+
+    str_parms_destroy(query);
+    str_parms_destroy(reply);
+    return str;
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    return (out->pcm_config.period_size * 1000) / out->pcm_config.rate;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+                          float right)
+{
+    return -ENOSYS;
+}
+
+static void *out_write_worker(void * args)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)args;
+    struct pcm *pcm = NULL;
+    uint8_t *buffer = NULL;
+    int buffer_frames;
+    int buffer_size;
+    bool restart = false;
+    bool shutdown = false;
+    while (true) {
+        pthread_mutex_lock(&out->lock);
+        while (out->worker_standby || restart) {
+            restart = false;
+            if (pcm) {
+                pcm_close(pcm); // Frees pcm
+                pcm = NULL;
+                free(buffer);
+                buffer=NULL;
+            }
+            if (out->worker_exit) {
+                break;
+            }
+            pthread_cond_wait(&out->worker_wake, &out->lock);
+        }
+
+        if (out->worker_exit) {
+            if (!out->worker_standby) {
+                ALOGE("Out worker not in standby before exiting");
+            }
+            shutdown = true;
+        }
+
+        while (!shutdown && audio_vbuffer_live(&out->buffer) == 0) {
+            pthread_cond_wait(&out->worker_wake, &out->lock);
+        }
+
+        if (shutdown) {
+            pthread_mutex_unlock(&out->lock);
+            break;
+        }
+
+        if (!pcm) {
+            pcm = pcm_open(PCM_CARD, PCM_DEVICE,
+                          PCM_OUT | PCM_MONOTONIC, &out->pcm_config);
+            if (!pcm_is_ready(pcm)) {
+                ALOGE("pcm_open(out) failed: %s: channels %d format %d rate %d",
+                  pcm_get_error(pcm),
+                  out->pcm_config.channels,
+                  out->pcm_config.format,
+                  out->pcm_config.rate
+                   );
+                pthread_mutex_unlock(&out->lock);
+                break;
+            }
+            buffer_frames = out->pcm_config.period_size;
+            buffer_size = pcm_frames_to_bytes(pcm, buffer_frames);
+            buffer = malloc(buffer_size);
+            if (!buffer) {
+                ALOGE("could not allocate write buffer");
+                pthread_mutex_unlock(&out->lock);
+                break;
+            }
+        }
+        int frames = audio_vbuffer_read(&out->buffer, buffer, buffer_frames);
+        pthread_mutex_unlock(&out->lock);
+        int ret = pcm_write(pcm, buffer, pcm_frames_to_bytes(pcm, frames));
+        if (ret != 0) {
+            ALOGE("pcm_write failed %s", pcm_get_error(pcm));
+            restart = true;
+        }
+    }
+    if (buffer) {
+        free(buffer);
+    }
+
+    return NULL;
+}
+
+// Call with in->lock held
+static void get_current_output_position(struct generic_stream_out *out,
+                                       uint64_t * position,
+                                       struct timespec * timestamp) {
+    struct timespec curtime = { .tv_sec = 0, .tv_nsec = 0 };
+    clock_gettime(CLOCK_MONOTONIC, &curtime);
+    const int64_t now_us = (curtime.tv_sec * 1000000000LL + curtime.tv_nsec) / 1000;
+    if (timestamp) {
+        *timestamp = curtime;
+    }
+    int64_t position_since_underrun;
+    if (out->standby) {
+        position_since_underrun = 0;
+    } else {
+        const int64_t first_us = (out->underrun_time.tv_sec * 1000000000LL +
+                                  out->underrun_time.tv_nsec) / 1000;
+        position_since_underrun = (now_us - first_us) *
+                out_get_sample_rate(&out->stream.common) /
+                1000000;
+        if (position_since_underrun < 0) {
+            position_since_underrun = 0;
+        }
+    }
+    *position = out->underrun_position + position_since_underrun;
+
+    // The device will reuse the same output stream leading to periods of
+    // underrun.
+    if (*position > out->frames_written) {
+        ALOGW("Not supplying enough data to HAL, expected position %" PRIu64 " , only wrote "
+              "%" PRIu64,
+              *position, out->frames_written);
+
+        *position = out->frames_written;
+        out->underrun_position = *position;
+        out->underrun_time = curtime;
+        out->frames_total_buffered = 0;
+    }
+}
+
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+                         size_t bytes)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    const size_t frames =  bytes / audio_stream_out_frame_size(stream);
+
+    pthread_mutex_lock(&out->lock);
+
+    if (out->worker_standby) {
+        out->worker_standby = false;
+    }
+
+    uint64_t current_position;
+    struct timespec current_time;
+
+    get_current_output_position(out, &current_position, &current_time);
+    const uint64_t now_us = (current_time.tv_sec * 1000000000LL +
+                             current_time.tv_nsec) / 1000;
+    if (out->standby) {
+        out->standby = false;
+        out->underrun_time = current_time;
+        out->frames_rendered = 0;
+        out->frames_total_buffered = 0;
+    }
+
+    size_t frames_written = audio_vbuffer_write(&out->buffer, buffer, frames);
+    pthread_cond_signal(&out->worker_wake);
+
+    /* Implementation just consumes bytes if we start getting backed up */
+    out->frames_written += frames;
+    out->frames_rendered += frames;
+    out->frames_total_buffered += frames;
+
+    // We simulate the audio device blocking when it's write buffers become
+    // full.
+
+    // At the beginning or after an underrun, try to fill up the vbuffer.
+    // This will be throttled by the PlaybackThread
+    int frames_sleep = out->frames_total_buffered < out->buffer.frame_count ? 0 : frames;
+
+    uint64_t sleep_time_us = frames_sleep * 1000000LL /
+                            out_get_sample_rate(&stream->common);
+
+    // If the write calls are delayed, subtract time off of the sleep to
+    // compensate
+    uint64_t time_since_last_write_us = now_us - out->last_write_time_us;
+    if (time_since_last_write_us < sleep_time_us) {
+        sleep_time_us -= time_since_last_write_us;
+    } else {
+        sleep_time_us = 0;
+    }
+    out->last_write_time_us = now_us + sleep_time_us;
+
+    pthread_mutex_unlock(&out->lock);
+
+    if (sleep_time_us > 0) {
+        usleep(sleep_time_us);
+    }
+
+    if (frames_written < frames) {
+        ALOGW("Hardware backing HAL too slow, could only write %zu of %zu frames", frames_written, frames);
+    }
+
+    /* Always consume all bytes */
+    return bytes;
+}
+
+static int out_get_presentation_position(const struct audio_stream_out *stream,
+                                   uint64_t *frames, struct timespec *timestamp)
+
+{
+    if (stream == NULL || frames == NULL || timestamp == NULL) {
+        return -EINVAL;
+    }
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+
+    pthread_mutex_lock(&out->lock);
+    get_current_output_position(out, frames, timestamp);
+    pthread_mutex_unlock(&out->lock);
+
+    return 0;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+                                   uint32_t *dsp_frames)
+{
+    if (stream == NULL || dsp_frames == NULL) {
+        return -EINVAL;
+    }
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    pthread_mutex_lock(&out->lock);
+    *dsp_frames = out->frames_rendered;
+    pthread_mutex_unlock(&out->lock);
+    return 0;
+}
+
+// Must be called with out->lock held
+static void do_out_standby(struct generic_stream_out *out)
+{
+    int frames_sleep = 0;
+    uint64_t sleep_time_us = 0;
+    if (out->standby) {
+        return;
+    }
+    while (true) {
+        get_current_output_position(out, &out->underrun_position, NULL);
+        frames_sleep = out->frames_written - out->underrun_position;
+
+        if (frames_sleep == 0) {
+            break;
+        }
+
+        sleep_time_us = frames_sleep * 1000000LL /
+                        out_get_sample_rate(&out->stream.common);
+
+        pthread_mutex_unlock(&out->lock);
+        usleep(sleep_time_us);
+        pthread_mutex_lock(&out->lock);
+    }
+    out->worker_standby = true;
+    out->standby = true;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    pthread_mutex_lock(&out->lock);
+    do_out_standby(out);
+    pthread_mutex_unlock(&out->lock);
+    return 0;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+    // out_add_audio_effect is a no op
+    return 0;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+    // out_remove_audio_effect is a no op
+    return 0;
+}
+
+static int out_get_next_write_timestamp(const struct audio_stream_out *stream,
+                                        int64_t *timestamp)
+{
+    return -ENOSYS;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    return in->req_config.sample_rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+    return -ENOSYS;
+}
+
+static int refine_output_parameters(uint32_t *sample_rate, audio_format_t *format, audio_channel_mask_t *channel_mask)
+{
+    static const uint32_t sample_rates [] = {8000,11025,16000,22050,24000,32000,
+                                            44100,48000};
+    static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t);
+    bool inval = false;
+    if (*format != AUDIO_FORMAT_PCM_16_BIT) {
+        *format = AUDIO_FORMAT_PCM_16_BIT;
+        inval = true;
+    }
+
+    int channel_count = popcount(*channel_mask);
+    if (channel_count != 1 && channel_count != 2) {
+        *channel_mask = AUDIO_CHANNEL_IN_STEREO;
+        inval = true;
+    }
+
+    int i;
+    for (i = 0; i < sample_rates_count; i++) {
+        if (*sample_rate < sample_rates[i]) {
+            *sample_rate = sample_rates[i];
+            inval=true;
+            break;
+        }
+        else if (*sample_rate == sample_rates[i]) {
+            break;
+        }
+        else if (i == sample_rates_count-1) {
+            // Cap it to the highest rate we support
+            *sample_rate = sample_rates[i];
+            inval=true;
+        }
+    }
+
+    if (inval) {
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int refine_input_parameters(uint32_t *sample_rate, audio_format_t *format, audio_channel_mask_t *channel_mask)
+{
+    static const uint32_t sample_rates [] = {8000, 11025, 16000, 22050, 44100, 48000};
+    static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t);
+    bool inval = false;
+    // Only PCM_16_bit is supported. If this is changed, stereo to mono drop
+    // must be fixed in in_read
+    if (*format != AUDIO_FORMAT_PCM_16_BIT) {
+        *format = AUDIO_FORMAT_PCM_16_BIT;
+        inval = true;
+    }
+
+    int channel_count = popcount(*channel_mask);
+    if (channel_count != 1 && channel_count != 2) {
+        *channel_mask = AUDIO_CHANNEL_IN_STEREO;
+        inval = true;
+    }
+
+    int i;
+    for (i = 0; i < sample_rates_count; i++) {
+        if (*sample_rate < sample_rates[i]) {
+            *sample_rate = sample_rates[i];
+            inval=true;
+            break;
+        }
+        else if (*sample_rate == sample_rates[i]) {
+            break;
+        }
+        else if (i == sample_rates_count-1) {
+            // Cap it to the highest rate we support
+            *sample_rate = sample_rates[i];
+            inval=true;
+        }
+    }
+
+    if (inval) {
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int check_input_parameters(uint32_t sample_rate, audio_format_t format,
+                                  audio_channel_mask_t channel_mask)
+{
+    return refine_input_parameters(&sample_rate, &format, &channel_mask);
+}
+
+static size_t get_input_buffer_size(uint32_t sample_rate, audio_format_t format,
+                                    audio_channel_mask_t channel_mask)
+{
+    size_t size;
+    int channel_count = popcount(channel_mask);
+    if (check_input_parameters(sample_rate, format, channel_mask) != 0)
+        return 0;
+
+    size = sample_rate*IN_PERIOD_MS/1000;
+    // Audioflinger expects audio buffers to be multiple of 16 frames
+    size = ((size + 15) / 16) * 16;
+    size *= sizeof(short) * channel_count;
+
+    return size;
+}
+
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    int size = get_input_buffer_size(in->req_config.sample_rate,
+                                 in->req_config.format,
+                                 in->req_config.channel_mask);
+
+    return size;
+}
+
+static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    return in->req_config.channel_mask;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    return in->req_config.format;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+    return -ENOSYS;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+
+    pthread_mutex_lock(&in->lock);
+    dprintf(fd, "\tin_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",
+                in_get_sample_rate(stream),
+                in_get_buffer_size(stream),
+                in_get_channels(stream),
+                in_get_format(stream),
+                in->device,
+                in->dev);
+    pthread_mutex_unlock(&in->lock);
+    return 0;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    struct str_parms *parms;
+    char value[32];
+    int ret = -ENOSYS;
+    int success;
+    long val;
+    char *end;
+
+    if (kvpairs == NULL || kvpairs[0] == 0) {
+        return 0;
+    }
+    pthread_mutex_lock(&in->lock);
+    if (in->standby) {
+        parms = str_parms_create_str(kvpairs);
+
+        success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                                value, sizeof(value));
+        if (success >= 0) {
+            errno = 0;
+            val = strtol(value, &end, 10);
+            if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int)val == val)) {
+                in->device = (int)val;
+                ret = 0;
+            }
+        }
+        // NO op for AUDIO_PARAMETER_DEVICE_CONNECT and AUDIO_PARAMETER_DEVICE_DISCONNECT
+        success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT,
+                                value, sizeof(value));
+        if (success >= 0) {
+            ret = 0;
+        }
+        success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT,
+                                value, sizeof(value));
+        if (success >= 0) {
+            ret = 0;
+        }
+
+        if (ret != 0) {
+            ALOGD("%s: Unsupported parameter %s", __FUNCTION__, kvpairs);
+        }
+
+        str_parms_destroy(parms);
+    }
+    pthread_mutex_unlock(&in->lock);
+    return ret;
+}
+
+static char * in_get_parameters(const struct audio_stream *stream,
+                                const char *keys)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    struct str_parms *query = str_parms_create_str(keys);
+    char *str = NULL;
+    char value[256];
+    struct str_parms *reply = str_parms_create();
+    int ret;
+    bool get = false;
+
+    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+    if (ret >= 0) {
+        str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, in->device);
+        get = true;
+    }
+
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
+        value[0] = 0;
+        strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
+        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
+        get = true;
+    }
+
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) {
+        value[0] = 0;
+        strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
+        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value);
+        get = true;
+    }
+
+    if (get) {
+        str = strdup(str_parms_to_str(reply));
+    }
+    else {
+        ALOGD("%s Unsupported paramter: %s", __FUNCTION__, keys);
+    }
+
+    str_parms_destroy(query);
+    str_parms_destroy(reply);
+    return str;
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+    // in_set_gain is a no op
+    return 0;
+}
+
+// Call with in->lock held
+static void get_current_input_position(struct generic_stream_in *in,
+                                       int64_t * position,
+                                       struct timespec * timestamp) {
+    struct timespec t = { .tv_sec = 0, .tv_nsec = 0 };
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    const int64_t now_us = (t.tv_sec * 1000000000LL + t.tv_nsec) / 1000;
+    if (timestamp) {
+        *timestamp = t;
+    }
+    int64_t position_since_standby;
+    if (in->standby) {
+        position_since_standby = 0;
+    } else {
+        const int64_t first_us = (in->standby_exit_time.tv_sec * 1000000000LL +
+                                  in->standby_exit_time.tv_nsec) / 1000;
+        position_since_standby = (now_us - first_us) *
+                in_get_sample_rate(&in->stream.common) /
+                1000000;
+        if (position_since_standby < 0) {
+            position_since_standby = 0;
+        }
+    }
+    *position = in->standby_position + position_since_standby;
+}
+
+// Must be called with in->lock held
+static void do_in_standby(struct generic_stream_in *in)
+{
+    if (in->standby) {
+        return;
+    }
+    in->worker_standby = true;
+    get_current_input_position(in, &in->standby_position, NULL);
+    in->standby = true;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    pthread_mutex_lock(&in->lock);
+    do_in_standby(in);
+    pthread_mutex_unlock(&in->lock);
+    return 0;
+}
+
+static void *in_read_worker(void * args)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)args;
+    struct pcm *pcm = NULL;
+    uint8_t *buffer = NULL;
+    size_t buffer_frames;
+    int buffer_size;
+
+    bool restart = false;
+    bool shutdown = false;
+    while (true) {
+        pthread_mutex_lock(&in->lock);
+        while (in->worker_standby || restart) {
+            restart = false;
+            if (pcm) {
+                pcm_close(pcm); // Frees pcm
+                pcm = NULL;
+                free(buffer);
+                buffer=NULL;
+            }
+            if (in->worker_exit) {
+                break;
+            }
+            pthread_cond_wait(&in->worker_wake, &in->lock);
+        }
+
+        if (in->worker_exit) {
+            if (!in->worker_standby) {
+                ALOGE("In worker not in standby before exiting");
+            }
+            shutdown = true;
+        }
+        if (shutdown) {
+            pthread_mutex_unlock(&in->lock);
+            break;
+        }
+        if (!pcm) {
+            pcm = pcm_open(PCM_CARD, PCM_DEVICE,
+                          PCM_IN | PCM_MONOTONIC, &in->pcm_config);
+            if (!pcm_is_ready(pcm)) {
+                ALOGE("pcm_open(in) failed: %s: channels %d format %d rate %d",
+                  pcm_get_error(pcm),
+                  in->pcm_config.channels,
+                  in->pcm_config.format,
+                  in->pcm_config.rate
+                   );
+                pthread_mutex_unlock(&in->lock);
+                break;
+            }
+            buffer_frames = in->pcm_config.period_size;
+            buffer_size = pcm_frames_to_bytes(pcm, buffer_frames);
+            buffer = malloc(buffer_size);
+            if (!buffer) {
+                ALOGE("could not allocate worker read buffer");
+                pthread_mutex_unlock(&in->lock);
+                break;
+            }
+        }
+        pthread_mutex_unlock(&in->lock);
+        int ret = pcm_read(pcm, buffer, pcm_frames_to_bytes(pcm, buffer_frames));
+        if (ret != 0) {
+            ALOGW("pcm_read failed %s", pcm_get_error(pcm));
+            restart = true;
+            continue;
+        }
+
+        pthread_mutex_lock(&in->lock);
+        size_t frames_written = audio_vbuffer_write(&in->buffer, buffer, buffer_frames);
+        pthread_mutex_unlock(&in->lock);
+
+        if (frames_written != buffer_frames) {
+            ALOGW("in_read_worker only could write %zu / %zu frames", frames_written, buffer_frames);
+        }
+    }
+    if (buffer) {
+        free(buffer);
+    }
+    return NULL;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
+                       size_t bytes)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    struct generic_audio_device *adev = in->dev;
+    const size_t frames =  bytes / audio_stream_in_frame_size(stream);
+    bool mic_mute = false;
+    size_t read_bytes = 0;
+
+    adev_get_mic_mute(&adev->device, &mic_mute);
+    pthread_mutex_lock(&in->lock);
+
+    if (in->worker_standby) {
+        in->worker_standby = false;
+    }
+    pthread_cond_signal(&in->worker_wake);
+
+    int64_t current_position;
+    struct timespec current_time;
+
+    get_current_input_position(in, &current_position, &current_time);
+    if (in->standby) {
+        in->standby = false;
+        in->standby_exit_time = current_time;
+        in->standby_frames_read = 0;
+    }
+
+    const int64_t frames_available = current_position - in->standby_position - in->standby_frames_read;
+    assert(frames_available >= 0);
+
+    const size_t frames_wait = ((uint64_t)frames_available > frames) ? 0 : frames - frames_available;
+
+    int64_t sleep_time_us  = frames_wait * 1000000LL /
+                             in_get_sample_rate(&stream->common);
+
+    pthread_mutex_unlock(&in->lock);
+
+    if (sleep_time_us > 0) {
+        usleep(sleep_time_us);
+    }
+
+    pthread_mutex_lock(&in->lock);
+    int read_frames = 0;
+    if (in->standby) {
+        ALOGW("Input put to sleep while read in progress");
+        goto exit;
+    }
+    in->standby_frames_read += frames;
+
+    if (popcount(in->req_config.channel_mask) == 1 &&
+        in->pcm_config.channels == 2) {
+        // Need to resample to mono
+        if (in->stereo_to_mono_buf_size < bytes*2) {
+            in->stereo_to_mono_buf = realloc(in->stereo_to_mono_buf,
+                                             bytes*2);
+            if (!in->stereo_to_mono_buf) {
+                ALOGE("Failed to allocate stereo_to_mono_buff");
+                goto exit;
+            }
+        }
+
+        read_frames = audio_vbuffer_read(&in->buffer, in->stereo_to_mono_buf, frames);
+
+        // Currently only pcm 16 is supported.
+        uint16_t *src = (uint16_t *)in->stereo_to_mono_buf;
+        uint16_t *dst = (uint16_t *)buffer;
+        size_t i;
+        // Resample stereo 16 to mono 16 by dropping one channel.
+        // The stereo stream is interleaved L-R-L-R
+        for (i = 0; i < frames; i++) {
+            *dst = *src;
+            src += 2;
+            dst += 1;
+        }
+    } else {
+        read_frames = audio_vbuffer_read(&in->buffer, buffer, frames);
+    }
+
+exit:
+    read_bytes = read_frames*audio_stream_in_frame_size(stream);
+
+    if (mic_mute) {
+        read_bytes = 0;
+    }
+
+    if (read_bytes < bytes) {
+        memset (&((uint8_t *)buffer)[read_bytes], 0, bytes-read_bytes);
+    }
+
+    pthread_mutex_unlock(&in->lock);
+
+    return bytes;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+    return 0;
+}
+
+static int in_get_capture_position(const struct audio_stream_in *stream,
+                                int64_t *frames, int64_t *time)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    pthread_mutex_lock(&in->lock);
+    struct timespec current_time;
+    get_current_input_position(in, frames, &current_time);
+    *time = (current_time.tv_sec * 1000000000LL + current_time.tv_nsec);
+    pthread_mutex_unlock(&in->lock);
+    return 0;
+}
+
+static int in_get_active_microphones(const struct audio_stream_in *stream,
+                                     struct audio_microphone_characteristic_t *mic_array,
+                                     size_t *mic_count)
+{
+    return adev_get_microphones(NULL, mic_array, mic_count);
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+    // in_add_audio_effect is a no op
+    return 0;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+    // in_add_audio_effect is a no op
+    return 0;
+}
+
+static int adev_open_output_stream(struct audio_hw_device *dev,
+                                   audio_io_handle_t handle,
+                                   audio_devices_t devices,
+                                   audio_output_flags_t flags,
+                                   struct audio_config *config,
+                                   struct audio_stream_out **stream_out,
+                                   const char *address __unused)
+{
+    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+    struct generic_stream_out *out;
+    int ret = 0;
+
+    if (refine_output_parameters(&config->sample_rate, &config->format, &config->channel_mask)) {
+        ALOGE("Error opening output stream format %d, channel_mask %04x, sample_rate %u",
+              config->format, config->channel_mask, config->sample_rate);
+        ret = -EINVAL;
+        goto error;
+    }
+
+    out = (struct generic_stream_out *)calloc(1, sizeof(struct generic_stream_out));
+
+    if (!out)
+        return -ENOMEM;
+
+    out->stream.common.get_sample_rate = out_get_sample_rate;
+    out->stream.common.set_sample_rate = out_set_sample_rate;
+    out->stream.common.get_buffer_size = out_get_buffer_size;
+    out->stream.common.get_channels = out_get_channels;
+    out->stream.common.get_format = out_get_format;
+    out->stream.common.set_format = out_set_format;
+    out->stream.common.standby = out_standby;
+    out->stream.common.dump = out_dump;
+    out->stream.common.set_parameters = out_set_parameters;
+    out->stream.common.get_parameters = out_get_parameters;
+    out->stream.common.add_audio_effect = out_add_audio_effect;
+    out->stream.common.remove_audio_effect = out_remove_audio_effect;
+    out->stream.get_latency = out_get_latency;
+    out->stream.set_volume = out_set_volume;
+    out->stream.write = out_write;
+    out->stream.get_render_position = out_get_render_position;
+    out->stream.get_presentation_position = out_get_presentation_position;
+    out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
+
+    pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
+    out->dev = adev;
+    out->device = devices;
+    memcpy(&out->req_config, config, sizeof(struct audio_config));
+    memcpy(&out->pcm_config, &pcm_config_out, sizeof(struct pcm_config));
+    out->pcm_config.rate = config->sample_rate;
+    out->pcm_config.period_size = out->pcm_config.rate*OUT_PERIOD_MS/1000;
+
+    out->standby = true;
+    out->underrun_position = 0;
+    out->underrun_time.tv_sec = 0;
+    out->underrun_time.tv_nsec = 0;
+    out->last_write_time_us = 0;
+    out->frames_total_buffered = 0;
+    out->frames_written = 0;
+    out->frames_rendered = 0;
+
+    ret = audio_vbuffer_init(&out->buffer,
+                      out->pcm_config.period_size*out->pcm_config.period_count,
+                      out->pcm_config.channels *
+                      pcm_format_to_bits(out->pcm_config.format) >> 3);
+    if (ret == 0) {
+        pthread_cond_init(&out->worker_wake, NULL);
+        out->worker_standby = true;
+        out->worker_exit = false;
+        pthread_create(&out->worker_thread, NULL, out_write_worker, out);
+
+    }
+    *stream_out = &out->stream;
+
+
+error:
+
+    return ret;
+}
+
+static void adev_close_output_stream(struct audio_hw_device *dev,
+                                     struct audio_stream_out *stream)
+{
+    struct generic_stream_out *out = (struct generic_stream_out *)stream;
+    pthread_mutex_lock(&out->lock);
+    do_out_standby(out);
+
+    out->worker_exit = true;
+    pthread_cond_signal(&out->worker_wake);
+    pthread_mutex_unlock(&out->lock);
+
+    pthread_join(out->worker_thread, NULL);
+    pthread_mutex_destroy(&out->lock);
+    audio_vbuffer_destroy(&out->buffer);
+    free(stream);
+}
+
+static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
+{
+    return 0;
+}
+
+static char * adev_get_parameters(const struct audio_hw_device *dev,
+                                  const char *keys)
+{
+    return strdup("");
+}
+
+static int adev_init_check(const struct audio_hw_device *dev)
+{
+    return 0;
+}
+
+static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+    // adev_set_voice_volume is a no op (simulates phones)
+    return 0;
+}
+
+static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+    return -ENOSYS;
+}
+
+static int adev_get_master_volume(struct audio_hw_device *dev, float *volume)
+{
+    return -ENOSYS;
+}
+
+static int adev_set_master_mute(struct audio_hw_device *dev, bool muted)
+{
+    return -ENOSYS;
+}
+
+static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted)
+{
+    return -ENOSYS;
+}
+
+static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
+{
+    // adev_set_mode is a no op (simulates phones)
+    return 0;
+}
+
+static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+    pthread_mutex_lock(&adev->lock);
+    adev->mic_mute = state;
+    pthread_mutex_unlock(&adev->lock);
+    return 0;
+}
+
+static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+    pthread_mutex_lock(&adev->lock);
+    *state = adev->mic_mute;
+    pthread_mutex_unlock(&adev->lock);
+    return 0;
+}
+
+
+static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
+                                         const struct audio_config *config)
+{
+    return get_input_buffer_size(config->sample_rate, config->format, config->channel_mask);
+}
+
+
+static void adev_close_input_stream(struct audio_hw_device *dev,
+                                   struct audio_stream_in *stream)
+{
+    struct generic_stream_in *in = (struct generic_stream_in *)stream;
+    pthread_mutex_lock(&in->lock);
+    do_in_standby(in);
+
+    in->worker_exit = true;
+    pthread_cond_signal(&in->worker_wake);
+    pthread_mutex_unlock(&in->lock);
+    pthread_join(in->worker_thread, NULL);
+
+    if (in->stereo_to_mono_buf != NULL) {
+        free(in->stereo_to_mono_buf);
+        in->stereo_to_mono_buf_size = 0;
+    }
+
+    pthread_mutex_destroy(&in->lock);
+    audio_vbuffer_destroy(&in->buffer);
+    free(stream);
+}
+
+
+static int adev_open_input_stream(struct audio_hw_device *dev,
+                                  audio_io_handle_t handle,
+                                  audio_devices_t devices,
+                                  struct audio_config *config,
+                                  struct audio_stream_in **stream_in,
+                                  audio_input_flags_t flags __unused,
+                                  const char *address __unused,
+                                  audio_source_t source __unused)
+{
+    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+    struct generic_stream_in *in;
+    int ret = 0;
+    if (refine_input_parameters(&config->sample_rate, &config->format, &config->channel_mask)) {
+        ALOGE("Error opening input stream format %d, channel_mask %04x, sample_rate %u",
+              config->format, config->channel_mask, config->sample_rate);
+        ret = -EINVAL;
+        goto error;
+    }
+
+    in = (struct generic_stream_in *)calloc(1, sizeof(struct generic_stream_in));
+    if (!in) {
+        ret = -ENOMEM;
+        goto error;
+    }
+
+    in->stream.common.get_sample_rate = in_get_sample_rate;
+    in->stream.common.set_sample_rate = in_set_sample_rate;         // no op
+    in->stream.common.get_buffer_size = in_get_buffer_size;
+    in->stream.common.get_channels = in_get_channels;
+    in->stream.common.get_format = in_get_format;
+    in->stream.common.set_format = in_set_format;                   // no op
+    in->stream.common.standby = in_standby;
+    in->stream.common.dump = in_dump;
+    in->stream.common.set_parameters = in_set_parameters;
+    in->stream.common.get_parameters = in_get_parameters;
+    in->stream.common.add_audio_effect = in_add_audio_effect;       // no op
+    in->stream.common.remove_audio_effect = in_remove_audio_effect; // no op
+    in->stream.set_gain = in_set_gain;                              // no op
+    in->stream.read = in_read;
+    in->stream.get_input_frames_lost = in_get_input_frames_lost;    // no op
+    in->stream.get_capture_position = in_get_capture_position;
+    in->stream.get_active_microphones = in_get_active_microphones;
+
+    pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL);
+    in->dev = adev;
+    in->device = devices;
+    memcpy(&in->req_config, config, sizeof(struct audio_config));
+    memcpy(&in->pcm_config, &pcm_config_in, sizeof(struct pcm_config));
+    in->pcm_config.rate = config->sample_rate;
+    in->pcm_config.period_size = in->pcm_config.rate*IN_PERIOD_MS/1000;
+
+    in->stereo_to_mono_buf = NULL;
+    in->stereo_to_mono_buf_size = 0;
+
+    in->standby = true;
+    in->standby_position = 0;
+    in->standby_exit_time.tv_sec = 0;
+    in->standby_exit_time.tv_nsec = 0;
+    in->standby_frames_read = 0;
+
+    ret = audio_vbuffer_init(&in->buffer,
+                      in->pcm_config.period_size*in->pcm_config.period_count,
+                      in->pcm_config.channels *
+                      pcm_format_to_bits(in->pcm_config.format) >> 3);
+    if (ret == 0) {
+        pthread_cond_init(&in->worker_wake, NULL);
+        in->worker_standby = true;
+        in->worker_exit = false;
+        pthread_create(&in->worker_thread, NULL, in_read_worker, in);
+    }
+
+    *stream_in = &in->stream;
+
+error:
+    return ret;
+}
+
+
+static int adev_dump(const audio_hw_device_t *dev, int fd)
+{
+    return 0;
+}
+
+static int adev_get_microphones(const audio_hw_device_t *dev,
+                                struct audio_microphone_characteristic_t *mic_array,
+                                size_t *mic_count)
+{
+    if (mic_count == NULL) {
+        return -ENOSYS;
+    }
+
+    if (*mic_count == 0) {
+        *mic_count = 1;
+        return 0;
+    }
+
+    if (mic_array == NULL) {
+        return -ENOSYS;
+    }
+
+    strncpy(mic_array->device_id, "mic_goldfish", AUDIO_MICROPHONE_ID_MAX_LEN - 1);
+    mic_array->device = AUDIO_DEVICE_IN_BUILTIN_MIC;
+    strncpy(mic_array->address, AUDIO_BOTTOM_MICROPHONE_ADDRESS,
+            AUDIO_DEVICE_MAX_ADDRESS_LEN - 1);
+    memset(mic_array->channel_mapping, AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED,
+           sizeof(mic_array->channel_mapping));
+    mic_array->location = AUDIO_MICROPHONE_LOCATION_UNKNOWN;
+    mic_array->group = 0;
+    mic_array->index_in_the_group = 0;
+    mic_array->sensitivity = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
+    mic_array->max_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
+    mic_array->min_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
+    mic_array->directionality = AUDIO_MICROPHONE_DIRECTIONALITY_UNKNOWN;
+    mic_array->num_frequency_responses = 0;
+    mic_array->geometric_location.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic_array->geometric_location.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic_array->geometric_location.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic_array->orientation.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic_array->orientation.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic_array->orientation.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+
+    *mic_count = 1;
+    return 0;
+}
+
+static int adev_close(hw_device_t *dev)
+{
+    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+    int ret = 0;
+    if (!adev)
+        return 0;
+
+    pthread_mutex_lock(&adev_init_lock);
+
+    if (audio_device_ref_count == 0) {
+        ALOGE("adev_close called when ref_count 0");
+        ret = -EINVAL;
+        goto error;
+    }
+
+    if ((--audio_device_ref_count) == 0) {
+        if (adev->mixer) {
+            mixer_close(adev->mixer);
+        }
+        free(adev);
+    }
+
+error:
+    pthread_mutex_unlock(&adev_init_lock);
+    return ret;
+}
+
+static int adev_open(const hw_module_t* module, const char* name,
+                     hw_device_t** device)
+{
+    static struct generic_audio_device *adev;
+
+    if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
+        return -EINVAL;
+
+    pthread_mutex_lock(&adev_init_lock);
+    if (audio_device_ref_count != 0) {
+        *device = &adev->device.common;
+        audio_device_ref_count++;
+        ALOGV("%s: returning existing instance of adev", __func__);
+        ALOGV("%s: exit", __func__);
+        goto unlock;
+    }
+    adev = calloc(1, sizeof(struct generic_audio_device));
+
+    pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL);
+
+    adev->device.common.tag = HARDWARE_DEVICE_TAG;
+    adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
+    adev->device.common.module = (struct hw_module_t *) module;
+    adev->device.common.close = adev_close;
+
+    adev->device.init_check = adev_init_check;               // no op
+    adev->device.set_voice_volume = adev_set_voice_volume;   // no op
+    adev->device.set_master_volume = adev_set_master_volume; // no op
+    adev->device.get_master_volume = adev_get_master_volume; // no op
+    adev->device.set_master_mute = adev_set_master_mute;     // no op
+    adev->device.get_master_mute = adev_get_master_mute;     // no op
+    adev->device.set_mode = adev_set_mode;                   // no op
+    adev->device.set_mic_mute = adev_set_mic_mute;
+    adev->device.get_mic_mute = adev_get_mic_mute;
+    adev->device.set_parameters = adev_set_parameters;       // no op
+    adev->device.get_parameters = adev_get_parameters;       // no op
+    adev->device.get_input_buffer_size = adev_get_input_buffer_size;
+    adev->device.open_output_stream = adev_open_output_stream;
+    adev->device.close_output_stream = adev_close_output_stream;
+    adev->device.open_input_stream = adev_open_input_stream;
+    adev->device.close_input_stream = adev_close_input_stream;
+    adev->device.dump = adev_dump;
+    adev->device.get_microphones = adev_get_microphones;
+
+    *device = &adev->device.common;
+
+    adev->mixer = mixer_open(PCM_CARD);
+    struct mixer_ctl *ctl;
+
+    // Set default mixer ctls
+    // Enable channels and set volume
+    for (int i = 0; i < (int)mixer_get_num_ctls(adev->mixer); i++) {
+        ctl = mixer_get_ctl(adev->mixer, i);
+        ALOGD("mixer %d name %s", i, mixer_ctl_get_name(ctl));
+        if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Volume") ||
+            !strcmp(mixer_ctl_get_name(ctl), "Capture Volume")) {
+            for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) {
+                ALOGD("set ctl %d to %d", z, 100);
+                mixer_ctl_set_percent(ctl, z, 100);
+            }
+            continue;
+        }
+        if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Switch") ||
+            !strcmp(mixer_ctl_get_name(ctl), "Capture Switch")) {
+            for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) {
+                ALOGD("set ctl %d to %d", z, 1);
+                mixer_ctl_set_value(ctl, z, 1);
+            }
+            continue;
+        }
+    }
+
+    audio_device_ref_count++;
+
+unlock:
+    pthread_mutex_unlock(&adev_init_lock);
+    return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+    .open = adev_open,
+};
+
+struct 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 = "Generic audio HW HAL",
+        .author = "The Android Open Source Project",
+        .methods = &hal_module_methods,
+    },
+};
diff --git a/guest/hals/audio/Android.mk b/guest/hals/audio/legacy/Android.mk
similarity index 95%
rename from guest/hals/audio/Android.mk
rename to guest/hals/audio/legacy/Android.mk
index 3af6e0f..8d4ef0a 100644
--- a/guest/hals/audio/Android.mk
+++ b/guest/hals/audio/legacy/Android.mk
@@ -55,11 +55,11 @@
     $(VSOC_STLPORT_STATIC_LIBS)
 
 LOCAL_CFLAGS := \
-    -Wall -Werror \
+    -Wall -Werror -std=c++17 \
     $(VSOC_VERSION_CFLAGS)
 
 
-LOCAL_MODULE := audio.primary.vsoc
+LOCAL_MODULE := audio.primary.cutf_ivsh
 LOCAL_VENDOR_MODULE := true
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/audio/audio_hal.cpp b/guest/hals/audio/legacy/audio_hal.cpp
similarity index 92%
rename from guest/hals/audio/audio_hal.cpp
rename to guest/hals/audio/legacy/audio_hal.cpp
index 74180b5..8b528a6 100644
--- a/guest/hals/audio/audio_hal.cpp
+++ b/guest/hals/audio/legacy/audio_hal.cpp
@@ -13,9 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "guest/hals/audio/vsoc_audio.h"
+#include "guest/hals/audio/legacy/vsoc_audio.h"
 
-#include "guest/hals/audio/audio_hal.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
 #include "guest/libs/platform_support/api_level_fixes.h"
 
 static hw_module_methods_t hal_module_methods = {
diff --git a/guest/hals/audio/audio_hal.h b/guest/hals/audio/legacy/audio_hal.h
similarity index 100%
rename from guest/hals/audio/audio_hal.h
rename to guest/hals/audio/legacy/audio_hal.h
diff --git a/guest/hals/audio/policy/Android.mk b/guest/hals/audio/legacy/policy/Android.mk
similarity index 100%
rename from guest/hals/audio/policy/Android.mk
rename to guest/hals/audio/legacy/policy/Android.mk
diff --git a/guest/hals/audio/policy/vsoc_audio_policy_hal.cpp b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
similarity index 98%
rename from guest/hals/audio/policy/vsoc_audio_policy_hal.cpp
rename to guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
index c0ca8f9..53c3127 100644
--- a/guest/hals/audio/policy/vsoc_audio_policy_hal.cpp
+++ b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
@@ -23,7 +23,7 @@
 #include <system/audio_policy.h>
 #include <hardware/audio_policy.h>
 
-#include "guest/hals/audio/policy/vsoc_audio_policy_hal.h"
+#include "guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h"
 
 namespace cvd {
 
diff --git a/guest/hals/audio/policy/vsoc_audio_policy_hal.h b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h
similarity index 100%
rename from guest/hals/audio/policy/vsoc_audio_policy_hal.h
rename to guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h
diff --git a/guest/hals/audio/vsoc_audio.cpp b/guest/hals/audio/legacy/vsoc_audio.cpp
similarity index 84%
rename from guest/hals/audio/vsoc_audio.cpp
rename to guest/hals/audio/legacy/vsoc_audio.cpp
index 38352ff..a59c04d 100644
--- a/guest/hals/audio/vsoc_audio.cpp
+++ b/guest/hals/audio/legacy/vsoc_audio.cpp
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "guest/hals/audio/audio_hal.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
 
 #include <inttypes.h>
 #include <stdio.h>
@@ -30,9 +30,9 @@
 #include "common/libs/threads/cuttlefish_thread.h"
 #include "common/libs/threads/thunkers.h"
 #include "common/vsoc/lib/circqueue_impl.h"
-#include "guest/hals/audio/vsoc_audio.h"
-#include "guest/hals/audio/vsoc_audio_input_stream.h"
-#include "guest/hals/audio/vsoc_audio_output_stream.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/platform_support/api_level_fixes.h"
 #include "guest/libs/remoter/remoter_framework_pkt.h"
 
@@ -41,15 +41,6 @@
 
 namespace cvd {
 
-namespace {
-template <typename F> struct HWDeviceThunker :
-  ThunkerBase<hw_device_t, GceAudio, F>{};
-template <typename F> struct AudioThunker :
-  ThunkerBase<audio_hw_device, GceAudio, F>{};
-template <typename F> struct AudioThreadThunker :
-  ThunkerBase<void, GceAudio, F>{};
-}
-
 GceAudio::~GceAudio() { }
 
 int GceAudio::Close() {
@@ -299,7 +290,7 @@
   rval->common.tag = HARDWARE_DEVICE_TAG;
   rval->common.version = version_;
   rval->common.module = const_cast<hw_module_t *>(module);
-  rval->common.close = HWDeviceThunker<int()>::call<&GceAudio::Close>;
+  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.
@@ -308,51 +299,45 @@
   // Skipping the assignment is ok: the memset in the constructor already
   // put a NULL here.
   rval->get_supported_devices =
-      AudioThunker<uint32_t()>::call<&GceAudio::GetSupportedDevices>;
+      cvd::thunk<audio_hw_device, &GceAudio::GetSupportedDevices>;
 #endif
-  rval->init_check = AudioThunker<int()>::call<&GceAudio::InitCheck>;
+  rval->init_check = cvd::thunk<audio_hw_device, &GceAudio::InitCheck>;
 
   rval->set_voice_volume =
-      AudioThunker<int(float)>::call<&GceAudio::SetVoiceVolume>;
+      cvd::thunk<audio_hw_device, &GceAudio::SetVoiceVolume>;
   rval->set_master_volume =
-      AudioThunker<int(float)>::call<&GceAudio::SetMasterVolume>;
+      cvd::thunk<audio_hw_device, &GceAudio::SetMasterVolume>;
   rval->get_master_volume =
-      AudioThunker<int(float*)>::call<&GceAudio::GetMasterVolume>;
+      cvd::thunk<audio_hw_device, &GceAudio::GetMasterVolume>;
 
 #if defined(AUDIO_DEVICE_API_VERSION_2_0)
   rval->set_master_mute =
-      AudioThunker<int(bool)>::call<&GceAudio::SetMasterMute>;
+      cvd::thunk<audio_hw_device, &GceAudio::SetMasterMute>;
   rval->get_master_mute =
-      AudioThunker<int(bool*)>::call<&GceAudio::GetMasterMute>;
+      cvd::thunk<audio_hw_device, &GceAudio::GetMasterMute>;
 #endif
 
-  rval->set_mode = AudioThunker<int(audio_mode_t)>::call<&GceAudio::SetMode>;
-  rval->set_mic_mute = AudioThunker<int(bool)>::call<&GceAudio::SetMicMute>;
-  rval->get_mic_mute =
-      AudioThunker<int(bool*)>::call<&GceAudio::GetMicMute>;
+  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 =
-      AudioThunker<int(const char*)>::call<&GceAudio::SetParameters>;
-  rval->get_parameters =
-      AudioThunker<char*(const char*)>::call<&GceAudio::GetParameters>;
+  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 =
-      AudioThunker<size_t(const audio_config*)>::call<
-        &GceAudio::GetInputBufferSize>;
+      cvd::thunk<audio_hw_device, &GceAudio::GetInputBufferSize>;
 
   rval->open_input_stream =
-      AudioThunker<GceAudio::OpenInputStreamHAL_t>::call<
-        &GceAudio::OpenInputStreamCurrentHAL>;
+      cvd::thunk<audio_hw_device, &GceAudio::OpenInputStreamCurrentHAL>;
   rval->close_input_stream =
-      AudioThunker<void(audio_stream_in*)>::call<&GceAudio::CloseInputStream>;
+      cvd::thunk<audio_hw_device, &GceAudio::CloseInputStream>;
 
   rval->open_output_stream =
-      AudioThunker<GceAudio::OpenOutputStreamHAL_t>::call<
-        &GceAudio::OpenOutputStreamCurrentHAL>;
+      cvd::thunk<audio_hw_device, &GceAudio::OpenOutputStreamCurrentHAL>;
   rval->close_output_stream =
-      AudioThunker<void(audio_stream_out*)>::call<&GceAudio::CloseOutputStream>;
+      cvd::thunk<audio_hw_device, &GceAudio::CloseOutputStream>;
 
-  rval->dump = AudioThunker<int(int)>::call<&GceAudio::Dump>;
+  rval->dump = cvd::thunk<audio_hw_device, &GceAudio::Dump>;
 
   *device = &rval->common;
   return 0;
diff --git a/guest/hals/audio/vsoc_audio.h b/guest/hals/audio/legacy/vsoc_audio.h
similarity index 98%
rename from guest/hals/audio/vsoc_audio.h
rename to guest/hals/audio/legacy/vsoc_audio.h
index 6ec9eee..a5f551a 100644
--- a/guest/hals/audio/vsoc_audio.h
+++ b/guest/hals/audio/legacy/vsoc_audio.h
@@ -23,8 +23,8 @@
 #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/audio_hal.h"
-#include "guest/hals/audio/vsoc_audio_input_stream.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+#include "guest/hals/audio/legacy/vsoc_audio_input_stream.h"
 #include "guest/libs/platform_support/api_level_fixes.h"
 
 namespace cvd {
diff --git a/guest/hals/audio/vsoc_audio_input_stream.cpp b/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
similarity index 76%
rename from guest/hals/audio/vsoc_audio_input_stream.cpp
rename to guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
index 4c91237..1ffd29b 100644
--- a/guest/hals/audio/vsoc_audio_input_stream.cpp
+++ b/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
@@ -25,20 +25,13 @@
 
 #include "common/libs/auto_resources/auto_resources.h"
 #include "common/libs/threads/thunkers.h"
-#include "guest/hals/audio/audio_hal.h"
-#include "guest/hals/audio/vsoc_audio.h"
-#include "guest/hals/audio/vsoc_audio_input_stream.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"
 #include "guest/libs/platform_support/api_level_fixes.h"
 
 namespace cvd {
 
-namespace {
-template <typename F> struct Thunker :
-  ThunkerBase<audio_stream, GceAudioInputStream, F>{};
-template <typename F> struct InThunker :
-  ThunkerBase<audio_stream_in, GceAudioInputStream, F>{};
-}
-
 #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);
@@ -63,36 +56,30 @@
       gain_(0.0),
       device_(devices) {
   common.get_sample_rate =
-      Thunker<uint32_t()>::call<&GceAudioInputStream::GetSampleRate>;
+      cvd::thunk<audio_stream, &GceAudioInputStream::GetSampleRate>;
   common.set_sample_rate =
-      Thunker<int(uint32_t)>::call<&GceAudioInputStream::SetSampleRate>;
+      cvd::thunk<audio_stream, &GceAudioInputStream::SetSampleRate>;
   common.get_buffer_size =
-      Thunker<size_t()>::call<&GceAudioInputStream::GetBufferSize>;
+      cvd::thunk<audio_stream, &GceAudioInputStream::GetBufferSize>;
   common.get_channels =
-      Thunker<audio_channel_mask_t()>::call<&GceAudioInputStream::GetChannels>;
-  common.get_device =
-      Thunker<audio_devices_t()>::call<&GceAudioInputStream::GetDevice>;
-  common.set_device =
-      Thunker<int(audio_devices_t)>::call<&GceAudioInputStream::SetDevice>;
-  common.get_format =
-      Thunker<audio_format_t()>::call<&GceAudioInputStream::GetFormat>;
-  common.set_format =
-      Thunker<int(audio_format_t)>::call<&GceAudioInputStream::SetFormat>;
-  common.standby =
-      Thunker<int()>::call<&GceAudioInputStream::Standby>;
-  common.dump =
-      Thunker<int(int)>::call<&GceAudioInputStream::Dump>;
+      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 =
-      Thunker<char*(const char *)>::call<&GceAudioInputStream::GetParameters>;
+      cvd::thunk<audio_stream, &GceAudioInputStream::GetParameters>;
   common.add_audio_effect =
-      Thunker<int(effect_handle_t)>::call<&GceAudioInputStream::AddAudioEffect>;
-  common.remove_audio_effect = Thunker<int(effect_handle_t)>::call<
+      cvd::thunk<audio_stream, &GceAudioInputStream::AddAudioEffect>;
+  common.remove_audio_effect = cvd::thunk<audio_stream,
     &GceAudioInputStream::RemoveAudioEffect>;
-  set_gain = InThunker<int(float)>::call<&GceAudioInputStream::SetGain>;
-  read = InThunker<ssize_t(void*, size_t)>::call<
+  set_gain = cvd::thunk<audio_stream_in, &GceAudioInputStream::SetGain>;
+  read = cvd::thunk<audio_stream_in,
     &GceAudioInputStream::Read>;
-  get_input_frames_lost = InThunker<uint32_t()>::call<
+  get_input_frames_lost = cvd::thunk<audio_stream_in,
     &GceAudioInputStream::GetInputFramesLost>;
   frame_size_ = GceAudioFrameSize(this);
   buffer_model_.reset(
diff --git a/guest/hals/audio/vsoc_audio_input_stream.h b/guest/hals/audio/legacy/vsoc_audio_input_stream.h
similarity index 98%
rename from guest/hals/audio/vsoc_audio_input_stream.h
rename to guest/hals/audio/legacy/vsoc_audio_input_stream.h
index 40d6b19..222194c 100644
--- a/guest/hals/audio/vsoc_audio_input_stream.h
+++ b/guest/hals/audio/legacy/vsoc_audio_input_stream.h
@@ -17,9 +17,9 @@
 
 #include <memory>
 
+#include "common/libs/utils/simulated_buffer.h"
 #include "common/vsoc/lib/vsoc_audio_message.h"
-#include "guest/hals/audio/audio_hal.h"
-#include "guest/hals/audio/simulated_buffer.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
 
 namespace cvd {
 
diff --git a/guest/hals/audio/vsoc_audio_output_stream.cpp b/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
similarity index 83%
rename from guest/hals/audio/vsoc_audio_output_stream.cpp
rename to guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
index b78351e..255b6fd 100644
--- a/guest/hals/audio/vsoc_audio_output_stream.cpp
+++ b/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
@@ -26,9 +26,9 @@
 #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/audio_hal.h"
-#include "guest/hals/audio/vsoc_audio.h"
-#include "guest/hals/audio/vsoc_audio_output_stream.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/platform_support/api_level_fixes.h"
 #include "guest/libs/remoter/remoter_framework_pkt.h"
 
@@ -53,14 +53,6 @@
 const size_t GceAudioOutputStream::kOutBufferSize;
 const size_t GceAudioOutputStream::kOutLatency;
 
-namespace {
-template <typename F> struct Thunker :
-  ThunkerBase<audio_stream, GceAudioOutputStream, F>{};
-
-template <typename F> struct OutThunker :
-  ThunkerBase<audio_stream_out, GceAudioOutputStream, F>{};
-}
-
 GceAudioOutputStream::GceAudioOutputStream(GceAudio* dev) :
     audio_stream_out(),
     dev_(dev),
@@ -285,50 +277,38 @@
       0;
 #endif
   out->common.get_sample_rate =
-      Thunker<uint32_t()>::call<&GceAudioOutputStream::GetSampleRate>;
+      cvd::thunk<audio_stream, &GceAudioOutputStream::GetSampleRate>;
   out->common.set_sample_rate =
-      Thunker<int(uint32_t)>::call<&GceAudioOutputStream::SetSampleRate>;
+      cvd::thunk<audio_stream, &GceAudioOutputStream::SetSampleRate>;
   out->common.get_buffer_size =
-      Thunker<size_t()>::call<&GceAudioOutputStream::GetBufferSize>;
+      cvd::thunk<audio_stream, &GceAudioOutputStream::GetBufferSize>;
   out->common.get_channels =
-      Thunker<audio_channel_mask_t()>::call<
-        &GceAudioOutputStream::GetChannels>;
-  out->common.get_format = Thunker<audio_format_t()>::call<
-    &GceAudioOutputStream::GetFormat>;
-  out->common.set_format = Thunker<int(audio_format_t)>::call<
-    &GceAudioOutputStream::SetFormat>;
-  out->common.standby = Thunker<int()>::call<&GceAudioOutputStream::Standby>;
-  out->common.dump = Thunker<int(int)>::call<&GceAudioOutputStream::Dump>;
-  out->common.get_device = Thunker<audio_devices_t()>::call<
-    &GceAudioOutputStream::GetDevice>;
-  out->common.set_device = Thunker<int(audio_devices_t)>::call<
-    &GceAudioOutputStream::SetDevice>;
+      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 =
-      Thunker<int(const char*)>::call<
-      &GceAudioOutputStream::SetParameters>;
+      cvd::thunk<audio_stream, &GceAudioOutputStream::SetParameters>;
   out->common.get_parameters =
-      Thunker<char*(const char *)>::call<
-        &GceAudioOutputStream::GetParameters>;
+      cvd::thunk<audio_stream, &GceAudioOutputStream::GetParameters>;
   out->common.add_audio_effect =
-      Thunker<int(effect_handle_t)>::call<
-        &GceAudioOutputStream::AddAudioEffect>;
+      cvd::thunk<audio_stream, &GceAudioOutputStream::AddAudioEffect>;
   out->common.remove_audio_effect =
-      Thunker<int(effect_handle_t)>::call<
-        &GceAudioOutputStream::RemoveAudioEffect>;
+      cvd::thunk<audio_stream, &GceAudioOutputStream::RemoveAudioEffect>;
+
   out->get_latency =
-      OutThunker<uint32_t()>::call<
-        &GceAudioOutputStream::GetLatency>;
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetLatency>;
   out->set_volume =
-      OutThunker<int(float, float)>::call<&GceAudioOutputStream::SetVolume>;
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::SetVolume>;
   out->write =
-      OutThunker<ssize_t(const void*, size_t)>::call<
-        &GceAudioOutputStream::Write>;
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::Write>;
   out->get_render_position =
-      OutThunker<int(uint32_t*)>::call<
-        &GceAudioOutputStream::GetRenderPosition>;
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetRenderPosition>;
   out->get_next_write_timestamp =
-      OutThunker<int(int64_t*)>::call<
-        &GceAudioOutputStream::GetNextWriteTimestamp>;
+      cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetNextWriteTimestamp>;
   out->device_ = devices;
   out->frame_size_ = GceAudioFrameSize(out.get());
 
diff --git a/guest/hals/audio/vsoc_audio_output_stream.h b/guest/hals/audio/legacy/vsoc_audio_output_stream.h
similarity index 98%
rename from guest/hals/audio/vsoc_audio_output_stream.h
rename to guest/hals/audio/legacy/vsoc_audio_output_stream.h
index 14ea2f4..70ced6e 100644
--- a/guest/hals/audio/vsoc_audio_output_stream.h
+++ b/guest/hals/audio/legacy/vsoc_audio_output_stream.h
@@ -17,9 +17,9 @@
 
 #include <memory>
 
+#include "common/libs/utils/simulated_buffer.h"
 #include "common/vsoc/lib/vsoc_audio_message.h"
-#include "guest/hals/audio/audio_hal.h"
-#include "guest/hals/audio/simulated_buffer.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
 
 namespace cvd {
 
diff --git a/guest/hals/camera/Android.mk b/guest/hals/camera/Android.mk
index ceaded9..0b2a87f 100644
--- a/guest/hals/camera/Android.mk
+++ b/guest/hals/camera/Android.mk
@@ -124,7 +124,7 @@
 	$(if $(enable_emulated_camera2),$(emulated_camera2_src),) \
 	$(if $(enable_emulated_camera3),$(emulated_camera3_src),)
 
-LOCAL_MODULE := camera.vsoc
+LOCAL_MODULE := camera.cutf
 LOCAL_MODULE_TAGS := optional
 LOCAL_VENDOR_MODULE := true
 
@@ -175,7 +175,7 @@
 LOCAL_C_INCLUDES += ${jpeg_c_includes}
 LOCAL_SRC_FILES := ${jpeg_src}
 
-LOCAL_MODULE := camera.vsoc.jpeg
+LOCAL_MODULE := camera.cutf.jpeg
 LOCAL_MODULE_TAGS := optional
 LOCAL_VENDOR_MODULE := true
 
diff --git a/guest/hals/camera/JpegCompressor.cpp b/guest/hals/camera/JpegCompressor.cpp
index 2dae838..99164c9 100644
--- a/guest/hals/camera/JpegCompressor.cpp
+++ b/guest/hals/camera/JpegCompressor.cpp
@@ -46,10 +46,10 @@
 
 NV21JpegCompressor::NV21JpegCompressor() {
   if (mDl == NULL) {
-    mDl = dlopen("/vendor/lib/hw/camera.vsoc.jpeg.so", RTLD_NOW);
+    mDl = dlopen("/vendor/lib/hw/camera.cutf.jpeg.so", RTLD_NOW);
   }
   if (mDl == NULL) {
-    mDl = dlopen("/system/lib/hw/camera.vsoc.jpeg.so", RTLD_NOW);
+    mDl = dlopen("/system/lib/hw/camera.cutf.jpeg.so", RTLD_NOW);
   }
   assert(mDl != NULL);
 
diff --git a/guest/hals/camera/media_codecs.xml b/guest/hals/camera/media_codecs.xml
deleted file mode 100644
index 87d11f2..0000000
--- a/guest/hals/camera/media_codecs.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!-- Copyright (C) 2012 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.
--->
-
-<!--
-<!DOCTYPE MediaCodecs [
-<!ELEMENT Include EMPTY>
-<!ATTLIST Include href CDATA #REQUIRED>
-<!ELEMENT MediaCodecs (Decoders|Encoders|Include)*>
-<!ELEMENT Decoders (MediaCodec|Include)*>
-<!ELEMENT Encoders (MediaCodec|Include)*>
-<!ELEMENT MediaCodec (Type|Quirk|Include)*>
-<!ATTLIST MediaCodec name CDATA #REQUIRED>
-<!ATTLIST MediaCodec type CDATA>
-<!ELEMENT Type EMPTY>
-<!ATTLIST Type name CDATA #REQUIRED>
-<!ELEMENT Quirk EMPTY>
-<!ATTLIST Quirk name CDATA #REQUIRED>
-]>
-
-There's a simple and a complex syntax to declare the availability of a
-media codec:
-
-A codec that properly follows the OpenMax spec and therefore doesn't have any
-quirks and that only supports a single content type can be declared like so:
-
-    <MediaCodec name="OMX.foo.bar" type="something/interesting" />
-
-If a codec has quirks OR supports multiple content types, the following syntax
-can be used:
-
-    <MediaCodec name="OMX.foo.bar" >
-        <Type name="something/interesting" />
-        <Type name="something/else" />
-        ...
-        <Quirk name="requires-allocate-on-input-ports" />
-        <Quirk name="requires-allocate-on-output-ports" />
-        <Quirk name="output-buffers-are-unreadable" />
-    </MediaCodec>
-
-Only the three quirks included above are recognized at this point:
-
-"requires-allocate-on-input-ports"
-    must be advertised if the component does not properly support specification
-    of input buffers using the OMX_UseBuffer(...) API but instead requires
-    OMX_AllocateBuffer to be used.
-
-"requires-allocate-on-output-ports"
-    must be advertised if the component does not properly support specification
-    of output buffers using the OMX_UseBuffer(...) API but instead requires
-    OMX_AllocateBuffer to be used.
-
-"output-buffers-are-unreadable"
-    must be advertised if the emitted output buffers of a decoder component
-    are not readable, i.e. use a custom format even though abusing one of
-    the official OMX colorspace constants.
-    Clients of such decoders will not be able to access the decoded data,
-    naturally making the component much less useful. The only use for
-    a component with this quirk is to render the output to the screen.
-    Audio decoders MUST NOT advertise this quirk.
-    Video decoders that advertise this quirk must be accompanied by a
-    corresponding color space converter for thumbnail extraction,
-    matching surfaceflinger support that can render the custom format to
-    a texture and possibly other code, so just DON'T USE THIS QUIRK.
-
--->
-
-<MediaCodecs>
-    <Include href="media_codecs_google_audio.xml" />
-    <Include href="media_codecs_google_telephony.xml" />
-    <Include href="media_codecs_google_video.xml" />
-</MediaCodecs>
diff --git a/guest/hals/camera/media_codecs_performance.xml b/guest/hals/camera/media_codecs_performance.xml
deleted file mode 100644
index f3eeb12..0000000
--- a/guest/hals/camera/media_codecs_performance.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!-- Copyright 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.
--->
-
-<!-- The 'range' values below are based on tests run 2016-04-13 on Ubuntu 14.04
-     x86-64, Xeon 2.8 GHz x 10
-
-     The range values are set to (meas / sqrt(tolerance)) and
-     (meas * sqrt(tolerance)).
-     These values maximize the 'success' window for the tests performed in
-     cts/libs/deviceutil/src/android/cts/util/MediaUtils.java.
-     That file defines 'tolerance' as sqrt(12.1).
-
-     Where multiple results were obtained, the geometric mean was used.
-
-        OMX.google.h264.encoder  video/avc            320x 240 measured 1294.2
-        OMX.google.h264.decoder  video/avc            320x 240 measured 7204.1, 9151.4
-
-        OMX.google.h263.encoder  video/3gpp           176x 144 measured 2127.0
-        OMX.google.h263.decoder  video/3gpp           176x 144 measured 7574.0
-
-        OMX.google.mpeg4.encoder video/mp4v-es        176x 144 measured 2783.8
-        OMX.google.mpeg4.decoder video/mp4v-es        176x 144 measured 6954.2
-
-        OMX.google.vp8.encoder   video/x-vnd.on2.vp8 1280x 720 measured  195.0
-        OMX.google.vp8.encoder   video/x-vnd.on2.vp8 1920x1080 measured   93.3,   91.1
-        OMX.google.vp8.decoder   video/x-vnd.on2.vp8 1280x 720 measured 1196.1, 1211.3
-        OMX.google.vp8.decoder   video/x-vnd.on2.vp8 1920x1080 measured  483.7,  497.6
--->
-
-<MediaCodecs>
-    <Encoders>
-        <MediaCodec name="OMX.google.h264.encoder" type="video/avc" update="true">
-            <Limit name="measured-frame-rate-320x240" range="694-2414" />
-        </MediaCodec>
-        <MediaCodec name="OMX.google.h263.encoder" type="video/3gpp" update="true">
-            <Limit name="measured-frame-rate-176x144" range="1140-3967" />
-        </MediaCodec>
-        <MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es" update="true">
-            <Limit name="measured-frame-rate-176x144" range="1493-5192" />
-        </MediaCodec>
-        <MediaCodec name="OMX.google.vp8.encoder" type="video/x-vnd.on2.vp8" update="true">
-            <Limit name="measured-frame-rate-1280x720" range="105-364" />
-            <Limit name="measured-frame-rate-1920x1080" range="49-172" />
-        </MediaCodec>
-    </Encoders>
-    <Decoders>
-        <MediaCodec name="OMX.google.h264.decoder" type="video/avc" update="true">
-            <Limit name="measured-frame-rate-320x240" range="4353-15114" />
-        </MediaCodec>
-        <MediaCodec name="OMX.google.h263.decoder" type="video/3gpp" update="true">
-            <Limit name="measured-frame-rate-176x144" range="4061-14126" />
-        </MediaCodec>
-        <MediaCodec name="OMX.google.hevc.decoder" type="video/hevc" update="true">
-          <Limit name="measured-frame-rate-352x288" range="1000-4000" />
-          <Limit name="measured-frame-rate-720x480" range="500-2000" />
-          <Limit name="measured-frame-rate-1280x720" range="100-1000" />
-          <Limit name="measured-frame-rate-1920x1080" range="50-700" />
-        </MediaCodec>
-        <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es" update="true">
-            <Limit name="measured-frame-rate-176x144" range="3729-12970" />
-        </MediaCodec>
-        <MediaCodec name="OMX.google.vp8.decoder" type="video/x-vnd.on2.vp8" update="true">
-            <Limit name="measured-frame-rate-1280x720" range="645-2245" />
-            <Limit name="measured-frame-rate-1920x1080" range="263-915" />
-        </MediaCodec>
-        <MediaCodec name="OMX.google.vp9.decoder" type="video/x-vnd.on2.vp9" update="true">
-            <Limit name="measured-frame-rate-320x240" range="645-2245" />
-            <Limit name="measured-frame-rate-640x360" range="500-3000" />
-            <Limit name="measured-frame-rate-1280x720" range="350-1500" />
-            <Limit name="measured-frame-rate-1920x1080" range="150-1000" />
-        </MediaCodec>
-    </Decoders>
-</MediaCodecs>
diff --git a/guest/hals/camera/media_profiles.xml b/guest/hals/camera/media_profiles.xml
deleted file mode 100644
index cd99857..0000000
--- a/guest/hals/camera/media_profiles.xml
+++ /dev/null
@@ -1,368 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-<!DOCTYPE MediaSettings [
-<!ELEMENT MediaSettings (CamcorderProfiles,
-                         EncoderOutputFileFormat+,
-                         VideoEncoderCap+,
-                         AudioEncoderCap+,
-                         VideoDecoderCap,
-                         AudioDecoderCap)>
-<!ELEMENT CamcorderProfiles (EncoderProfile+, ImageEncoding+, ImageDecoding, Camera)>
-<!ELEMENT EncoderProfile (Video, Audio)>
-<!ATTLIST EncoderProfile quality (high|low) #REQUIRED>
-<!ATTLIST EncoderProfile fileFormat (mp4|3gp) #REQUIRED>
-<!ATTLIST EncoderProfile duration (30|60) #REQUIRED>
-<!ATTLIST EncoderProfile cameraId (0|1) #REQUIRED>
-<!ELEMENT Video EMPTY>
-<!ATTLIST Video codec (h264|h263|m4v) #REQUIRED>
-<!ATTLIST Video bitRate CDATA #REQUIRED>
-<!ATTLIST Video width CDATA #REQUIRED>
-<!ATTLIST Video height CDATA #REQUIRED>
-<!ATTLIST Video frameRate CDATA #REQUIRED>
-<!ELEMENT Audio EMPTY>
-<!ATTLIST Audio codec (amrnb|amrwb|aac) #REQUIRED>
-<!ATTLIST Audio bitRate CDATA #REQUIRED>
-<!ATTLIST Audio sampleRate CDATA #REQUIRED>
-<!ATTLIST Audio channels (1|2) #REQUIRED>
-<!ELEMENT ImageEncoding EMPTY>
-<!ATTLIST ImageEncoding quality (90|80|70|60|50|40) #REQUIRED>
-<!ELEMENT ImageDecoding EMPTY>
-<!ATTLIST ImageDecoding memCap CDATA #REQUIRED>
-<!ELEMENT Camera EMPTY>
-<!ELEMENT EncoderOutputFileFormat EMPTY>
-<!ATTLIST EncoderOutputFileFormat name (mp4|3gp) #REQUIRED>
-<!ELEMENT VideoEncoderCap EMPTY>
-<!ATTLIST VideoEncoderCap name (h264|h263|m4v|wmv) #REQUIRED>
-<!ATTLIST VideoEncoderCap enabled (true|false) #REQUIRED>
-<!ATTLIST VideoEncoderCap minBitRate CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap maxBitRate CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap minFrameWidth CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap maxFrameWidth CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap minFrameHeight CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap maxFrameHeight CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap minFrameRate CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap maxFrameRate CDATA #REQUIRED>
-<!ELEMENT AudioEncoderCap EMPTY>
-<!ATTLIST AudioEncoderCap name (amrnb|amrwb|aac|wma) #REQUIRED>
-<!ATTLIST AudioEncoderCap enabled (true|false) #REQUIRED>
-<!ATTLIST AudioEncoderCap minBitRate CDATA #REQUIRED>
-<!ATTLIST AudioEncoderCap maxBitRate CDATA #REQUIRED>
-<!ATTLIST AudioEncoderCap minSampleRate CDATA #REQUIRED>
-<!ATTLIST AudioEncoderCap maxSampleRate CDATA #REQUIRED>
-<!ATTLIST AudioEncoderCap minChannels (1|2) #REQUIRED>
-<!ATTLIST AudioEncoderCap maxChannels (1|2) #REQUIRED>
-<!ELEMENT VideoDecoderCap EMPTY>
-<!ATTLIST VideoDecoderCap name (wmv) #REQUIRED>
-<!ATTLIST VideoDecoderCap enabled (true|false) #REQUIRED>
-<!ELEMENT AudioDecoderCap EMPTY>
-<!ATTLIST AudioDecoderCap name (wma) #REQUIRED>
-<!ATTLIST AudioDecoderCap enabled (true|false) #REQUIRED>
-]>
-<!--
-     This file is used to declare the multimedia profiles and capabilities
-     on an android-powered device.
--->
-<MediaSettings>
-    <!-- Each camcorder profile defines a set of predefined configuration parameters -->
-    <CamcorderProfiles cameraId="0">
-
-        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
-            <Video codec="m4v"
-                   bitRate="128000"
-                   width="320"
-                   height="240"
-                   frameRate="15" />
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
-            <Video codec="h264"
-                   bitRate="192000"
-                   width="176"
-                   height="144"
-                   frameRate="30" />
-            <!-- audio setting is ignored -->
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <ImageEncoding quality="95" />
-        <ImageEncoding quality="80" />
-        <ImageEncoding quality="70" />
-        <ImageDecoding memCap="20000000" />
-
-    </CamcorderProfiles>
-
-    <CamcorderProfiles cameraId="1">
-
-        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
-            <Video codec="m4v"
-                   bitRate="128000"
-                   width="320"
-                   height="240"
-                   frameRate="15" />
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
-            <Video codec="h264"
-                   bitRate="192000"
-                   width="176"
-                   height="144"
-                   frameRate="30" />
-            <!-- audio setting is ignored -->
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <ImageEncoding quality="95" />
-        <ImageEncoding quality="80" />
-        <ImageEncoding quality="70" />
-        <ImageDecoding memCap="20000000" />
-
-    </CamcorderProfiles>
-
-    <CamcorderProfiles cameraId="2">
-
-        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
-            <Video codec="m4v"
-                   bitRate="128000"
-                   width="320"
-                   height="240"
-                   frameRate="15" />
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
-            <Video codec="h264"
-                   bitRate="192000"
-                   width="176"
-                   height="144"
-                   frameRate="30" />
-            <!-- audio setting is ignored -->
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <ImageEncoding quality="95" />
-        <ImageEncoding quality="80" />
-        <ImageEncoding quality="70" />
-        <ImageDecoding memCap="20000000" />
-
-    </CamcorderProfiles>
-
-    <CamcorderProfiles cameraId="3">
-
-        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
-            <Video codec="m4v"
-                   bitRate="128000"
-                   width="320"
-                   height="240"
-                   frameRate="15" />
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
-            <Video codec="h264"
-                   bitRate="192000"
-                   width="176"
-                   height="144"
-                   frameRate="30" />
-            <!-- audio setting is ignored -->
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <ImageEncoding quality="95" />
-        <ImageEncoding quality="80" />
-        <ImageEncoding quality="70" />
-        <ImageDecoding memCap="20000000" />
-
-    </CamcorderProfiles>
-
-    <CamcorderProfiles cameraId="4">
-
-        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
-            <Video codec="m4v"
-                   bitRate="128000"
-                   width="320"
-                   height="240"
-                   frameRate="15" />
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
-            <Video codec="h264"
-                   bitRate="192000"
-                   width="176"
-                   height="144"
-                   frameRate="30" />
-            <!-- audio setting is ignored -->
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <ImageEncoding quality="95" />
-        <ImageEncoding quality="80" />
-        <ImageEncoding quality="70" />
-        <ImageDecoding memCap="20000000" />
-
-    </CamcorderProfiles>
-
-    <CamcorderProfiles cameraId="5">
-
-        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
-            <Video codec="m4v"
-                   bitRate="128000"
-                   width="320"
-                   height="240"
-                   frameRate="15" />
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
-            <Video codec="h264"
-                   bitRate="192000"
-                   width="176"
-                   height="144"
-                   frameRate="30" />
-            <!-- audio setting is ignored -->
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <ImageEncoding quality="95" />
-        <ImageEncoding quality="80" />
-        <ImageEncoding quality="70" />
-        <ImageDecoding memCap="20000000" />
-
-    </CamcorderProfiles>
-
-    <CamcorderProfiles cameraId="6">
-
-        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
-            <Video codec="m4v"
-                   bitRate="128000"
-                   width="320"
-                   height="240"
-                   frameRate="15" />
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
-            <Video codec="h264"
-                   bitRate="192000"
-                   width="176"
-                   height="144"
-                   frameRate="30" />
-            <!-- audio setting is ignored -->
-            <Audio codec="amrnb"
-                   bitRate="12200"
-                   sampleRate="8000"
-                   channels="1" />
-        </EncoderProfile>
-
-        <ImageEncoding quality="95" />
-        <ImageEncoding quality="80" />
-        <ImageEncoding quality="70" />
-        <ImageDecoding memCap="20000000" />
-
-    </CamcorderProfiles>
-
-    <EncoderOutputFileFormat name="3gp" />
-    <EncoderOutputFileFormat name="mp4" />
-
-    <!--
-         If a codec is not enabled, it is invisible to the applications
-         In other words, the applications won't be able to use the codec
-         or query the capabilities of the codec at all if it is disabled
-    -->
-    <VideoEncoderCap name="h264" enabled="true"
-        minBitRate="64000" maxBitRate="192000"
-        minFrameWidth="176" maxFrameWidth="320"
-        minFrameHeight="144" maxFrameHeight="240"
-        minFrameRate="15" maxFrameRate="30" />
-
-    <VideoEncoderCap name="h263" enabled="true"
-        minBitRate="64000" maxBitRate="192000"
-        minFrameWidth="176" maxFrameWidth="320"
-        minFrameHeight="144" maxFrameHeight="240"
-        minFrameRate="15" maxFrameRate="30" />
-
-    <VideoEncoderCap name="m4v" enabled="true"
-        minBitRate="64000" maxBitRate="192000"
-        minFrameWidth="176" maxFrameWidth="320"
-        minFrameHeight="144" maxFrameHeight="240"
-        minFrameRate="15" maxFrameRate="30" />
-
-    <AudioEncoderCap name="aac" enabled="true"
-        minBitRate="8000" maxBitRate="96000"
-        minSampleRate="8000" maxSampleRate="48000"
-        minChannels="1" maxChannels="1" />
-
-    <AudioEncoderCap name="amrwb" enabled="true"
-        minBitRate="6600" maxBitRate="23050"
-        minSampleRate="16000" maxSampleRate="16000"
-        minChannels="1" maxChannels="1" />
-
-    <AudioEncoderCap name="amrnb" enabled="true"
-        minBitRate="5525" maxBitRate="12200"
-        minSampleRate="8000" maxSampleRate="8000"
-        minChannels="1" maxChannels="1" />
-
-    <!--
-        FIXME:
-        We do not check decoder capabilities at present
-        At present, we only check whether windows media is visible
-        for TEST applications. For other applications, we do
-        not perform any checks at all.
-    -->
-    <VideoDecoderCap name="wmv" enabled="false"/>
-    <AudioDecoderCap name="wma" enabled="false"/>
-</MediaSettings>
diff --git a/guest/hals/gatekeeper/Android.mk b/guest/hals/gatekeeper/Android.mk
index 03f684a..85d7346 100644
--- a/guest/hals/gatekeeper/Android.mk
+++ b/guest/hals/gatekeeper/Android.mk
@@ -24,7 +24,7 @@
 endif
 
 LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE := gatekeeper.vsoc
+LOCAL_MODULE := gatekeeper.cutf
 LOCAL_MODULE_RELATIVE_PATH := hw
 
 LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
diff --git a/guest/hals/gps/Android.mk b/guest/hals/gps/Android.mk
index 1829c2f..9443980 100644
--- a/guest/hals/gps/Android.mk
+++ b/guest/hals/gps/Android.mk
@@ -28,7 +28,7 @@
 
 LOCAL_SHARED_LIBRARIES := liblog libcutils
 LOCAL_SRC_FILES := gps_vsoc.cpp gps_thread.cpp
-LOCAL_MODULE := gps.vsoc
+LOCAL_MODULE := gps.cutf
 LOCAL_C_INCLUDES := device/google/cuttlefish_common
 
 LOCAL_HEADER_LIBRARIES := \
diff --git a/guest/hals/gralloc/Android.mk b/guest/hals/gralloc/Android.mk
index 5fe2d54..68ffffa 100644
--- a/guest/hals/gralloc/Android.mk
+++ b/guest/hals/gralloc/Android.mk
@@ -16,7 +16,7 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := gralloc.vsoc-future
+LOCAL_MODULE := gralloc.cutf_ivsh-future
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
diff --git a/guest/hals/gralloc/legacy/Android.mk b/guest/hals/gralloc/legacy/Android.mk
index fe01635..57dae50 100644
--- a/guest/hals/gralloc/legacy/Android.mk
+++ b/guest/hals/gralloc/legacy/Android.mk
@@ -29,7 +29,7 @@
     $(VSOC_VERSION_CFLAGS)
 
 include $(CLEAR_VARS)
-LOCAL_MODULE := gralloc.vsoc
+LOCAL_MODULE := gralloc.cutf
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_MODULE_TAGS := optional
 
diff --git a/guest/hals/health/storage/Android.bp b/guest/hals/health/storage/Android.bp
index e81fea0..5c62bdf 100644
--- a/guest/hals/health/storage/Android.bp
+++ b/guest/hals/health/storage/Android.bp
@@ -17,7 +17,7 @@
 cc_binary {
     name: "android.hardware.health.storage@1.0-service.cuttlefish",
     vendor: true,
-    defaults: ["hidl_defaults"],
+    defaults: ["hidl_defaults", "cuttlefish_health_storage"],
     relative_install_path: "hw",
     init_rc: ["android.hardware.health.storage@1.0-service.cuttlefish.rc"],
     srcs: [
@@ -37,8 +37,4 @@
         "libhidltransport",
         "libutils",
     ],
-
-    vintf_fragments: [
-        "manifest_android.hardware.health.storage@1.0.cuttlefish.xml",
-    ],
 }
diff --git a/guest/hals/hwcomposer/common/Android.bp b/guest/hals/hwcomposer/common/Android.bp
new file mode 100644
index 0000000..5e16041
--- /dev/null
+++ b/guest/hals/hwcomposer/common/Android.bp
@@ -0,0 +1,22 @@
+// 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_static {
+    name: "hwcomposer_common",
+    defaults: ["cuttlefish_guest_only"],
+    vendor: true,
+    srcs: ["hwcomposer.cpp"],
+    shared_libs: ["libhardware", "liblog"],
+}
diff --git a/guest/hals/hwcomposer/common/hwcomposer.cpp b/guest/hals/hwcomposer/common/hwcomposer.cpp
new file mode 100644
index 0000000..f18534b
--- /dev/null
+++ b/guest/hals/hwcomposer/common/hwcomposer.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.cf_x86"
+#define HWC_REMOVE_DEPRECATED_VERSIONS 1
+
+#include "hwcomposer.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <string>
+
+#include <log/log.h>
+
+namespace cvd {
+
+void* hwc_vsync_thread(void* data) {
+  struct hwc_composer_device_data_t* pdev =
+      (struct hwc_composer_device_data_t*)data;
+  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 timestamp.
+    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;
+}
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/guest/hals/hwcomposer/common/hwcomposer.h
similarity index 67%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to guest/hals/hwcomposer/common/hwcomposer.h
index b6a037b..8c57e71 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/guest/hals/hwcomposer/common/hwcomposer.h
@@ -1,6 +1,6 @@
 #pragma once
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -15,10 +15,15 @@
  * limitations under the License.
  */
 
-#include "hwcomposer_common.h"
+#include <hardware/hwcomposer.h>
 
 namespace cvd {
+  struct hwc_composer_device_data_t {
+    const hwc_procs_t* procs;
+    pthread_t vsync_thread;
+    int64_t vsync_base_timestamp;
+    int32_t vsync_period_ns;
+  };
 
-bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
-
+  void* hwc_vsync_thread(void* data);
 }  // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/Android.bp b/guest/hals/hwcomposer/cutf_cvm/Android.bp
new file mode 100644
index 0000000..9e3962b
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/Android.bp
@@ -0,0 +1,44 @@
+// 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_cvm",
+    relative_install_path: "hw",
+    defaults: ["cuttlefish_guest_only"],
+    vendor: true,
+    srcs: [
+        "hwcomposer.cpp",
+        "geometry_utils.cpp",
+        "vsoc_composer.cpp",
+        "base_composer.cpp",
+    ],
+    include_dirs: [
+        "device/google/cuttlefish_common",
+    ],
+    export_include_dirs: ["."],
+    static_libs: ["libyuv_static", "hwcomposer_common"],
+    shared_libs: [
+        "liblog",
+        "libhardware",
+        "libbase",
+        "libcutils",
+        "libutils",
+        "libsync",
+        "libhardware",
+        "libjpeg",
+        "libcuttlefish_utils",
+        "libcuttlefish_fs",
+    ],
+}
diff --git a/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp b/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp
new file mode 100644
index 0000000..23be7e1
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base_composer.h"
+
+#include <string.h>
+
+#include <cutils/properties.h>
+#include <hardware/gralloc.h>
+#include <log/log.h>
+
+#include <common/libs/utils/size_utils.h>
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+
+namespace cvd {
+
+BaseComposer::BaseComposer(int64_t vsync_base_timestamp)
+    : vsync_base_timestamp_(vsync_base_timestamp) {
+  vsync_period_ns_ = 1000000000 / frame_buffer_.refresh_rate();
+  hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+                reinterpret_cast<const hw_module_t**>(&gralloc_module_));
+}
+
+BaseComposer::~BaseComposer() {}
+
+void BaseComposer::Dump(char* buff __unused, int buff_len __unused) {}
+
+int BaseComposer::PostFrameBufferTarget(buffer_handle_t buffer_handle) {
+  int fb_index = frame_buffer_.NextScreenBuffer();
+  void* frame_buffer = frame_buffer_.GetBuffer(fb_index);
+  const private_handle_t* p_handle =
+      reinterpret_cast<const private_handle_t*>(buffer_handle);
+  void* buffer;
+  int retval = gralloc_module_->lock(gralloc_module_, buffer_handle,
+                                     GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
+                                     p_handle->x_res, p_handle->y_res, &buffer);
+  if (retval != 0) {
+    ALOGE("Got error code %d from lock function", retval);
+    return -1;
+  }
+  memcpy(frame_buffer, buffer, frame_buffer_.buffer_size());
+  frame_buffer_.Broadcast(fb_index);
+  return 0;
+}  // namespace cvd
+
+int BaseComposer::PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+  // find unsupported overlays
+  for (size_t i = 0; i < num_layers; i++) {
+    if (IS_TARGET_FRAMEBUFFER(layers[i].compositionType)) {
+      continue;
+    }
+    layers[i].compositionType = HWC_FRAMEBUFFER;
+  }
+  return 0;
+}
+
+int BaseComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+  for (size_t idx = 0; idx < num_layers; idx++) {
+    if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+      return PostFrameBufferTarget(layers[idx].handle);
+    }
+  }
+  return -1;
+}
+
+FrameBuffer::FrameBuffer()
+    : screen_server_(cvd::SharedFD::VsockClient(
+          2, property_get_int32("ro.boot.vsock_frames_port", 5580),
+          SOCK_STREAM)),
+      broadcast_thread_([this]() { BroadcastLoop(); }) {
+  if (screen_server_->IsOpen()) {
+    // TODO(b/128842613): Get this info from the configuration server
+    int32_t screen_params[4];
+    auto res = screen_server_->Read(screen_params, sizeof(screen_params));
+    if (res == sizeof(screen_params)) {
+      x_res_ = screen_params[0];
+      y_res_ = screen_params[1];
+      dpi_ = screen_params[2];
+      refresh_rate_ = screen_params[3];
+    } else {
+      LOG(ERROR) << "Unable to get screen configuration parameters from screen "
+                 << "server (" << res << "): " << screen_server_->StrError();
+    }
+  } else {
+    LOG(ERROR) << "Unable to connect to screen server: "
+               << screen_server_->StrError();
+  }
+  // This needs to happen no matter what, otherwise there won't be a buffer for
+  // the set calls to compose on.
+  inner_buffer_ = std::vector<char>(FrameBuffer::buffer_size() * 8);
+}
+
+FrameBuffer::~FrameBuffer() {
+  running_ = false;
+  broadcast_thread_.join();
+}
+
+int FrameBuffer::NextScreenBuffer() {
+  int num_buffers = inner_buffer_.size() / buffer_size();
+  last_frame_buffer_ =
+      num_buffers > 0 ? (last_frame_buffer_ + 1) % num_buffers : -1;
+  return last_frame_buffer_;
+}
+
+void FrameBuffer::BroadcastLoop() {
+  if (!screen_server_->IsOpen()) {
+    LOG(ERROR) << "Broadcaster thread exiting due to no connection to screen"
+               << " server. Compositions will occur, but frames won't be sent"
+               << " anywhere";
+    return;
+  }
+  int32_t current_seq = 0;
+  int32_t current_offset;
+  while (running_) {
+    {
+      std::unique_lock<std::mutex> lock(mutex_);
+      while (current_seq == current_seq_) {
+        cond_var_.wait(lock);
+      }
+      current_offset = current_offset_;
+      current_seq = current_seq_;
+    }
+    int32_t size = buffer_size();
+    screen_server_->Write(&size, sizeof(size));
+    auto buff = static_cast<char*>(GetBuffer(current_offset));
+    while (size > 0) {
+      auto written = screen_server_->Write(buff, size);
+      size -= written;
+      buff += written;
+    }
+  }
+}
+
+void FrameBuffer::Broadcast(int32_t offset) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  current_offset_ = offset;
+  current_seq_++;
+  cond_var_.notify_all();
+}
+void* FrameBuffer::GetBuffer(int fb_index) {
+  return &inner_buffer_[buffer_size() * fb_index];
+}
+size_t FrameBuffer::buffer_size() {
+  return (line_length() * y_res()) + 4;
+}
+int32_t FrameBuffer::x_res() { return x_res_; }
+int32_t FrameBuffer::y_res() { return y_res_; }
+int32_t FrameBuffer::line_length() {
+  return cvd::AlignToPowerOf2(x_res() * bytes_per_pixel(), 4);
+}
+int32_t FrameBuffer::bytes_per_pixel() { return 4; }
+int32_t FrameBuffer::dpi() { return dpi_; }
+int32_t FrameBuffer::refresh_rate() { return refresh_rate_; }
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/base_composer.h b/guest/hals/hwcomposer/cutf_cvm/base_composer.h
new file mode 100644
index 0000000..5a23176
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/base_composer.h
@@ -0,0 +1,98 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <hardware/gralloc.h>
+#include <common/libs/fs/shared_fd.h>
+#include "hwcomposer.h"
+
+namespace cvd {
+
+class FrameBuffer{
+ public:
+  FrameBuffer();
+  ~FrameBuffer();
+
+  void Broadcast(int32_t offset);
+  int NextScreenBuffer();
+  void* GetBuffer(int fb_index);
+  size_t buffer_size();
+  int32_t x_res();
+  int32_t y_res();
+  int32_t line_length();
+  int32_t bytes_per_pixel();
+  int32_t dpi();
+  int32_t refresh_rate();
+ private:
+  void BroadcastLoop();
+
+  std::vector<char> inner_buffer_;
+  int last_frame_buffer_ = 0;
+  cvd::SharedFD screen_server_;
+  std::thread broadcast_thread_;
+  int32_t current_offset_ = 0;
+  int32_t current_seq_ = 0;
+  std::mutex mutex_;
+  std::condition_variable cond_var_;
+  bool running_ = true;
+  int32_t x_res_{720};
+  int32_t y_res_{1280};
+  int32_t dpi_{160};
+  int32_t refresh_rate_{60};
+};
+
+class BaseComposer {
+ public:
+  BaseComposer(int64_t vsync_base_timestamp);
+  ~BaseComposer();
+
+  // Sets the composition type of each layer and returns the number of layers
+  // to be composited by the hwcomposer.
+  int PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers);
+  // Returns 0 if successful.
+  int SetLayers(size_t num_layers, vsoc_hwc_layer* layers);
+  void Dump(char* buff, int buff_len);
+
+  int32_t x_res() {
+    return frame_buffer_.x_res();
+  }
+  int32_t y_res() {
+    return frame_buffer_.y_res();
+  }
+  int32_t dpi() {
+    return frame_buffer_.dpi();
+  }
+  int32_t refresh_rate() {
+    return frame_buffer_.refresh_rate();
+  }
+
+ protected:
+  const gralloc_module_t* gralloc_module_;
+  int64_t vsync_base_timestamp_;
+  int32_t vsync_period_ns_;
+  FrameBuffer frame_buffer_;
+
+ private:
+  // Returns buffer offset or negative on error.
+  int PostFrameBufferTarget(buffer_handle_t handle);
+};
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.cpp b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp
similarity index 100%
copy from guest/hals/hwcomposer/legacy/geometry_utils.cpp
copy to guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
similarity index 95%
rename from guest/hals/hwcomposer/legacy/geometry_utils.h
rename to guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
index b6a037b..937283f 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
 
 namespace cvd {
 
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.cpp b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
similarity index 81%
copy from guest/hals/hwcomposer/legacy/hwcomposer.cpp
copy to guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
index 9711656..3db7f80 100644
--- a/guest/hals/hwcomposer/legacy/hwcomposer.cpp
+++ b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
@@ -53,36 +53,26 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
-#include "common/vsoc/lib/screen_region_view.h"
 #include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+#include "guest/hals/hwcomposer/common/hwcomposer.h"
 #include <sync/sync.h>
 
 #include "base_composer.h"
 #include "geometry_utils.h"
-#include "hwcomposer_common.h"
-#include "stats_keeper.h"
+#include "hwcomposer.h"
 #include "vsoc_composer.h"
 
-using vsoc::screen::ScreenRegionView;
-
 #ifdef USE_OLD_HWCOMPOSER
 typedef cvd::BaseComposer InnerComposerType;
 #else
 typedef cvd::VSoCComposer InnerComposerType;
 #endif
 
-#ifdef GATHER_STATS
-typedef cvd::StatsKeepingComposer<InnerComposerType> ComposerType;
-#else
 typedef InnerComposerType ComposerType;
-#endif
 
 struct vsoc_hwc_composer_device_1_t {
   vsoc_hwc_device base;
-  const hwc_procs_t* procs;
-  pthread_t vsync_thread;
-  int64_t vsync_base_timestamp;
-  int32_t vsync_period_ns;
+  cvd::hwc_composer_device_data_t vsync_data;
   ComposerType* composer;
 };
 
@@ -309,7 +299,7 @@
                                     const hwc_procs_t* procs) {
   struct vsoc_hwc_composer_device_1_t* pdev =
       (struct vsoc_hwc_composer_device_1_t*)dev;
-  pdev->procs = procs;
+  pdev->vsync_data.procs = procs;
 }
 
 static int vsoc_hwc_query(vsoc_hwc_device* dev, int what, int* value) {
@@ -322,7 +312,7 @@
       value[0] = 0;
       break;
     case HWC_VSYNC_PERIOD:
-      value[0] = pdev->vsync_period_ns;
+      value[0] = pdev->vsync_data.vsync_period_ns;
       break;
     default:
       // unsupported query
@@ -345,65 +335,6 @@
   return -EINVAL;
 }
 
-static void* hwc_vsync_thread(void* data) {
-  struct vsoc_hwc_composer_device_1_t* pdev =
-      (struct vsoc_hwc_composer_device_1_t*)data;
-  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 timestamp.
-    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;
-}
-
 static int vsoc_hwc_blank(vsoc_hwc_device* /*dev*/, int disp, int /*blank*/) {
   if (!IS_PRIMARY_DISPLAY(disp)) return -EINVAL;
   return 0;
@@ -430,22 +361,21 @@
 #if VSOC_PLATFORM_SDK_AFTER(J)
 static int32_t vsoc_hwc_attribute(struct vsoc_hwc_composer_device_1_t* pdev,
                                   const uint32_t attribute) {
-  auto screen_view = ScreenRegionView::GetInstance();
   switch (attribute) {
     case HWC_DISPLAY_VSYNC_PERIOD:
-      return pdev->vsync_period_ns;
+      return pdev->vsync_data.vsync_period_ns;
     case HWC_DISPLAY_WIDTH:
-      return screen_view->x_res();
+      return pdev->composer->x_res();
     case HWC_DISPLAY_HEIGHT:
-      return screen_view->y_res();
+      return pdev->composer->y_res();
     case HWC_DISPLAY_DPI_X:
-      ALOGI("Reporting DPI_X of %d", screen_view->dpi());
+      ALOGI("Reporting DPI_X of %d", pdev->composer->dpi());
       // The number of pixels per thousand inches
-      return screen_view->dpi() * 1000;
+      return pdev->composer->dpi() * 1000;
     case HWC_DISPLAY_DPI_Y:
-      ALOGI("Reporting DPI_Y of %d", screen_view->dpi());
+      ALOGI("Reporting DPI_Y of %d", pdev->composer->dpi());
       // The number of pixels per thousand inches
-      return screen_view->dpi() * 1000;
+      return pdev->composer->dpi() * 1000;
     default:
       ALOGE("unknown display attribute %u", attribute);
       return -EINVAL;
@@ -476,8 +406,8 @@
   struct vsoc_hwc_composer_device_1_t* dev =
       (struct vsoc_hwc_composer_device_1_t*)device;
   ALOGE("vsoc_hwc_close");
-  pthread_kill(dev->vsync_thread, SIGTERM);
-  pthread_join(dev->vsync_thread, NULL);
+  pthread_kill(dev->vsync_data.vsync_thread, SIGTERM);
+  pthread_join(dev->vsync_data.vsync_thread, NULL);
   delete dev->composer;
   delete dev;
   return 0;
@@ -497,14 +427,12 @@
     return -ENOMEM;
   }
 
-  int refreshRate = ScreenRegionView::GetInstance()->refresh_rate_hz();
-  dev->vsync_period_ns = 1000000000 / refreshRate;
   struct timespec rt;
   if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
     ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
           strerror(errno));
   }
-  dev->vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+  dev->vsync_data.vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
 
   dev->base.common.tag = HARDWARE_DEVICE_TAG;
   dev->base.common.version = VSOC_HWC_DEVICE_API_VERSION;
@@ -525,10 +453,10 @@
   dev->base.getDisplayConfigs = vsoc_hwc_get_display_configs;
   dev->base.getDisplayAttributes = vsoc_hwc_get_display_attributes;
 #endif
-  dev->composer =
-      new ComposerType(dev->vsync_base_timestamp, dev->vsync_period_ns);
-
-  int ret = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev);
+  dev->composer = new ComposerType(dev->vsync_data.vsync_base_timestamp);
+  dev->vsync_data.vsync_period_ns = 1000000000 / dev->composer->refresh_rate();
+  int ret = pthread_create(&dev->vsync_data.vsync_thread,
+                           NULL, cvd::hwc_vsync_thread, &dev->vsync_data);
   if (ret) {
     ALOGE("failed to start vsync thread: %s", strerror(ret));
     ret = -ret;
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer_common.h b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.h
similarity index 100%
copy from guest/hals/hwcomposer/legacy/hwcomposer_common.h
copy to guest/hals/hwcomposer/cutf_cvm/hwcomposer.h
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.cpp b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
similarity index 94%
copy from guest/hals/hwcomposer/legacy/vsoc_composer.cpp
copy to guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
index 179e930..5b55e84 100644
--- a/guest/hals/hwcomposer/legacy/vsoc_composer.cpp
+++ b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
@@ -27,12 +27,9 @@
 #include <libyuv.h>
 #include <system/graphics.h>
 
-#include "common/vsoc/lib/screen_region_view.h"
+#include <common/libs/utils/size_utils.h>
 
 #include "geometry_utils.h"
-#include "hwcomposer_common.h"
-
-using vsoc::screen::ScreenRegionView;
 
 namespace cvd {
 
@@ -177,9 +174,9 @@
   uint8_t* src_y = src.buffer;
   int stride_y = stride_in_pixels;
   uint8_t* src_v = src_y + stride_y * src.height;
-  int stride_v = ScreenRegionView::align(stride_y / 2, 16);
+  int stride_v = cvd::AlignToPowerOf2(stride_y / 2, 4);
   uint8_t* src_u = src_v + stride_v *  src.height / 2;
-  int stride_u = ScreenRegionView::align(stride_y / 2, 16);
+  int stride_u = cvd::AlignToPowerOf2(stride_y / 2, 4);
 
   // Adjust for crop
   src_y += src.crop_y * stride_y + src.crop_x;
@@ -317,7 +314,7 @@
 
   uint8_t* src_buffer;
   uint8_t* dst_buffer = reinterpret_cast<uint8_t*>(
-      ScreenRegionView::GetInstance()->GetBuffer(buffer_idx));
+      frame_buffer_.GetBuffer(buffer_idx));
   int retval = gralloc_module_->lock(
       gralloc_module_, src_layer->handle, GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
       src_priv_handle->x_res, src_priv_handle->y_res,
@@ -345,10 +342,9 @@
       src_layer->sourceCrop.bottom - src_layer->sourceCrop.top;
   src_layer_spec.format = src_priv_handle->format;
 
-  auto screen_view = ScreenRegionView::GetInstance();
-  BufferSpec dst_layer_spec(dst_buffer, screen_view->buffer_size(),
-                            screen_view->x_res(), screen_view->y_res(),
-                            screen_view->line_length());
+  BufferSpec dst_layer_spec(dst_buffer, frame_buffer_.buffer_size(),
+                            frame_buffer_.x_res(), frame_buffer_.y_res(),
+                            frame_buffer_.line_length());
   dst_layer_spec.crop_x = src_layer->displayFrame.left;
   dst_layer_spec.crop_y = src_layer->displayFrame.top;
   dst_layer_spec.crop_width =
@@ -377,12 +373,12 @@
   int y_res = src_layer->displayFrame.bottom - src_layer->displayFrame.top;
   size_t output_frame_size =
       x_res *
-    ScreenRegionView::align(y_res * screen_view->bytes_per_pixel(), 16);
+    cvd::AlignToPowerOf2(y_res * frame_buffer_.bytes_per_pixel(), 4);
   while (needed_tmp_buffers > 0) {
     BufferSpec tmp(RotateTmpBuffer(needed_tmp_buffers), output_frame_size,
                    x_res, y_res,
-                   ScreenRegionView::align(
-                       x_res * screen_view->bytes_per_pixel(), 16));
+                   cvd::AlignToPowerOf2(
+                       x_res * frame_buffer_.bytes_per_pixel(), 4));
     dest_buffer_stack.push_back(tmp);
     needed_tmp_buffers--;
   }
@@ -403,8 +399,8 @@
       // Make width and height match the crop sizes on the source
       int src_width = src_layer_spec.crop_width;
       int src_height = src_layer_spec.crop_height;
-      int dst_stride = ScreenRegionView::align(
-          src_width * screen_view->bytes_per_pixel(), 16);
+      int dst_stride = cvd::AlignToPowerOf2(
+          src_width * frame_buffer_.bytes_per_pixel(), 4);
       size_t needed_size = dst_stride * src_height;
       dst_buffer_spec.width = src_width;
       dst_buffer_spec.height = src_height;
@@ -444,7 +440,7 @@
       // TODO (jemoreira): Aligment (To align here may cause the needed size to
       // be bigger than the buffer, so care should be taken)
       dst_buffer_spec.stride =
-          dst_buffer_spec.width * screen_view->bytes_per_pixel();
+          dst_buffer_spec.width * frame_buffer_.bytes_per_pixel();
     }
     retval = DoScaling(src_layer_spec, dst_buffer_spec, needs_vflip);
     needs_vflip = false;
@@ -504,11 +500,10 @@
 
 /* static */ const int VSoCComposer::kNumTmpBufferPieces = 2;
 
-VSoCComposer::VSoCComposer(int64_t vsync_base_timestamp,
-                           int32_t vsync_period_ns)
-    : BaseComposer(vsync_base_timestamp, vsync_period_ns),
+VSoCComposer::VSoCComposer(int64_t vsync_base_timestamp)
+    : BaseComposer(vsync_base_timestamp),
       tmp_buffer_(kNumTmpBufferPieces *
-                  ScreenRegionView::GetInstance()->buffer_size()) {}
+                  frame_buffer_.buffer_size()) {}
 
 VSoCComposer::~VSoCComposer() {}
 
@@ -556,7 +551,7 @@
 
 int VSoCComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
   int targetFbs = 0;
-  int buffer_idx = NextScreenBuffer();
+  int buffer_idx = frame_buffer_.NextScreenBuffer();
 
   // The framebuffer target layer should be composed if at least one layers was
   // marked HWC_FRAMEBUFFER or if it's the only layer in the composition
@@ -596,7 +591,7 @@
   if (targetFbs != 1) {
     ALOGW("Saw %zu layers, posted=%d", num_layers, targetFbs);
   }
-  Broadcast(buffer_idx);
+  frame_buffer_.Broadcast(buffer_idx);
   return 0;
 }
 
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.h b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
similarity index 92%
copy from guest/hals/hwcomposer/legacy/vsoc_composer.h
copy to guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
index 48f290f..7007931 100644
--- a/guest/hals/hwcomposer/legacy/vsoc_composer.h
+++ b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
@@ -22,13 +22,13 @@
 #include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
 
 #include "base_composer.h"
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
 
 namespace cvd {
 
 class VSoCComposer : public BaseComposer {
  public:
-  VSoCComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns);
+  VSoCComposer(int64_t vsync_base_timestamp);
   ~VSoCComposer();
 
   // override
diff --git a/guest/hals/hwcomposer/Android.mk b/guest/hals/hwcomposer/vsoc-future/Android.mk
similarity index 96%
rename from guest/hals/hwcomposer/Android.mk
rename to guest/hals/hwcomposer/vsoc-future/Android.mk
index 1a8c44f..bcc104e 100644
--- a/guest/hals/hwcomposer/Android.mk
+++ b/guest/hals/hwcomposer/vsoc-future/Android.mk
@@ -16,7 +16,7 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := hwcomposer.vsoc-future
+LOCAL_MODULE := hwcomposer.cutf_ivsh-future
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
diff --git a/guest/hals/hwcomposer/hwcomposer.cpp b/guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp
similarity index 100%
rename from guest/hals/hwcomposer/hwcomposer.cpp
rename to guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp
diff --git a/guest/hals/hwcomposer/legacy/Android.mk b/guest/hals/hwcomposer/vsoc/Android.mk
similarity index 93%
rename from guest/hals/hwcomposer/legacy/Android.mk
rename to guest/hals/hwcomposer/vsoc/Android.mk
index f2fe5c3..2d22f2d 100644
--- a/guest/hals/hwcomposer/legacy/Android.mk
+++ b/guest/hals/hwcomposer/vsoc/Android.mk
@@ -21,7 +21,7 @@
 include $(CLEAR_VARS)
 include $(LOCAL_PATH)/hwcomposer.mk
 LOCAL_CFLAGS += -DUSE_OLD_HWCOMPOSER -Wall -Werror
-LOCAL_MODULE := hwcomposer.vsoc-deprecated
+LOCAL_MODULE := hwcomposer.cutf_ivsh-deprecated
 
 # See b/67109557
 ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
@@ -33,7 +33,7 @@
 # New hwcomposer, performs software composition
 include $(CLEAR_VARS)
 include $(LOCAL_PATH)/hwcomposer.mk
-LOCAL_MODULE := hwcomposer.vsoc
+LOCAL_MODULE := hwcomposer.cutf_ivsh
 LOCAL_VENDOR_MODULE := true
 
 # See b/67109557
diff --git a/guest/hals/hwcomposer/legacy/base_composer.cpp b/guest/hals/hwcomposer/vsoc/base_composer.cpp
similarity index 100%
rename from guest/hals/hwcomposer/legacy/base_composer.cpp
rename to guest/hals/hwcomposer/vsoc/base_composer.cpp
diff --git a/guest/hals/hwcomposer/legacy/base_composer.h b/guest/hals/hwcomposer/vsoc/base_composer.h
similarity index 98%
rename from guest/hals/hwcomposer/legacy/base_composer.h
rename to guest/hals/hwcomposer/vsoc/base_composer.h
index 9e8d10c..f3467ef 100644
--- a/guest/hals/hwcomposer/legacy/base_composer.h
+++ b/guest/hals/hwcomposer/vsoc/base_composer.h
@@ -18,7 +18,7 @@
 #include <functional>
 
 #include <hardware/gralloc.h>
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
 
 namespace cvd {
 
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.cpp b/guest/hals/hwcomposer/vsoc/geometry_utils.cpp
similarity index 100%
rename from guest/hals/hwcomposer/legacy/geometry_utils.cpp
rename to guest/hals/hwcomposer/vsoc/geometry_utils.cpp
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/guest/hals/hwcomposer/vsoc/geometry_utils.h
similarity index 95%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to guest/hals/hwcomposer/vsoc/geometry_utils.h
index b6a037b..937283f 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/guest/hals/hwcomposer/vsoc/geometry_utils.h
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
 
 namespace cvd {
 
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.cpp b/guest/hals/hwcomposer/vsoc/hwcomposer.cpp
similarity index 85%
rename from guest/hals/hwcomposer/legacy/hwcomposer.cpp
rename to guest/hals/hwcomposer/vsoc/hwcomposer.cpp
index 9711656..16b63ba 100644
--- a/guest/hals/hwcomposer/legacy/hwcomposer.cpp
+++ b/guest/hals/hwcomposer/vsoc/hwcomposer.cpp
@@ -55,11 +55,12 @@
 
 #include "common/vsoc/lib/screen_region_view.h"
 #include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+#include "guest/hals/hwcomposer/common/hwcomposer.h"
 #include <sync/sync.h>
 
 #include "base_composer.h"
 #include "geometry_utils.h"
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
 #include "stats_keeper.h"
 #include "vsoc_composer.h"
 
@@ -79,10 +80,7 @@
 
 struct vsoc_hwc_composer_device_1_t {
   vsoc_hwc_device base;
-  const hwc_procs_t* procs;
-  pthread_t vsync_thread;
-  int64_t vsync_base_timestamp;
-  int32_t vsync_period_ns;
+  cvd::hwc_composer_device_data_t vsync_data;
   ComposerType* composer;
 };
 
@@ -309,7 +307,7 @@
                                     const hwc_procs_t* procs) {
   struct vsoc_hwc_composer_device_1_t* pdev =
       (struct vsoc_hwc_composer_device_1_t*)dev;
-  pdev->procs = procs;
+  pdev->vsync_data.procs = procs;
 }
 
 static int vsoc_hwc_query(vsoc_hwc_device* dev, int what, int* value) {
@@ -322,7 +320,7 @@
       value[0] = 0;
       break;
     case HWC_VSYNC_PERIOD:
-      value[0] = pdev->vsync_period_ns;
+      value[0] = pdev->vsync_data.vsync_period_ns;
       break;
     default:
       // unsupported query
@@ -345,65 +343,6 @@
   return -EINVAL;
 }
 
-static void* hwc_vsync_thread(void* data) {
-  struct vsoc_hwc_composer_device_1_t* pdev =
-      (struct vsoc_hwc_composer_device_1_t*)data;
-  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 timestamp.
-    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;
-}
-
 static int vsoc_hwc_blank(vsoc_hwc_device* /*dev*/, int disp, int /*blank*/) {
   if (!IS_PRIMARY_DISPLAY(disp)) return -EINVAL;
   return 0;
@@ -433,7 +372,7 @@
   auto screen_view = ScreenRegionView::GetInstance();
   switch (attribute) {
     case HWC_DISPLAY_VSYNC_PERIOD:
-      return pdev->vsync_period_ns;
+      return pdev->vsync_data.vsync_period_ns;
     case HWC_DISPLAY_WIDTH:
       return screen_view->x_res();
     case HWC_DISPLAY_HEIGHT:
@@ -476,8 +415,8 @@
   struct vsoc_hwc_composer_device_1_t* dev =
       (struct vsoc_hwc_composer_device_1_t*)device;
   ALOGE("vsoc_hwc_close");
-  pthread_kill(dev->vsync_thread, SIGTERM);
-  pthread_join(dev->vsync_thread, NULL);
+  pthread_kill(dev->vsync_data.vsync_thread, SIGTERM);
+  pthread_join(dev->vsync_data.vsync_thread, NULL);
   delete dev->composer;
   delete dev;
   return 0;
@@ -498,13 +437,13 @@
   }
 
   int refreshRate = ScreenRegionView::GetInstance()->refresh_rate_hz();
-  dev->vsync_period_ns = 1000000000 / refreshRate;
+  dev->vsync_data.vsync_period_ns = 1000000000 / refreshRate;
   struct timespec rt;
   if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
     ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
           strerror(errno));
   }
-  dev->vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+  dev->vsync_data.vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
 
   dev->base.common.tag = HARDWARE_DEVICE_TAG;
   dev->base.common.version = VSOC_HWC_DEVICE_API_VERSION;
@@ -526,9 +465,9 @@
   dev->base.getDisplayAttributes = vsoc_hwc_get_display_attributes;
 #endif
   dev->composer =
-      new ComposerType(dev->vsync_base_timestamp, dev->vsync_period_ns);
+      new ComposerType(dev->vsync_data.vsync_base_timestamp, dev->vsync_data.vsync_period_ns);
 
-  int ret = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev);
+  int ret = pthread_create(&dev->vsync_data.vsync_thread, NULL, cvd::hwc_vsync_thread, &dev->vsync_data);
   if (ret) {
     ALOGE("failed to start vsync thread: %s", strerror(ret));
     ret = -ret;
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer_common.h b/guest/hals/hwcomposer/vsoc/hwcomposer.h
similarity index 100%
rename from guest/hals/hwcomposer/legacy/hwcomposer_common.h
rename to guest/hals/hwcomposer/vsoc/hwcomposer.h
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.mk b/guest/hals/hwcomposer/vsoc/hwcomposer.mk
similarity index 95%
rename from guest/hals/hwcomposer/legacy/hwcomposer.mk
rename to guest/hals/hwcomposer/vsoc/hwcomposer.mk
index 1414dca..cb27c95 100644
--- a/guest/hals/hwcomposer/legacy/hwcomposer.mk
+++ b/guest/hals/hwcomposer/vsoc/hwcomposer.mk
@@ -34,14 +34,15 @@
     $(VSOC_STLPORT_LIBS)
 
 LOCAL_STATIC_LIBRARIES := \
-    libyuv_static
+    libyuv_static \
+    hwcomposer_common \
 
 LOCAL_SRC_FILES := \
     geometry_utils.cpp \
     hwcomposer.cpp \
     vsoc_composer.cpp \
     stats_keeper.cpp \
-    base_composer.cpp
+    base_composer.cpp \
 
 LOCAL_CFLAGS += \
     -DGATHER_STATS \
diff --git a/guest/hals/hwcomposer/legacy/stats_keeper.cpp b/guest/hals/hwcomposer/vsoc/stats_keeper.cpp
similarity index 100%
rename from guest/hals/hwcomposer/legacy/stats_keeper.cpp
rename to guest/hals/hwcomposer/vsoc/stats_keeper.cpp
diff --git a/guest/hals/hwcomposer/legacy/stats_keeper.h b/guest/hals/hwcomposer/vsoc/stats_keeper.h
similarity index 99%
rename from guest/hals/hwcomposer/legacy/stats_keeper.h
rename to guest/hals/hwcomposer/vsoc/stats_keeper.h
index c813920..e1dd330 100644
--- a/guest/hals/hwcomposer/legacy/stats_keeper.h
+++ b/guest/hals/hwcomposer/vsoc/stats_keeper.h
@@ -24,7 +24,7 @@
 #include "common/libs/time/monotonic_time.h"
 #include "common/vsoc/lib/screen_region_view.h"
 
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
 
 namespace cvd {
 
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.cpp b/guest/hals/hwcomposer/vsoc/vsoc_composer.cpp
similarity index 99%
rename from guest/hals/hwcomposer/legacy/vsoc_composer.cpp
rename to guest/hals/hwcomposer/vsoc/vsoc_composer.cpp
index 179e930..33b08e1 100644
--- a/guest/hals/hwcomposer/legacy/vsoc_composer.cpp
+++ b/guest/hals/hwcomposer/vsoc/vsoc_composer.cpp
@@ -30,7 +30,6 @@
 #include "common/vsoc/lib/screen_region_view.h"
 
 #include "geometry_utils.h"
-#include "hwcomposer_common.h"
 
 using vsoc::screen::ScreenRegionView;
 
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.h b/guest/hals/hwcomposer/vsoc/vsoc_composer.h
similarity index 97%
rename from guest/hals/hwcomposer/legacy/vsoc_composer.h
rename to guest/hals/hwcomposer/vsoc/vsoc_composer.h
index 48f290f..0e8e2c2 100644
--- a/guest/hals/hwcomposer/legacy/vsoc_composer.h
+++ b/guest/hals/hwcomposer/vsoc/vsoc_composer.h
@@ -22,7 +22,7 @@
 #include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
 
 #include "base_composer.h"
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
 
 namespace cvd {
 
diff --git a/guest/hals/lights/Android.mk b/guest/hals/lights/Android.mk
index 6add0f2..8c292dc 100644
--- a/guest/hals/lights/Android.mk
+++ b/guest/hals/lights/Android.mk
@@ -29,7 +29,7 @@
 LOCAL_HEADER_LIBRARIES := libhardware_headers
 LOCAL_SHARED_LIBRARIES := liblog libcutils
 LOCAL_SRC_FILES := lights_vsoc.c
-LOCAL_MODULE := lights.vsoc
+LOCAL_MODULE := lights.cutf
 LOCAL_CFLAGS += -DLIGHT_BACKLIGHT -DLOG_TAG=\"VSoC-lights\" $(VSOC_VERSION_CFLAGS)
 LOCAL_VENDOR_MODULE := true
 include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/power/Android.mk b/guest/hals/power/Android.mk
index d5ead05..d73bc0f 100644
--- a/guest/hals/power/Android.mk
+++ b/guest/hals/power/Android.mk
@@ -32,7 +32,7 @@
 LOCAL_HEADER_LIBRARIES := libhardware_headers libutils_headers
 LOCAL_SHARED_LIBRARIES := liblog libcutils
 LOCAL_SRC_FILES := power.c
-LOCAL_MODULE := power.vsoc
+LOCAL_MODULE := power.cutf
 LOCAL_MODULE_TAGS := optional
 LOCAL_VENDOR_MODULE := true
 include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/ril/Android.mk b/guest/hals/ril/Android.mk
index ccf0910..0110fd2 100644
--- a/guest/hals/ril/Android.mk
+++ b/guest/hals/ril/Android.mk
@@ -17,17 +17,17 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-  vsoc_ril.cpp
+  cuttlefish_ril.cpp
 
 LOCAL_SHARED_LIBRARIES := \
   liblog \
   libcutils \
   libutils \
+  ${CUTTLEFISH_LIBRIL_NAME} \
   libcuttlefish_fs \
   cuttlefish_net \
   cuttlefish_auto_resources \
   libbase \
-  vsoc_lib
 
 LOCAL_C_INCLUDES := \
     device/google/cuttlefish_common \
@@ -38,59 +38,7 @@
   -Werror \
   $(VSOC_VERSION_CFLAGS)
 
-# only for PLATFORM_VERSION greater or equal to Q
-ifeq ($(PLATFORM_VERSION), $(word 1, $(sort Q $(PLATFORM_VERSION))))
-
-    LOCAL_SRC_FILES += \
-        libril/ril.cpp \
-        libril/ril_service.cpp \
-        libril/ril_event.cpp \
-        libril/RilSapSocket.cpp \
-        libril/sap_service.cpp
-
-    LOCAL_SHARED_LIBRARIES += \
-        libhardware_legacy \
-        libhidlbase  \
-        libhidltransport \
-        libhwbinder \
-        librilutils \
-        android.hardware.radio@1.0 \
-        android.hardware.radio@1.1 \
-        android.hardware.radio.deprecated@1.0 \
-        android.hardware.radio@1.2 \
-        android.hardware.radio@1.3 \
-        android.hardware.radio@1.4
-
-
-    LOCAL_STATIC_LIBRARIES := \
-        libprotobuf-c-nano-enable_malloc \
-
-    LOCAL_C_INCLUDES += \
-        external/nanopb-c \
-        hardware/ril/include \
-        hardware/ril/libril
-
-    LOCAL_CFLAGS += \
-        -Wextra \
-        -Wno-unused-parameter
-
-    LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
-    ifeq ($(SIM_COUNT), 2)
-        LOCAL_CFLAGS += -DANDROID_MULTI_SIM -DDSDA_RILD1
-        LOCAL_CFLAGS += -DANDROID_SIM_COUNT_2
-    endif
-
-    ifneq ($(DISABLE_RILD_OEM_HOOK),)
-        LOCAL_CFLAGS += -DOEM_HOOK_DISABLED
-    endif
-else
-    $(info Use deprecated libril)
-    LOCAL_SHARED_LIBRARIES += \
-        libril
-endif
-
-LOCAL_MODULE:= libvsoc-ril
+LOCAL_MODULE:= libcuttlefish-ril
 LOCAL_MODULE_TAGS := optional
 LOCAL_VENDOR_MODULE := true
 
@@ -100,3 +48,5 @@
 endif
 
 include $(BUILD_SHARED_LIBRARY)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/hals/ril/vsoc_ril.cpp b/guest/hals/ril/cuttlefish_ril.cpp
similarity index 95%
rename from guest/hals/ril/vsoc_ril.cpp
rename to guest/hals/ril/cuttlefish_ril.cpp
index d6138f0..0acc452 100644
--- a/guest/hals/ril/vsoc_ril.cpp
+++ b/guest/hals/ril/cuttlefish_ril.cpp
@@ -14,9 +14,10 @@
 ** limitations under the License.
 */
 
-#include "guest/hals/ril/vsoc_ril.h"
+#include "guest/hals/ril/cuttlefish_ril.h"
 
 #include <cutils/properties.h>
+#include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #include <time.h>
@@ -26,13 +27,13 @@
 #include <string>
 #include <vector>
 
+#include "common/libs/constants/ril.h"
 #include "common/libs/net/netlink_client.h"
 #include "common/libs/net/network_interface.h"
 #include "common/libs/net/network_interface_manager.h"
-#include "common/vsoc/lib/ril_region_view.h"
 #include "guest/libs/platform_support/api_level_fixes.h"
 
-#define VSOC_RIL_VERSION_STRING "Android VSoC RIL 1.4"
+#define CUTTLEFISH_RIL_VERSION_STRING "Android Cuttlefish RIL 1.4"
 
 /* Modem Technology bits */
 #define MDM_GSM 0x01
@@ -61,6 +62,49 @@
   RUIM_NETWORK_PERSONALIZATION = 11
 } SIM_Status;
 
+class RilConfig {
+ public:
+  static void InitRilConfig();
+
+  static char* address_and_prefixlength() {
+    return RilConfig::global_ril_config_.address_and_prefixlength_;
+  }
+
+  static char* dns() {
+    return RilConfig::global_ril_config_.dns_;
+  }
+
+  static char* gateway() {
+    return RilConfig::global_ril_config_.gateway_;
+  }
+
+  static char* ipaddr() {
+    return RilConfig::global_ril_config_.ipaddr_;
+  }
+
+  static int prefixlen() {
+    return RilConfig::global_ril_config_.prefixlen_;
+  }
+
+  static char* broadcast() {
+    return RilConfig::global_ril_config_.broadcast_;
+  }
+
+ private:
+  RilConfig() = default;
+  RilConfig(const RilConfig&) = default;
+
+  char ipaddr_[16]; // xxx.xxx.xxx.xxx\0 = 16 bytes
+  char gateway_[16];
+  char dns_[16];
+  char broadcast_[16];
+  char address_and_prefixlength_[19]; // <ipaddr>/dd
+  int prefixlen_;
+
+  static RilConfig global_ril_config_;
+};
+RilConfig RilConfig::global_ril_config_;
+
 static const struct RIL_Env* gce_ril_env;
 
 static const struct timeval TIMEVAL_SIMPOLL = {3, 0};
@@ -130,6 +174,43 @@
   return false;
 }
 
+static bool ReadStringProperty(char* dst, const char* key, size_t max_size) {
+  char buffer[PROPERTY_VALUE_MAX];
+  auto res = property_get(key, buffer, NULL);
+  if (res < 0) {
+    ALOGE("Failed to read property %s", key);
+    return false;
+  }
+  if (res > static_cast<int>(max_size - 1)) {
+    ALOGE("Invalid value in property %s: value too long: %s", key, buffer);
+    return false;
+  }
+  snprintf(dst, res + 1, "%s", buffer);
+  return true;
+}
+
+void RilConfig::InitRilConfig() {
+  RilConfig tmp_config;
+  ReadStringProperty(&tmp_config.ipaddr_[0], CUTTLEFISH_RIL_ADDR_PROPERTY,
+                     sizeof(tmp_config.ipaddr_));
+  ReadStringProperty(&tmp_config.gateway_[0],
+                     CUTTLEFISH_RIL_GATEWAY_PROPERTY,
+                     sizeof(tmp_config.gateway_));
+  ReadStringProperty(&tmp_config.dns_[0], CUTTLEFISH_RIL_DNS_PROPERTY,
+                     sizeof(tmp_config.dns_));
+  ReadStringProperty(&tmp_config.broadcast_[0],
+                     CUTTLEFISH_RIL_BROADCAST_PROPERTY,
+                     sizeof(tmp_config.broadcast_));
+  tmp_config.prefixlen_ =
+      property_get_int32(CUTTLEFISH_RIL_PREFIXLEN_PROPERTY, 30);
+
+  snprintf(&tmp_config.address_and_prefixlength_[0],
+           sizeof(tmp_config.address_and_prefixlength_), "%s/%d",
+           tmp_config.ipaddr_, tmp_config.prefixlen_);
+
+  RilConfig::global_ril_config_ = tmp_config;
+}
+
 // TearDownNetworkInterface disables network interface.
 // This call returns true, if operation was successful.
 bool TearDownNetworkInterface() {
@@ -187,13 +268,11 @@
         break;
     }
 
-    auto ril_region_view = vsoc::ril::RilRegionView::GetInstance();
-
     responses[index].ifname = (char*)"rmnet0";
     responses[index].addresses =
-      const_cast<char*>(ril_region_view->address_and_prefix_length());
-    responses[index].dnses = (char*)ril_region_view->data()->dns;
-    responses[index].gateways = (char*)ril_region_view->data()->gateway;
+      const_cast<char*>(RilConfig::address_and_prefixlength());
+    responses[index].dnses = RilConfig::dns();
+    responses[index].gateways = RilConfig::gateway();
 #if VSOC_PLATFORM_SDK_AFTER(N_MR1)
     responses[index].pcscf = (char*)"";
     responses[index].mtu = 1440;
@@ -304,7 +383,7 @@
   }
 
   if (call.connection_type_ != DataCall::kConnTypeIPv4) {
-    ALOGE("Non-IPv4 connections are not supported by VSOC RIL.");
+    ALOGE("Non-IPv4 connections are not supported by Cuttlefish RIL.");
     gce_ril_env->OnRequestComplete(t, RIL_E_INVALID_ARGUMENTS, NULL, 0);
     return;
   }
@@ -316,10 +395,8 @@
   }
 
   if (gDataCalls.empty()) {
-    auto ril_region_view = vsoc::ril::RilRegionView::GetInstance();
-    SetUpNetworkInterface(ril_region_view->data()->ipaddr,
-                          ril_region_view->data()->prefixlen,
-                          ril_region_view->data()->broadcast);
+    SetUpNetworkInterface(RilConfig::ipaddr(), RilConfig::prefixlen(),
+                          RilConfig::broadcast());
   }
 
   gDataCalls[gNextDataCallId] = call;
@@ -1378,6 +1455,7 @@
   gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
 }
 
+#if VSOC_PLATFORM_SDK_AFTER(P)
 static void request_set_carrier_restrictions4(void* /*data*/,
                                               size_t /*datalen*/,
                                               RIL_Token t) {
@@ -1391,6 +1469,7 @@
   // Carrier restrictions are not supported on cuttlefish, as they are specific for locked devices
   gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
 }
+#endif
 
 static RIL_RadioState gce_ril_current_state() {
   ALOGV("Reporting radio state %d", gRadioPowerState);
@@ -1407,8 +1486,8 @@
 }
 
 static const char* gce_ril_get_version(void) {
-  ALOGV("Reporting VSOC version " VSOC_RIL_VERSION_STRING);
-  return VSOC_RIL_VERSION_STRING;
+  ALOGV("Reporting Cuttlefish version " CUTTLEFISH_RIL_VERSION_STRING);
+  return CUTTLEFISH_RIL_VERSION_STRING;
 }
 
 static int s_cell_info_rate_ms = INT_MAX;
@@ -2295,13 +2374,19 @@
     return;
   }
 
-  // Ignore all non-power requests when RADIO_STATE_OFF (except
-  // RIL_REQUEST_GET_SIM_STATUS)
-  if (gRadioPowerState == RADIO_STATE_OFF &&
-      !(request == RIL_REQUEST_RADIO_POWER ||
-        request == RIL_REQUEST_GET_SIM_STATUS)) {
-    gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
-    return;
+  // Ignore all non-power requests when RADIO_STATE_OFF.
+  if (gRadioPowerState == RADIO_STATE_OFF) {
+    switch (request) {
+      case RIL_REQUEST_GET_SIM_STATUS:
+      case RIL_REQUEST_OPERATOR:
+      case RIL_REQUEST_RADIO_POWER:
+      case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
+        // Process all the above, even though the radio is off
+        break;
+      default:
+        gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+        return;
+    }
   }
 
   ALOGV("Received request %d", request);
@@ -2581,10 +2666,10 @@
   }
 }
 
-#define VSOC_RIL_VERSION 6
+#define CUTTLEFISH_RIL_VERSION 6
 
 static const RIL_RadioFunctions ril_callbacks = {
-    VSOC_RIL_VERSION,     gce_ril_on_request, gce_ril_current_state,
+    CUTTLEFISH_RIL_VERSION,     gce_ril_on_request, gce_ril_current_state,
     gce_ril_on_supports, gce_ril_on_cancel,  gce_ril_get_version};
 
 extern "C" {
@@ -2594,6 +2679,8 @@
   time(&gce_ril_start_time);
   gce_ril_env = env;
 
+  RilConfig::InitRilConfig();
+
   TearDownNetworkInterface();
 
   init_modem_supported_network_types();
diff --git a/guest/hals/ril/vsoc_ril.h b/guest/hals/ril/cuttlefish_ril.h
similarity index 96%
rename from guest/hals/ril/vsoc_ril.h
rename to guest/hals/ril/cuttlefish_ril.h
index 19909bb..10340d8 100644
--- a/guest/hals/ril/vsoc_ril.h
+++ b/guest/hals/ril/cuttlefish_ril.h
@@ -18,7 +18,7 @@
 
 #define RIL_SHLIB
 
-#define LOG_TAG "VSoCRil"
+#define LOG_TAG "CuttlefishRil"
 
 #include <log/log.h>
 #include <stdint.h>
diff --git a/guest/hals/ril/libril/Android.mk b/guest/hals/ril/libril/Android.mk
new file mode 100644
index 0000000..ad02bf6
--- /dev/null
+++ b/guest/hals/ril/libril/Android.mk
@@ -0,0 +1,66 @@
+# 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.
+
+# We're forced to use Android.mk here because:
+#   This depends on headers in hardware/ril/libril
+#   hardware/ril/libril is still on Android.mk
+
+ifeq (libril-cuttlefish-fork,$(CUTTLEFISH_LIBRIL_NAME))
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_VENDOR_MODULE := true
+LOCAL_MODULE := libril-cuttlefish-fork
+LOCAL_SRC_FILES:= \
+    ril.cpp \
+    ril_service.cpp \
+    ril_event.cpp \
+    RilSapSocket.cpp \
+    sap_service.cpp \
+
+
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    libutils \
+    libcutils \
+    libhardware_legacy \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
+    librilutils \
+    android.hardware.radio@1.0 \
+    android.hardware.radio@1.1 \
+    android.hardware.radio.deprecated@1.0 \
+    android.hardware.radio@1.2 \
+    android.hardware.radio@1.3 \
+    android.hardware.radio@1.4 \
+
+LOCAL_STATIC_LIBRARIES := \
+    libprotobuf-c-nano-enable_malloc \
+
+LOCAL_C_INCLUDES += \
+    device/google/cuttlefish_common \
+    hardware/include \
+    external/nanopb-c \
+    hardware/ril/include \
+    hardware/ril/libril
+
+LOCAL_CFLAGS += \
+    -Wextra \
+    -Wno-unused-parameter
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := hardware/ril/include
+
+include $(BUILD_SHARED_LIBRARY)
+endif
diff --git a/guest/hals/rild/Android.mk b/guest/hals/rild/Android.mk
index f3c9757..01c709b 100644
--- a/guest/hals/rild/Android.mk
+++ b/guest/hals/rild/Android.mk
@@ -13,51 +13,35 @@
 # limitations under the License.
 
 # only for PLATFORM_VERSION greater or equal to Q
-ifeq ($(PLATFORM_VERSION), $(word 1, $(sort Q $(PLATFORM_VERSION))))
+ifeq (true,$(ENABLE_CUTTLEFISH_RILD))
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
 
-    ifndef ENABLE_VENDOR_RIL_SERVICE
+LOCAL_SRC_FILES:= \
+    rild_cuttlefish.c
 
-        LOCAL_PATH:= $(call my-dir)
-        include $(CLEAR_VARS)
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libdl \
+    liblog \
+    libril-cuttlefish-fork
 
-        LOCAL_SRC_FILES:= \
-            rild_cuttlefish.c
+LOCAL_C_INCLUDES += \
+    device/google/cuttlefish_common
 
-        LOCAL_SHARED_LIBRARIES := \
-            libcutils \
-            libdl \
-            liblog \
-            libvsoc-ril
+# Temporary hack for broken vendor RILs.
+LOCAL_WHOLE_STATIC_LIBRARIES := \
+    librilutils
 
-        LOCAL_C_INCLUDES += \
-            device/google/cuttlefish_common
+LOCAL_CFLAGS := -DRIL_SHLIB
+LOCAL_CFLAGS += -Wall -Wextra -Werror
 
-        # Temporary hack for broken vendor RILs.
-        LOCAL_WHOLE_STATIC_LIBRARIES := \
-            librilutils
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE:= libcuttlefish-rild
+LOCAL_OVERRIDES_PACKAGES := rild
+PACKAGES.$(LOCAL_MODULE).OVERRIDES := rild
+LOCAL_INIT_RC := rild_cuttlefish.rc
 
-        LOCAL_CFLAGS := -DRIL_SHLIB
-        LOCAL_CFLAGS += -Wall -Wextra -Werror
-
-        ifeq ($(SIM_COUNT), 2)
-            LOCAL_CFLAGS += -DANDROID_MULTI_SIM
-            LOCAL_CFLAGS += -DANDROID_SIM_COUNT_2
-        endif
-
-        LOCAL_MODULE_RELATIVE_PATH := hw
-        LOCAL_PROPRIETARY_MODULE := true
-        LOCAL_MODULE:= libvsoc-rild
-        LOCAL_OVERRIDES_PACKAGES := rild
-        PACKAGES.$(LOCAL_MODULE).OVERRIDES := rild
-        ifeq ($(PRODUCT_COMPATIBLE_PROPERTY),true)
-            LOCAL_INIT_RC := rild_cuttlefish.rc
-            LOCAL_CFLAGS += -DPRODUCT_COMPATIBLE_PROPERTY
-        else
-            LOCAL_INIT_RC := rild_cuttlefish.legacy.rc
-        endif
-
-        include $(BUILD_EXECUTABLE)
-
-    endif
-
+include $(BUILD_EXECUTABLE)
 endif
diff --git a/guest/hals/rild/rild_cuttlefish.c b/guest/hals/rild/rild_cuttlefish.c
index 064a4d1..c2efe44 100644
--- a/guest/hals/rild/rild_cuttlefish.c
+++ b/guest/hals/rild/rild_cuttlefish.c
@@ -36,13 +36,8 @@
 #include <sys/types.h>
 #include <guest/hals/ril/libril/ril_ex.h>
 
-#if defined(PRODUCT_COMPATIBLE_PROPERTY)
 #define LIB_PATH_PROPERTY   "vendor.rild.libpath"
 #define LIB_ARGS_PROPERTY   "vendor.rild.libargs"
-#else
-#define LIB_PATH_PROPERTY   "rild.libpath"
-#define LIB_ARGS_PROPERTY   "rild.libargs"
-#endif
 #define MAX_LIB_ARGS        16
 
 static void usage(const char *argv0) {
diff --git a/guest/hals/rild/rild_cuttlefish.legacy.rc b/guest/hals/rild/rild_cuttlefish.legacy.rc
index 43898ce..e9c2d07 100644
--- a/guest/hals/rild/rild_cuttlefish.legacy.rc
+++ b/guest/hals/rild/rild_cuttlefish.legacy.rc
@@ -1,4 +1,4 @@
-service ril-daemon /vendor/bin/hw/libvsoc-rild
+service ril-daemon /vendor/bin/hw/libcuttlefish-rild
     class main
     user radio
     group radio cache inet misc audio log readproc wakelock
diff --git a/guest/hals/rild/rild_cuttlefish.rc b/guest/hals/rild/rild_cuttlefish.rc
index e7d8d9a..9990a7a 100644
--- a/guest/hals/rild/rild_cuttlefish.rc
+++ b/guest/hals/rild/rild_cuttlefish.rc
@@ -1,4 +1,4 @@
-service vendor.ril-daemon /vendor/bin/hw/libvsoc-rild
+service vendor.ril-daemon /vendor/bin/hw/libcuttlefish-rild
     class main
     user radio
     group radio cache inet misc audio log readproc wakelock
diff --git a/guest/hals/sensors/Android.mk b/guest/hals/sensors/Android.mk
index 8dd8a52..e9a1a1a 100644
--- a/guest/hals/sensors/Android.mk
+++ b/guest/hals/sensors/Android.mk
@@ -44,6 +44,7 @@
 
 LOCAL_CFLAGS := -DLOG_TAG=\"VSoC-Sensors\" \
     $(VSOC_VERSION_CFLAGS) \
+    -std=c++17 \
     -Werror -Wall -Wno-missing-field-initializers -Wno-unused-parameter
 
 LOCAL_C_INCLUDES := \
@@ -56,7 +57,7 @@
     libcuttlefish_remoter_framework \
     $(VSOC_STLPORT_STATIC_LIBS)
 
-LOCAL_MODULE := sensors.vsoc
+LOCAL_MODULE := sensors.cutf
 LOCAL_VENDOR_MODULE := true
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/sensors/vsoc_sensors.cpp b/guest/hals/sensors/vsoc_sensors.cpp
index 86809a0..336cd65 100644
--- a/guest/hals/sensors/vsoc_sensors.cpp
+++ b/guest/hals/sensors/vsoc_sensors.cpp
@@ -40,17 +40,6 @@
 
 namespace cvd {
 
-namespace {
-template <typename F>
-struct HWDeviceThunker : ThunkerBase<hw_device_t, GceSensors, F> {};
-template <typename F>
-struct SensorsThunker : ThunkerBase<sensors_poll_device_t, GceSensors, F> {};
-template <typename F>
-struct SensorsThunker1 : ThunkerBase<sensors_poll_device_1, GceSensors, F> {};
-template <typename F>
-struct SensorsThreadThunker : ThunkerBase<void, GceSensors, F> {};
-}
-
 int GceSensors::total_sensor_count_ = -1;
 SensorInfo* GceSensors::sensor_infos_ = NULL;
 const int GceSensors::kInjectedEventWaitPeriods = 3;
@@ -111,28 +100,28 @@
     rval->common.tag = HARDWARE_DEVICE_TAG;
     rval->common.version = VSOC_SENSOR_DEVICE_VERSION;
     rval->common.module = (struct hw_module_t*)module;
-    rval->common.close = HWDeviceThunker<int()>::call<&GceSensors::Close>;
-    rval->poll =
-        SensorsThunker<int(sensors_event_t*, int)>::call<&GceSensors::Poll>;
-    rval->activate = SensorsThunker<int(int, int)>::call<&GceSensors::Activate>;
-    rval->setDelay =
-        SensorsThunker<int(int, int64_t)>::call<&GceSensors::SetDelay>;
+    rval->common.close = cvd::thunk<hw_device_t, &GceSensors::Close>;
+
+    rval->poll = cvd::thunk<sensors_poll_device_t, &GceSensors::Poll>;
+    rval->activate = cvd::thunk<sensors_poll_device_t, &GceSensors::Activate>;
+    rval->setDelay = cvd::thunk<sensors_poll_device_t, &GceSensors::SetDelay>;
 #if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_0)
-    rval->batch = SensorsThunker1<int(int, int, int64_t,
-                                      int64_t)>::call<&GceSensors::Batch>;
+
+    rval->batch = cvd::thunk<sensors_poll_device_1, &GceSensors::Batch>;
 #endif
 #if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_1)
-    rval->flush = SensorsThunker1<int(int)>::call<&GceSensors::Flush>;
+    rval->flush = cvd::thunk<sensors_poll_device_1, &GceSensors::Flush>;
 #endif
 #if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_4)
-    rval->inject_sensor_data = SensorsThunker1<int(
-        const sensors_event_t*)>::call<&GceSensors::InjectSensorData>;
+    rval->inject_sensor_data =
+        cvd::thunk<sensors_poll_device_1, &GceSensors::InjectSensorData>;
 #endif
 
     // Spawn a thread to listen for incoming data from the remoter.
     int err = pthread_create(
         &rval->receiver_thread_, NULL,
-        SensorsThreadThunker<void*()>::call<&GceSensors::Receiver>, rval);
+        cvd::thunk<void, &GceSensors::Receiver>,
+        rval);
     if (err) {
       ALOGE("GceSensors::%s: Unable to start receiver thread (%s)",
             __FUNCTION__, strerror(err));
diff --git a/guest/libs/platform_support/api_level_fixes.h b/guest/libs/platform_support/api_level_fixes.h
index 66fe137..c49a8c2 100644
--- a/guest/libs/platform_support/api_level_fixes.h
+++ b/guest/libs/platform_support/api_level_fixes.h
@@ -41,6 +41,10 @@
 #include <time.h>
 
 #ifndef VSOC_PLATFORM_SDK_VERSION
+#include "platform_version.h"
+#endif
+
+#ifndef VSOC_PLATFORM_SDK_VERSION
 #error VSOC_PLATFORM_SDK_VERSION is not set. Check your Android.mk
 #endif
 
diff --git a/guest/vsoc/lib/Android.mk b/guest/vsoc/lib/Android.mk
index ad27f69..73240f4 100644
--- a/guest/vsoc/lib/Android.mk
+++ b/guest/vsoc/lib/Android.mk
@@ -61,3 +61,27 @@
 LOCAL_MULTILIB := first
 LOCAL_VENDOR_MODULE := true
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := vsoc_managed_region_e2e_test
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := managed_region_e2e_test.cpp
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_kernel
+
+LOCAL_CFLAGS += -DGTEST_OS_LINUX_ANDROID -DGTEST_HAS_STD_STRING -Werror -Wall
+
+LOCAL_STATIC_LIBRARIES := \
+    libgtest
+
+LOCAL_SHARED_LIBRARIES := \
+    vsoc_lib \
+    cuttlefish_auto_resources \
+    libcuttlefish_fs \
+    libbase
+
+LOCAL_MULTILIB := first
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_EXECUTABLE)
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/guest_region_e2e_test.cpp b/guest/vsoc/lib/guest_region_e2e_test.cpp
index 8e3181e..5fd644a 100644
--- a/guest/vsoc/lib/guest_region_e2e_test.cpp
+++ b/guest/vsoc/lib/guest_region_e2e_test.cpp
@@ -18,29 +18,12 @@
  * 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"
-// TODO(b/64462568) Move the manager tests to a separate target
-#include "guest/vsoc/lib/manager_region_view.h"
 
 #include <android-base/logging.h>
 #include <gtest/gtest.h>
 
-#define DEATH_TEST_MESSAGE "abort converted to exit of 2 during death test"
-
-using vsoc::layout::e2e_test::E2EManagedTestRegionLayout;
-using vsoc::layout::e2e_test::E2EManagerTestRegionLayout;
-
-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);
-  });
-}
-
 template <typename View>
 void DeathTestView() {
   disable_tombstones();
@@ -151,92 +134,14 @@
               ".*" DEATH_TEST_MESSAGE ".*");
 }
 
-// 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);
   int rval = RUN_ALL_TESTS();
@@ -244,6 +149,8 @@
     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/host/commands/ivserver/qemu_client.cc b/host/commands/ivserver/qemu_client.cc
index 3a61d78..660c915 100644
--- a/host/commands/ivserver/qemu_client.cc
+++ b/host/commands/ivserver/qemu_client.cc
@@ -70,7 +70,7 @@
   //    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()) {
+  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();
@@ -84,7 +84,7 @@
   //    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()) {
+  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();
diff --git a/host/commands/kernel_log_monitor/kernel_log_server.cc b/host/commands/kernel_log_monitor/kernel_log_server.cc
index ddb9b25..0ecf5fe 100644
--- a/host/commands/kernel_log_monitor/kernel_log_server.cc
+++ b/host/commands/kernel_log_monitor/kernel_log_server.cc
@@ -26,6 +26,11 @@
 using cvd::SharedFD;
 
 namespace {
+static const std::map<std::string, std::string> kInformationalPatterns = {
+    {"] Linux version ", "GUEST_KERNEL_VERSION: "},
+    {"GUEST_BUILD_FINGERPRINT: ", "GUEST_BUILD_FINGERPRINT: "},
+};
+
 static const std::map<std::string, monitor::BootEvent> kStageToEventMap = {
     {"VIRTUAL_DEVICE_BOOT_STARTED", monitor::BootEvent::BootStarted},
     {"VIRTUAL_DEVICE_BOOT_COMPLETED", monitor::BootEvent::BootCompleted},
@@ -121,6 +126,14 @@
   // Detect VIRTUAL_DEVICE_BOOT_*
   for (ssize_t i=0; i<ret; i++) {
     if ('\n' == buf[i]) {
+      for (auto& info_kv : kInformationalPatterns) {
+        auto& match = info_kv.first;
+        auto& prefix = info_kv.second;
+        auto pos = line_.find(match);
+        if (std::string::npos != pos) {
+          LOG(INFO) << prefix << line_.substr(pos + match.size());
+        }
+      }
       for (auto& stage_kv : kStageToEventMap) {
         auto& stage = stage_kv.first;
         auto event = stage_kv.second;
diff --git a/host/commands/launch/Android.bp b/host/commands/launch/Android.bp
index b34bc93..49672b1 100644
--- a/host/commands/launch/Android.bp
+++ b/host/commands/launch/Android.bp
@@ -18,10 +18,13 @@
     srcs: [
         "main.cc",
         "screen_region_handler.cc",
-        "ril_region_handler.cc",
+        "ril_config.cc",
         "vsoc_shared_memory.cc",
-        "wifi_region_handler.cc",
         "boot_image_unpacker.cc",
+        "process_monitor.cc",
+        "launch.cc",
+        "data_image.cc",
+        "flags.cc",
     ],
     header_libs: [
         "cuttlefish_glog",
@@ -32,8 +35,6 @@
         "libcuttlefish_strings",
         "libcuttlefish_utils",
         "cuttlefish_auto_resources",
-        "libicuuc",
-        "libandroidicu",
         "libbase",
         "libnl"
     ],
@@ -45,5 +46,5 @@
         "libxml2",
         "libjsoncpp",
     ],
-    defaults: ["cuttlefish_host_only"],
+    defaults: ["cuttlefish_host_only", "cuttlefish_libicuuc"],
 }
diff --git a/host/commands/launch/boot_image_unpacker.cc b/host/commands/launch/boot_image_unpacker.cc
index 5558029..e830646 100644
--- a/host/commands/launch/boot_image_unpacker.cc
+++ b/host/commands/launch/boot_image_unpacker.cc
@@ -100,4 +100,26 @@
                      path);
 }
 
+bool BootImageUnpacker::Unpack(const std::string& ramdisk_image_path,
+                               const std::string& kernel_image_path) {
+  if (HasRamdiskImage()) {
+    if (!ExtractRamdiskImage(ramdisk_image_path)) {
+      LOG(ERROR) << "Error extracting ramdisk from boot image";
+      return false;
+    }
+  }
+  if (!kernel_image_path.empty()) {
+    if (HasKernelImage()) {
+      if (!ExtractKernelImage(kernel_image_path)) {
+        LOG(ERROR) << "Error extracting kernel from boot image";
+        return false;
+      }
+    } else {
+      LOG(ERROR) << "No kernel found on boot image";
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace cvd
diff --git a/host/commands/launch/boot_image_unpacker.h b/host/commands/launch/boot_image_unpacker.h
index 69fc7bd..05fe671 100644
--- a/host/commands/launch/boot_image_unpacker.h
+++ b/host/commands/launch/boot_image_unpacker.h
@@ -45,6 +45,9 @@
   // as root.
   bool ExtractRamdiskImage(const std::string& path) const;
 
+  bool Unpack(const std::string& ramdisk_image_path,
+              const std::string& kernel_image_path);
+
  private:
   BootImageUnpacker(SharedFD boot_image, const std::string& cmdline,
                     uint32_t kernel_image_size, uint32_t kernel_image_offset,
diff --git a/host/commands/launch/data_image.cc b/host/commands/launch/data_image.cc
new file mode 100644
index 0000000..9bdc421
--- /dev/null
+++ b/host/commands/launch/data_image.cc
@@ -0,0 +1,136 @@
+#include "host/commands/launch/data_image.h"
+
+#include <glog/logging.h>
+
+#include "common/libs/utils/files.h"
+#include "common/libs/utils/subprocess.h"
+
+namespace {
+const std::string kDataPolicyUseExisting = "use_existing";
+const std::string kDataPolicyCreateIfMissing = "create_if_missing";
+const std::string kDataPolicyAlwaysCreate = "always_create";
+const std::string kDataPolicyResizeUpTo= "resize_up_to";
+
+const int FSCK_ERROR_CORRECTED = 1;
+const int FSCK_ERROR_CORRECTED_REQUIRES_REBOOT = 2;
+
+bool ForceFsckImage(const char* data_image) {
+  int fsck_status = cvd::execute({"/sbin/e2fsck", "-y", "-f", data_image});
+  if (fsck_status & ~(FSCK_ERROR_CORRECTED|FSCK_ERROR_CORRECTED_REQUIRES_REBOOT)) {
+    LOG(ERROR) << "`e2fsck -y -f " << data_image << "` failed with code "
+               << fsck_status;
+    return false;
+  }
+  return true;
+}
+
+bool ResizeImage(const char* data_image, int data_image_mb) {
+  auto file_mb = cvd::FileSize(data_image) >> 20;
+  if (file_mb > data_image_mb) {
+    LOG(ERROR) << data_image << " is already " << file_mb << " MB, will not "
+               << "resize down.";
+    return false;
+  } else if (file_mb == data_image_mb) {
+    LOG(INFO) << data_image << " is already the right size";
+    return true;
+  } else {
+    off_t raw_target = static_cast<off_t>(data_image_mb) << 20;
+    int truncate_status =
+        cvd::SharedFD::Open(data_image, O_RDWR)->Truncate(raw_target);
+    if (truncate_status != 0) {
+      LOG(ERROR) << "`truncate --size=" << data_image_mb << "M "
+                  << data_image << "` failed with code " << truncate_status;
+      return false;
+    }
+    bool fsck_success = ForceFsckImage(data_image);
+    if (!fsck_success) {
+      return false;
+    }
+    int resize_status = cvd::execute({"/sbin/resize2fs", data_image});
+    if (resize_status != 0) {
+      LOG(ERROR) << "`resize2fs " << data_image << "` failed with code "
+                 << resize_status;
+      return false;
+    }
+    fsck_success = ForceFsckImage(data_image);
+    if (!fsck_success) {
+      return false;
+    }
+  }
+  return true;
+}
+} // namespace
+
+void CreateBlankImage(
+    const std::string& image, int image_mb, const std::string& image_fmt) {
+  LOG(INFO) << "Creating " << image;
+  std::string of = "of=";
+  of += image;
+  std::string count = "count=";
+  count += std::to_string(image_mb);
+  cvd::execute({"/bin/dd", "if=/dev/zero", of, "bs=1M", count});
+  if (image_fmt != "none") {
+    cvd::execute({"/sbin/mkfs", "-t", image_fmt, image}, {"PATH=/sbin"});
+  }
+}
+
+bool ApplyDataImagePolicy(const vsoc::CuttlefishConfig& config) {
+  std::string data_image = config.data_image_path();
+  bool data_exists = cvd::FileHasContent(data_image.c_str());
+  bool remove{};
+  bool create{};
+  bool resize{};
+
+  if (config.data_policy() == kDataPolicyUseExisting) {
+    if (!data_exists) {
+      LOG(ERROR) << "Specified data image file does not exists: " << data_image;
+      return false;
+    }
+    if (config.blank_data_image_mb() > 0) {
+      LOG(ERROR) << "You should NOT use -blank_data_image_mb with -data_policy="
+                 << kDataPolicyUseExisting;
+      return false;
+    }
+    create = false;
+    remove = false;
+    resize = false;
+  } else if (config.data_policy() == kDataPolicyAlwaysCreate) {
+    remove = data_exists;
+    create = true;
+    resize = false;
+  } else if (config.data_policy() == kDataPolicyCreateIfMissing) {
+    create = !data_exists;
+    remove = false;
+    resize = false;
+  } else if (config.data_policy() == kDataPolicyResizeUpTo) {
+    create = false;
+    remove = false;
+    resize = true;
+  } else {
+    LOG(ERROR) << "Invalid data_policy: " << config.data_policy();
+    return false;
+  }
+
+  if (remove) {
+    cvd::RemoveFile(data_image.c_str());
+  }
+
+  if (create) {
+    if (config.blank_data_image_mb() <= 0) {
+      LOG(ERROR) << "-blank_data_image_mb is required to create data image";
+      return false;
+    }
+    CreateBlankImage(data_image.c_str(), config.blank_data_image_mb(),
+                     config.blank_data_image_fmt());
+  } else if (resize) {
+    if (!data_exists) {
+      LOG(ERROR) << data_image << " does not exist, but resizing was requested";
+      return false;
+    }
+    return ResizeImage(data_image.c_str(), config.blank_data_image_mb());
+  } else {
+    LOG(INFO) << data_image << " exists. Not creating it.";
+  }
+
+  return true;
+}
diff --git a/host/commands/launch/data_image.h b/host/commands/launch/data_image.h
new file mode 100644
index 0000000..1cf9ca2
--- /dev/null
+++ b/host/commands/launch/data_image.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <string>
+
+#include "host/libs/config/cuttlefish_config.h"
+
+bool ApplyDataImagePolicy(const vsoc::CuttlefishConfig& config);
+void CreateBlankImage(
+    const std::string& image, int image_mb, const std::string& image_fmt);
diff --git a/host/commands/launch/flags.cc b/host/commands/launch/flags.cc
new file mode 100644
index 0000000..6cadcf0
--- /dev/null
+++ b/host/commands/launch/flags.cc
@@ -0,0 +1,662 @@
+#include "host/commands/launch/flags.h"
+
+#include <iostream>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/strings/str_split.h"
+#include "common/libs/utils/environment.h"
+#include "common/libs/utils/files.h"
+#include "common/vsoc/lib/vsoc_memory.h"
+#include "host/commands/launch/boot_image_unpacker.h"
+#include "host/commands/launch/data_image.h"
+#include "host/commands/launch/launch.h"
+#include "host/commands/launch/launcher_defs.h"
+#include "host/commands/launch/ril_config.h"
+#include "host/libs/vm_manager/crosvm_manager.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+#include "host/libs/vm_manager/vm_manager.h"
+
+using vsoc::GetPerInstanceDefault;
+using cvd::LauncherExitCodes;
+
+DEFINE_string(
+    system_image, "",
+    "Path to the system image, if empty it is assumed to be a file named "
+    "system.img in the directory specified by -system_image_dir");
+DEFINE_string(cache_image, "", "Location of the cache partition image.");
+DEFINE_string(metadata_image, "", "Location of the metadata partition image "
+              "to be generated.");
+DEFINE_int32(blank_metadata_image_mb, 16,
+             "The size of the blank metadata image to generate, MB.");
+DEFINE_int32(cpus, 2, "Virtual CPU count.");
+DEFINE_string(data_image, "", "Location of the data partition image.");
+DEFINE_string(data_policy, "use_existing", "How to handle userdata partition."
+            " Either 'use_existing', 'create_if_missing', 'resize_up_to', or "
+            "'always_create'.");
+DEFINE_int32(blank_data_image_mb, 0,
+             "The size of the blank data image to generate, MB.");
+DEFINE_string(blank_data_image_fmt, "ext4",
+              "The fs format for the blank data image. Used with mkfs.");
+DEFINE_string(qemu_gdb, "",
+              "Debug flag to pass to qemu. e.g. -qemu_gdb=tcp::1234");
+
+DEFINE_int32(x_res, 720, "Width of the screen in pixels");
+DEFINE_int32(y_res, 1280, "Height of the screen in pixels");
+DEFINE_int32(dpi, 160, "Pixels per inch for the screen");
+DEFINE_int32(refresh_rate_hz, 60, "Screen refresh rate in Hertz");
+DEFINE_int32(num_screen_buffers, 3, "The number of screen buffers");
+DEFINE_string(kernel_path, "",
+              "Path to the kernel. Overrides the one from the boot image");
+DEFINE_bool(decompress_kernel, false,
+            "Whether to decompress the kernel image. Required for crosvm.");
+DEFINE_string(kernel_decompresser_executable,
+              vsoc::DefaultHostArtifactsPath("bin/extract-vmlinux"),
+             "Path to the extract-vmlinux executable.");
+DEFINE_string(extra_kernel_cmdline, "",
+              "Additional flags to put on the kernel command line");
+DEFINE_int32(loop_max_part, 7, "Maximum number of loop partitions");
+DEFINE_string(console, "ttyS0", "Console device for the guest kernel.");
+DEFINE_string(androidboot_console, "ttyS1",
+              "Console device for the Android framework");
+DEFINE_string(
+    hardware_name, "",
+    "The codename of the device's hardware, one of {cutf_ivsh, cutf_cvm}");
+DEFINE_string(guest_security, "selinux",
+              "The security module to use in the guest");
+DEFINE_bool(guest_enforce_security, false,
+            "Whether to run in enforcing mode (non permissive). Ignored if "
+            "-guest_security is empty.");
+DEFINE_bool(guest_audit_security, true,
+            "Whether to log security audits.");
+DEFINE_string(boot_image, "", "Location of cuttlefish boot image.");
+DEFINE_int32(memory_mb, 2048,
+             "Total amount of memory available for guest, MB.");
+std::string g_default_mempath{vsoc::GetDefaultMempath()};
+DEFINE_string(mempath, g_default_mempath.c_str(),
+              "Target location for the shmem file.");
+DEFINE_string(mobile_interface, "", // default handled on ParseCommandLine
+              "Network interface to use for mobile networking");
+DEFINE_string(mobile_tap_name, "", // default handled on ParseCommandLine
+              "The name of the tap interface to use for mobile");
+std::string g_default_serial_number{GetPerInstanceDefault("CUTTLEFISHCVD")};
+DEFINE_string(serial_number, g_default_serial_number.c_str(),
+              "Serial number to use for the device");
+DEFINE_string(instance_dir, "", // default handled on ParseCommandLine
+              "A directory to put all instance specific files");
+DEFINE_string(
+    vm_manager, vm_manager::QemuManager::name(),
+    "What virtual machine manager to use, one of {qemu_cli, crosvm}");
+DEFINE_string(system_image_dir, vsoc::DefaultGuestImagePath(""),
+              "Location of the system partition images.");
+DEFINE_string(vendor_image, "", "Location of the vendor partition image.");
+DEFINE_string(product_image, "", "Location of the product partition image.");
+
+DEFINE_bool(deprecated_boot_completed, false, "Log boot completed message to"
+            " host kernel. This is only used during transition of our clients."
+            " Will be deprecated soon.");
+DEFINE_bool(start_vnc_server, true, "Whether to start the vnc server process.");
+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.");
+DEFINE_string(kernel_log_monitor_binary,
+              vsoc::DefaultHostArtifactsPath("bin/kernel_log_monitor"),
+              "Location of the log monitor binary.");
+DEFINE_string(ivserver_binary,
+              vsoc::DefaultHostArtifactsPath("bin/ivserver"),
+              "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.");
+DEFINE_string(socket_vsock_proxy_binary,
+              vsoc::DefaultHostArtifactsPath("bin/socket_vsock_proxy"),
+              "Location of the socket_vsock_proxy binary.");
+DEFINE_string(adb_mode, "",
+              "Mode for ADB connection. Can be 'usb' for USB forwarding, "
+              "'tunnel' for a TCP connection tunneled through VSoC, "
+              "'vsock_tunnel' for a TCP connection tunneled through vsock, "
+              "'native_vsock' for a  direct connection to the guest ADB over "
+              "vsock, 'vsock_half_tunnel' for a TCP connection forwarded to "
+              "the guest ADB server, or a comma separated list of types as in "
+              "'usb,tunnel'");
+DEFINE_bool(run_adb_connector, true,
+            "Maintain adb connection by sending 'adb connect' commands to the "
+            "server. Only relevant with -adb_mode=tunnel or vsock_tunnel");
+DEFINE_string(adb_connector_binary,
+              vsoc::DefaultHostArtifactsPath("bin/adb_connector"),
+              "Location of the adb_connector binary. Only relevant if "
+              "-run_adb_connector is true");
+DEFINE_int32(vhci_port, GetPerInstanceDefault(0), "VHCI port to use for usb");
+DEFINE_string(guest_mac_address,
+              GetPerInstanceDefault("00:43:56:44:80:"), // 00:43:56:44:80:0x
+              "MAC address of the wifi interface to be created on the guest.");
+DEFINE_string(host_mac_address,
+              "42:00:00:00:00:00",
+              "MAC address of the wifi interface running on the host.");
+DEFINE_string(wifi_tap_name, "", // default handled on ParseCommandLine
+              "The name of the tap interface to use for wifi");
+DEFINE_int32(vsock_guest_cid,
+             vsoc::GetDefaultPerInstanceVsockCid(),
+             "Guest identifier for vsock. Disabled if under 3.");
+
+// TODO(b/72969289) This should be generated
+DEFINE_string(dtb, "", "Path to the cuttlefish.dtb file");
+DEFINE_string(gsi_fstab,
+              vsoc::DefaultHostArtifactsPath("config/gsi.fstab"),
+              "Path to the GSI fstab file");
+
+DEFINE_string(uuid, vsoc::GetPerInstanceDefault(vsoc::kDefaultUuidPrefix),
+              "UUID to use for the device. Random if not specified");
+DEFINE_bool(daemon, false,
+            "Run cuttlefish in background, the launcher exits on boot "
+            "completed/failed");
+
+DEFINE_string(device_title, "", "Human readable name for the instance, "
+              "used by the vnc_server for its server title");
+DEFINE_string(setupwizard_mode, "DISABLED",
+            "One of DISABLED,OPTIONAL,REQUIRED");
+
+DEFINE_string(qemu_binary,
+              "/usr/bin/qemu-system-x86_64",
+              "The qemu binary to use");
+DEFINE_string(crosvm_binary,
+              vsoc::DefaultHostArtifactsPath("bin/crosvm"),
+              "The Crosvm binary to use");
+DEFINE_bool(restart_subprocesses, true, "Restart any crashed host process");
+DEFINE_bool(run_e2e_test, true, "Run e2e test after device launches");
+DEFINE_string(e2e_test_binary,
+              vsoc::DefaultHostArtifactsPath("bin/host_region_e2e_test"),
+              "Location of the region end to end test binary");
+DEFINE_string(logcat_receiver_binary,
+              vsoc::DefaultHostArtifactsPath("bin/logcat_receiver"),
+              "Binary for the logcat server");
+DEFINE_string(logcat_mode, "", "How to send android's log messages from "
+                               "guest to host. One of [serial, vsock]");
+DEFINE_int32(logcat_vsock_port, vsoc::GetPerInstanceDefault(5620),
+             "The port for logcat over vsock");
+DEFINE_int32(frames_vsock_port, vsoc::GetPerInstanceDefault(5580),
+             "The vsock port to receive frames from the guest on");
+namespace {
+
+template<typename S, typename T>
+static std::string concat(const S& s, const T& t) {
+  std::ostringstream os;
+  os << s << t;
+  return os.str();
+}
+
+bool ResolveInstanceFiles() {
+  if (FLAGS_system_image_dir.empty()) {
+    LOG(ERROR) << "--system_image_dir must be specified.";
+    return false;
+  }
+
+  // If user did not specify location of either of these files, expect them to
+  // be placed in --system_image_dir location.
+  std::string default_system_image = FLAGS_system_image_dir + "/system.img";
+  SetCommandLineOptionWithMode("system_image", default_system_image.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  std::string default_boot_image = FLAGS_system_image_dir + "/boot.img";
+  SetCommandLineOptionWithMode("boot_image", default_boot_image.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  std::string default_cache_image = FLAGS_system_image_dir + "/cache.img";
+  SetCommandLineOptionWithMode("cache_image", default_cache_image.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  std::string default_data_image = FLAGS_system_image_dir + "/userdata.img";
+  SetCommandLineOptionWithMode("data_image", default_data_image.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  std::string default_vendor_image = FLAGS_system_image_dir + "/vendor.img";
+  SetCommandLineOptionWithMode("vendor_image", default_vendor_image.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  std::string default_metadata_image = FLAGS_system_image_dir + "/metadata.img";
+  SetCommandLineOptionWithMode("metadata_image", default_metadata_image.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  std::string default_product_image = FLAGS_system_image_dir + "/product.img";
+  SetCommandLineOptionWithMode("product_image", default_product_image.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+
+  return true;
+}
+
+std::string GetCuttlefishEnvPath() {
+  return cvd::StringFromEnv("HOME", ".") + "/.cuttlefish.sh";
+}
+
+// Initializes the config object and saves it to file. It doesn't return it, all
+// further uses of the config should happen through the singleton
+bool InitializeCuttlefishConfiguration(
+    const cvd::BootImageUnpacker& boot_image_unpacker) {
+  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)) {
+    LOG(ERROR) << "Invalid vm_manager: " << FLAGS_vm_manager;
+    return false;
+  }
+  tmp_config_obj.set_vm_manager(FLAGS_vm_manager);
+
+  tmp_config_obj.set_serial_number(FLAGS_serial_number);
+
+  tmp_config_obj.set_cpus(FLAGS_cpus);
+  tmp_config_obj.set_memory_mb(FLAGS_memory_mb);
+
+  tmp_config_obj.set_dpi(FLAGS_dpi);
+  tmp_config_obj.set_setupwizard_mode(FLAGS_setupwizard_mode);
+  tmp_config_obj.set_x_res(FLAGS_x_res);
+  tmp_config_obj.set_y_res(FLAGS_y_res);
+  tmp_config_obj.set_num_screen_buffers(FLAGS_num_screen_buffers);
+  tmp_config_obj.set_refresh_rate_hz(FLAGS_refresh_rate_hz);
+  tmp_config_obj.set_gdb_flag(FLAGS_qemu_gdb);
+  std::vector<std::string> adb = cvd::StrSplit(FLAGS_adb_mode, ',');
+  tmp_config_obj.set_adb_mode(std::set<std::string>(adb.begin(), adb.end()));
+  tmp_config_obj.set_adb_ip_and_port("127.0.0.1:" + std::to_string(GetHostPort()));
+
+  tmp_config_obj.set_device_title(FLAGS_device_title);
+  if (FLAGS_kernel_path.size()) {
+    tmp_config_obj.set_kernel_image_path(FLAGS_kernel_path);
+    tmp_config_obj.set_use_unpacked_kernel(false);
+  } else {
+    tmp_config_obj.set_kernel_image_path(
+        tmp_config_obj.PerInstancePath("kernel"));
+    tmp_config_obj.set_use_unpacked_kernel(true);
+  }
+  tmp_config_obj.set_decompress_kernel(FLAGS_decompress_kernel);
+  if (FLAGS_decompress_kernel) {
+    tmp_config_obj.set_decompressed_kernel_image_path(
+        tmp_config_obj.PerInstancePath("vmlinux"));
+  }
+
+  auto ramdisk_path = tmp_config_obj.PerInstancePath("ramdisk.img");
+  bool use_ramdisk = boot_image_unpacker.HasRamdiskImage();
+  if (!use_ramdisk) {
+    LOG(INFO) << "No ramdisk present; assuming system-as-root build";
+    ramdisk_path = "";
+  }
+
+  // This needs to be done here because the dtb path depends on the presence of
+  // the ramdisk
+  if (FLAGS_dtb.empty()) {
+    if (use_ramdisk) {
+      FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/initrd-root.dtb");
+    } else {
+      FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/system-root.dtb");
+    }
+  }
+
+  tmp_config_obj.add_kernel_cmdline(boot_image_unpacker.kernel_cmdline());
+  if (!use_ramdisk) {
+    tmp_config_obj.add_kernel_cmdline("root=/dev/vda");
+  }
+  tmp_config_obj.add_kernel_cmdline("init=/init");
+  tmp_config_obj.add_kernel_cmdline(
+      concat("androidboot.serialno=", FLAGS_serial_number));
+  tmp_config_obj.add_kernel_cmdline("mac80211_hwsim.radios=0");
+  tmp_config_obj.add_kernel_cmdline(concat("androidboot.lcd_density=", FLAGS_dpi));
+  tmp_config_obj.add_kernel_cmdline(
+      concat("androidboot.setupwizard_mode=", FLAGS_setupwizard_mode));
+  tmp_config_obj.add_kernel_cmdline(concat("loop.max_part=", FLAGS_loop_max_part));
+  if (!FLAGS_console.empty()) {
+    tmp_config_obj.add_kernel_cmdline(concat("console=", FLAGS_console));
+  }
+  if (!FLAGS_androidboot_console.empty()) {
+    tmp_config_obj.add_kernel_cmdline(
+        concat("androidboot.console=", FLAGS_androidboot_console));
+  }
+  if (!FLAGS_hardware_name.empty()) {
+    tmp_config_obj.add_kernel_cmdline(
+        concat("androidboot.hardware=", FLAGS_hardware_name));
+  }
+  if (FLAGS_logcat_mode == cvd::kLogcatVsockMode) {
+    tmp_config_obj.add_kernel_cmdline(concat("androidboot.vsock_logcat_port=",
+                                             FLAGS_logcat_vsock_port));
+  }
+  tmp_config_obj.set_hardware_name(FLAGS_hardware_name);
+  if (!FLAGS_guest_security.empty()) {
+    tmp_config_obj.add_kernel_cmdline(concat("security=", FLAGS_guest_security));
+    if (FLAGS_guest_enforce_security) {
+      tmp_config_obj.add_kernel_cmdline("enforcing=1");
+    } else {
+      tmp_config_obj.add_kernel_cmdline("enforcing=0");
+      tmp_config_obj.add_kernel_cmdline("androidboot.selinux=permissive");
+    }
+    if (FLAGS_guest_audit_security) {
+      tmp_config_obj.add_kernel_cmdline("audit=1");
+    } else {
+      tmp_config_obj.add_kernel_cmdline("audit=0");
+    }
+  }
+  if (FLAGS_run_e2e_test) {
+    tmp_config_obj.add_kernel_cmdline("androidboot.vsoc_e2e_test=1");
+  }
+  if (FLAGS_extra_kernel_cmdline.size()) {
+    tmp_config_obj.add_kernel_cmdline(FLAGS_extra_kernel_cmdline);
+  }
+
+  tmp_config_obj.set_ramdisk_image_path(ramdisk_path);
+  tmp_config_obj.set_system_image_path(FLAGS_system_image);
+  tmp_config_obj.set_cache_image_path(FLAGS_cache_image);
+  tmp_config_obj.set_data_image_path(FLAGS_data_image);
+  tmp_config_obj.set_vendor_image_path(FLAGS_vendor_image);
+  tmp_config_obj.set_metadata_image_path(FLAGS_metadata_image);
+  tmp_config_obj.set_product_image_path(FLAGS_product_image);
+  tmp_config_obj.set_dtb_path(FLAGS_dtb);
+  tmp_config_obj.set_gsi_fstab_path(FLAGS_gsi_fstab);
+
+  tmp_config_obj.set_mempath(FLAGS_mempath);
+  tmp_config_obj.set_ivshmem_qemu_socket_path(
+      tmp_config_obj.PerInstancePath("ivshmem_socket_qemu"));
+  tmp_config_obj.set_ivshmem_client_socket_path(
+      tmp_config_obj.PerInstancePath("ivshmem_socket_client"));
+  tmp_config_obj.set_ivshmem_vector_count(memory_layout.GetRegions().size());
+
+  if (AdbUsbEnabled(tmp_config_obj)) {
+    tmp_config_obj.set_usb_v1_socket_name(tmp_config_obj.PerInstancePath("usb-v1"));
+    tmp_config_obj.set_vhci_port(FLAGS_vhci_port);
+    tmp_config_obj.set_usb_ip_socket_name(tmp_config_obj.PerInstancePath("usb-ip"));
+  }
+
+  tmp_config_obj.set_kernel_log_socket_name(tmp_config_obj.PerInstancePath("kernel-log"));
+  tmp_config_obj.set_deprecated_boot_completed(FLAGS_deprecated_boot_completed);
+  tmp_config_obj.set_console_path(tmp_config_obj.PerInstancePath("console"));
+  tmp_config_obj.set_logcat_path(tmp_config_obj.PerInstancePath("logcat"));
+  tmp_config_obj.set_logcat_receiver_binary(FLAGS_logcat_receiver_binary);
+  tmp_config_obj.set_launcher_log_path(tmp_config_obj.PerInstancePath("launcher.log"));
+  tmp_config_obj.set_launcher_monitor_socket_path(
+      tmp_config_obj.PerInstancePath("launcher_monitor.sock"));
+
+  tmp_config_obj.set_mobile_bridge_name(FLAGS_mobile_interface);
+  tmp_config_obj.set_mobile_tap_name(FLAGS_mobile_tap_name);
+  ConfigureRil(&tmp_config_obj);
+
+  tmp_config_obj.set_wifi_tap_name(FLAGS_wifi_tap_name);
+
+  tmp_config_obj.set_wifi_guest_mac_addr(FLAGS_guest_mac_address);
+  tmp_config_obj.set_wifi_host_mac_addr(FLAGS_host_mac_address);
+
+  tmp_config_obj.set_vsock_guest_cid(FLAGS_vsock_guest_cid);
+
+  tmp_config_obj.set_entropy_source("/dev/urandom");
+  tmp_config_obj.set_uuid(FLAGS_uuid);
+
+  tmp_config_obj.set_qemu_binary(FLAGS_qemu_binary);
+  tmp_config_obj.set_crosvm_binary(FLAGS_crosvm_binary);
+  tmp_config_obj.set_ivserver_binary(FLAGS_ivserver_binary);
+  tmp_config_obj.set_kernel_log_monitor_binary(FLAGS_kernel_log_monitor_binary);
+
+  tmp_config_obj.set_enable_vnc_server(FLAGS_start_vnc_server);
+  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);
+  tmp_config_obj.set_virtual_usb_manager_binary(
+      FLAGS_virtual_usb_manager_binary);
+  tmp_config_obj.set_socket_forward_proxy_binary(
+      FLAGS_socket_forward_proxy_binary);
+  tmp_config_obj.set_socket_vsock_proxy_binary(FLAGS_socket_vsock_proxy_binary);
+  tmp_config_obj.set_run_as_daemon(FLAGS_daemon);
+  tmp_config_obj.set_run_e2e_test(FLAGS_run_e2e_test);
+  tmp_config_obj.set_e2e_test_binary(FLAGS_e2e_test_binary);
+
+  tmp_config_obj.set_data_policy(FLAGS_data_policy);
+  tmp_config_obj.set_blank_data_image_mb(FLAGS_blank_data_image_mb);
+  tmp_config_obj.set_blank_data_image_fmt(FLAGS_blank_data_image_fmt);
+
+  if(!AdbUsbEnabled(tmp_config_obj)) {
+    tmp_config_obj.disable_usb_adb();
+  }
+
+  tmp_config_obj.set_logcat_mode(FLAGS_logcat_mode);
+  tmp_config_obj.set_logcat_vsock_port(FLAGS_logcat_vsock_port);
+  tmp_config_obj.set_frames_vsock_port(FLAGS_frames_vsock_port);
+  if (!tmp_config_obj.enable_ivserver()) {
+    tmp_config_obj.add_kernel_cmdline(concat("androidboot.vsock_frames_port=",
+                                             FLAGS_frames_vsock_port));
+  }
+
+  tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
+
+  auto config_file = GetConfigFilePath(tmp_config_obj);
+  auto config_link = vsoc::GetGlobalConfigFileLink();
+  // Save the config object before starting any host process
+  if (!tmp_config_obj.SaveToFile(config_file)) {
+    LOG(ERROR) << "Unable to save config object";
+    return false;
+  }
+  setenv(vsoc::kCuttlefishConfigEnvVarName, config_file.c_str(), true);
+  if (symlink(config_file.c_str(), config_link.c_str()) != 0) {
+    LOG(ERROR) << "Failed to create symlink to config file at " << config_link
+               << ": " << strerror(errno);
+    return false;
+  }
+
+  return true;
+}
+
+void SetDefaultFlagsForQemu() {
+  auto default_mobile_interface = GetPerInstanceDefault("cvd-mbr-");
+  SetCommandLineOptionWithMode("mobile_interface",
+                               default_mobile_interface.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_mobile_tap_name = GetPerInstanceDefault("cvd-mtap-");
+  SetCommandLineOptionWithMode("mobile_tap_name",
+                               default_mobile_tap_name.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_wifi_tap_name = GetPerInstanceDefault("cvd-wtap-");
+  SetCommandLineOptionWithMode("wifi_tap_name",
+                               default_wifi_tap_name.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_instance_dir =
+      cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
+  SetCommandLineOptionWithMode("instance_dir",
+                               default_instance_dir.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("hardware_name", "cutf_ivsh",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("adb_mode", "tunnel",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("decompress_kernel", "false",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatSerialMode,
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+}
+
+void SetDefaultFlagsForCrosvm() {
+  auto default_mobile_interface = GetPerInstanceDefault("cvd-mbr-");
+  SetCommandLineOptionWithMode("mobile_interface",
+                               default_mobile_interface.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_mobile_tap_name = GetPerInstanceDefault("cvd-mtap-");
+  SetCommandLineOptionWithMode("mobile_tap_name",
+                               default_mobile_tap_name.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_wifi_tap_name = GetPerInstanceDefault("cvd-wtap-");
+  SetCommandLineOptionWithMode("wifi_tap_name",
+                               default_wifi_tap_name.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_instance_dir =
+      cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
+  SetCommandLineOptionWithMode("instance_dir",
+                               default_instance_dir.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("hardware_name", "cutf_cvm",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("adb_mode", "vsock_tunnel",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("decompress_kernel", "true",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("run_e2e_test", "false",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatVsockMode,
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+}
+
+bool ParseCommandLineFlags(int* argc, char*** argv) {
+  // The config_file is created by the launcher, so the launcher is the only
+  // host process that doesn't use the flag.
+  // Set the default to empty.
+  google::SetCommandLineOptionWithMode("config_file", "",
+                                       gflags::SET_FLAGS_DEFAULT);
+  google::ParseCommandLineNonHelpFlags(argc, argv, true);
+  bool invalid_manager = false;
+  if (FLAGS_vm_manager == vm_manager::QemuManager::name()) {
+    SetDefaultFlagsForQemu();
+  } else if (FLAGS_vm_manager == vm_manager::CrosvmManager::name()) {
+    SetDefaultFlagsForCrosvm();
+  } else {
+    std::cerr << "Unknown Virtual Machine Manager: " << FLAGS_vm_manager
+              << std::endl;
+    invalid_manager = true;
+  }
+  google::HandleCommandLineHelpFlags();
+  if (invalid_manager) {
+    return false;
+  }
+  // Set the env variable to empty (in case the caller passed a value for it).
+  unsetenv(vsoc::kCuttlefishConfigEnvVarName);
+
+  return ResolveInstanceFiles();
+}
+
+bool CleanPriorFiles() {
+  // Everything on the instance directory
+  std::string prior_files = FLAGS_instance_dir + "/*";
+  // The shared memory file
+  prior_files += " " + FLAGS_mempath;
+  // The environment file
+  prior_files += " " + GetCuttlefishEnvPath();
+  // The global link to the config file
+  prior_files += " " + vsoc::GetGlobalConfigFileLink();
+  LOG(INFO) << "Assuming prior files of " << prior_files;
+  std::string fuser_cmd = "fuser " + prior_files + " 2> /dev/null";
+  int rval = std::system(fuser_cmd.c_str());
+  // fuser returns 0 if any of the files are open
+  if (WEXITSTATUS(rval) == 0) {
+    LOG(ERROR) << "Clean aborted: files are in use";
+    return false;
+  }
+  std::string clean_command = "rm -rf " + prior_files;
+  rval = std::system(clean_command.c_str());
+  if (WEXITSTATUS(rval) != 0) {
+    LOG(ERROR) << "Remove of files failed";
+    return false;
+  }
+  return true;
+}
+
+bool DecompressKernel(const std::string& src, const std::string& dst) {
+  cvd::Command decomp_cmd(FLAGS_kernel_decompresser_executable);
+  decomp_cmd.AddParameter(src);
+  auto output_file = cvd::SharedFD::Creat(dst.c_str(), 0666);
+  if (!output_file->IsOpen()) {
+    LOG(ERROR) << "Unable to create decompressed image file: "
+               << output_file->StrError();
+    return false;
+  }
+  decomp_cmd.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdOut, output_file);
+  auto decomp_proc = decomp_cmd.Start(false);
+  return decomp_proc.Started() && decomp_proc.Wait() == 0;
+}
+} // namespace
+
+vsoc::CuttlefishConfig* InitFilesystemAndCreateConfig(int* argc, char*** argv) {
+  if (!ParseCommandLineFlags(argc, argv)) {
+    LOG(ERROR) << "Failed to parse command arguments";
+    exit(LauncherExitCodes::kArgumentParsingError);
+  }
+
+  // Clean up prior files before saving the config file (doing it after would
+  // delete it)
+  if (!CleanPriorFiles()) {
+    LOG(ERROR) << "Failed to clean prior files";
+    exit(LauncherExitCodes::kPrioFilesCleanupError);
+  }
+  // Create instance directory if it doesn't exist.
+  if (!cvd::DirectoryExists(FLAGS_instance_dir.c_str())) {
+    LOG(INFO) << "Setting up " << FLAGS_instance_dir;
+    if (mkdir(FLAGS_instance_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
+      LOG(ERROR) << "Failed to create instance directory: "
+                 << FLAGS_instance_dir << ". Error: " << errno;
+      exit(LauncherExitCodes::kInstanceDirCreationError);
+    }
+  }
+
+  if (!cvd::FileHasContent(FLAGS_boot_image)) {
+    LOG(ERROR) << "File not found: " << FLAGS_boot_image;
+    exit(cvd::kCuttlefishConfigurationInitError);
+  }
+
+  auto boot_img_unpacker = cvd::BootImageUnpacker::FromImage(FLAGS_boot_image);
+
+  if (!InitializeCuttlefishConfiguration(*boot_img_unpacker)) {
+    LOG(ERROR) << "Failed to initialize configuration";
+    exit(LauncherExitCodes::kCuttlefishConfigurationInitError);
+  }
+  // Do this early so that the config object is ready for anything that needs it
+  auto config = vsoc::CuttlefishConfig::Get();
+  if (!config) {
+    LOG(ERROR) << "Failed to obtain config singleton";
+    exit(LauncherExitCodes::kCuttlefishConfigurationInitError);
+  }
+
+  if (!boot_img_unpacker->Unpack(config->ramdisk_image_path(),
+                                 config->use_unpacked_kernel()
+                                     ? config->kernel_image_path()
+                                     : "")) {
+    LOG(ERROR) << "Failed to unpack boot image";
+    exit(LauncherExitCodes::kBootImageUnpackError);
+  }
+
+  if (config->decompress_kernel()) {
+    if (!DecompressKernel(config->kernel_image_path(),
+        config->decompressed_kernel_image_path())) {
+      LOG(ERROR) << "Failed to decompress kernel";
+      exit(LauncherExitCodes::kKernelDecompressError);
+    }
+  }
+
+  ValidateAdbModeFlag(*config);
+
+  // Create data if necessary
+  if (!ApplyDataImagePolicy(*config)) {
+    exit(cvd::kCuttlefishConfigurationInitError);
+  }
+
+  CreateBlankImage(FLAGS_metadata_image, FLAGS_blank_metadata_image_mb, "none");
+
+  // Check that the files exist
+  for (const auto& file :
+       {config->system_image_path(), config->vendor_image_path(),
+        config->cache_image_path(), config->data_image_path(),
+        config->metadata_image_path(), config->product_image_path()}) {
+    if (!cvd::FileHasContent(file.c_str())) {
+      LOG(ERROR) << "File not found: " << file;
+      exit(cvd::kCuttlefishConfigurationInitError);
+    }
+  }
+
+  return config;
+}
+
+std::string GetConfigFilePath(const vsoc::CuttlefishConfig& config) {
+  return config.PerInstancePath("cuttlefish_config.json");
+}
diff --git a/host/commands/launch/flags.h b/host/commands/launch/flags.h
new file mode 100644
index 0000000..9ca6f58
--- /dev/null
+++ b/host/commands/launch/flags.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "host/libs/config/cuttlefish_config.h"
+
+vsoc::CuttlefishConfig* InitFilesystemAndCreateConfig(int* argc, char*** argv);
+std::string GetConfigFilePath(const vsoc::CuttlefishConfig& config);
diff --git a/host/commands/launch/launch.cc b/host/commands/launch/launch.cc
new file mode 100644
index 0000000..766efb0
--- /dev/null
+++ b/host/commands/launch/launch.cc
@@ -0,0 +1,312 @@
+#include "host/commands/launch/launch.h"
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/size_utils.h"
+#include "common/vsoc/shm/screen_layout.h"
+#include "host/commands/launch/launcher_defs.h"
+#include "host/commands/launch/pre_launch_initializers.h"
+#include "host/commands/launch/vsoc_shared_memory.h"
+
+using cvd::LauncherExitCodes;
+using cvd::MonitorEntry;
+
+namespace {
+
+constexpr char kAdbModeTunnel[] = "tunnel";
+constexpr char kAdbModeNativeVsock[] = "native_vsock";
+constexpr char kAdbModeVsockTunnel[] = "vsock_tunnel";
+constexpr char kAdbModeVsockHalfTunnel[] = "vsock_half_tunnel";
+constexpr char kAdbModeUsb[] = "usb";
+
+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() {
+  return std::string{"--host_ports="} + std::to_string(GetHostPort());
+}
+
+std::string GetAdbConnectorTcpArg() {
+  return std::string{"--addresses=127.0.0.1:"} + std::to_string(GetHostPort());
+}
+
+std::string GetAdbConnectorVsockArg(const vsoc::CuttlefishConfig& config) {
+  return std::string{"--addresses=vsock:"}
+      + std::to_string(config.vsock_guest_cid())
+      + std::string{":5555"};
+}
+
+bool AdbModeEnabled(const vsoc::CuttlefishConfig& config, const char* mode) {
+  return config.adb_mode().count(mode) > 0;
+}
+
+bool AdbTunnelEnabled(const vsoc::CuttlefishConfig& config) {
+  return AdbModeEnabled(config, kAdbModeTunnel);
+}
+
+bool AdbVsockTunnelEnabled(const vsoc::CuttlefishConfig& config) {
+  return config.vsock_guest_cid() > 2
+      && AdbModeEnabled(config, kAdbModeVsockTunnel);
+}
+
+bool AdbVsockHalfTunnelEnabled(const vsoc::CuttlefishConfig& config) {
+  return config.vsock_guest_cid() > 2
+      && AdbModeEnabled(config, kAdbModeVsockHalfTunnel);
+}
+
+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()
+      && (tunnel || vsock_tunnel || vsock_half_tunnel);
+}
+
+bool AdbVsockConnectorEnabled(const vsoc::CuttlefishConfig& config) {
+  return config.run_adb_connector()
+      && AdbModeEnabled(config, kAdbModeNativeVsock);
+}
+
+cvd::OnSocketReadyCb GetOnSubprocessExitCallback(
+    const vsoc::CuttlefishConfig& config) {
+  if (config.restart_subprocesses()) {
+    return cvd::ProcessMonitor::RestartOnExitCb;
+  } else {
+    return cvd::ProcessMonitor::DoNotMonitorCb;
+  }
+}
+} // namespace
+
+int GetHostPort() {
+  constexpr int kFirstHostPort = 6520;
+  return vsoc::GetPerInstanceDefault(kFirstHostPort);
+}
+
+bool LogcatReceiverEnabled(const vsoc::CuttlefishConfig& config) {
+  return config.logcat_mode() == cvd::kLogcatVsockMode;
+}
+
+bool AdbUsbEnabled(const vsoc::CuttlefishConfig& config) {
+  return AdbModeEnabled(config, kAdbModeUsb);
+}
+
+void ValidateAdbModeFlag(const vsoc::CuttlefishConfig& config) {
+  if (!AdbUsbEnabled(config) && !AdbTunnelEnabled(config)
+      && !AdbVsockTunnelEnabled(config)) {
+    LOG(INFO) << "ADB not enabled";
+  }
+}
+
+cvd::Command GetIvServerCommand(const vsoc::CuttlefishConfig& config) {
+  // Resize gralloc 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;
+}
+
+// Build the kernel log monitor command. If boot_event_pipe is not NULL, a
+// subscription to boot events from the kernel log monitor will be created and
+// events will appear on *boot_events_pipe
+cvd::Command GetKernelLogMonitorCommand(const vsoc::CuttlefishConfig& config,
+                                        cvd::SharedFD* boot_events_pipe) {
+  auto log_name = config.kernel_log_socket_name();
+  auto server = cvd::SharedFD::SocketLocalServer(log_name.c_str(), false,
+                                                 SOCK_STREAM, 0666);
+  cvd::Command kernel_log_monitor(config.kernel_log_monitor_binary());
+  kernel_log_monitor.AddParameter("-log_server_fd=", server);
+  if (boot_events_pipe) {
+    cvd::SharedFD pipe_write_end;
+    if (!cvd::SharedFD::Pipe(boot_events_pipe, &pipe_write_end)) {
+      LOG(ERROR) << "Unable to create boot events pipe: " << strerror(errno);
+      std::exit(LauncherExitCodes::kPipeIOError);
+    }
+    kernel_log_monitor.AddParameter("-subscriber_fd=", pipe_write_end);
+  }
+  return kernel_log_monitor;
+}
+
+void LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+                                   cvd::ProcessMonitor* process_monitor) {
+  if (!LogcatReceiverEnabled(config)) {
+    return;
+  }
+  auto port = config.logcat_vsock_port();
+  auto socket = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
+  if (!socket->IsOpen()) {
+    LOG(ERROR) << "Unable to create logcat server socket: "
+               << socket->StrError();
+    std::exit(LauncherExitCodes::kLogcatServerError);
+  }
+  cvd::Command cmd(config.logcat_receiver_binary());
+  cmd.AddParameter("-server_fd=", socket);
+  process_monitor->StartSubprocess(std::move(cmd),
+                                   GetOnSubprocessExitCallback(config));
+}
+
+void LaunchUsbServerIfEnabled(const vsoc::CuttlefishConfig& config,
+                              cvd::ProcessMonitor* process_monitor) {
+  if (!AdbUsbEnabled(config)) {
+    return;
+  }
+  auto socket_name = config.usb_v1_socket_name();
+  auto usb_v1_server = cvd::SharedFD::SocketLocalServer(
+      socket_name.c_str(), false, SOCK_STREAM, 0666);
+  if (!usb_v1_server->IsOpen()) {
+    LOG(ERROR) << "Unable to create USB v1 server socket: "
+               << usb_v1_server->StrError();
+    std::exit(cvd::LauncherExitCodes::kUsbV1SocketError);
+  }
+  cvd::Command usb_server(config.virtual_usb_manager_binary());
+  usb_server.AddParameter("-usb_v1_fd=", usb_v1_server);
+  process_monitor->StartSubprocess(std::move(usb_server),
+                                   GetOnSubprocessExitCallback(config));
+}
+
+cvd::SharedFD CreateVncInputServer(const std::string& path) {
+  auto server = cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
+  if (!server->IsOpen()) {
+    LOG(ERROR) << "Unable to create mouse server: "
+               << server->StrError();
+    return cvd::SharedFD();
+  }
+  return server;
+}
+
+void LaunchVNCServerIfEnabled(const vsoc::CuttlefishConfig& config,
+                              cvd::ProcessMonitor* process_monitor,
+                              std::function<bool(MonitorEntry*)> callback) {
+  if (config.enable_vnc_server()) {
+    // Launch the vnc server, don't wait for it to complete
+    auto port_options = "-port=" + std::to_string(config.vnc_server_port());
+    cvd::Command vnc_server(config.vnc_server_binary());
+    vnc_server.AddParameter(port_options);
+    if (!config.enable_ivserver()) {
+      // When the ivserver is not enabled, the vnc touch_server needs to serve
+      // on unix sockets and send input events to whoever connects to it (namely
+      // crosvm)
+      auto touch_server = CreateVncInputServer(config.touch_socket_path());
+      if (!touch_server->IsOpen()) {
+        return;
+      }
+      vnc_server.AddParameter("-touch_fd=", touch_server);
+
+      auto keyboard_server =
+          CreateVncInputServer(config.keyboard_socket_path());
+      if (!keyboard_server->IsOpen()) {
+        return;
+      }
+      vnc_server.AddParameter("-keyboard_fd=", keyboard_server);
+      // TODO(b/128852363): This should be handled through the wayland mock
+      //  instead.
+      // Additionally it receives the frame updates from a virtual socket
+      // instead
+      auto frames_server =
+          cvd::SharedFD::VsockServer(config.frames_vsock_port(), SOCK_STREAM);
+      if (!frames_server->IsOpen()) {
+        return;
+      }
+      vnc_server.AddParameter("-frame_server_fd=", frames_server);
+    }
+    process_monitor->StartSubprocess(std::move(vnc_server), callback);
+  }
+}
+
+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) {
+  if (AdbTcpConnectorEnabled(config)) {
+    cvd::Command adb_connector(config.adb_connector_binary());
+    adb_connector.AddParameter(GetAdbConnectorTcpArg());
+    process_monitor->StartSubprocess(std::move(adb_connector),
+                                     GetOnSubprocessExitCallback(config));
+  }
+  if (AdbVsockConnectorEnabled(config)) {
+    cvd::Command adb_connector(config.adb_connector_binary());
+    adb_connector.AddParameter(GetAdbConnectorVsockArg(config));
+    process_monitor->StartSubprocess(std::move(adb_connector),
+                                     GetOnSubprocessExitCallback(config));
+  }
+}
+
+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());
+    process_monitor->StartSubprocess(std::move(adb_tunnel),
+                                     GetOnSubprocessExitCallback(config));
+  }
+}
+
+void LaunchSocketVsockProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
+                                 const vsoc::CuttlefishConfig& config) {
+  if (AdbVsockTunnelEnabled(config)) {
+    cvd::Command adb_tunnel(config.socket_vsock_proxy_binary());
+    adb_tunnel.AddParameter("--vsock_port=6520");
+    adb_tunnel.AddParameter(
+        std::string{"--tcp_port="} + std::to_string(GetHostPort()));
+    adb_tunnel.AddParameter(std::string{"--vsock_guest_cid="} +
+                            std::to_string(config.vsock_guest_cid()));
+    process_monitor->StartSubprocess(std::move(adb_tunnel),
+                                     GetOnSubprocessExitCallback(config));
+  }
+  if (AdbVsockHalfTunnelEnabled(config)) {
+    cvd::Command adb_tunnel(config.socket_vsock_proxy_binary());
+    adb_tunnel.AddParameter("--vsock_port=5555");
+    adb_tunnel.AddParameter(
+        std::string{"--tcp_port="} + std::to_string(GetHostPort()));
+    adb_tunnel.AddParameter(std::string{"--vsock_guest_cid="} +
+                            std::to_string(config.vsock_guest_cid()));
+    process_monitor->StartSubprocess(std::move(adb_tunnel),
+                                     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/launch/launch.h b/host/commands/launch/launch.h
new file mode 100644
index 0000000..550ce02
--- /dev/null
+++ b/host/commands/launch/launch.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <functional>
+
+#include "common/libs/utils/subprocess.h"
+#include "host/commands/launch/process_monitor.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+int GetHostPort();
+bool AdbUsbEnabled(const vsoc::CuttlefishConfig& config);
+void ValidateAdbModeFlag(const vsoc::CuttlefishConfig& config);
+
+cvd::Command GetIvServerCommand(const vsoc::CuttlefishConfig& config);
+cvd::Command GetKernelLogMonitorCommand(const vsoc::CuttlefishConfig& config,
+                                        cvd::SharedFD* boot_events_pipe);
+void LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+                                   cvd::ProcessMonitor* process_monitor);
+void LaunchUsbServerIfEnabled(const vsoc::CuttlefishConfig& config,
+                              cvd::ProcessMonitor* process_monitor);
+void 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);
+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);
diff --git a/host/commands/launch/launcher_defs.h b/host/commands/launch/launcher_defs.h
index 2732fdf..9a78a4e 100644
--- a/host/commands/launch/launcher_defs.h
+++ b/host/commands/launch/launcher_defs.h
@@ -17,6 +17,9 @@
 
 namespace cvd {
 
+constexpr char kLogcatSerialMode[] = "serial";
+constexpr char kLogcatVsockMode[] = "vsock";
+
 enum LauncherExitCodes : int {
   kSuccess = 0,
   kArgumentParsingError = 1,
@@ -34,6 +37,9 @@
   kMonitorCreationFailed = 13,
   kServerError = 14,
   kUsbV1SocketError = 15,
+  kE2eTestFailed = 16,
+  kKernelDecompressError = 17,
+  kLogcatServerError = 18,
 };
 
 // Actions supported by the launcher server
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index f9d453b..ff2c7dd 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -37,6 +37,7 @@
 
 #include <gflags/gflags.h>
 #include <glog/logging.h>
+#include <host/libs/vm_manager/crosvm_manager.h>
 
 #include "common/libs/fs/shared_fd.h"
 #include "common/libs/fs/shared_select.h"
@@ -48,764 +49,151 @@
 #include "common/vsoc/lib/vsoc_memory.h"
 #include "common/vsoc/shm/screen_layout.h"
 #include "host/commands/launch/boot_image_unpacker.h"
+#include "host/commands/launch/data_image.h"
+#include "host/commands/launch/flags.h"
+#include "host/commands/launch/launch.h"
 #include "host/commands/launch/launcher_defs.h"
-#include "host/commands/launch/pre_launch_initializers.h"
+#include "host/commands/launch/process_monitor.h"
 #include "host/commands/launch/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/vm_manager.h"
-#include "host/libs/vm_manager/libvirt_manager.h"
 #include "host/libs/vm_manager/qemu_manager.h"
 
 using vsoc::GetPerInstanceDefault;
 using cvd::LauncherExitCodes;
 
-DEFINE_string(
-    system_image, "",
-    "Path to the system image, if empty it is assumed to be a file named "
-    "system.img in the directory specified by -system_image_dir");
-DEFINE_string(cache_image, "", "Location of the cache partition image.");
-DEFINE_int32(cpus, 2, "Virtual CPU count.");
-DEFINE_string(data_image, "", "Location of the data partition image.");
-DEFINE_string(data_policy, "use_existing", "How to handle userdata partition."
-            " Either 'use_existing', 'create_if_missing', 'resize_up_to', or "
-            "'always_create'.");
-DEFINE_int32(blank_data_image_mb, 0,
-             "The size of the blank data image to generate, MB.");
-DEFINE_string(blank_data_image_fmt, "ext4",
-              "The fs format for the blank data image. Used with mkfs.");
-DEFINE_string(qemu_gdb, "",
-              "Debug flag to pass to qemu. e.g. -qemu_gdb=tcp::1234");
-
-DEFINE_int32(x_res, 720, "Width of the screen in pixels");
-DEFINE_int32(y_res, 1280, "Height of the screen in pixels");
-DEFINE_int32(dpi, 160, "Pixels per inch for the screen");
-DEFINE_int32(refresh_rate_hz, 60, "Screen refresh rate in Hertz");
-DEFINE_int32(num_screen_buffers, 3, "The number of screen buffers");
-
-DEFINE_bool(disable_app_armor_security, false,
-            "Disable AppArmor security in libvirt. For debug only.");
-DEFINE_bool(disable_dac_security, false,
-            "Disable DAC security in libvirt. For debug only.");
-DEFINE_string(kernel_path, "",
-              "Path to the kernel. Overrides the one from the boot image");
-DEFINE_string(extra_kernel_cmdline, "",
-              "Additional flags to put on the kernel command line");
-DEFINE_int32(loop_max_part, 7, "Maximum number of loop partitions");
-DEFINE_string(console, "ttyS0", "Console device for the guest kernel.");
-DEFINE_string(androidboot_console, "ttyS1",
-              "Console device for the Android framework");
-DEFINE_string(hardware_name, "vsoc",
-              "The codename of the device's hardware");
-DEFINE_string(guest_security, "selinux",
-              "The security module to use in the guest");
-DEFINE_bool(guest_enforce_security, true,
-            "Whether to run in enforcing mode (non permissive). Ignored if "
-            "-guest_security is empty.");
-DEFINE_bool(guest_audit_security, true,
-            "Whether to log security audits.");
-DEFINE_string(boot_image, "", "Location of cuttlefish boot image.");
-DEFINE_int32(memory_mb, 2048,
-             "Total amount of memory available for guest, MB.");
-std::string g_default_mempath{vsoc::GetDefaultMempath()};
-DEFINE_string(mempath, g_default_mempath.c_str(),
-              "Target location for the shmem file.");
-DEFINE_string(mobile_interface, "", // default handled on ParseCommandLine
-              "Network interface to use for mobile networking");
-DEFINE_string(mobile_tap_name, "", // default handled on ParseCommandLine
-              "The name of the tap interface to use for mobile");
-std::string g_default_serial_number{GetPerInstanceDefault("CUTTLEFISHCVD")};
-DEFINE_string(serial_number, g_default_serial_number.c_str(),
-              "Serial number to use for the device");
-DEFINE_string(instance_dir, "", // default handled on ParseCommandLine
-              "A directory to put all instance specific files");
-DEFINE_string(
-    vm_manager,
-    vsoc::HostSupportsQemuCli() ? vm_manager::QemuManager::name()
-                                : vm_manager::LibvirtManager::name(),
-    "What virtual machine manager to use, one of libvirt or qemu_cli");
-DEFINE_string(system_image_dir, vsoc::DefaultGuestImagePath(""),
-              "Location of the system partition images.");
-DEFINE_string(vendor_image, "", "Location of the vendor partition image.");
-
-DEFINE_bool(deprecated_boot_completed, false, "Log boot completed message to"
-            " host kernel. This is only used during transition of our clients."
-            " Will be deprecated soon.");
-DEFINE_bool(start_vnc_server, true, "Whether to start the vnc server process.");
-DEFINE_string(vnc_server_binary,
-              vsoc::DefaultHostArtifactsPath("bin/vnc_server"),
-              "Location of the vnc server binary.");
-DEFINE_string(virtual_usb_manager_binary,
-              vsoc::DefaultHostArtifactsPath("bin/virtual_usb_manager"),
-              "Location of the virtual usb manager binary.");
-DEFINE_string(kernel_log_monitor_binary,
-              vsoc::DefaultHostArtifactsPath("bin/kernel_log_monitor"),
-              "Location of the log monitor binary.");
-DEFINE_string(ivserver_binary,
-              vsoc::DefaultHostArtifactsPath("bin/ivserver"),
-              "Location of the ivshmem server binary.");
-DEFINE_int32(vnc_server_port, GetPerInstanceDefault(6444),
-             "The port on which the vnc server should listen");
-DEFINE_string(socket_forward_proxy_binary,
-              vsoc::DefaultHostArtifactsPath("bin/socket_forward_proxy"),
-              "Location of the socket_forward_proxy binary.");
-DEFINE_string(adb_mode, "tunnel",
-              "Mode for adb connection. Can be 'usb' for usb forwarding, "
-              "'tunnel' for tcp connection, or a comma separated list of types "
-              "as in 'usb,tunnel'");
-DEFINE_bool(run_adb_connector, true,
-            "Maintain adb connection by sending 'adb connect' commands to the "
-            "server. Only relevant with -adb_mode=tunnel");
-DEFINE_string(adb_connector_binary,
-              vsoc::DefaultHostArtifactsPath("bin/adb_connector"),
-              "Location of the adb_connector binary. Only relevant if "
-              "-run_adb_connector is true");
-DEFINE_int32(vhci_port, GetPerInstanceDefault(0), "VHCI port to use for usb");
-DEFINE_string(guest_mac_address,
-              GetPerInstanceDefault("00:43:56:44:80:"), // 00:43:56:44:80:0x
-              "MAC address of the wifi interface to be created on the guest.");
-DEFINE_string(host_mac_address,
-              "42:00:00:00:00:00",
-              "MAC address of the wifi interface running on the host.");
-DEFINE_string(wifi_interface, "", // default handled on ParseCommandLine
-              "Network interface to use for wifi");
-DEFINE_string(wifi_tap_name, "", // default handled on ParseCommandLine
-              "The name of the tap interface to use for wifi");
-// TODO(b/72969289) This should be generated
-DEFINE_string(dtb, "", "Path to the cuttlefish.dtb file");
-
-DEFINE_string(uuid, vsoc::GetPerInstanceDefault(vsoc::kDefaultUuidPrefix),
-              "UUID to use for the device. Random if not specified");
-DEFINE_bool(daemon, false,
-            "Run cuttlefish in background, the launcher exits on boot "
-            "completed/failed");
-
-DEFINE_string(device_title, "", "Human readable name for the instance, "
-              "used by the vnc_server for its server title");
-DEFINE_string(setupwizard_mode, "DISABLED",
-	      "One of DISABLED,OPTIONAL,REQUIRED");
-
-DEFINE_string(qemu_binary,
-              "/usr/bin/qemu-system-x86_64",
-              "The qemu binary to use");
-DEFINE_string(hypervisor_uri, "qemu:///system", "Hypervisor cannonical uri.");
-DEFINE_bool(log_xml, false, "Log the XML machine configuration");
-
-DECLARE_string(config_file);
-
 namespace {
-const std::string kDataPolicyUseExisting = "use_existing";
-const std::string kDataPolicyCreateIfMissing = "create_if_missing";
-const std::string kDataPolicyAlwaysCreate = "always_create";
-const std::string kDataPolicyResizeUpTo= "resize_up_to";
 
-constexpr char kAdbModeTunnel[] = "tunnel";
-constexpr char kAdbModeUsb[] = "usb";
-
-void CreateBlankImage(
-    const std::string& image, int image_mb, const std::string& image_fmt) {
-  LOG(INFO) << "Creating " << image;
-  std::string of = "of=";
-  of += image;
-  std::string count = "count=";
-  count += std::to_string(image_mb);
-  cvd::execute({"/bin/dd", "if=/dev/zero", of, "bs=1M", count});
-  cvd::execute({"/sbin/mkfs", "-t", image_fmt, image}, {"PATH=/sbin"});
-}
-
-void RemoveFile(const std::string& file) {
-  LOG(INFO) << "Removing " << file;
-  cvd::execute({"/bin/rm", "-f", file});
-}
-
-const int FSCK_ERROR_CORRECTED = 1;
-const int FSCK_ERROR_CORRECTED_REQUIRES_REBOOT = 2;
-
-bool ForceFsckImage(const char* data_image) {
-  int fsck_status = cvd::execute({"/sbin/e2fsck", "-y", "-f", data_image});
-  if (fsck_status & ~(FSCK_ERROR_CORRECTED|FSCK_ERROR_CORRECTED_REQUIRES_REBOOT)) {
-    LOG(ERROR) << "`e2fsck -y -f " << data_image << "` failed with code "
-               << fsck_status;
-    return false;
-  }
-  return true;
-}
-
-bool ResizeImage(const char* data_image, int data_image_mb) {
-  auto file_mb = cvd::FileSize(data_image) >> 20;
-  if (file_mb > data_image_mb) {
-    LOG(ERROR) << data_image << " is already " << file_mb << " MB, will not "
-               << "resize down.";
-    return false;
-  } else if (file_mb == data_image_mb) {
-    LOG(INFO) << data_image << " is already the right size";
-    return true;
+cvd::OnSocketReadyCb GetOnSubprocessExitCallback(
+    const vsoc::CuttlefishConfig& config) {
+  if (config.restart_subprocesses()) {
+    return cvd::ProcessMonitor::RestartOnExitCb;
   } else {
-    off_t raw_target = static_cast<off_t>(data_image_mb) << 20;
-    int truncate_status =
-        cvd::SharedFD::Open(data_image, O_RDWR)->Truncate(raw_target);
-    if (truncate_status != 0) {
-      LOG(ERROR) << "`truncate --size=" << data_image_mb << "M "
-                  << data_image << "` failed with code " << truncate_status;
-      return false;
+    return cvd::ProcessMonitor::DoNotMonitorCb;
+  }
+}
+
+// Maintains the state of the boot process, once a final state is reached
+// (success or failure) it sends the appropriate exit code to the foreground
+// launcher process
+class CvdBootStateMachine {
+ public:
+  CvdBootStateMachine(cvd::SharedFD fg_launcher_pipe)
+      : fg_launcher_pipe_(fg_launcher_pipe), state_(kBootStarted) {}
+
+  // Returns true if the machine is left in a final state
+  bool OnBootEvtReceived(cvd::SharedFD boot_events_pipe) {
+    monitor::BootEvent evt;
+    auto bytes_read = boot_events_pipe->Read(&evt, sizeof(evt));
+    if (bytes_read != sizeof(evt)) {
+      LOG(ERROR) << "Fail to read a complete event, read " << bytes_read
+                 << " bytes only instead of the expected " << sizeof(evt);
+      state_ |= kGuestBootFailed;
+    } else if (evt == monitor::BootEvent::BootCompleted) {
+      LOG(INFO) << "Virtual device booted successfully";
+      state_ |= kGuestBootCompleted;
+    } else if (evt == monitor::BootEvent::BootFailed) {
+      LOG(ERROR) << "Virtual device failed to boot";
+      state_ |= kGuestBootFailed;
+    }  // Ignore the other signals
+
+    return MaybeWriteToForegroundLauncher();
+  }
+
+  bool  OnE2eTestCompleted(int exit_code) {
+    if (exit_code != 0) {
+      LOG(ERROR) << "VSoC e2e test failed";
+      state_ |= kE2eTestFailed;
+    } else {
+      LOG(INFO) << "VSoC e2e test passed";
+      state_ |= kE2eTestPassed;
     }
-    bool fsck_success = ForceFsckImage(data_image);
-    if (!fsck_success) {
-      return false;
-    }
-    int resize_status = cvd::execute({"/sbin/resize2fs", data_image});
-    if (resize_status != 0) {
-      LOG(ERROR) << "`resize2fs " << data_image << "` failed with code "
-                 << resize_status;
-      return false;
-    }
-    fsck_success = ForceFsckImage(data_image);
-    if (!fsck_success) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool ApplyDataImagePolicy(const char* data_image) {
-  bool data_exists = cvd::FileHasContent(data_image);
-  bool remove{};
-  bool create{};
-  bool resize{};
-
-  if (FLAGS_data_policy == kDataPolicyUseExisting) {
-    if (!data_exists) {
-      LOG(ERROR) << "Specified data image file does not exists: " << data_image;
-      return false;
-    }
-    if (FLAGS_blank_data_image_mb > 0) {
-      LOG(ERROR) << "You should NOT use -blank_data_image_mb with -data_policy="
-                 << kDataPolicyUseExisting;
-      return false;
-    }
-    create = false;
-    remove = false;
-    resize = false;
-  } else if (FLAGS_data_policy == kDataPolicyAlwaysCreate) {
-    remove = data_exists;
-    create = true;
-    resize = false;
-  } else if (FLAGS_data_policy == kDataPolicyCreateIfMissing) {
-    create = !data_exists;
-    remove = false;
-    resize = false;
-  } else if (FLAGS_data_policy == kDataPolicyResizeUpTo) {
-    create = false;
-    remove = false;
-    resize = true;
-  } else {
-    LOG(ERROR) << "Invalid data_policy: " << FLAGS_data_policy;
-    return false;
+    return MaybeWriteToForegroundLauncher();
   }
 
-  if (remove) {
-    RemoveFile(data_image);
+  bool BootCompleted() const {
+    bool boot_completed = state_ & kGuestBootCompleted;
+    bool test_passed_or_disabled =
+        (state_ & kE2eTestPassed) || (state_ & kE2eTestDisabled);
+    bool something_failed =
+        state_ & ~(kGuestBootCompleted | kE2eTestPassed | kE2eTestDisabled);
+    return boot_completed && test_passed_or_disabled && !something_failed;
   }
 
-  if (create) {
-    if (FLAGS_blank_data_image_mb <= 0) {
-      LOG(ERROR) << "-blank_data_image_mb is required to create data image";
-      return false;
-    }
-    CreateBlankImage(
-        data_image, FLAGS_blank_data_image_mb, FLAGS_blank_data_image_fmt);
-  } else if (resize) {
-    if (!data_exists) {
-      LOG(ERROR) << data_image << " does not exist, but resizing was requested";
-      return false;
-    }
-    return ResizeImage(data_image, FLAGS_blank_data_image_mb);
-  } else {
-    LOG(INFO) << data_image << " exists. Not creating it.";
+  bool DisableE2eTests() {
+    state_ |= kE2eTestDisabled;
+    return MaybeWriteToForegroundLauncher();
   }
 
-  return true;
-}
-
-std::string GetConfigFilePath(const vsoc::CuttlefishConfig& config) {
-  return config.PerInstancePath("cuttlefish_config.json");
-}
-
-std::string GetGuestPortArg() {
-  constexpr int kEmulatorPort = 5555;
-  return std::string{"--guest_ports="} + std::to_string(kEmulatorPort);
-}
-
-int GetHostPort() {
-  constexpr int kFirstHostPort = 6520;
-  return vsoc::GetPerInstanceDefault(kFirstHostPort);
-}
-
-std::string GetHostPortArg() {
-  return std::string{"--host_ports="} + std::to_string(GetHostPort());
-}
-
-std::string GetAdbConnectorPortArg() {
-  return std::string{"--ports="} + std::to_string(GetHostPort());
-}
-
-bool AdbModeEnabled(const char* mode) {
-  auto modes = cvd::StrSplit(FLAGS_adb_mode, ',');
-  return std::find(modes.begin(), modes.end(), mode) != modes.end();
-}
-
-bool AdbTunnelEnabled() {
-  return AdbModeEnabled(kAdbModeTunnel);
-}
-
-bool AdbUsbEnabled() {
-  return AdbModeEnabled(kAdbModeUsb);
-}
-
-void ValidateAdbModeFlag() {
-  if (!AdbUsbEnabled() && !AdbTunnelEnabled()) {
-    LOG(INFO) << "ADB not enabled";
-  }
-}
-
-cvd::SharedFD CreateIvServerUnixSocket(const std::string& path) {
-  return cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM,
-                                          0666);
-}
-
-bool AdbConnectorEnabled() {
-  return FLAGS_run_adb_connector && AdbTunnelEnabled();
-}
-
-void LaunchUsbServerIfEnabled(const vsoc::CuttlefishConfig& config) {
-  if (!AdbUsbEnabled()) {
-    return;
-  }
-  auto socket_name = config.usb_v1_socket_name();
-  auto usb_v1_server = cvd::SharedFD::SocketLocalServer(
-      socket_name.c_str(), false, SOCK_STREAM, 0666);
-  if (!usb_v1_server->IsOpen()) {
-    LOG(ERROR) << "Unable to create USB v1 server socket: "
-               << usb_v1_server->StrError();
-    std::exit(cvd::LauncherExitCodes::kUsbV1SocketError);
-  }
-  cvd::Command usb_server(FLAGS_virtual_usb_manager_binary);
-  usb_server.AddParameter("-usb_v1_fd=", usb_v1_server);
-  usb_server.Start();
-}
-
-void LaunchKernelLogMonitor(const vsoc::CuttlefishConfig& config,
-                            cvd::SharedFD boot_events_pipe) {
-  auto log_name = config.kernel_log_socket_name();
-  auto server = cvd::SharedFD::SocketLocalServer(log_name.c_str(), false,
-                                                 SOCK_STREAM, 0666);
-  cvd::Command kernel_log_monitor(FLAGS_kernel_log_monitor_binary);
-  kernel_log_monitor.AddParameter("-log_server_fd=", server);
-  if (boot_events_pipe->IsOpen()) {
-    kernel_log_monitor.AddParameter("-subscriber_fd=", boot_events_pipe);
-  }
-  kernel_log_monitor.Start();
-}
-
-void LaunchIvServer(const vsoc::CuttlefishConfig& config) {
-  // Resize gralloc region
-  auto actual_width = cvd::AlignToPowerOf2(FLAGS_x_res * 4, 4);  // align to 16
-  uint32_t screen_buffers_size =
-      FLAGS_num_screen_buffers *
-      cvd::AlignToPageSize(actual_width * FLAGS_y_res + 16 /* padding */);
-  screen_buffers_size +=
-      (FLAGS_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(FLAGS_ivserver_binary);
-  ivserver.AddParameter(
-      "-qemu_socket_fd=",
-      CreateIvServerUnixSocket(config.ivshmem_qemu_socket_path()));
-  ivserver.AddParameter(
-      "-client_socket_fd=",
-      CreateIvServerUnixSocket(config.ivshmem_client_socket_path()));
-  ivserver.Start();
-}
-
-void LaunchAdbConnectorIfEnabled() {
-  if (AdbConnectorEnabled()) {
-    cvd::Command adb_connector(FLAGS_adb_connector_binary);
-    adb_connector.AddParameter(GetAdbConnectorPortArg());
-    adb_connector.Start();
-  }
-}
-
-void LaunchSocketForwardProxyIfEnabled() {
-  if (AdbTunnelEnabled()) {
-    cvd::Command adb_tunnel(FLAGS_socket_forward_proxy_binary);
-    adb_tunnel.AddParameter(GetGuestPortArg());
-    adb_tunnel.AddParameter(GetHostPortArg());
-    adb_tunnel.Start();
-  }
-}
-
-void LaunchVNCServerIfEnabled() {
-  if (FLAGS_start_vnc_server) {
-    // Launch the vnc server, don't wait for it to complete
-    auto port_options = "-port=" + std::to_string(FLAGS_vnc_server_port);
-    cvd::Command vnc_server(FLAGS_vnc_server_binary);
-    vnc_server.AddParameter(port_options);
-    vnc_server.Start();
-  }
-}
-
-bool ResolveInstanceFiles() {
-  if (FLAGS_system_image_dir.empty()) {
-    LOG(ERROR) << "--system_image_dir must be specified.";
-    return false;
+  bool BootFailed() const {
+    return state_ & (kGuestBootFailed | kE2eTestFailed);
   }
 
-  // If user did not specify location of either of these files, expect them to
-  // be placed in --system_image_dir location.
-  if (FLAGS_system_image.empty()) {
-    FLAGS_system_image = FLAGS_system_image_dir + "/system.img";
+ private:
+  void SendExitCode(cvd::LauncherExitCodes exit_code) {
+    fg_launcher_pipe_->Write(&exit_code, sizeof(exit_code));
+    // The foreground process will exit after receiving the exit code, if we try
+    // to write again we'll get a SIGPIPE
+    fg_launcher_pipe_->Close();
   }
-  if (FLAGS_boot_image.empty()) {
-    FLAGS_boot_image = FLAGS_system_image_dir + "/boot.img";
-  }
-  if (FLAGS_cache_image.empty()) {
-    FLAGS_cache_image = FLAGS_system_image_dir + "/cache.img";
-  }
-  if (FLAGS_data_image.empty()) {
-    FLAGS_data_image = FLAGS_system_image_dir + "/userdata.img";
-  }
-  if (FLAGS_vendor_image.empty()) {
-    FLAGS_vendor_image = FLAGS_system_image_dir + "/vendor.img";
-  }
-
-  // Create data if necessary
-  if (!ApplyDataImagePolicy(FLAGS_data_image.c_str())) {
-    return false;
-  }
-
-  // Check that the files exist
-  for (const auto& file :
-       {FLAGS_system_image, FLAGS_vendor_image, FLAGS_cache_image,
-        FLAGS_data_image, FLAGS_boot_image}) {
-    if (!cvd::FileHasContent(file.c_str())) {
-      LOG(ERROR) << "File not found: " << file;
-      return false;
-    }
-  }
-  return true;
-}
-
-bool UnpackBootImage(const cvd::BootImageUnpacker& boot_image_unpacker,
-                     const vsoc::CuttlefishConfig& config) {
-  if (boot_image_unpacker.HasRamdiskImage()) {
-    if (!boot_image_unpacker.ExtractRamdiskImage(
-            config.ramdisk_image_path())) {
-      LOG(ERROR) << "Error extracting ramdisk from boot image";
-      return false;
-    }
-  }
-  if (!FLAGS_kernel_path.size()) {
-    if (boot_image_unpacker.HasKernelImage()) {
-      if (!boot_image_unpacker.ExtractKernelImage(
-              config.kernel_image_path())) {
-        LOG(ERROR) << "Error extracting kernel from boot image";
+  bool MaybeWriteToForegroundLauncher() {
+    if (fg_launcher_pipe_->IsOpen()) {
+      if (BootCompleted()) {
+        SendExitCode(cvd::LauncherExitCodes::kSuccess);
+      } else if (state_ & kGuestBootFailed) {
+        SendExitCode(cvd::LauncherExitCodes::kVirtualDeviceBootFailed);
+      } else if (state_ & kE2eTestFailed) {
+        SendExitCode(cvd::LauncherExitCodes::kE2eTestFailed);
+      } else {
+        // No final state was reached
         return false;
       }
-    } else {
-      LOG(ERROR) << "No kernel found on boot image";
-      return false;
     }
+    // Either we sent the code before or just sent it, in any case the state is
+    // final
+    return true;
   }
-  return true;
+
+  cvd::SharedFD fg_launcher_pipe_;
+  int state_;
+  static const int kBootStarted = 0;
+  static const int kGuestBootCompleted = 1 << 0;
+  static const int kGuestBootFailed = 1 << 1;
+  static const int kE2eTestPassed = 1 << 2;
+  static const int kE2eTestFailed = 1 << 3;
+  static const int kE2eTestDisabled = 1 << 4;
+};
+
+// Abuse the process monitor to make it call us back when boot events are ready
+void SetUpHandlingOfBootEvents(
+    cvd::ProcessMonitor* process_monitor, cvd::SharedFD boot_events_pipe,
+    std::shared_ptr<CvdBootStateMachine> state_machine) {
+  process_monitor->MonitorExistingSubprocess(
+      // A dummy command, so logs are desciptive
+      cvd::Command("boot_events_listener"),
+      // A dummy subprocess, with the boot events pipe as control socket
+      cvd::Subprocess(-1, boot_events_pipe),
+      [boot_events_pipe, state_machine](cvd::MonitorEntry*) {
+        auto sent_code = state_machine->OnBootEvtReceived(boot_events_pipe);
+        return !sent_code;
+      });
 }
 
-template<typename S, typename T>
-static std::string concat(const S& s, const T& t) {
-  std::ostringstream os;
-  os << s << t;
-  return os.str();
-}
-
-std::string GetCuttlefishEnvPath() {
-  return cvd::StringFromEnv("HOME", ".") + "/.cuttlefish.sh";
-}
-
-// Initializes the config object and saves it to file. It doesn't return it, all
-// further uses of the config should happen through the singleton
-bool InitializeCuttlefishConfiguration(
-    const cvd::BootImageUnpacker& boot_image_unpacker) {
-  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)) {
-    LOG(ERROR) << "Invalid vm_manager: " << FLAGS_vm_manager;
-    return false;
-  }
-  tmp_config_obj.set_vm_manager(FLAGS_vm_manager);
-
-  tmp_config_obj.set_serial_number(FLAGS_serial_number);
-
-  tmp_config_obj.set_cpus(FLAGS_cpus);
-  tmp_config_obj.set_memory_mb(FLAGS_memory_mb);
-
-  tmp_config_obj.set_dpi(FLAGS_dpi);
-  tmp_config_obj.set_setupwizard_mode(FLAGS_setupwizard_mode);
-  tmp_config_obj.set_x_res(FLAGS_x_res);
-  tmp_config_obj.set_y_res(FLAGS_y_res);
-  tmp_config_obj.set_refresh_rate_hz(FLAGS_refresh_rate_hz);
-  tmp_config_obj.set_gdb_flag(FLAGS_qemu_gdb);
-  tmp_config_obj.set_adb_mode(FLAGS_adb_mode);
-  tmp_config_obj.set_adb_ip_and_port("127.0.0.1:" + std::to_string(GetHostPort()));
-
-  tmp_config_obj.set_device_title(FLAGS_device_title);
-  if (FLAGS_kernel_path.size()) {
-    tmp_config_obj.set_kernel_image_path(FLAGS_kernel_path);
+void LaunchE2eTestIfEnabled(cvd::ProcessMonitor* process_monitor,
+                            std::shared_ptr<CvdBootStateMachine> state_machine,
+                            const vsoc::CuttlefishConfig& config) {
+  if (config.run_e2e_test()) {
+    process_monitor->StartSubprocess(
+        cvd::Command(config.e2e_test_binary()),
+        [state_machine](cvd::MonitorEntry* entry) {
+          auto test_result = entry->proc->Wait();
+          state_machine->OnE2eTestCompleted(test_result);
+          return false;
+        });
   } else {
-    tmp_config_obj.set_kernel_image_path(
-        tmp_config_obj.PerInstancePath("kernel"));
+    state_machine->DisableE2eTests();
   }
-
-  auto ramdisk_path = tmp_config_obj.PerInstancePath("ramdisk.img");
-  bool use_ramdisk = boot_image_unpacker.HasRamdiskImage();
-  if (!use_ramdisk) {
-    LOG(INFO) << "No ramdisk present; assuming system-as-root build";
-    ramdisk_path = "";
-  }
-
-  // This needs to be done here because the dtb path depends on the presence of
-  // the ramdisk
-  if (FLAGS_dtb.empty()) {
-    if (use_ramdisk) {
-      FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/initrd-root.dtb");
-    } else {
-      FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/system-root.dtb");
-    }
-  }
-
-  tmp_config_obj.add_kernel_cmdline(boot_image_unpacker.kernel_cmdline());
-  if (!use_ramdisk) {
-    tmp_config_obj.add_kernel_cmdline("root=/dev/vda init=/init");
-  }
-  tmp_config_obj.add_kernel_cmdline(
-      concat("androidboot.serialno=", FLAGS_serial_number));
-  tmp_config_obj.add_kernel_cmdline("mac80211_hwsim.radios=0");
-  tmp_config_obj.add_kernel_cmdline(concat("androidboot.lcd_density=", FLAGS_dpi));
-  tmp_config_obj.add_kernel_cmdline(
-      concat("androidboot.setupwizard_mode=", FLAGS_setupwizard_mode));
-  tmp_config_obj.add_kernel_cmdline(concat("loop.max_part=", FLAGS_loop_max_part));
-  if (!FLAGS_console.empty()) {
-    tmp_config_obj.add_kernel_cmdline(concat("console=", FLAGS_console));
-  }
-  if (!FLAGS_androidboot_console.empty()) {
-    tmp_config_obj.add_kernel_cmdline(
-        concat("androidboot.console=", FLAGS_androidboot_console));
-  }
-  if (!FLAGS_hardware_name.empty()) {
-    tmp_config_obj.add_kernel_cmdline(
-        concat("androidboot.hardware=", FLAGS_hardware_name));
-  }
-  if (!FLAGS_guest_security.empty()) {
-    tmp_config_obj.add_kernel_cmdline(concat("security=", FLAGS_guest_security));
-    if (FLAGS_guest_enforce_security) {
-      tmp_config_obj.add_kernel_cmdline("enforcing=1");
-    } else {
-      tmp_config_obj.add_kernel_cmdline("enforcing=0");
-      tmp_config_obj.add_kernel_cmdline("androidboot.selinux=permissive");
-    }
-    if (FLAGS_guest_audit_security) {
-      tmp_config_obj.add_kernel_cmdline("audit=1");
-    } else {
-      tmp_config_obj.add_kernel_cmdline("audit=0");
-    }
-  }
-  if (FLAGS_extra_kernel_cmdline.size()) {
-    tmp_config_obj.add_kernel_cmdline(FLAGS_extra_kernel_cmdline);
-  }
-
-  tmp_config_obj.set_ramdisk_image_path(ramdisk_path);
-  tmp_config_obj.set_system_image_path(FLAGS_system_image);
-  tmp_config_obj.set_cache_image_path(FLAGS_cache_image);
-  tmp_config_obj.set_data_image_path(FLAGS_data_image);
-  tmp_config_obj.set_vendor_image_path(FLAGS_vendor_image);
-  tmp_config_obj.set_dtb_path(FLAGS_dtb);
-
-  tmp_config_obj.set_mempath(FLAGS_mempath);
-  tmp_config_obj.set_ivshmem_qemu_socket_path(
-      tmp_config_obj.PerInstancePath("ivshmem_socket_qemu"));
-  tmp_config_obj.set_ivshmem_client_socket_path(
-      tmp_config_obj.PerInstancePath("ivshmem_socket_client"));
-  tmp_config_obj.set_ivshmem_vector_count(memory_layout.GetRegions().size());
-
-  if (AdbUsbEnabled()) {
-    tmp_config_obj.set_usb_v1_socket_name(tmp_config_obj.PerInstancePath("usb-v1"));
-    tmp_config_obj.set_vhci_port(FLAGS_vhci_port);
-    tmp_config_obj.set_usb_ip_socket_name(tmp_config_obj.PerInstancePath("usb-ip"));
-  }
-
-  tmp_config_obj.set_kernel_log_socket_name(tmp_config_obj.PerInstancePath("kernel-log"));
-  tmp_config_obj.set_deprecated_boot_completed(FLAGS_deprecated_boot_completed);
-  tmp_config_obj.set_console_path(tmp_config_obj.PerInstancePath("console"));
-  tmp_config_obj.set_logcat_path(tmp_config_obj.PerInstancePath("logcat"));
-  tmp_config_obj.set_launcher_log_path(tmp_config_obj.PerInstancePath("launcher.log"));
-  tmp_config_obj.set_launcher_monitor_socket_path(
-      tmp_config_obj.PerInstancePath("launcher_monitor.sock"));
-
-  tmp_config_obj.set_mobile_bridge_name(FLAGS_mobile_interface);
-  tmp_config_obj.set_mobile_tap_name(FLAGS_mobile_tap_name);
-
-  tmp_config_obj.set_wifi_bridge_name(FLAGS_wifi_interface);
-  tmp_config_obj.set_wifi_tap_name(FLAGS_wifi_tap_name);
-
-  tmp_config_obj.set_wifi_guest_mac_addr(FLAGS_guest_mac_address);
-  tmp_config_obj.set_wifi_host_mac_addr(FLAGS_host_mac_address);
-
-  tmp_config_obj.set_entropy_source("/dev/urandom");
-  tmp_config_obj.set_uuid(FLAGS_uuid);
-
-  tmp_config_obj.set_disable_dac_security(FLAGS_disable_dac_security);
-  tmp_config_obj.set_disable_app_armor_security(FLAGS_disable_app_armor_security);
-
-  tmp_config_obj.set_qemu_binary(FLAGS_qemu_binary);
-  tmp_config_obj.set_hypervisor_uri(FLAGS_hypervisor_uri);
-  tmp_config_obj.set_log_xml(FLAGS_log_xml);
-
-  if(!AdbUsbEnabled()) {
-    tmp_config_obj.disable_usb_adb();
-  }
-
-  tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
-
-  auto config_file = GetConfigFilePath(tmp_config_obj);
-  auto config_link = vsoc::GetGlobalConfigFileLink();
-  // Save the config object before starting any host process
-  if (!tmp_config_obj.SaveToFile(config_file)) {
-    LOG(ERROR) << "Unable to save config object";
-    return false;
-  }
-  setenv(vsoc::kCuttlefishConfigEnvVarName, config_file.c_str(), true);
-  if (symlink(config_file.c_str(), config_link.c_str()) != 0) {
-    LOG(ERROR) << "Failed to create symlink to config file at " << config_link
-               << ": " << strerror(errno);
-    return false;
-  }
-
-  return true;
-}
-
-void SetDefaultFlagsForQemu() {
-  auto default_mobile_interface = GetPerInstanceDefault("cvd-mbr-");
-  SetCommandLineOptionWithMode("mobile_interface",
-                               default_mobile_interface.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-  auto default_mobile_tap_name = GetPerInstanceDefault("cvd-mtap-");
-  SetCommandLineOptionWithMode("mobile_tap_name",
-                               default_mobile_tap_name.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-  auto default_wifi_interface = GetPerInstanceDefault("cvd-wbr-");
-  SetCommandLineOptionWithMode("wifi_interface",
-                               default_wifi_interface.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-  auto default_wifi_tap_name = GetPerInstanceDefault("cvd-wtap-");
-  SetCommandLineOptionWithMode("wifi_tap_name",
-                               default_wifi_tap_name.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-  auto default_instance_dir =
-      cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
-  SetCommandLineOptionWithMode("instance_dir",
-                               default_instance_dir.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-}
-
-void SetDefaultFlagsForLibvirt() {
-  auto default_mobile_interface = GetPerInstanceDefault("cvd-mobile-");
-  SetCommandLineOptionWithMode("mobile_interface",
-                               default_mobile_interface.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-  auto default_mobile_tap_name = GetPerInstanceDefault("amobile");
-  SetCommandLineOptionWithMode("mobile_tap_name",
-                               default_mobile_tap_name.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-  auto default_wifi_interface = GetPerInstanceDefault("cvd-wifi-");
-  SetCommandLineOptionWithMode("wifi_interface",
-                               default_wifi_interface.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-  auto default_wifi_tap_name = GetPerInstanceDefault("awifi");
-  SetCommandLineOptionWithMode("wifi_tap_name",
-                               default_wifi_tap_name.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-  auto default_instance_dir =
-      "/var/run/libvirt-" +
-      vsoc::GetPerInstanceDefault(vsoc::kDefaultUuidPrefix);
-  SetCommandLineOptionWithMode("instance_dir",
-                               default_instance_dir.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-}
-
-bool ParseCommandLineFlags(int* argc, char*** argv) {
-  // The config_file is created by the launcher, so the launcher is the only
-  // host process that doesn't use the flag.
-  // Set the default to empty.
-  google::SetCommandLineOptionWithMode("config_file", "",
-                                       gflags::SET_FLAGS_DEFAULT);
-  google::ParseCommandLineNonHelpFlags(argc, argv, true);
-  bool invalid_manager = false;
-  if (FLAGS_vm_manager == vm_manager::LibvirtManager::name()) {
-    SetDefaultFlagsForLibvirt();
-  } else if (FLAGS_vm_manager == vm_manager::QemuManager::name()) {
-    SetDefaultFlagsForQemu();
-  } else {
-    std::cerr << "Unknown Virtual Machine Manager: " << FLAGS_vm_manager
-              << std::endl;
-    invalid_manager = true;
-  }
-  google::HandleCommandLineHelpFlags();
-  if (invalid_manager) {
-    return false;
-  }
-  // Set the env variable to empty (in case the caller passed a value for it).
-  unsetenv(vsoc::kCuttlefishConfigEnvVarName);
-
-  ValidateAdbModeFlag();
-
-  return ResolveInstanceFiles();
-}
-
-bool CleanPriorFiles() {
-  // Everything on the instance directory
-  std::string prior_files = FLAGS_instance_dir + "/*";
-  // The shared memory file
-  prior_files += " " + FLAGS_mempath;
-  // The environment file
-  prior_files += " " + GetCuttlefishEnvPath();
-  // The global link to the config file
-  prior_files += " " + vsoc::GetGlobalConfigFileLink();
-  LOG(INFO) << "Assuming prior files of " << prior_files;
-  std::string fuser_cmd = "fuser " + prior_files + " 2> /dev/null";
-  int rval = std::system(fuser_cmd.c_str());
-  // fuser returns 0 if any of the files are open
-  if (WEXITSTATUS(rval) == 0) {
-    LOG(ERROR) << "Clean aborted: files are in use";
-    return false;
-  }
-  std::string clean_command = "rm -rf " + prior_files;
-  if (FLAGS_vm_manager == vm_manager::LibvirtManager::name()) {
-    // Libvirt runs as libvirt-qemu so we need sudo to delete the files it
-    // creates
-    clean_command = "sudo " + clean_command;
-  }
-  rval = std::system(clean_command.c_str());
-  if (WEXITSTATUS(rval) != 0) {
-    LOG(ERROR) << "Remove of files failed";
-    return false;
-  }
-  return true;
 }
 
 bool WriteCuttlefishEnvironment(const vsoc::CuttlefishConfig& config) {
@@ -818,7 +206,7 @@
   std::string config_env = "export CUTTLEFISH_PER_INSTANCE_PATH=\"" +
                            config.PerInstancePath(".") + "\"\n";
   config_env += "export ANDROID_SERIAL=";
-  if (AdbUsbEnabled()) {
+  if (AdbUsbEnabled(config)) {
     config_env += config.serial_number();
   } else {
     config_env += "127.0.0.1:" + std::to_string(GetHostPort());
@@ -841,24 +229,22 @@
     // Explicitly close here, otherwise we may end up reading forever if the
     // child process dies.
     write_end->Close();
-    monitor::BootEvent evt;
-    while(true) {
-      auto bytes_read = read_end->Read(&evt, sizeof(evt));
-      if (bytes_read != sizeof(evt)) {
-        LOG(ERROR) << "Fail to read a complete event, read " << bytes_read
-                   << " bytes only instead of the expected " << sizeof(evt);
-        std::exit(LauncherExitCodes::kPipeIOError);
-      }
-      if (evt == monitor::BootEvent::BootCompleted) {
-        LOG(INFO) << "Virtual device booted successfully";
-        std::exit(LauncherExitCodes::kSuccess);
-      }
-      if (evt == monitor::BootEvent::BootFailed) {
-        LOG(ERROR) << "Virtual device failed to boot";
-        std::exit(LauncherExitCodes::kVirtualDeviceBootFailed);
-      }
-      // Do nothing for the other signals
+    LauncherExitCodes exit_code;
+    auto bytes_read = read_end->Read(&exit_code, sizeof(exit_code));
+    if (bytes_read != sizeof(exit_code)) {
+      LOG(ERROR) << "Failed to read a complete exit code, read " << bytes_read
+                 << " bytes only instead of the expected " << sizeof(exit_code);
+      exit_code = LauncherExitCodes::kPipeIOError;
+    } else if (exit_code == LauncherExitCodes::kSuccess) {
+      LOG(INFO) << "Virtual device booted successfully";
+    } else if (exit_code == LauncherExitCodes::kVirtualDeviceBootFailed) {
+      LOG(ERROR) << "Virtual device failed to boot";
+    } else if (exit_code == LauncherExitCodes::kE2eTestFailed) {
+      LOG(ERROR) << "Host VSoC region end to end test failed";
+    } else {
+      LOG(ERROR) << "Unexpected exit code: " << exit_code;
     }
+    std::exit(exit_code);
   } else {
     // The child returns the write end of the pipe
     if (daemon(/*nochdir*/ 1, /*noclose*/ 1) != 0) {
@@ -961,38 +347,8 @@
 
 int main(int argc, char** argv) {
   ::android::base::InitLogging(argv, android::base::StderrLogger);
-  if (!ParseCommandLineFlags(&argc, &argv)) {
-    LOG(ERROR) << "Failed to parse command arguments";
-    return LauncherExitCodes::kArgumentParsingError;
-  }
 
-  // Clean up prior files before saving the config file (doing it after would
-  // delete it)
-  if (!CleanPriorFiles()) {
-    LOG(ERROR) << "Failed to clean prior files";
-    return LauncherExitCodes::kPrioFilesCleanupError;
-  }
-  // For now it has to be the vm manager who ensures the instance dir exists
-  // because in the case of the libvirt manager root privileges are required to
-  // create and set acls on the directory
-  if (!vm_manager::VmManager::EnsureInstanceDirExists(FLAGS_vm_manager,
-                                                      FLAGS_instance_dir)) {
-    LOG(ERROR) << "Failed to create instance directory";
-    return LauncherExitCodes::kInstanceDirCreationError;
-  }
-
-  auto boot_img_unpacker = cvd::BootImageUnpacker::FromImage(FLAGS_boot_image);
-
-  if (!InitializeCuttlefishConfiguration(*boot_img_unpacker)) {
-    LOG(ERROR) << "Failed to initialize configuration";
-    return LauncherExitCodes::kCuttlefishConfigurationInitError;
-  }
-  // Do this early so that the config object is ready for anything that needs it
-  auto config = vsoc::CuttlefishConfig::Get();
-  if (!config) {
-    LOG(ERROR) << "Failed to obtain config singleton";
-    return LauncherExitCodes::kCuttlefishConfigurationInitError;
-  }
+  auto config = InitFilesystemAndCreateConfig(&argc, &argv);
 
   auto vm_manager = vm_manager::VmManager::Get(config->vm_manager(), config);
 
@@ -1009,23 +365,12 @@
     return LauncherExitCodes::kInvalidHostConfiguration;
   }
 
-  if (!vm_manager->EnsureInstanceDirExists(FLAGS_vm_manager,
-                                           FLAGS_instance_dir)) {
-    LOG(ERROR) << "Failed to create instance directory: " << FLAGS_instance_dir;
-    return LauncherExitCodes::kInstanceDirCreationError;
-  }
-
-  if (!UnpackBootImage(*boot_img_unpacker, *config)) {
-    LOG(ERROR) << "Failed to unpack boot image";
-    return LauncherExitCodes::kBootImageUnpackError;
-  }
-
   if (!WriteCuttlefishEnvironment(*config)) {
     LOG(ERROR) << "Unable to write cuttlefish environment file";
   }
 
   LOG(INFO) << "The following files contain useful debugging information:";
-  if (FLAGS_daemon) {
+  if (config->run_as_daemon()) {
     LOG(INFO) << "  Launcher log: " << config->launcher_log_path();
   }
   LOG(INFO) << "  Android's logcat output: " << config->logcat_path();
@@ -1043,10 +388,10 @@
                << launcher_monitor_socket->StrError();
     return cvd::LauncherExitCodes::kMonitorCreationFailed;
   }
-  cvd::SharedFD boot_events_pipe;
-  if (FLAGS_daemon) {
-    boot_events_pipe = DaemonizeLauncher(*config);
-    if (!boot_events_pipe->IsOpen()) {
+  cvd::SharedFD foreground_launcher_pipe;
+  if (config->run_as_daemon()) {
+    foreground_launcher_pipe = DaemonizeLauncher(*config);
+    if (!foreground_launcher_pipe->IsOpen()) {
       return LauncherExitCodes::kDaemonizationError;
     }
   } else {
@@ -1061,23 +406,44 @@
     }
   }
 
-  LaunchKernelLogMonitor(*config, boot_events_pipe);
-  LaunchUsbServerIfEnabled(*config);
-  LaunchIvServer(*config);
+  auto boot_state_machine =
+      std::make_shared<CvdBootStateMachine>(foreground_launcher_pipe);
 
-  // Initialize the regions that require so before the VM starts.
-  PreLaunchInitializers::Initialize(*config);
+  // Monitor and restart host processes supporting the CVD
+  cvd::ProcessMonitor process_monitor;
+
+  cvd::SharedFD boot_events_pipe;
+  // Only subscribe to boot events if running as daemon
+  process_monitor.StartSubprocess(
+      GetKernelLogMonitorCommand(*config,
+                                 config->run_as_daemon()
+                                   ? &boot_events_pipe
+                                   : nullptr),
+      GetOnSubprocessExitCallback(*config));
+
+  SetUpHandlingOfBootEvents(&process_monitor, boot_events_pipe,
+                            boot_state_machine);
+
+  LaunchLogcatReceiverIfEnabled(*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);
 
   // Start the guest VM
-  if (!vm_manager->Start()) {
-    LOG(ERROR) << "Unable to start vm_manager";
-    // TODO(111453282): All host processes should die here.
-    return LauncherExitCodes::kVMCreationError;
-  }
+  process_monitor.StartSubprocess(vm_manager->StartCommand(),
+                                  GetOnSubprocessExitCallback(*config));
 
-  LaunchSocketForwardProxyIfEnabled();
-  LaunchVNCServerIfEnabled();
-  LaunchAdbConnectorIfEnabled();
+  // Start other host processes
+  LaunchSocketForwardProxyIfEnabled(&process_monitor, *config);
+  LaunchSocketVsockProxyIfEnabled(&process_monitor, *config);
+  LaunchVNCServerIfEnabled(*config, &process_monitor,
+                           GetOnSubprocessExitCallback(*config));
+  LaunchStreamAudioIfEnabled(*config, &process_monitor,
+                             GetOnSubprocessExitCallback(*config));
+  LaunchAdbConnectorIfEnabled(&process_monitor, *config);
 
   ServerLoop(launcher_monitor_socket, vm_manager); // Should not return
   LOG(ERROR) << "The server loop returned, it should never happen!!";
diff --git a/host/commands/launch/pre_launch_initializers.h b/host/commands/launch/pre_launch_initializers.h
index 925cdf8..79f6eed 100644
--- a/host/commands/launch/pre_launch_initializers.h
+++ b/host/commands/launch/pre_launch_initializers.h
@@ -25,14 +25,10 @@
 // To add initializers for more regions declare here, implement in its own
 // source file and call from PreLaunchInitializers::Initialize().
 void InitializeScreenRegion(const vsoc::CuttlefishConfig& config);
-void InitializeRilRegion(const vsoc::CuttlefishConfig& config);
-void InitializeWifiRegion(const vsoc::CuttlefishConfig& config);
 
 class PreLaunchInitializers {
  public:
   static void Initialize(const vsoc::CuttlefishConfig& config) {
     InitializeScreenRegion(config);
-    InitializeRilRegion(config);
-    InitializeWifiRegion(config);
   }
 };
diff --git a/host/commands/launch/process_monitor.cc b/host/commands/launch/process_monitor.cc
new file mode 100644
index 0000000..a405180
--- /dev/null
+++ b/host/commands/launch/process_monitor.cc
@@ -0,0 +1,185 @@
+/*
+ * 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 <assert.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <map>
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_select.h"
+#include "host/commands/launch/process_monitor.h"
+
+namespace cvd {
+
+namespace {
+
+void NotifyThread(SharedFD fd) {
+  // The restarter thread is (likely) blocked on a call to select, to make it
+  // wake up and do some work we write something (anything, the content is not
+  // important) into the main side of the socket pair so that the call to select
+  // returns and the notification fd (restarter side of the socket pair) is
+  // marked as ready to read.
+  char buffer = 'a';
+  fd->Write(&buffer, sizeof(buffer));
+}
+
+void ConsumeNotifications(SharedFD fd) {
+  // Once the starter thread is waken up due to a notification, the calls to
+  // select will continue to return immediately unless we read what was written
+  // on the main side of the socket pair. More than one notification can
+  // accumulate before the restarter thread consumes them, so we attempt to read
+  // more than it's written to consume them all at once. In the unlikely case of
+  // more than 8 notifications acummulating we simply read the first 8 and have
+  // another iteration on the restarter thread loop.
+  char buffer[8];
+  fd->Read(buffer, sizeof(buffer));
+}
+
+}  // namespace
+
+ProcessMonitor::ProcessMonitor() {
+  if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &thread_comm_main_,
+                            &thread_comm_monitor_)) {
+    LOG(ERROR) << "Unable to create restarter communication socket pair: "
+               << strerror(errno);
+    return;
+  }
+  monitor_thread_ = std::thread([this]() { MonitorRoutine(); });
+}
+
+void ProcessMonitor::StartSubprocess(Command cmd, OnSocketReadyCb callback) {
+  auto proc = cmd.Start(true);
+  if (!proc.Started()) {
+    LOG(ERROR) << "Failed to start process";
+    return;
+  }
+  MonitorExistingSubprocess(std::move(cmd), std::move(proc), callback);
+}
+
+void ProcessMonitor::MonitorExistingSubprocess(Command cmd, Subprocess proc,
+                                               OnSocketReadyCb callback) {
+  {
+    std::lock_guard<std::mutex> lock(processes_mutex_);
+    monitored_processes_.push_back(MonitorEntry());
+    auto& entry = monitored_processes_.back();
+    entry.cmd.reset(new Command(std::move(cmd)));
+    entry.proc.reset(new Subprocess(std::move(proc)));
+    entry.on_control_socket_ready_cb = callback;
+  }
+  // Wake the restarter thread up so that it starts monitoring this subprocess
+  // Do this after releasing the lock so that the restarter thread is free to
+  // begin work as soon as select returns.
+  NotifyThread(thread_comm_main_);
+}
+
+bool ProcessMonitor::RestartOnExitCb(MonitorEntry* entry) {
+  // Make sure the process actually exited
+  char buffer[16];
+  auto bytes_read = entry->proc->control_socket()->Read(buffer, sizeof(buffer));
+  if (bytes_read > 0) {
+    LOG(WARNING) << "Subprocess " << entry->cmd->GetShortName() << " wrote "
+                 << bytes_read
+                 << " bytes on the control socket, this is unexpected";
+    // The process may not have exited, continue monitoring without restarting
+    return true;
+  }
+
+  LOG(INFO) << "Detected exit of monitored subprocess";
+  // Make sure the subprocess isn't left in a zombie state, and that the
+  // pid is logged
+  int wstatus;
+  auto wait_ret = TEMP_FAILURE_RETRY(entry->proc->Wait(&wstatus, 0));
+  // None of the error conditions specified on waitpid(2) apply
+  assert(wait_ret > 0);
+  if (WIFEXITED(wstatus)) {
+    LOG(INFO) << "Subprocess " << entry->cmd->GetShortName() << " ("
+              << wait_ret << ") has exited with exit code "
+              << WEXITSTATUS(wstatus);
+  } else if (WIFSIGNALED(wstatus)) {
+    LOG(ERROR) << "Subprocess " << entry->cmd->GetShortName() << " ("
+               << wait_ret << ") was interrupted by a signal: "
+               << WTERMSIG(wstatus);
+  } else {
+    LOG(INFO) << "subprocess " << entry->cmd->GetShortName() << " ("
+               << wait_ret << ") has exited for unknown reasons";
+  }
+  entry->proc.reset(new Subprocess(entry->cmd->Start(true)));
+  return true;
+}
+
+bool ProcessMonitor::DoNotMonitorCb(MonitorEntry*) {
+  return false;
+}
+
+void ProcessMonitor::MonitorRoutine() {
+  LOG(INFO) << "Started monitoring subprocesses";
+  do {
+    SharedFDSet read_set;
+    read_set.Set(thread_comm_monitor_);
+    {
+      std::lock_guard<std::mutex> lock(processes_mutex_);
+      for (auto& monitored_process: monitored_processes_) {
+        auto control_socket = monitored_process.proc->control_socket();
+        if (!control_socket->IsOpen())  {
+          LOG(ERROR) << "The control socket for "
+                     << monitored_process.cmd->GetShortName()
+                     << " is closed, it's effectively NOT being monitored";
+        }
+        read_set.Set(control_socket);
+      }
+    }
+    // We can't call select while holding the lock as it would lead to a
+    // deadlock (restarter thread waiting for notifications from main thread,
+    // main thread waiting for the lock)
+    int num_fds = cvd::Select(&read_set, nullptr, nullptr, nullptr);
+    if (num_fds < 0) {
+      LOG(ERROR) << "Select call returned error on restarter thread: "
+                 << strerror(errno);
+    }
+    if (num_fds > 0) {
+      // Try the communication fd, it's the most likely to be set
+      if (read_set.IsSet(thread_comm_monitor_)) {
+        --num_fds;
+        ConsumeNotifications(thread_comm_monitor_);
+      }
+    }
+    {
+      std::lock_guard<std::mutex> lock(processes_mutex_);
+      // Keep track of the number of file descriptors ready for read, chances
+      // are we don't need to go over the entire list of subprocesses
+      auto it = monitored_processes_.begin();
+      while (it != monitored_processes_.end()) {
+        auto control_socket = it->proc->control_socket();
+        bool keep_monitoring = true;
+        if (read_set.IsSet(control_socket)) {
+          --num_fds;
+          keep_monitoring = it->on_control_socket_ready_cb(&(*it));
+        }
+        if (keep_monitoring) {
+          ++it;
+        } else {
+          it = monitored_processes_.erase(it);
+        }
+      }
+    }
+    assert(num_fds == 0);
+  } while (true);
+}
+
+}  // namespace cvd
diff --git a/host/commands/launch/process_monitor.h b/host/commands/launch/process_monitor.h
new file mode 100644
index 0000000..a375653
--- /dev/null
+++ b/host/commands/launch/process_monitor.h
@@ -0,0 +1,63 @@
+/*
+ * 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 <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <common/libs/utils/subprocess.h>
+
+namespace cvd {
+
+struct MonitorEntry;
+using OnSocketReadyCb = std::function<bool(MonitorEntry*)>;
+
+struct MonitorEntry {
+  std::unique_ptr<Command> cmd;
+  std::unique_ptr<Subprocess> proc;
+  OnSocketReadyCb on_control_socket_ready_cb;
+};
+
+// Keeps track of launched subprocesses, restarts them if they unexpectedly exit
+class ProcessMonitor {
+ public:
+  ProcessMonitor();
+  // Starts a managed subprocess with a controlling socket. The
+  // on_control_socket_ready_cb callback will be called when data is ready to be
+  // read from the socket or the subprocess has ended. No member functions of
+  // the process monitor object should be called from the callback as it may
+  // lead to a dealock. If the callback returns false the subprocess will no
+  // longer be monitored
+  void StartSubprocess(Command cmd, OnSocketReadyCb on_control_socket_ready_cb);
+  // Monitors an alreacy started subprocess
+  void MonitorExistingSubprocess(Command cmd, Subprocess sub_process,
+                                 OnSocketReadyCb on_control_socket_ready_cb);
+  static bool RestartOnExitCb(MonitorEntry* entry);
+  static bool DoNotMonitorCb(MonitorEntry* entry);
+
+ private:
+  void MonitorRoutine();
+
+  std::vector<MonitorEntry> monitored_processes_;
+  // Used for communication with the restarter thread
+  cvd::SharedFD thread_comm_main_, thread_comm_monitor_;
+  std::thread monitor_thread_;
+  // Protects access to the monitored_processes_
+  std::mutex processes_mutex_;
+};
+}  // namespace cvd
diff --git a/host/commands/launch/ril_region_handler.cc b/host/commands/launch/ril_config.cc
similarity index 76%
rename from host/commands/launch/ril_region_handler.cc
rename to host/commands/launch/ril_config.cc
index b3bb00a..195256a 100644
--- a/host/commands/launch/ril_region_handler.cc
+++ b/host/commands/launch/ril_config.cc
@@ -20,11 +20,13 @@
 #include <string.h>
 
 #include <memory>
+#include <sstream>
 #include <string>
 
-#include "common/vsoc/lib/ril_region_view.h"
-#include "host/commands/launch/pre_launch_initializers.h"
-#include "host/libs/config/cuttlefish_config.h"
+#include <glog/logging.h>
+
+#include "common/libs/constants/ril.h"
+#include "host/commands/launch/ril_config.h"
 
 namespace {
 
@@ -119,31 +121,31 @@
     return ret;
   }
 };
+
+template <typename T>
+std::string BuildPropertyDefinition(const std::string& prop_name,
+                                  const T& prop_value) {
+  std::ostringstream stream;
+  stream << prop_name << "=" << prop_value;
+  return stream.str();
+}
 }  // namespace
 
-void InitializeRilRegion(const vsoc::CuttlefishConfig& config) {
+void ConfigureRil(vsoc::CuttlefishConfig* config) {
   NetConfig netconfig;
-  if (!netconfig.ObtainConfig(config.mobile_bridge_name())) {
+  if (!netconfig.ObtainConfig(config->mobile_bridge_name())) {
     LOG(ERROR) << "Unable to obtain the network configuration";
     return;
   }
 
-  auto region =
-      vsoc::ril::RilRegionView::GetInstance(vsoc::GetDomain().c_str());
-
-  if (!region) {
-    LOG(ERROR) << "Ril region was not found";
-    return;
-  }
-
-  auto dest = region->data();
-
-  snprintf(dest->ipaddr, sizeof(dest->ipaddr), "%s",
-           netconfig.ril_ipaddr.c_str());
-  snprintf(dest->gateway, sizeof(dest->gateway), "%s",
-           netconfig.ril_gateway.c_str());
-  snprintf(dest->dns, sizeof(dest->dns), "%s", netconfig.ril_dns.c_str());
-  snprintf(dest->broadcast, sizeof(dest->broadcast), "%s",
-           netconfig.ril_broadcast.c_str());
-  dest->prefixlen = netconfig.ril_prefixlen;
+  config->add_kernel_cmdline(BuildPropertyDefinition(
+      CUTTLEFISH_RIL_ADDR_PROPERTY, netconfig.ril_ipaddr));
+  config->add_kernel_cmdline(BuildPropertyDefinition(
+      CUTTLEFISH_RIL_GATEWAY_PROPERTY, netconfig.ril_gateway));
+  config->add_kernel_cmdline(BuildPropertyDefinition(
+      CUTTLEFISH_RIL_DNS_PROPERTY, netconfig.ril_dns));
+  config->add_kernel_cmdline(BuildPropertyDefinition(
+      CUTTLEFISH_RIL_BROADCAST_PROPERTY, netconfig.ril_broadcast));
+  config->add_kernel_cmdline(BuildPropertyDefinition(
+      CUTTLEFISH_RIL_PREFIXLEN_PROPERTY, netconfig.ril_prefixlen));
 }
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/host/commands/launch/ril_config.h
similarity index 73%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to host/commands/launch/ril_config.h
index b6a037b..49a43e8 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/host/commands/launch/ril_config.h
@@ -1,6 +1,7 @@
 #pragma once
+
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -15,10 +16,6 @@
  * limitations under the License.
  */
 
-#include "hwcomposer_common.h"
+#include "host/libs/config/cuttlefish_config.h"
 
-namespace cvd {
-
-bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
-
-}  // namespace cvd
+void ConfigureRil(vsoc::CuttlefishConfig* config);
\ No newline at end of file
diff --git a/host/commands/launch/wifi_region_handler.cc b/host/commands/launch/wifi_region_handler.cc
deleted file mode 100644
index 5c001b7..0000000
--- a/host/commands/launch/wifi_region_handler.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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 <string>
-
-#include <glog/logging.h>
-
-#include "common/vsoc/lib/wifi_exchange_view.h"
-#include "host/commands/launch/pre_launch_initializers.h"
-#include "host/libs/config/cuttlefish_config.h"
-
-using vsoc::wifi::WifiExchangeView;
-
-void InitializeWifiRegion(const vsoc::CuttlefishConfig& config) {
-  auto region = WifiExchangeView::GetInstance(vsoc::GetDomain().c_str());
-  if (!region) {
-    LOG(FATAL) << "Wifi region not found";
-    return;
-  }
-  WifiExchangeView::MacAddress guest_mac, host_mac;
-  if (!WifiExchangeView::ParseMACAddress(config.wifi_guest_mac_addr(),
-                                         &guest_mac)) {
-    LOG(FATAL) << "Unable to parse guest mac address: "
-               << config.wifi_guest_mac_addr();
-    return;
-  }
-  LOG(INFO) << "Setting guest mac to " << config.wifi_guest_mac_addr();
-  region->SetGuestMACAddress(guest_mac);
-  if (!WifiExchangeView::ParseMACAddress(config.wifi_host_mac_addr(),
-                                         &host_mac)) {
-    LOG(FATAL) << "Unable to parse guest mac address: "
-               << config.wifi_guest_mac_addr();
-    return;
-  }
-  LOG(INFO) << "Setting host mac to " << config.wifi_host_mac_addr();
-  region->SetHostMACAddress(host_mac);
-}
diff --git a/host/commands/logcat_receiver/Android.bp b/host/commands/logcat_receiver/Android.bp
new file mode 100644
index 0000000..16f3bf9
--- /dev/null
+++ b/host/commands/logcat_receiver/Android.bp
@@ -0,0 +1,37 @@
+//
+// 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_host {
+    name: "logcat_receiver",
+    srcs: [
+        "main.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcuttlefish_fs",
+        "liblog",
+        "libcuttlefish_utils",
+        "cuttlefish_auto_resources",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libgflags",
+        "libjsoncpp",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/commands/logcat_receiver/main.cpp b/host/commands/logcat_receiver/main.cpp
new file mode 100644
index 0000000..e9cbad2
--- /dev/null
+++ b/host/commands/logcat_receiver/main.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+DEFINE_int32(
+    server_fd, -1,
+    "File descriptor to an already created vsock server. If negative a new "
+    "server will be created at the port specified on the config file");
+
+int main(int argc, char** argv) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  auto config = vsoc::CuttlefishConfig::Get();
+
+  auto path = config->logcat_path();
+  auto logcat_file =
+      cvd::SharedFD::Open(path.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0666);
+  CHECK(logcat_file->IsOpen())
+      << "Unable to open logcat file: " << logcat_file->StrError();
+
+  cvd::SharedFD server_fd;
+  if (FLAGS_server_fd < 0) {
+    unsigned int port = config->logcat_vsock_port();
+    server_fd = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
+  } else {
+    server_fd = cvd::SharedFD::Dup(FLAGS_server_fd);
+    close(FLAGS_server_fd);
+  }
+
+  CHECK(server_fd->IsOpen()) << "Error creating or inheriting logcat server: "
+                             << server_fd->StrError();
+
+  // Server loop
+  while (true) {
+    auto conn = cvd::SharedFD::Accept(*server_fd);
+
+    while (true) {
+      char buff[1024];
+      auto read = conn->Read(buff, sizeof(buff));
+      if (read <= 0) {
+        // Close here to ensure the other side gets reset if it's still
+        // connected
+        conn->Close();
+        LOG(WARNING) << "Detected close from the other side";
+        break;
+      }
+      auto written = logcat_file->Write(buff, read);
+      CHECK(written == read)
+          << "Error writing to log file: " << logcat_file->StrError()
+          << ". This is unrecoverable.";
+    }
+  }
+  return 0;
+}
\ No newline at end of file
diff --git a/host/commands/record_audio/main.cc b/host/commands/record_audio/main.cc
index 18466f3..3c024b8 100644
--- a/host/commands/record_audio/main.cc
+++ b/host/commands/record_audio/main.cc
@@ -50,7 +50,7 @@
   auto worker = audio_data_rv->StartWorker();
 
   std::unique_ptr<WaveWriter> writer;
-  int64_t frameCount = 0ll;
+  int64_t frameCount = 0LL;
 
   // The configuration the writer is setup for.
   gce_audio_message writer_hdr;
diff --git a/host/commands/stop_cvd/Android.bp b/host/commands/stop_cvd/Android.bp
index 25ab59c..b8322b5 100644
--- a/host/commands/stop_cvd/Android.bp
+++ b/host/commands/stop_cvd/Android.bp
@@ -26,8 +26,6 @@
         "libcuttlefish_fs",
         "libcuttlefish_utils",
         "cuttlefish_auto_resources",
-        "libicuuc",
-        "libandroidicu",
     ],
     static_libs: [
         "libcuttlefish_host_config",
@@ -36,5 +34,5 @@
         "libgflags",
         "libxml2",
     ],
-    defaults: ["cuttlefish_host_only"],
+    defaults: ["cuttlefish_host_only", "cuttlefish_libicuuc"],
 }
diff --git a/host/commands/stop_cvd/main.cc b/host/commands/stop_cvd/main.cc
index e537314..b631d75 100644
--- a/host/commands/stop_cvd/main.cc
+++ b/host/commands/stop_cvd/main.cc
@@ -44,7 +44,6 @@
 #include "host/commands/launch/launcher_defs.h"
 #include "host/libs/config/cuttlefish_config.h"
 #include "host/libs/vm_manager/vm_manager.h"
-#include "host/libs/vm_manager/libvirt_manager.h"
 
 DEFINE_int32(wait_for_launcher, 5,
              "How many seconds to wait for the launcher to respond to the stop "
@@ -54,12 +53,9 @@
 // Gets a set of the possible process groups of a previous launch
 std::set<pid_t> GetCandidateProcessGroups() {
   std::string cmd = "fuser";
-  // Add the instance directory for qemu
+  // Add the instance directory
   cmd += " " + cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime/*";
-  // Add the instance directory for libvirt
-  auto libvirt_instance_dir =
-      std::string("/var/run/libvirt-") + vsoc::kDefaultUuidPrefix;
-  cmd += " " + vsoc::GetPerInstanceDefault(libvirt_instance_dir.c_str()) + "/*";
+  // Add the shared memory file
   cmd += " " + vsoc::GetPerInstanceDefault("/dev/shm/cvd-");
   std::shared_ptr<FILE> cmd_out(popen(cmd.c_str(), "r"), pclose);
   if (!cmd_out) {
@@ -84,22 +80,6 @@
 
 int FallBackStop() {
   auto exit_code = 1; // Having to fallback is an error
-  if (vm_manager::VmManager::IsVmManagerSupported(
-          vm_manager::LibvirtManager::name())) {
-    // Libvirt doesn't run as the same user as stop_cvd, so we must stop it
-    // through the manager. Qemu on the other hand would get killed by the
-    // commands below.
-    vsoc::CuttlefishConfig config;
-    config.set_instance_dir(std::string("/var/run/libvirt-") +
-                            vsoc::kDefaultUuidPrefix);
-    auto vm_manager =
-      vm_manager::VmManager::Get(vm_manager::LibvirtManager::name(), &config);
-    if (!vm_manager->Stop()) {
-      LOG(WARNING) << "Failed to stop the libvirt domain: Is it still running? "
-                      "Is it using qemu_cli?";
-      exit_code |= 2;
-    }
-  }
 
   auto process_groups = GetCandidateProcessGroups();
   for (auto pgid: process_groups) {
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/adb_connector/main.cpp b/host/frontend/adb_connector/main.cpp
index 27574ee..e046b3d 100644
--- a/host/frontend/adb_connector/main.cpp
+++ b/host/frontend/adb_connector/main.cpp
@@ -30,18 +30,18 @@
 #include "host/libs/config/cuttlefish_config.h"
 #include "host/libs/adb_connection_maintainer/adb_connection_maintainer.h"
 
-DEFINE_string(ports, "", "Comma-separated list of ports to 'adb connect' to");
+DEFINE_string(addresses, "", "Comma-separated list of addresses to 'adb connect' to");
 
 namespace {
-void LaunchConnectionMaintainerThread(int port) {
-  std::thread(cvd::EstablishAndMaintainConnection, port).detach();
+void LaunchConnectionMaintainerThread(const std::string& address) {
+  std::thread(cvd::EstablishAndMaintainConnection, address).detach();
 }
 
-std::vector<int> ParsePortsList(std::string ports) {
+std::vector<std::string> ParseAddressList(std::string ports) {
   std::replace(ports.begin(), ports.end(), ',', ' ');
   std::istringstream port_stream{ports};
-  return {std::istream_iterator<int>{port_stream},
-          std::istream_iterator<int>{}};
+  return {std::istream_iterator<std::string>{port_stream},
+          std::istream_iterator<std::string>{}};
 }
 
 [[noreturn]] void SleepForever() {
@@ -53,10 +53,10 @@
 
 int main(int argc, char* argv[]) {
   gflags::ParseCommandLineFlags(&argc, &argv, true);
-  CHECK(!FLAGS_ports.empty()) << "Must specify --ports flag";
+  CHECK(!FLAGS_addresses.empty()) << "Must specify --addresses flag";
 
-  for (auto port : ParsePortsList(FLAGS_ports)) {
-    LaunchConnectionMaintainerThread(port);
+  for (auto address : ParseAddressList(FLAGS_addresses)) {
+    LaunchConnectionMaintainerThread(address);
   }
 
   SleepForever();
diff --git a/host/frontend/stream_audio/Android.bp b/host/frontend/stream_audio/Android.bp
new file mode 100644
index 0000000..ab5ff8e
--- /dev/null
+++ b/host/frontend/stream_audio/Android.bp
@@ -0,0 +1,37 @@
+//
+// 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",
+    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..e0b97ac
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/Android.bp
@@ -0,0 +1,27 @@
+//
+// 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",
+    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 241df83..fae5a17 100644
--- a/host/frontend/vnc_server/Android.bp
+++ b/host/frontend/vnc_server/Android.bp
@@ -20,6 +20,7 @@
         "frame_buffer_watcher.cpp",
         "jpeg_compressor.cpp",
         "main.cpp",
+        "screen_connector.cpp",
         "simulated_hw_composer.cpp",
         "virtual_inputs.cpp",
         "vnc_client_connection.cpp",
diff --git a/host/frontend/vnc_server/screen_connector.cpp b/host/frontend/vnc_server/screen_connector.cpp
new file mode 100644
index 0000000..ea82f7e
--- /dev/null
+++ b/host/frontend/vnc_server/screen_connector.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/frontend/vnc_server/screen_connector.h"
+
+#include <atomic>
+#include <condition_variable>
+
+#include <gflags/gflags.h>
+
+#include <common/vsoc/lib/screen_region_view.h>
+#include <host/libs/config/cuttlefish_config.h>
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+DEFINE_int32(frame_server_fd, -1, "");
+
+namespace cvd {
+namespace vnc {
+
+namespace {
+class VSoCScreenConnector : public ScreenConnector {
+ public:
+  int WaitForNewFrameSince(std::uint32_t* seq_num) override {
+    if (!screen_view_) return -1;
+    return screen_view_->WaitForNewFrameSince(seq_num);
+  }
+
+  void* GetBuffer(int buffer_idx) override {
+    if (!screen_view_) return nullptr;
+    return screen_view_->GetBuffer(buffer_idx);
+  }
+
+ private:
+  vsoc::screen::ScreenRegionView* screen_view_ =
+      vsoc::screen::ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
+};
+
+// TODO(b/128852363): Substitute with one based on memory shared with the
+//  wayland mock
+class SocketBasedScreenConnector : public ScreenConnector {
+ public:
+  SocketBasedScreenConnector(vsoc::CuttlefishConfig* config) : config_(config) {
+    screen_server_thread_ = std::thread([this]() { ServerLoop(); });
+  }
+
+  int WaitForNewFrameSince(std::uint32_t* seq_num) override {
+    std::unique_lock<std::mutex> lock(new_frame_mtx_);
+    while (seq_num_ == *seq_num) {
+      new_frame_cond_var_.wait(lock);
+    }
+    return newest_buffer_;
+  }
+
+  void* GetBuffer(int buffer_idx) override {
+    if (buffer_idx < 0) return nullptr;
+    buffer_idx %= NUM_BUFFERS_;
+    return &buffer_[buffer_idx * ScreenSizeInBytes()];
+  }
+
+ private:
+  static constexpr int NUM_BUFFERS_ = 4;
+
+  void ServerLoop() {
+    if (FLAGS_frame_server_fd < 0) {
+      LOG(FATAL) << "Invalid file descriptor: " << FLAGS_frame_server_fd;
+      return;
+    }
+    auto server = SharedFD::Dup(FLAGS_frame_server_fd);
+    close(FLAGS_frame_server_fd);
+    if (!server->IsOpen()) {
+      LOG(FATAL) << "Unable to dup screen server: " << server->StrError();
+      return;
+    }
+
+    int current_buffer = 0;
+
+    while (1) {
+      auto conn = SharedFD::Accept(*server);
+      while (conn->IsOpen()) {
+        SendScreenParameters(conn);
+
+        int32_t size = 0;
+        conn->Read(&size, sizeof(size));
+        auto buff = reinterpret_cast<uint8_t*>(GetBuffer(current_buffer));
+        while (size > 0) {
+          auto read = conn->Read(buff, size);
+          if (read < 0) {
+            LOG(ERROR) << "Failed to read from hwcomposer: "
+                       << conn->StrError();
+            return;
+          }
+          size -= read;
+          buff += read;
+        }
+        BroadcastNewFrame(current_buffer);
+        current_buffer = (current_buffer + 1) % NUM_BUFFERS_;
+      }
+    }
+  }
+
+  void SendScreenParameters(SharedFD conn) const {
+    // TODO(b/128842613): Send this info from the configuration server
+    int32_t screen_params[4];
+    screen_params[0] = config_->x_res();
+    screen_params[1] = config_->y_res();
+    screen_params[2] = config_->dpi();
+    screen_params[3] = config_->refresh_rate_hz();
+    int buff_size = sizeof(screen_params);
+    int res = conn->Write(screen_params, buff_size);
+    if (res != buff_size) {
+          LOG(FATAL)
+              << "Unable to send full screen parameters to the hwcomposer ("
+              << res << "): " << conn->StrError();
+        }
+  }
+
+  void BroadcastNewFrame(int buffer_idx) {
+    {
+      std::lock_guard<std::mutex> lock(new_frame_mtx_);
+      seq_num_++;
+      newest_buffer_ = buffer_idx;
+    }
+    new_frame_cond_var_.notify_all();
+  }
+
+  vsoc::CuttlefishConfig* config_;
+  std::vector<std::uint8_t> buffer_ =
+      std::vector<std::uint8_t>(NUM_BUFFERS_ * ScreenSizeInBytes());
+  std::uint32_t seq_num_{0};
+  int newest_buffer_ = 0;
+  std::condition_variable new_frame_cond_var_;
+  std::mutex new_frame_mtx_;
+  std::thread screen_server_thread_;
+};
+}  // namespace
+
+ScreenConnector* ScreenConnector::Get() {
+  auto config = vsoc::CuttlefishConfig::Get();
+  if (config->enable_ivserver()) {
+    return new VSoCScreenConnector();
+  } else {
+    return new SocketBasedScreenConnector(config);
+  }
+}
+
+}  // namespace vnc
+}  // namespace cvd
\ No newline at end of file
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/host/frontend/vnc_server/screen_connector.h
similarity index 61%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to host/frontend/vnc_server/screen_connector.h
index b6a037b..9c7b9e0 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/host/frontend/vnc_server/screen_connector.h
@@ -1,6 +1,5 @@
-#pragma once
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -15,10 +14,25 @@
  * limitations under the License.
  */
 
-#include "hwcomposer_common.h"
+#pragma once
+
+#include <cstdint>
 
 namespace cvd {
+namespace vnc {
 
-bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
+class ScreenConnector {
+ public:
+  static ScreenConnector* Get();
 
-}  // namespace cvd
+  virtual ~ScreenConnector() = default;
+
+  virtual int WaitForNewFrameSince(std::uint32_t* seq_num) = 0;
+  virtual void* GetBuffer(int buffer_idx) = 0;
+
+ protected:
+  ScreenConnector() = default;
+};
+
+}  // namespace vnc
+}  // namespace cvd
\ No newline at end of file
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index c8b7f03..f7061ef 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -18,7 +18,6 @@
 
 #include "host/frontend/vnc_server/vnc_utils.h"
 #include "host/libs/config/cuttlefish_config.h"
-#include "common/vsoc/lib/screen_region_view.h"
 
 using cvd::vnc::SimulatedHWComposer;
 using vsoc::screen::ScreenRegionView;
@@ -74,12 +73,11 @@
   auto screen_height = ActualScreenHeight();
   Message raw_screen;
   std::uint64_t stripe_seq_num = 1;
-  auto screen_view = ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
   while (!closed()) {
     bb_->WaitForAtLeastOneClientConnection();
-    int buffer_idx = screen_view->WaitForNewFrameSince(&previous_seq_num);
+    int buffer_idx = screen_connector_->WaitForNewFrameSince(&previous_seq_num);
     const char* frame_start =
-        static_cast<char*>(screen_view->GetBuffer(buffer_idx));
+        static_cast<char*>(screen_connector_->GetBuffer(buffer_idx));
     raw_screen.assign(frame_start, frame_start + ScreenSizeInBytes());
 
     for (int i = 0; i < kNumStripes; ++i) {
diff --git a/host/frontend/vnc_server/simulated_hw_composer.h b/host/frontend/vnc_server/simulated_hw_composer.h
index 5d6e3e2..802cc7f 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.h
+++ b/host/frontend/vnc_server/simulated_hw_composer.h
@@ -26,6 +26,7 @@
 #include "common/libs/thread_safe_queue/thread_safe_queue.h"
 #include "common/libs/threads/thread_annotations.h"
 #include "host/frontend/vnc_server/blackboard.h"
+#include "host/frontend/vnc_server/screen_connector.h"
 
 namespace cvd {
 namespace vnc {
@@ -59,6 +60,7 @@
   BlackBoard* bb_{};
   ThreadSafeQueue<Stripe> stripes_;
   std::thread stripe_maker_;
+  std::shared_ptr<ScreenConnector> screen_connector_{ScreenConnector::Get()};
 };
 }  // namespace vnc
 }  // namespace cvd
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
index b2f519c..e7e3757 100644
--- a/host/frontend/vnc_server/virtual_inputs.cpp
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -22,11 +22,20 @@
 #include <mutex>
 #include "keysyms.h"
 
+#include <common/libs/fs/shared_select.h>
+#include <host/libs/config/cuttlefish_config.h>
+
 using cvd::vnc::VirtualInputs;
 using vsoc::input_events::InputEventsRegionView;
 
+DEFINE_int32(touch_fd, -1,
+             "A fd for a socket where to accept touch connections");
+
+DEFINE_int32(keyboard_fd, -1,
+             "A fd for a socket where to accept keyboard connections");
+
 namespace {
-void AddKeyMappings(std::map<uint32_t, uint32_t>* key_mapping) {
+void AddKeyMappings(std::map<uint32_t, uint16_t>* key_mapping) {
   (*key_mapping)[cvd::xk::AltLeft] = KEY_LEFTALT;
   (*key_mapping)[cvd::xk::ControlLeft] = KEY_LEFTCTRL;
   (*key_mapping)[cvd::xk::ShiftLeft] = KEY_LEFTSHIFT;
@@ -214,30 +223,132 @@
   (*key_mapping)[cvd::xk::Menu] = KEY_MENU;
   (*key_mapping)[cvd::xk::VNCMenu] = KEY_MENU;
 }
+
+void InitInputEvent(struct input_event* evt, uint16_t type, uint16_t code,
+                    int32_t value) {
+  evt->type = type;
+  evt->code = code;
+  evt->value = value;
+}
+
 }  // namespace
 
-VirtualInputs::VirtualInputs()
-  : input_events_region_view_{
-      vsoc::input_events::InputEventsRegionView::GetInstance(
-          vsoc::GetDomain().c_str())} {
-  if (!input_events_region_view_) {
-    LOG(FATAL) << "Failed to open Input events region view";
+class VSoCVirtualInputs : public VirtualInputs {
+ public:
+  VSoCVirtualInputs()
+      : input_events_region_view_{
+            vsoc::input_events::InputEventsRegionView::GetInstance(
+                vsoc::GetDomain().c_str())} {
+    if (!input_events_region_view_) {
+      LOG(FATAL) << "Failed to open Input events region view";
+    }
   }
-  AddKeyMappings(&keymapping_);
-}
 
-void VirtualInputs::GenerateKeyPressEvent(int key_code, bool down) {
-  if (keymapping_.count(key_code)) {
-    input_events_region_view_->HandleKeyboardEvent(down, keymapping_[key_code]);
+  void GenerateKeyPressEvent(int code, bool down) override {
+    if (keymapping_.count(code)) {
+      input_events_region_view_->HandleKeyboardEvent(down, keymapping_[code]);
+    } else {
+      LOG(ERROR) << "Unknown keycode" << code;
+    }
+  }
+
+  void PressPowerButton(bool down) override {
+    input_events_region_view_->HandlePowerButtonEvent(down);
+  }
+
+  void HandlePointerEvent(bool touch_down, int x, int y) override {
+    input_events_region_view_->HandleSingleTouchEvent(touch_down, x, y);
+  }
+
+ private:
+  vsoc::input_events::InputEventsRegionView* input_events_region_view_{};
+};
+
+class SocketVirtualInputs : public VirtualInputs {
+ public:
+  SocketVirtualInputs()
+      : client_connector_([this]() { ClientConnectorLoop(); }) {}
+
+  void GenerateKeyPressEvent(int key_code, bool down) override {
+    struct input_event events[2];
+    InitInputEvent(&events[0], EV_KEY, keymapping_[key_code], down);
+    InitInputEvent(&events[1], EV_SYN, 0, 0);
+
+    SendEvents(keyboard_socket_, events, sizeof(events));
+  }
+
+  void PressPowerButton(bool down) override {
+    struct input_event events[2];
+    InitInputEvent(&events[0], EV_KEY, KEY_POWER, down);
+    InitInputEvent(&events[1], EV_SYN, 0, 0);
+
+    SendEvents(keyboard_socket_, events, sizeof(events));
+  }
+
+  void HandlePointerEvent(bool touch_down, int x, int y) override {
+    // TODO(b/124121375): Use multitouch when available
+    struct input_event events[4];
+    InitInputEvent(&events[0], EV_ABS, ABS_X, x);
+    InitInputEvent(&events[1], EV_ABS, ABS_Y, y);
+    InitInputEvent(&events[2], EV_KEY, BTN_TOUCH, touch_down);
+    InitInputEvent(&events[3], EV_SYN, 0, 0);
+
+    SendEvents(touch_socket_, events, sizeof(events));
+  }
+
+ private:
+  void SendEvents(cvd::SharedFD socket, void* event_buffer, int byte_count) {
+    std::lock_guard<std::mutex> lock(socket_mutex_);
+    if (!socket->IsOpen()) {
+      // This is unlikely as it would only happen between the start of the vnc
+      // server and the connection of the VMM to the socket.
+      // If it happens, just drop the events as the VM is not yet ready to
+      // handle it.
+      return;
+    }
+    auto ret = socket->Write(event_buffer, byte_count);
+    if (ret < 0) {
+      LOG(ERROR) << "Error sending input event: " << socket->StrError();
+    }
+  }
+
+  void ClientConnectorLoop() {
+    auto touch_server = cvd::SharedFD::Dup(FLAGS_touch_fd);
+    close(FLAGS_touch_fd);
+    FLAGS_touch_fd = -1;
+
+    auto keyboard_server = cvd::SharedFD::Dup(FLAGS_keyboard_fd);
+    close(FLAGS_keyboard_fd);
+    FLAGS_keyboard_fd = -1;
+
+    while (1) {
+      cvd::SharedFDSet read_set;
+      read_set.Set(touch_server);
+      read_set.Set(keyboard_server);
+      cvd::Select(&read_set, nullptr, nullptr, nullptr);
+      {
+        std::lock_guard<std::mutex> lock(socket_mutex_);
+        if (read_set.IsSet(touch_server)) {
+          touch_socket_ = cvd::SharedFD::Accept(*touch_server);
+        }
+        if (read_set.IsSet(keyboard_server)) {
+          keyboard_socket_ = cvd::SharedFD::Accept(*keyboard_server);
+        }
+      }
+    }
+  }
+  cvd::SharedFD touch_socket_;
+  cvd::SharedFD keyboard_socket_;
+  std::thread client_connector_;
+  std::mutex socket_mutex_;
+};
+
+VirtualInputs::VirtualInputs() { AddKeyMappings(&keymapping_); }
+
+VirtualInputs* VirtualInputs::Get() {
+  if (vsoc::CuttlefishConfig::Get()->enable_ivserver()) {
+    return new VSoCVirtualInputs();
   } else {
-    LOG(INFO) << "Unknown keycode" << key_code;
+    return new SocketVirtualInputs();
   }
 }
-
-void VirtualInputs::PressPowerButton(bool down) {
-  input_events_region_view_->HandlePowerButtonEvent(down);
-}
-
-void VirtualInputs::HandlePointerEvent(bool touch_down, int x, int y) {
-  input_events_region_view_->HandleSingleTouchEvent(touch_down, x, y);
-}
diff --git a/host/frontend/vnc_server/virtual_inputs.h b/host/frontend/vnc_server/virtual_inputs.h
index c937a12..f92693b 100644
--- a/host/frontend/vnc_server/virtual_inputs.h
+++ b/host/frontend/vnc_server/virtual_inputs.h
@@ -28,15 +28,18 @@
 
 class VirtualInputs {
  public:
+  static VirtualInputs* Get();
+
+  virtual ~VirtualInputs() = default;
+
+  virtual void GenerateKeyPressEvent(int code, bool down) = 0;
+  virtual void PressPowerButton(bool down) = 0;
+  virtual void HandlePointerEvent(bool touch_down, int x, int y) = 0;
+
+ protected:
   VirtualInputs();
 
-  void GenerateKeyPressEvent(int code, bool down);
-  void PressPowerButton(bool down);
-  void HandlePointerEvent(bool touch_down, int x, int y);
-
- private:
-  vsoc::input_events::InputEventsRegionView* input_events_region_view_{};
-  std::map<uint32_t, uint32_t> keymapping_;
+  std::map<uint32_t, uint16_t> keymapping_;
 };
 
 }  // namespace vnc
diff --git a/host/frontend/vnc_server/vnc_client_connection.cpp b/host/frontend/vnc_server/vnc_client_connection.cpp
index 25b007d..a8faf47 100644
--- a/host/frontend/vnc_server/vnc_client_connection.cpp
+++ b/host/frontend/vnc_server/vnc_client_connection.cpp
@@ -76,47 +76,6 @@
 constexpr size_t kPointerEventLength = 5;
 constexpr size_t kClientCutTextLength = 7;  // more bytes follow
 
-void AppendInNetworkByteOrder(Message* msg, const std::uint8_t b) {
-  msg->push_back(b);
-}
-
-void AppendInNetworkByteOrder(Message* msg, const std::uint16_t s) {
-  const std::uint16_t n = htons(s);
-  auto p = reinterpret_cast<const std::uint8_t*>(&n);
-  msg->insert(msg->end(), p, p + sizeof n);
-}
-
-void AppendInNetworkByteOrder(Message* msg, const std::uint32_t w) {
-  const std::uint32_t n = htonl(w);
-  auto p = reinterpret_cast<const std::uint8_t*>(&n);
-  msg->insert(msg->end(), p, p + sizeof n);
-}
-
-void AppendInNetworkByteOrder(Message* msg, const int32_t w) {
-  std::uint32_t u{};
-  std::memcpy(&u, &w, sizeof u);
-  AppendInNetworkByteOrder(msg, u);
-}
-
-void AppendInNetworkByteOrder(Message* msg, const std::string& str) {
-  msg->insert(msg->end(), str.begin(), str.end());
-}
-
-void AppendToMessage(Message*) {}
-
-template <typename T, typename... Ts>
-void AppendToMessage(Message* msg, T v, Ts... vals) {
-  AppendInNetworkByteOrder(msg, v);
-  AppendToMessage(msg, vals...);
-}
-
-template <typename... Ts>
-Message CreateMessage(Ts... vals) {
-  Message m;
-  AppendToMessage(&m, vals...);
-  return m;
-}
-
 std::string HostName() {
   auto config = vsoc::CuttlefishConfig::Get();
   return !config || config->device_title().empty() ? std::string{"localhost"}
@@ -174,9 +133,9 @@
 }  // namespace vnc
 }  // namespace cvd
 
-VncClientConnection::VncClientConnection(ClientSocket client,
-                                         VirtualInputs* virtual_inputs,
-                                         BlackBoard* bb, bool aggressive)
+VncClientConnection::VncClientConnection(
+    ClientSocket client, std::shared_ptr<VirtualInputs> virtual_inputs,
+    BlackBoard* bb, bool aggressive)
     : client_{std::move(client)}, virtual_inputs_{virtual_inputs}, bb_{bb} {
   frame_buffer_request_handler_tid_ = std::thread(
       &VncClientConnection::FrameBufferUpdateRequestHandler, this, aggressive);
@@ -225,7 +184,8 @@
 void VncClientConnection::SetupProtocol() {
   static constexpr char kRFBVersion[] = "RFB 003.008\n";
   static constexpr auto kVersionLen = (sizeof kRFBVersion) - 1;
-  client_.Send(reinterpret_cast<const std::uint8_t*>(kRFBVersion), kVersionLen);
+  client_.SendNoSignal(reinterpret_cast<const std::uint8_t*>(kRFBVersion),
+                       kVersionLen);
   auto client_protocol = client_.Recv(kVersionLen);
   if (std::memcmp(&client_protocol[0], kRFBVersion,
                   std::min(kVersionLen, client_protocol.size())) != 0) {
@@ -239,7 +199,7 @@
   static constexpr std::uint8_t kNoneSecurity = 0x1;
   // The first '0x1' indicates the number of items that follow
   static constexpr std::uint8_t kOnlyNoneSecurity[] = {0x01, kNoneSecurity};
-  client_.Send(kOnlyNoneSecurity);
+  client_.SendNoSignal(kOnlyNoneSecurity);
   auto client_security = client_.Recv(1);
   if (client_.closed()) {
     return;
@@ -249,7 +209,7 @@
                << static_cast<int>(client_security.front());
   }
   static constexpr std::uint8_t kZero[4] = {};
-  client_.Send(kZero);
+  client_.SendNoSignal(kZero);
 }
 
 void VncClientConnection::GetClientInit() {
@@ -259,7 +219,7 @@
 void VncClientConnection::SendServerInit() {
   const std::string server_name = HostName();
   std::lock_guard<std::mutex> guard(m_);
-  auto server_init = CreateMessage(
+  auto server_init = cvd::CreateMessage(
       static_cast<std::uint16_t>(ScreenWidth()),
       static_cast<std::uint16_t>(ScreenHeight()), pixel_format_.bits_per_pixel,
       pixel_format_.depth, pixel_format_.big_endian, pixel_format_.true_color,
@@ -268,22 +228,22 @@
       pixel_format_.blue_shift, std::uint16_t{},  // padding
       std::uint8_t{},                             // padding
       static_cast<std::uint32_t>(server_name.size()), server_name);
-  client_.Send(server_init);
+  client_.SendNoSignal(server_init);
 }
 
 Message VncClientConnection::MakeFrameBufferUpdateHeader(
     std::uint16_t num_stripes) {
-  return CreateMessage(std::uint8_t{0},  // message-type
-                       std::uint8_t{},   // padding
-                       std::uint16_t{num_stripes});
+  return cvd::CreateMessage(std::uint8_t{0},  // message-type
+                            std::uint8_t{},   // padding
+                            std::uint16_t{num_stripes});
 }
 
 void VncClientConnection::AppendRawStripeHeader(Message* frame_buffer_update,
                                                 const Stripe& stripe) {
   static constexpr int32_t kRawEncoding = 0;
-  AppendToMessage(frame_buffer_update, std::uint16_t{stripe.x},
-                  std::uint16_t{stripe.y}, std::uint16_t{stripe.width},
-                  std::uint16_t{stripe.height}, kRawEncoding);
+  cvd::AppendToMessage(frame_buffer_update, std::uint16_t{stripe.x},
+                       std::uint16_t{stripe.y}, std::uint16_t{stripe.width},
+                       std::uint16_t{stripe.height}, kRawEncoding);
 }
 
 void VncClientConnection::AppendJpegSize(Message* frame_buffer_update,
@@ -293,22 +253,23 @@
   constexpr size_t kJpegSizeThreeByteMax = 4194303;
 
   if (jpeg_size <= kJpegSizeOneByteMax) {
-    AppendToMessage(frame_buffer_update, static_cast<std::uint8_t>(jpeg_size));
+    cvd::AppendToMessage(frame_buffer_update,
+                         static_cast<std::uint8_t>(jpeg_size));
   } else if (jpeg_size <= kJpegSizeTwoByteMax) {
     auto sz = static_cast<std::uint32_t>(jpeg_size);
-    AppendToMessage(frame_buffer_update,
-                    static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
-                    static_cast<std::uint8_t>((sz >> 7) & 0xFF));
+    cvd::AppendToMessage(frame_buffer_update,
+                         static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
+                         static_cast<std::uint8_t>((sz >> 7) & 0xFF));
   } else {
     if (jpeg_size > kJpegSizeThreeByteMax) {
       LOG(FATAL) << "jpeg size is too big: " << jpeg_size << " must be under "
                  << kJpegSizeThreeByteMax;
     }
     const auto sz = static_cast<std::uint32_t>(jpeg_size);
-    AppendToMessage(frame_buffer_update,
-                    static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
-                    static_cast<std::uint8_t>(((sz >> 7) & 0x7F) | 0x80),
-                    static_cast<std::uint8_t>((sz >> 14) & 0xFF));
+    cvd::AppendToMessage(frame_buffer_update,
+                         static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
+                         static_cast<std::uint8_t>(((sz >> 7) & 0x7F) | 0x80),
+                         static_cast<std::uint8_t>((sz >> 14) & 0xFF));
   }
 }
 
@@ -353,8 +314,8 @@
 void VncClientConnection::AppendJpegStripeHeader(Message* frame_buffer_update,
                                                  const Stripe& stripe) {
   static constexpr std::uint8_t kJpegEncoding = 0x90;
-  AppendToMessage(frame_buffer_update, stripe.x, stripe.y, stripe.width,
-                  stripe.height, kTightEncoding, kJpegEncoding);
+  cvd::AppendToMessage(frame_buffer_update, stripe.x, stripe.y, stripe.width,
+                       stripe.height, kTightEncoding, kJpegEncoding);
   AppendJpegSize(frame_buffer_update, stripe.jpeg_data.size());
 }
 
@@ -401,7 +362,7 @@
                          ? "portrait"
                          : "landscape")
                  << " mode";
-      client_.Send(MakeFrameBufferUpdate(stripes));
+      client_.SendNoSignal(MakeFrameBufferUpdate(stripes));
     }
     if (aggressive) {
       bb_->FrameBufferUpdateRequestReceived(this);
@@ -411,13 +372,13 @@
 
 void VncClientConnection::SendDesktopSizeUpdate() {
   static constexpr int32_t kDesktopSizeEncoding = -223;
-  client_.Send(CreateMessage(std::uint8_t{0},   // message-type,
-                             std::uint8_t{},    // padding
-                             std::uint16_t{1},  // one pseudo rectangle
-                             std::uint16_t{0}, std::uint16_t{0},
-                             static_cast<std::uint16_t>(ScreenWidth()),
-                             static_cast<std::uint16_t>(ScreenHeight()),
-                             kDesktopSizeEncoding));
+  client_.SendNoSignal(cvd::CreateMessage(
+      std::uint8_t{0},   // message-type,
+      std::uint8_t{},    // padding
+      std::uint16_t{1},  // one pseudo rectangle
+      std::uint16_t{0}, std::uint16_t{0},
+      static_cast<std::uint16_t>(ScreenWidth()),
+      static_cast<std::uint16_t>(ScreenHeight()), kDesktopSizeEncoding));
 }
 
 bool VncClientConnection::IsUrgent(
diff --git a/host/frontend/vnc_server/vnc_client_connection.h b/host/frontend/vnc_server/vnc_client_connection.h
index 80aaad1..73beae1 100644
--- a/host/frontend/vnc_server/vnc_client_connection.h
+++ b/host/frontend/vnc_server/vnc_client_connection.h
@@ -35,7 +35,8 @@
 
 class VncClientConnection {
  public:
-  VncClientConnection(ClientSocket client, VirtualInputs* virtual_inputs,
+  VncClientConnection(ClientSocket client,
+                      std::shared_ptr<VirtualInputs> virtual_inputs,
                       BlackBoard* bb, bool aggressive);
   VncClientConnection(const VncClientConnection&) = delete;
   VncClientConnection& operator=(const VncClientConnection&) = delete;
@@ -139,7 +140,7 @@
   ClientSocket client_;
   bool control_key_down_ = false;
   bool meta_key_down_ = false;
-  VirtualInputs* virtual_inputs_{};
+  std::shared_ptr<VirtualInputs> virtual_inputs_{};
 
   FrameBufferUpdateRequest previous_update_request_{};
   BlackBoard* bb_;
diff --git a/host/frontend/vnc_server/vnc_server.cpp b/host/frontend/vnc_server/vnc_server.cpp
index 03f5dbe..3aba467 100644
--- a/host/frontend/vnc_server/vnc_server.cpp
+++ b/host/frontend/vnc_server/vnc_server.cpp
@@ -28,7 +28,10 @@
 using cvd::vnc::VncServer;
 
 VncServer::VncServer(int port, bool aggressive)
-    : server_(port), frame_buffer_watcher_{&bb_}, aggressive_{aggressive} {}
+    : server_(port),
+    virtual_inputs_(VirtualInputs::Get()),
+    frame_buffer_watcher_{&bb_},
+    aggressive_{aggressive} {}
 
 void VncServer::MainLoop() {
   while (true) {
@@ -50,7 +53,7 @@
   // data members. In the current setup, if the VncServer is destroyed with
   // clients still running, the clients will all be left with dangling
   // pointers.
-  VncClientConnection client(std::move(sock), &virtual_inputs_, &bb_,
+  VncClientConnection client(std::move(sock), virtual_inputs_, &bb_,
                              aggressive_);
   client.StartSession();
 }
diff --git a/host/frontend/vnc_server/vnc_server.h b/host/frontend/vnc_server/vnc_server.h
index d88835c..66e17e0 100644
--- a/host/frontend/vnc_server/vnc_server.h
+++ b/host/frontend/vnc_server/vnc_server.h
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+#include <memory>
 #include <string>
 #include <thread>
 #include <utility>
@@ -46,7 +47,7 @@
   void StartClientThread(ClientSocket sock);
 
   ServerSocket server_;
-  VirtualInputs virtual_inputs_;
+  std::shared_ptr<VirtualInputs> virtual_inputs_;
   BlackBoard bb_;
   FrameBufferWatcher frame_buffer_watcher_;
   bool aggressive_{};
diff --git a/host/frontend/vnc_server/vnc_utils.h b/host/frontend/vnc_server/vnc_utils.h
index 3eac9f3..194e828 100644
--- a/host/frontend/vnc_server/vnc_utils.h
+++ b/host/frontend/vnc_server/vnc_utils.h
@@ -21,6 +21,7 @@
 #include <utility>
 #include <vector>
 
+#include "common/libs/utils/size_utils.h"
 #include "common/libs/tcp_socket/tcp_socket.h"
 #include "common/vsoc/lib/screen_region_view.h"
 #include "host/libs/config/cuttlefish_config.h"
@@ -68,16 +69,12 @@
 
 // The width of the screen regardless of orientation. Does not change.
 inline int ActualScreenWidth() {
-  return vsoc::screen::ScreenRegionView::GetInstance(
-             vsoc::GetDomain().c_str())
-      ->x_res();
+  return AlignToPowerOf2(vsoc::CuttlefishConfig::Get()->x_res(), 4);
 }
 
 // The height of the screen regardless of orientation. Does not change.
 inline int ActualScreenHeight() {
-  return vsoc::screen::ScreenRegionView::GetInstance(
-             vsoc::GetDomain().c_str())
-      ->y_res();
+  return vsoc::CuttlefishConfig::Get()->y_res();
 }
 
 inline int ScreenSizeInBytes() {
diff --git a/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp b/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp
index 026622e..1336951 100644
--- a/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp
+++ b/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp
@@ -36,27 +36,20 @@
   return ss.str();
 }
 
-std::string MakeIPAndPort(int port) {
-  static constexpr char kLocalHostPrefix[] = "127.0.0.1:";
-  return kLocalHostPrefix + std::to_string(port);
-}
-
 std::string MakeShellUptimeMessage() {
   return MakeMessage("shell,raw:cut -d. -f1 /proc/uptime");
 }
 
-std::string MakeTransportMessage(int port) {
-  return MakeMessage("host:transport:" + MakeIPAndPort(port));
+std::string MakeTransportMessage(const std::string& address) {
+  return MakeMessage("host:transport:" + address);
 }
 
-std::string MakeConnectMessage(int port) {
-  static constexpr char kConnectPrefix[] = "host:connect:";
-  return MakeMessage(kConnectPrefix + MakeIPAndPort(port));
+std::string MakeConnectMessage(const std::string& address) {
+  return MakeMessage("host:connect:" + address);
 }
 
-std::string MakeDisconnectMessage(int port) {
-  static constexpr char kDisonnectPrefix[] = "host:disconnect:";
-  return MakeMessage(kDisonnectPrefix + MakeIPAndPort(port));
+std::string MakeDisconnectMessage(const std::string& address) {
+  return MakeMessage("host:connect:" + address);
 }
 
 // returns true if successfully sent the whole message
@@ -116,10 +109,12 @@
   return AdbSendMessage(sock, message);
 }
 
-bool AdbConnect(int port) { return AdbSendMessage(MakeConnectMessage(port)); }
+bool AdbConnect(const std::string& address) {
+  return AdbSendMessage(MakeConnectMessage(address));
+}
 
-bool AdbDisconnect(int port) {
-  return AdbSendMessage(MakeDisconnectMessage(port));
+bool AdbDisconnect(const std::string& address) {
+  return AdbSendMessage(MakeDisconnectMessage(address));
 }
 
 bool IsInteger(const std::string& str) {
@@ -173,22 +168,22 @@
 // seconds is much larger than seems necessary so we should be more than okay.
 static constexpr int kAdbCommandGapTime = 5;
 
-void EstablishConnection(int port) {
-  LOG(INFO) << "Attempting to connect to device on port " << port;
-  while (!AdbConnect(port)) {
+void EstablishConnection(const std::string& address) {
+  LOG(INFO) << "Attempting to connect to device with address " << address;
+  while (!AdbConnect(address)) {
     sleep(kAdbCommandGapTime);
   }
-  LOG(INFO) << "adb connect message for " << port << " successfully sent";
+  LOG(INFO) << "adb connect message for " << address << " successfully sent";
   sleep(kAdbCommandGapTime);
 }
 
-void WaitForAdbDisconnection(int port) {
+void WaitForAdbDisconnection(const std::string& address) {
   // adb daemon doesn't seem to handle quick, successive messages well. The
   // sleeps stabilize the communication.
-  LOG(INFO) << "Watching for disconnect on port " << port;
+  LOG(INFO) << "Watching for disconnect on " << address;
   while (true) {
     auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
-    if (!AdbSendMessage(sock, MakeTransportMessage(port))) {
+    if (!AdbSendMessage(sock, MakeTransportMessage(address))) {
       LOG(INFO) << "transport message failed, response body: "
                 << RecvAdbResponse(sock);
       break;
@@ -203,19 +198,19 @@
       LOG(INFO) << "couldn't read uptime result";
       break;
     }
-    LOG(DEBUG) << "device on port " << port << " uptime " << uptime;
+    LOG(DEBUG) << "device on " << address << " uptime " << uptime;
     sleep(kAdbCommandGapTime);
   }
   LOG(INFO) << "Sending adb disconnect";
-  AdbDisconnect(port);
+  AdbDisconnect(address);
   sleep(kAdbCommandGapTime);
 }
 
 }  // namespace
 
-[[noreturn]] void cvd::EstablishAndMaintainConnection(int port) {
+[[noreturn]] void cvd::EstablishAndMaintainConnection(std::string address) {
   while (true) {
-    EstablishConnection(port);
-    WaitForAdbDisconnection(port);
+    EstablishConnection(address);
+    WaitForAdbDisconnection(address);
   }
 }
diff --git a/host/libs/adb_connection_maintainer/adb_connection_maintainer.h b/host/libs/adb_connection_maintainer/adb_connection_maintainer.h
index d3378ce..ca5584f 100644
--- a/host/libs/adb_connection_maintainer/adb_connection_maintainer.h
+++ b/host/libs/adb_connection_maintainer/adb_connection_maintainer.h
@@ -17,6 +17,6 @@
 
 namespace cvd {
 
-[[noreturn]] void EstablishAndMaintainConnection(int port);
+[[noreturn]] void EstablishAndMaintainConnection(std::string address);
 
 }  // namespace cvd
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 1a838b8..af690ed 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -31,6 +31,8 @@
 
 #include "common/libs/utils/environment.h"
 #include "common/libs/utils/files.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+
 
 namespace {
 
@@ -67,6 +69,7 @@
 const char* kSerialNumber = "serial_number";
 const char* kInstanceDir = "instance_dir";
 const char* kVmManager = "vm_manager";
+const char* kHardwareName = "hardware_name";
 const char* kDeviceTitle = "device_title";
 
 const char* kCpus = "cpus";
@@ -74,9 +77,13 @@
 const char* kDpi = "dpi";
 const char* kXRes = "x_res";
 const char* kYRes = "y_res";
+const char* kNumScreenBuffers = "num_screen_buffers";
 const char* kRefreshRateHz = "refresh_rate_hz";
 
 const char* kKernelImagePath = "kernel_image_path";
+const char* kUseUnpackedKernel = "use_unpacked_kernel";
+const char* kDecompressedKernelImagePath = "decompressed_kernel_image_path";
+const char* kDecompressKernel = "decompress_kernel";
 const char* kGdbFlag = "gdb_flag";
 const char* kKernelCmdline = "kernel_cmdline";
 const char* kRamdiskImagePath = "ramdisk_image_path";
@@ -85,6 +92,8 @@
 const char* kCacheImagePath = "cache_image_path";
 const char* kDataImagePath = "data_image_path";
 const char* kVendorImagePath = "vendor_image_path";
+const char* kMetadataImagePath = "metadata_image_path";
+const char* kProductImagePath = "product_image_path";
 const char* kUsbV1SocketName = "usb_v1_socket_name";
 const char* kVhciPort = "vhci_port";
 const char* kUsbIpSocketName = "usb_ip_socket_name";
@@ -95,6 +104,7 @@
 const char* kLauncherLogPath = "launcher_log_path";
 const char* kLauncherMonitorPath = "launcher_monitor_socket";
 const char* kDtbPath = "dtb_path";
+const char* kGsiFstabPath = "gsi.fstab_path";
 
 const char* kMempath = "mempath";
 const char* kIvshmemQemuSocketPath = "ivshmem_qemu_socket_path";
@@ -103,24 +113,51 @@
 
 const char* kMobileBridgeName = "mobile_bridge_name";
 const char* kMobileTapName = "mobile_tap_name";
-const char* kWifiBridgeName = "wifi_bridge_name";
 const char* kWifiTapName = "wifi_tap_name";
 const char* kWifiGuestMacAddr = "wifi_guest_mac_addr";
 const char* kWifiHostMacAddr = "wifi_host_mac_addr";
 const char* kEntropySource = "entropy_source";
+const char* kVsockGuestCid = "vsock_guest_cid";
 
 const char* kUuid = "uuid";
-const char* kDisableDacSecurity = "disable_dac_security";
-const char* kDisableAppArmorSecurity = "disable_app_armor_security";
 const char* kCuttlefishEnvPath = "cuttlefish_env_path";
 
 const char* kAdbMode = "adb_mode";
 const char* kAdbIPAndPort = "adb_ip_and_port";
 const char* kSetupWizardMode = "setupwizard_mode";
 
-const char* kLogXml = "log_xml";
-const char* kHypervisorUri = "hypervisor_uri";
 const char* kQemuBinary = "qemu_binary";
+const char* kCrosvmBinary = "crosvm_binary";
+const char* kIvServerBinary = "ivserver_binary";
+const char* kKernelLogMonitorBinary = "kernel_log_monitor_binary";
+
+const char* kEnableVncServer = "enable_vnc_server";
+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";
+const char* kVirtualUsbManagerBinary = "virtual_usb_manager_binary";
+const char* kSocketForwardProxyBinary = "socket_forward_proxy_binary";
+const char* kSocketVsockProxyBinary = "socket_vsock_proxy_binary";
+
+const char* kRunAsDaemon = "run_as_daemon";
+const char* kRunE2eTest = "run_e2e_test";
+const char* kE2eTestBinary = "e2e_test_binary";
+
+const char* kDataPolicy = "data_policy";
+const char* kBlankDataImageMb = "blank_data_image_mb";
+const char* kBlankDataImageFmt = "blank_data_image_fmt";
+
+const char* kLogcatMode = "logcat_mode";
+const char* kLogcatVsockPort = "logcat_vsock_port";
+const char* kFramesVsockPort = "frames_vsock_port";
+const char* kLogcatReceiverBinary = "logcat_receiver_binary";
 }  // namespace
 
 namespace vsoc {
@@ -139,6 +176,13 @@
   (*dictionary_)[kVmManager] = name;
 }
 
+std::string CuttlefishConfig::hardware_name() const {
+  return (*dictionary_)[kHardwareName].asString();
+}
+void CuttlefishConfig::set_hardware_name(const std::string& name) {
+  (*dictionary_)[kHardwareName] = name;
+}
+
 std::string CuttlefishConfig::serial_number() const {
   return (*dictionary_)[kSerialNumber].asString();
 }
@@ -165,6 +209,13 @@
 int CuttlefishConfig::y_res() const { return (*dictionary_)[kYRes].asInt(); }
 void CuttlefishConfig::set_y_res(int y_res) { (*dictionary_)[kYRes] = y_res; }
 
+int CuttlefishConfig::num_screen_buffers() const {
+  return (*dictionary_)[kNumScreenBuffers].asInt();
+}
+void CuttlefishConfig::set_num_screen_buffers(int num_screen_buffers) {
+  (*dictionary_)[kNumScreenBuffers] = num_screen_buffers;
+}
+
 int CuttlefishConfig::refresh_rate_hz() const {
   return (*dictionary_)[kRefreshRateHz].asInt();
 }
@@ -188,6 +239,29 @@
   SetPath(kKernelImagePath, kernel_image_path);
 }
 
+bool CuttlefishConfig::use_unpacked_kernel() const {
+  return (*dictionary_)[kUseUnpackedKernel].asBool();
+}
+
+void CuttlefishConfig::set_use_unpacked_kernel(bool use_unpacked_kernel) {
+  (*dictionary_)[kUseUnpackedKernel] = use_unpacked_kernel;
+}
+
+bool CuttlefishConfig::decompress_kernel() const {
+  return (*dictionary_)[kDecompressKernel].asBool();
+}
+void CuttlefishConfig::set_decompress_kernel(bool decompress_kernel) {
+  (*dictionary_)[kDecompressKernel] = decompress_kernel;
+}
+
+std::string CuttlefishConfig::decompressed_kernel_image_path() const {
+  return (*dictionary_)[kDecompressedKernelImagePath].asString();
+}
+void CuttlefishConfig::set_decompressed_kernel_image_path(
+    const std::string& path) {
+  SetPath(kDecompressedKernelImagePath, path);
+}
+
 std::string CuttlefishConfig::gdb_flag() const {
   return (*dictionary_)[kGdbFlag].asString();
 }
@@ -207,7 +281,7 @@
 void CuttlefishConfig::set_kernel_cmdline(
     const std::set<std::string>& kernel_cmdline) {
   Json::Value args_json_obj(Json::arrayValue);
-  for (auto arg : kernel_cmdline) {
+  for (const auto& arg : kernel_cmdline) {
     args_json_obj.append(arg);
   }
   (*dictionary_)[kKernelCmdline] = args_json_obj;
@@ -215,7 +289,7 @@
 void CuttlefishConfig::add_kernel_cmdline(
     const std::set<std::string>& extra_args) {
   std::set<std::string> cmdline = kernel_cmdline();
-  for (auto arg : extra_args) {
+  for (const auto& arg : extra_args) {
     if (cmdline.count(arg)) {
       LOG(ERROR) << "Kernel argument " << arg << " is duplicated";
     }
@@ -278,6 +352,22 @@
   SetPath(kVendorImagePath, vendor_image_path);
 }
 
+std::string CuttlefishConfig::metadata_image_path() const {
+  return (*dictionary_)[kMetadataImagePath].asString();
+}
+void CuttlefishConfig::set_metadata_image_path(
+    const std::string& metadata_image_path) {
+  SetPath(kMetadataImagePath, metadata_image_path);
+}
+
+std::string CuttlefishConfig::product_image_path() const {
+  return (*dictionary_)[kProductImagePath].asString();
+}
+void CuttlefishConfig::set_product_image_path(
+    const std::string& product_image_path) {
+  SetPath(kProductImagePath, product_image_path);
+}
+
 std::string CuttlefishConfig::dtb_path() const {
   return (*dictionary_)[kDtbPath].asString();
 }
@@ -285,6 +375,13 @@
   SetPath(kDtbPath, dtb_path);
 }
 
+std::string CuttlefishConfig::gsi_fstab_path() const {
+  return (*dictionary_)[kGsiFstabPath].asString();
+}
+void CuttlefishConfig::set_gsi_fstab_path(const std::string& path){
+  SetPath(kGsiFstabPath, path);
+}
+
 std::string CuttlefishConfig::mempath() const {
   return (*dictionary_)[kMempath].asString();
 }
@@ -392,14 +489,6 @@
   (*dictionary_)[kMobileBridgeName] = mobile_bridge_name;
 }
 
-std::string CuttlefishConfig::wifi_bridge_name() const {
-  return (*dictionary_)[kWifiBridgeName].asString();
-}
-void CuttlefishConfig::set_wifi_bridge_name(
-    const std::string& wifi_bridge_name) {
-  (*dictionary_)[kWifiBridgeName] = wifi_bridge_name;
-}
-
 std::string CuttlefishConfig::wifi_guest_mac_addr() const {
   return (*dictionary_)[kWifiGuestMacAddr].asString();
 }
@@ -437,6 +526,14 @@
   (*dictionary_)[kEntropySource] = entropy_source;
 }
 
+int CuttlefishConfig::vsock_guest_cid() const {
+  return (*dictionary_)[kVsockGuestCid].asInt();
+}
+
+void CuttlefishConfig::set_vsock_guest_cid(int vsock_guest_cid) {
+  (*dictionary_)[kVsockGuestCid] = vsock_guest_cid;
+}
+
 std::string CuttlefishConfig::uuid() const {
   return (*dictionary_)[kUuid].asString();
 }
@@ -444,13 +541,6 @@
   (*dictionary_)[kUuid] = uuid;
 }
 
-bool CuttlefishConfig::disable_dac_security() const {
-  return (*dictionary_)[kDisableDacSecurity].asBool();
-}
-void CuttlefishConfig::set_disable_dac_security(bool disable_dac_security) {
-  (*dictionary_)[kDisableDacSecurity] = disable_dac_security;
-}
-
 void CuttlefishConfig::set_cuttlefish_env_path(const std::string& path) {
   SetPath(kCuttlefishEnvPath, path);
 }
@@ -458,20 +548,20 @@
   return (*dictionary_)[kCuttlefishEnvPath].asString();
 }
 
-bool CuttlefishConfig::disable_app_armor_security() const {
-  return (*dictionary_)[kDisableAppArmorSecurity].asBool();
-}
-void CuttlefishConfig::set_disable_app_armor_security(
-    bool disable_app_armor_security) {
-  (*dictionary_)[kDisableAppArmorSecurity] = disable_app_armor_security;
+std::set<std::string> CuttlefishConfig::adb_mode() const {
+  std::set<std::string> args_set;
+  for (auto& mode : (*dictionary_)[kAdbMode]) {
+    args_set.insert(mode.asString());
+  }
+  return args_set;
 }
 
-std::string CuttlefishConfig::adb_mode() const {
-  return (*dictionary_)[kAdbMode].asString();
-}
-
-void CuttlefishConfig::set_adb_mode(const std::string& mode) {
-  (*dictionary_)[kAdbMode] = mode;
+void CuttlefishConfig::set_adb_mode(const std::set<std::string>& mode) {
+  Json::Value mode_json_obj(Json::arrayValue);
+  for (const auto& arg : mode) {
+    mode_json_obj.append(arg);
+  }
+  (*dictionary_)[kAdbMode] = mode_json_obj;
 }
 
 std::string CuttlefishConfig::adb_ip_and_port() const {
@@ -483,9 +573,14 @@
 }
 
 std::string CuttlefishConfig::adb_device_name() const {
-  if (adb_mode().find("tunnel") != std::string::npos) {
+  // TODO(schuffelen): Deal with duplication between here and launch.cc
+  bool tunnelMode = adb_mode().count("tunnel") > 0;
+  bool vsockTunnel = adb_mode().count("vsock_tunnel") > 0;
+  bool vsockHalfProxy = adb_mode().count("vsock_half_proxy") > 0;
+  bool nativeVsock = adb_mode().count("native_vsock") > 0;
+  if (tunnelMode || vsockTunnel || vsockHalfProxy || nativeVsock) {
     return adb_ip_and_port();
-  } else if (adb_mode().find("usb") != std::string::npos) {
+  } else if (adb_mode().count("usb") > 0) {
     return serial_number();
   }
   LOG(ERROR) << "no adb_mode found, returning bad device name";
@@ -508,22 +603,6 @@
   (*dictionary_)[kSetupWizardMode] = mode;
 }
 
-bool CuttlefishConfig::log_xml() const {
-  return (*dictionary_)[kLogXml].asBool();
-}
-
-void CuttlefishConfig::set_log_xml(bool log_xml) {
-  (*dictionary_)[kLogXml] = log_xml;
-}
-
-std::string CuttlefishConfig::hypervisor_uri() const {
-  return (*dictionary_)[kHypervisorUri].asString();
-}
-
-void CuttlefishConfig::set_hypervisor_uri(const std::string& hypervisor_uri) {
-  (*dictionary_)[kHypervisorUri] = hypervisor_uri;
-}
-
 std::string CuttlefishConfig::qemu_binary() const {
   return (*dictionary_)[kQemuBinary].asString();
 }
@@ -532,6 +611,226 @@
   (*dictionary_)[kQemuBinary] = qemu_binary;
 }
 
+std::string CuttlefishConfig::crosvm_binary() const {
+  return (*dictionary_)[kCrosvmBinary].asString();
+}
+
+void CuttlefishConfig::set_crosvm_binary(const std::string& crosvm_binary) {
+  (*dictionary_)[kCrosvmBinary] = crosvm_binary;
+}
+
+std::string CuttlefishConfig::ivserver_binary() const {
+  return (*dictionary_)[kIvServerBinary].asString();
+}
+
+void CuttlefishConfig::set_ivserver_binary(const std::string& ivserver_binary) {
+  (*dictionary_)[kIvServerBinary] = ivserver_binary;
+}
+
+std::string CuttlefishConfig::kernel_log_monitor_binary() const {
+  return (*dictionary_)[kKernelLogMonitorBinary].asString();
+}
+
+void CuttlefishConfig::set_kernel_log_monitor_binary(
+    const std::string& kernel_log_monitor_binary) {
+  (*dictionary_)[kKernelLogMonitorBinary] = kernel_log_monitor_binary;
+}
+
+bool CuttlefishConfig::enable_vnc_server() const {
+  return (*dictionary_)[kEnableVncServer].asBool();
+}
+
+void CuttlefishConfig::set_enable_vnc_server(bool enable_vnc_server) {
+  (*dictionary_)[kEnableVncServer] = enable_vnc_server;
+}
+
+std::string CuttlefishConfig::vnc_server_binary() const {
+  return (*dictionary_)[kVncServerBinary].asString();
+}
+
+void CuttlefishConfig::set_vnc_server_binary(
+    const std::string& vnc_server_binary) {
+  (*dictionary_)[kVncServerBinary] = vnc_server_binary;
+}
+
+int CuttlefishConfig::vnc_server_port() const {
+  return (*dictionary_)[kVncServerPort].asInt();
+}
+
+void CuttlefishConfig::set_vnc_server_port(int vnc_server_port) {
+  (*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();
+}
+
+void CuttlefishConfig::set_restart_subprocesses(bool restart_subprocesses) {
+  (*dictionary_)[kRestartSubprocesses] = restart_subprocesses;
+}
+
+bool CuttlefishConfig::run_adb_connector() const {
+  return (*dictionary_)[kRunAdbConnector].asBool();
+}
+
+void CuttlefishConfig::set_run_adb_connector(bool run_adb_connector) {
+  (*dictionary_)[kRunAdbConnector] = run_adb_connector;
+}
+
+std::string CuttlefishConfig::adb_connector_binary() const {
+  return (*dictionary_)[kAdbConnectorBinary].asString();
+}
+
+void CuttlefishConfig::set_adb_connector_binary(
+    const std::string& adb_connector_binary) {
+  (*dictionary_)[kAdbConnectorBinary] = adb_connector_binary;
+}
+
+std::string CuttlefishConfig::virtual_usb_manager_binary() const {
+  return (*dictionary_)[kVirtualUsbManagerBinary].asString();
+}
+
+void CuttlefishConfig::set_virtual_usb_manager_binary(
+    const std::string& virtual_usb_manager_binary) {
+  (*dictionary_)[kVirtualUsbManagerBinary] = virtual_usb_manager_binary;
+}
+
+std::string CuttlefishConfig::socket_forward_proxy_binary() const {
+  return (*dictionary_)[kSocketForwardProxyBinary].asString();
+}
+
+void CuttlefishConfig::set_socket_forward_proxy_binary(
+    const std::string& socket_forward_proxy_binary) {
+  (*dictionary_)[kSocketForwardProxyBinary] = socket_forward_proxy_binary;
+}
+
+std::string CuttlefishConfig::socket_vsock_proxy_binary() const {
+  return (*dictionary_)[kSocketVsockProxyBinary].asString();
+}
+
+void CuttlefishConfig::set_socket_vsock_proxy_binary(
+    const std::string& socket_vsock_proxy_binary) {
+  (*dictionary_)[kSocketVsockProxyBinary] = socket_vsock_proxy_binary;
+}
+
+bool CuttlefishConfig::run_as_daemon() const {
+  return (*dictionary_)[kRunAsDaemon].asBool();
+}
+
+void CuttlefishConfig::set_run_as_daemon(bool run_as_daemon) {
+  (*dictionary_)[kRunAsDaemon] = run_as_daemon;
+}
+
+bool CuttlefishConfig::run_e2e_test() const {
+  return (*dictionary_)[kRunE2eTest].asBool();
+}
+
+void CuttlefishConfig::set_run_e2e_test(bool run_e2e_test) {
+  (*dictionary_)[kRunE2eTest] = run_e2e_test;
+}
+
+std::string CuttlefishConfig::e2e_test_binary() const {
+  return (*dictionary_)[kE2eTestBinary].asString();
+}
+
+void CuttlefishConfig::set_e2e_test_binary(const std::string& e2e_test_binary) {
+  (*dictionary_)[kE2eTestBinary] = e2e_test_binary;
+}
+
+std::string CuttlefishConfig::data_policy() const {
+  return (*dictionary_)[kDataPolicy].asString();
+}
+
+void CuttlefishConfig::set_data_policy(const std::string& data_policy) {
+  (*dictionary_)[kDataPolicy] = data_policy;
+}
+
+int CuttlefishConfig::blank_data_image_mb() const {
+  return (*dictionary_)[kBlankDataImageMb].asInt();
+}
+
+void CuttlefishConfig::set_blank_data_image_mb(int blank_data_image_mb) {
+  (*dictionary_)[kBlankDataImageMb] = blank_data_image_mb;
+}
+
+std::string CuttlefishConfig::blank_data_image_fmt() const {
+  return (*dictionary_)[kBlankDataImageFmt].asString();
+}
+
+void CuttlefishConfig::set_blank_data_image_fmt(const std::string& blank_data_image_fmt) {
+  (*dictionary_)[kBlankDataImageFmt] = blank_data_image_fmt;
+}
+
+
+void CuttlefishConfig::set_logcat_mode(const std::string& mode) {
+  (*dictionary_)[kLogcatMode] = mode;
+}
+
+std::string CuttlefishConfig::logcat_mode() const {
+  return (*dictionary_)[kLogcatMode].asString();
+}
+
+void CuttlefishConfig::set_logcat_vsock_port(int port) {
+  (*dictionary_)[kLogcatVsockPort] = port;
+}
+
+int CuttlefishConfig::logcat_vsock_port() const {
+  return (*dictionary_)[kLogcatVsockPort].asInt();
+}
+
+void CuttlefishConfig::set_frames_vsock_port(int port) {
+  (*dictionary_)[kFramesVsockPort] = port;
+}
+
+int CuttlefishConfig::frames_vsock_port() const {
+  return (*dictionary_)[kFramesVsockPort].asInt();
+}
+
+void CuttlefishConfig::set_logcat_receiver_binary(const std::string& binary) {
+  SetPath(kLogcatReceiverBinary, binary);
+}
+
+std::string CuttlefishConfig::logcat_receiver_binary() const {
+  return (*dictionary_)[kLogcatReceiverBinary].asString();
+}
+
+bool CuttlefishConfig::enable_ivserver() const {
+  return hardware_name() == "cutf_ivsh";
+}
+
+std::string CuttlefishConfig::touch_socket_path() const {
+  return PerInstancePath("touch.sock");
+}
+
+std::string CuttlefishConfig::keyboard_socket_path() const {
+  return PerInstancePath("keyboard.sock");
+}
+
 // Creates the (initially empty) config object and populates it with values from
 // the config file if the CUTTLEFISH_CONFIG_FILE env variable is present.
 // Returns nullptr if there was an error loading from file
@@ -614,12 +913,7 @@
 
 std::string GetDefaultPerInstanceDir() {
   std::ostringstream stream;
-  if (HostSupportsQemuCli()) {
-    stream << std::getenv("HOME") << "/cuttlefish_runtime";
-  } else {
-    stream << "/var/run/libvirt-" << kDefaultUuidPrefix << std::setfill('0')
-           << std::setw(2) << GetInstance();
-  }
+  stream << std::getenv("HOME") << "/cuttlefish_runtime";
   return stream.str();
 }
 
@@ -627,6 +921,11 @@
   return GetPerInstanceDefault("/var/run/shm/cvd-");
 }
 
+int GetDefaultPerInstanceVsockCid() {
+  constexpr int kFirstGuestCid = 3;
+  return vsoc::HostSupportsVsock() ? GetPerInstanceDefault(kFirstGuestCid) : 0;
+}
+
 std::string DefaultHostArtifactsPath(const std::string& file_name) {
   return (cvd::StringFromEnv("ANDROID_HOST_OUT",
                              cvd::StringFromEnv("HOME", ".")) +
@@ -647,4 +946,11 @@
           "/usr/lib/cuttlefish-common/bin/capability_query.py qemu_cli") == 0;
   return supported;
 }
+
+bool HostSupportsVsock() {
+  static bool supported =
+      std::system(
+          "/usr/lib/cuttlefish-common/bin/capability_query.py vsock") == 0;
+  return supported;
+}
 }  // namespace vsoc
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index fd1a26e..668badc 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -57,6 +57,9 @@
   std::string vm_manager() const;
   void set_vm_manager(const std::string& name);
 
+  std::string hardware_name() const;
+  void set_hardware_name(const std::string& name);
+
   std::string serial_number() const;
   void set_serial_number(const std::string& serial_number);
 
@@ -75,12 +78,36 @@
   int y_res() const;
   void set_y_res(int y_res);
 
+  int num_screen_buffers() const;
+  void set_num_screen_buffers(int num_screen_buffers);
+
   int refresh_rate_hz() const;
   void set_refresh_rate_hz(int refresh_rate_hz);
 
+  // Returns kernel image extracted from the boot image or the user-provided one
+  // if given by command line to the launcher. This function should not be used
+  // to get the kernel image the vmm should boot, GetKernelImageToUse() should
+  // be used instead.
   std::string kernel_image_path() const;
   void set_kernel_image_path(const std::string& kernel_image_path);
 
+  bool decompress_kernel() const;
+  void set_decompress_kernel(bool decompress_kernel);
+
+  // Returns the path to the kernel image that should be given to the vm manager
+  // to boot, takes into account whether the original image was decompressed or
+  // not.
+  std::string GetKernelImageToUse() const {
+    return decompress_kernel() ? decompressed_kernel_image_path()
+                               : kernel_image_path();
+  }
+
+  std::string decompressed_kernel_image_path() const;
+  void set_decompressed_kernel_image_path(const std::string& path);
+
+  bool use_unpacked_kernel() const;
+  void set_use_unpacked_kernel(bool use_unpacked_kernel);
+
   std::set<std::string> kernel_cmdline() const;
   void set_kernel_cmdline(const std::set<std::string>& kernel_cmdline);
   void add_kernel_cmdline(const std::string& arg);
@@ -105,9 +132,18 @@
   std::string vendor_image_path() const;
   void set_vendor_image_path(const std::string& vendor_image_path);
 
+  std::string metadata_image_path() const;
+  void set_metadata_image_path(const std::string& metadata_image_path);
+
+  std::string product_image_path() const;
+  void set_product_image_path(const std::string& product_image_path);
+
   std::string dtb_path() const;
   void set_dtb_path(const std::string& dtb_path);
 
+  std::string gsi_fstab_path() const;
+  void set_gsi_fstab_path(const std::string& path);
+
   std::string mempath() const;
   void set_mempath(const std::string& mempath);
 
@@ -145,6 +181,9 @@
   std::string logcat_path() const;
   void set_logcat_path(const std::string& logcat_path);
 
+  std::string logcat_receiver_binary() const;
+  void set_logcat_receiver_binary(const std::string& binary);
+
   std::string launcher_log_path() const;
   void set_launcher_log_path(const std::string& launcher_log_path);
 
@@ -158,9 +197,6 @@
   std::string mobile_tap_name() const;
   void set_mobile_tap_name(const std::string& mobile_tap_name);
 
-  std::string wifi_bridge_name() const;
-  void set_wifi_bridge_name(const std::string& wifi_bridge_name);
-
   std::string wifi_tap_name() const;
   void set_wifi_tap_name(const std::string& wifi_tap_name);
 
@@ -173,20 +209,17 @@
   std::string entropy_source() const;
   void set_entropy_source(const std::string& entropy_source);
 
+  void set_vsock_guest_cid(int vsock_guest_cid);
+  int vsock_guest_cid() const;
+
   std::string uuid() const;
   void set_uuid(const std::string& uuid);
 
-  bool disable_dac_security() const;
-  void set_disable_dac_security(bool disable_dac_security);
-
-  bool disable_app_armor_security() const;
-  void set_disable_app_armor_security(bool disable_app_armor_security);
-
   void set_cuttlefish_env_path(const std::string& path);
   std::string cuttlefish_env_path() const;
 
-  void set_adb_mode(const std::string& mode);
-  std::string adb_mode() const;
+  void set_adb_mode(const std::set<std::string>& modes);
+  std::set<std::string> adb_mode() const;
 
   void set_adb_ip_and_port(const std::string& ip_port);
   std::string adb_ip_and_port() const;
@@ -199,15 +232,87 @@
   void set_setupwizard_mode(const std::string& title);
   std::string setupwizard_mode() const;
 
-  void set_log_xml(bool log_xml);
-  bool log_xml() const;
-
-  void set_hypervisor_uri(const std::string& hypervisor_uri);
-  std::string hypervisor_uri() const;
-
   void set_qemu_binary(const std::string& qemu_binary);
   std::string qemu_binary() const;
 
+  void set_crosvm_binary(const std::string& crosvm_binary);
+  std::string crosvm_binary() const;
+
+  void set_ivserver_binary(const std::string& ivserver_binary);
+  std::string ivserver_binary() const;
+
+  void set_kernel_log_monitor_binary(
+      const std::string& kernel_log_monitor_binary);
+  std::string kernel_log_monitor_binary() const;
+
+  void set_enable_vnc_server(bool enable_vnc_server);
+  bool enable_vnc_server() const;
+
+  void set_vnc_server_port(int vnc_server_port);
+  int vnc_server_port() const;
+
+  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;
+
+  void set_run_adb_connector(bool run_adb_connector);
+  bool run_adb_connector() const;
+
+  void set_adb_connector_binary(const std::string& adb_connector_binary);
+  std::string adb_connector_binary() const;
+
+  void set_virtual_usb_manager_binary(const std::string& binary);
+  std::string virtual_usb_manager_binary() const;
+
+  void set_socket_forward_proxy_binary(const std::string& binary);
+  std::string socket_forward_proxy_binary() const;
+
+  void set_socket_vsock_proxy_binary(const std::string& binary);
+  std::string socket_vsock_proxy_binary() const;
+
+  void set_run_as_daemon(bool run_as_daemon);
+  bool run_as_daemon() const;
+
+  void set_run_e2e_test(bool run_e2e_test);
+  bool run_e2e_test() const;
+
+  void set_e2e_test_binary(const std::string& e2e_test_binary);
+  std::string e2e_test_binary() const;
+
+  void set_data_policy(const std::string& data_policy);
+  std::string data_policy() const;
+
+  void set_blank_data_image_mb(int blank_data_image_mb);
+  int blank_data_image_mb() const;
+
+  void set_blank_data_image_fmt(const std::string& blank_data_image_fmt);
+  std::string blank_data_image_fmt() const;
+
+  void set_logcat_mode(const std::string& mode);
+  std::string logcat_mode() const;
+
+  void set_logcat_vsock_port(int port);
+  int logcat_vsock_port() const;
+
+  void set_frames_vsock_port(int port);
+  int frames_vsock_port() const;
+
+  bool enable_ivserver() const;
+
+  std::string touch_socket_path() const;
+  std::string keyboard_socket_path() const;
+
  private:
   std::unique_ptr<Json::Value> dictionary_;
 
@@ -237,11 +342,12 @@
 
 std::string GetDefaultPerInstanceDir();
 std::string GetDefaultMempath();
+int GetDefaultPerInstanceVsockCid();
 
 std::string DefaultHostArtifactsPath(const std::string& file);
 std::string DefaultGuestImagePath(const std::string& file);
 
-// Whether the installed host packages support calling qemu directly instead of
-// through libvirt
+// Whether the host supports qemu
 bool HostSupportsQemuCli();
+bool HostSupportsVsock();
 }  // namespace vsoc
diff --git a/host/libs/vm_manager/Android.bp b/host/libs/vm_manager/Android.bp
index 0d077ea..6c638ea 100644
--- a/host/libs/vm_manager/Android.bp
+++ b/host/libs/vm_manager/Android.bp
@@ -16,7 +16,7 @@
 cc_library_host_static {
     name: "libcuttlefish_vm_manager",
     srcs: [
-        "libvirt_manager.cpp",
+        "crosvm_manager.cpp",
         "qemu_manager.cpp",
         "vm_manager.cpp",
     ],
diff --git a/host/libs/vm_manager/cf_qemu.sh b/host/libs/vm_manager/cf_qemu.sh
index ee46ef2..e037c3c 100755
--- a/host/libs/vm_manager/cf_qemu.sh
+++ b/host/libs/vm_manager/cf_qemu.sh
@@ -16,6 +16,33 @@
 # limitations under the License.
 #
 
+print_command() {
+  binary=$1; shift
+  binary_args=("$@")
+  printf %s "${binary}"
+  for i in "${binary_args[@]}"; do
+    case "$i" in
+      -*) printf "\\%s  %s " $'\n' "$i" ;;
+      *) printf "%s " "$i" ;;
+    esac
+  done
+  echo
+}
+
+exec_run() {
+  binary=$1; shift
+  binary_args=("$@")
+  print_command "${binary}" "${binary_args[@]}"
+  exec "${binary}" "${binary_args[@]}"
+}
+
+run() {
+  binary=$1; shift
+  binary_args=("$@")
+  print_command "${binary}" "${binary_args[@]}"
+  "${binary}" "${binary_args[@]}"
+}
+
 default_instance_number() {
     if [[ "${USER::5}" == "vsoc-" ]]; then
         echo "${USER: -2}"
@@ -30,15 +57,33 @@
 default_mobile_tap_name="cvd-mtap-${CUTTLEFISH_INSTANCE}"
 default_wifi_tap_name="cvd-wtap-${CUTTLEFISH_INSTANCE}"
 
+qemu_binary=${qemu_binary=/usr/bin/qemu-system-x86_64}
+dtc_binary=${dtc_binary:-dtc}
+
 if [[ -z "${ivshmem_vector_count}" ]]; then
     echo "The required ivshmem_vector_count environment variable is not set" >&2
     exit 1
 fi
 
+if [[ "${qemu_binary##*/}" = "qemu-system-aarch64" ]]; then
+  # On ARM, the early console can be PCI, and ISA is not supported
+  kernel_console_serial="pci-serial"
+  machine="virt,gic_version=2"
+  cpu=cortex-a53
+else
+  # On x86, the early console must be ISA, not PCI, so we start to get kernel
+  # messages as soon as possible. ISA devices do not have 'addr' assignments.
+  kernel_console_serial="isa-serial"
+  machine="pc-i440fx-2.8,accel=kvm"
+  cpu=host
+fi
+
+# Put anything here that might affect the machine configuration generated by
+# QEMU. Anything which connects statefully to another service (like a socket)
+# should be added in another section below.
 args=(
-    -enable-kvm
     -name "guest=${instance_name:-${default_instance_name}},debug-threads=on"
-    -machine "pc-i440fx-2.8,accel=kvm,usb=off,dump-guest-core=off"
+    -machine "${machine},usb=off,dump-guest-core=off"
     -m "${memory_mb:-2048}"
     -realtime mlock=off
     -smp "${cpus:-2},sockets=${cpus:-2},cores=1,threads=1"
@@ -46,43 +91,115 @@
     -display none
     -no-user-config
     -nodefaults
-    -chardev "socket,id=charmonitor,path=${monitor_path:-${default_dir}/qemu_monitor.sock},nowait"
-    -mon "chardev=charmonitor,id=monitor,mode=control"
     -rtc "base=utc"
     -no-shutdown
     -boot "strict=on"
     -kernel "${kernel_image_path:-${HOME}/kernel}"
     -append "${kernel_cmdline:-"loop.max_part=7 console=ttyS0 androidboot.console=ttyS1 androidboot.hardware=vsoc enforcing=0 audit=1 androidboot.selinux=permissive mac80211_hwsim.radios=0 security=selinux buildvariant=userdebug  androidboot.serialno=CUTTLEFISHCVD01 androidboot.lcd_density=160"}"
     -dtb "${dtb_path:-${HOME}/config/cuttlefish.dtb}"
-    -device "piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2"
-    -device "virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x4"
+    -device "piix3-usb-uhci,id=usb,addr=0x1.0x2"
+    -device "virtio-serial-pci,id=virtio-serial0"
     -drive "file=${system_image_path:-${HOME}/system.img},format=raw,if=none,id=drive-virtio-disk0,aio=threads"
-    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1"
+    -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1"
     -drive "file=${data_image_path:-${HOME}/userdata.img},format=raw,if=none,id=drive-virtio-disk1,aio=threads"
-    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk1,id=virtio-disk1"
+    -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk1,id=virtio-disk1"
     -drive "file=${cache_image_path:-${HOME}/cache.img},format=raw,if=none,id=drive-virtio-disk2,aio=threads"
-    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x7,drive=drive-virtio-disk2,id=virtio-disk2"
+    -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk2,id=virtio-disk2"
     -drive "file=${vendor_image_path:-${HOME}/vendor.img},format=raw,if=none,id=drive-virtio-disk3,aio=threads"
-    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x8,drive=drive-virtio-disk3,id=virtio-disk3"
+    -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk3,id=virtio-disk3"
+    -drive "file=${metadata_image_path:-${HOME}/metadata.img},format=raw,if=none,id=drive-virtio-disk4,aio=threads"
+    -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk4,id=virtio-disk4"
+    -drive "file=${product_image_path:-${HOME}/product.img},format=raw,if=none,id=drive-virtio-disk5,aio=threads"
+    -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk5,id=virtio-disk5"
     -netdev "tap,id=hostnet0,ifname=${wifi_tap_name:-${default_wifi_tap_name}},script=no,downscript=no"
-    -device "virtio-net-pci,netdev=hostnet0,id=net0,bus=pci.0,addr=0x2"
+    -device "virtio-net-pci,netdev=hostnet0,id=net0"
     -netdev "tap,id=hostnet1,ifname=${mobile_tap_name:-${default_mobile_tap_name}},script=no,downscript=no"
-    -device "virtio-net-pci,netdev=hostnet1,id=net1,bus=pci.0,addr=0x3"
-    -chardev "socket,id=charserial0,path=${kernel_log_socket_name:-${default_dir}/kernel-log}"
-    -device "isa-serial,chardev=charserial0,id=serial0"
-    -chardev "socket,id=charserial1,path=${console_path:-${default_dir}/console},server,nowait"
-    -device "isa-serial,chardev=charserial1,id=serial1"
-    -chardev "file,id=charchannel0,path=${logcat_path:-${default_dir}/logcat},append=on"
-    -device "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=cf-logcat"
-    -device "virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x9"
+    -device "virtio-net-pci,netdev=hostnet1,id=net1"
+    -device "virtio-balloon-pci,id=balloon0"
     -object "rng-random,id=objrng0,filename=/dev/urandom"
-    -device "virtio-rng-pci,rng=objrng0,id=rng0,max-bytes=1024,period=2000,bus=pci.0,addr=0xa"
+    -device "virtio-rng-pci,rng=objrng0,id=rng0,max-bytes=1024,period=2000"
+    -cpu "${cpu}"
+    -msg "timestamp=on"
+    -device "AC97"
+)
+
+if [[ -n "${dtb_path}" ]]; then
+  if [[ "${qemu_binary##*/}" = "qemu-system-aarch64" ]]; then
+    # Decompile the dt fragment to include in our machine FDT
+    dtsi_path="${default_dir}/android.dtsi"
+    dtc_args=(
+      -I dtb
+      "${dtb_path}"
+      -O dts
+      -o "${dtsi_path}"
+    )
+    run "${dtc_binary}" "${dtc_args[@]}"
+
+    # Remove duplicate version definition from the dtsi
+    sed_binary=sed
+    sed_args=(
+      -i "/^\/dts-v1\/;$/d"
+      ${dtsi_path}
+    )
+    run "${sed_binary}" "${sed_args[@]}"
+
+    # Dump the machine FDT blob
+    dts_path="${default_dir}/cuttlefish.dts"
+    dtb_path="${default_dir}/cuttlefish.dtb"
+    dtb_args=(-machine "dumpdtb=${dtb_path}")
+    run "${qemu_binary}" "${args[@]}" "${dtb_args[@]}"
+
+    # Decompile the FDT blob
+    dtc_args=(
+      -I dtb
+      ${dtb_path}
+      -O dts
+      -o ${dts_path}
+    )
+    run "${dtc_binary}" "${dtc_args[@]}"
+
+    # Concatenate the dts and dtsi sources
+    echo "cat ${dtsi_path} >>${dts_path}"
+    echo
+    cat ${dtsi_path} >>${dts_path}
+
+    # Compile the patched machine FDT
+    dtc_args=(
+      -i "${dts_path%/*}"
+      -I dts
+      "${dts_path}"
+      -O dtb
+      -o "${dtb_path}"
+    )
+    run "${dtc_binary}" "${dtc_args[@]}"
+  fi
+
+  args+=(-dtb "${dtb_path}")
+fi
+
+# The services providing these sockets don't expect multiple connections,
+# so we must not have them in 'args' when we dump the machine FDT. It's
+# OK to add them now, after the dumping and patching has completed.
+# The (maybe patched) DTB can also be provided now.
+
+args+=(
+    -chardev "socket,id=charmonitor,path=${monitor_path:-${default_dir}/qemu_monitor.sock},server,nowait"
+    -mon "chardev=charmonitor,id=monitor,mode=control"
+    -chardev "socket,id=charserial0,path=${kernel_log_socket_name:-${default_dir}/kernel-log}"
+    -device "${kernel_console_serial},chardev=charserial0,id=serial0"
+    -chardev "socket,id=charserial1,path=${console_path:-${default_dir}/console},server,nowait"
+    -device "pci-serial,chardev=charserial1,id=serial1"
     -chardev "socket,path=${ivshmem_qemu_socket_path:-${default_dir}/ivshmem_socket_qemu},id=ivsocket"
     -device "ivshmem-doorbell,chardev=ivsocket,vectors=${ivshmem_vector_count}"
-    -cpu host
-    -msg "timestamp=on"
 )
 
+if [[ "${logcat_mode}" == "serial" ]]; then
+    args+=(
+        -chardev "file,id=charchannel0,path=${logcat_path:-${default_dir}/logcat},append=on"
+        -device "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=cf-logcat"
+    )
+fi
+
 if [[ -n "${gdb_flag}" ]]; then
   args+=(-gdb "${gdb_flag}")
 fi
@@ -97,14 +214,10 @@
       -device "virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel1,id=channel1,name=cf-gadget-usb-v1"
   )
 fi
-printf %s "exec ${qemu_binary=/usr/bin/qemu-system-x86_64}"
-for i in "${args[@]}"; do
-  case "$i" in
-    -*) printf "\\%s  %s " $'\n' "$i" ;;
-    *) printf "%s " "$i" ;;
-  esac
-done
-echo
 
-exec "${qemu_binary=/usr/bin/qemu-system-x86_64}" \
-  "${args[@]}"
+if [[ ${vsock_guest_cid:-0} -gt 2 ]]; then
+  args+=(-device "vhost-vsock-pci,guest-cid=${vsock_guest_cid}")
+fi
+
+export QEMU_AUDIO_DRV=none
+exec_run "${qemu_binary}" "${args[@]}"
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
new file mode 100644
index 0000000..1605931
--- /dev/null
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/libs/vm_manager/crosvm_manager.h"
+
+#include <string>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "common/libs/utils/network.h"
+#include "common/libs/utils/subprocess.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+
+namespace vm_manager {
+
+namespace {
+
+std::string GetControlSocketPath(const vsoc::CuttlefishConfig* config) {
+  return config->PerInstancePath("crosvm_control.sock");
+}
+
+cvd::SharedFD ConnectToLogMonitor(const std::string& log_monitor_name) {
+  return cvd::SharedFD::SocketLocalClient(log_monitor_name.c_str(), false,
+                                          SOCK_STREAM);
+}
+
+void AddTapFdParameter(cvd::Command* crosvm_cmd, const std::string& tap_name) {
+  auto tap_fd = cvd::OpenTapInterface(tap_name);
+  if (tap_fd->IsOpen()) {
+    crosvm_cmd->AddParameter("--tap-fd=", tap_fd);
+  } else {
+    LOG(ERROR) << "Unable to connect to " << tap_name << ": "
+               << tap_fd->StrError();
+  }
+}
+
+}  // namespace
+
+const std::string CrosvmManager::name() { return "crosvm"; }
+
+CrosvmManager::CrosvmManager(const vsoc::CuttlefishConfig* config)
+    : VmManager(config) {}
+
+cvd::Command CrosvmManager::StartCommand() {
+  if (!config_->ramdisk_image_path().empty()) {
+    // TODO re-enable ramdisk when crosvm supports it
+    LOG(FATAL) << "initramfs not supported";
+    return cvd::Command("/bin/false");
+  }
+
+  // TODO Add aarch64 support
+  // TODO Add the tap interfaces (--tap-fd)
+  // TODO Redirect logcat output
+
+  // Run crosvm directly instead of through a cf_crosvm.sh script. The kernel
+  // logs are on crosvm's standard output, so we need to redirect it to the log
+  // monitor socket, a helper script will print more than just the logs to
+  // standard output.
+  cvd::Command command(config_->crosvm_binary());
+  command.AddParameter("run");
+
+  command.AddParameter("--null-audio");
+  command.AddParameter("--mem=", config_->memory_mb());
+  command.AddParameter("--cpus=", config_->cpus());
+  command.AddParameter("--params=", config_->kernel_cmdline_as_string());
+  command.AddParameter("--rwdisk=", config_->system_image_path());
+  command.AddParameter("--rwdisk=", config_->data_image_path());
+  command.AddParameter("--rwdisk=", config_->cache_image_path());
+  command.AddParameter("--rwdisk=", config_->vendor_image_path());
+  command.AddParameter("--rwdisk=", config_->metadata_image_path());
+  command.AddParameter("--rwdisk=", config_->product_image_path());
+  command.AddParameter("--socket=", GetControlSocketPath(config_));
+  command.AddParameter("--android-fstab=", config_->gsi_fstab_path());
+  command.AddParameter("--single-touch=", config_->touch_socket_path(), ":",
+                       config_->x_res(), ":", config_->y_res());
+  command.AddParameter("--keyboard=", config_->keyboard_socket_path());
+
+  AddTapFdParameter(&command, config_->wifi_tap_name());
+  AddTapFdParameter(&command, config_->mobile_tap_name());
+
+  // TODO remove this (use crosvm's seccomp files)
+  command.AddParameter("--disable-sandbox");
+
+  if (config_->vsock_guest_cid() >= 2) {
+    command.AddParameter("--cid=", config_->vsock_guest_cid());
+  }
+
+  auto kernel_log_connection =
+      ConnectToLogMonitor(config_->kernel_log_socket_name());
+  if (!kernel_log_connection->IsOpen()) {
+    LOG(WARNING) << "Unable to connect to log monitor: "
+                 << kernel_log_connection->StrError();
+  } else {
+    command.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdOut,
+                          kernel_log_connection);
+  }
+
+  auto dev_null = cvd::SharedFD::Open("/dev/null", O_RDONLY);
+  if (dev_null->IsOpen()) {
+    command.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdIn, dev_null);
+  } else {
+    LOG(ERROR) << "Unable to open /dev/null for stdin redirection";
+  }
+
+  // This needs to be the last parameter
+  command.AddParameter(config_->GetKernelImageToUse());
+
+  return command;
+}
+
+bool CrosvmManager::Stop() {
+  cvd::Command command(config_->crosvm_binary());
+  command.AddParameter("stop");
+  command.AddParameter(GetControlSocketPath(config_));
+
+  auto process = command.Start();
+
+  return process.Wait() == 0;
+}
+
+}  // namespace vm_manager
diff --git a/host/libs/vm_manager/libvirt_manager.h b/host/libs/vm_manager/crosvm_manager.h
similarity index 65%
rename from host/libs/vm_manager/libvirt_manager.h
rename to host/libs/vm_manager/crosvm_manager.h
index 6bb99d1..7574e51 100644
--- a/host/libs/vm_manager/libvirt_manager.h
+++ b/host/libs/vm_manager/crosvm_manager.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -15,25 +15,25 @@
  */
 #pragma once
 
-#include <string>
-
 #include "host/libs/vm_manager/vm_manager.h"
 
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/subprocess.h"
+
 namespace vm_manager {
 
-class LibvirtManager : public VmManager {
+// Starts a guest VM with crosvm. It requires the host package to support the
+// qemu-cli capability (for network only).
+class CrosvmManager : public VmManager {
  public:
   static const std::string name();
   static bool EnsureInstanceDirExists(const std::string& instance_dir);
 
-  LibvirtManager(const vsoc::CuttlefishConfig* config);
-  virtual ~LibvirtManager() = default;
+  CrosvmManager(const vsoc::CuttlefishConfig* config);
+  virtual ~CrosvmManager() = default;
 
-  bool Start() override;
+  cvd::Command StartCommand() override;
   bool Stop() override;
-
-  bool ValidateHostConfiguration(
-      std::vector<std::string>* config_commands) const override;
 };
 
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/libvirt_manager.cpp b/host/libs/vm_manager/libvirt_manager.cpp
deleted file mode 100644
index 24ff33b..0000000
--- a/host/libs/vm_manager/libvirt_manager.cpp
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * 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/libs/vm_manager/libvirt_manager.h"
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <cstdlib>
-#include <iomanip>
-#include <sstream>
-#include <string>
-
-#include <glog/logging.h>
-#include <libxml/tree.h>
-
-#include "common/libs/utils/files.h"
-#include "common/libs/utils/subprocess.h"
-#include "common/libs/utils/users.h"
-#include "host/libs/config/cuttlefish_config.h"
-
-// A lot of useful information about the document created here can be found on
-// these websites:
-// - https://libvirt.org/formatdomain.html
-// - https://wiki.libvirt.org/page/Virtio
-namespace vm_manager {
-
-namespace {
-// This trivial no-op helper function serves purpose of making libxml2 happy.
-// Apparently, *most* (not all!) string literals in libxml2 have to be of
-// unsigned char* (aka xmlChar*) type.
-inline const xmlChar* xc(const char* str) {
-  return reinterpret_cast<const xmlChar*>(str);
-}
-
-// Helper functions that allow us to combine any set of arguments to a single
-// string.
-// Example:
-//   concat("Answer", ' ', "is: ", 42);
-// will produce string "Answer is: 42"
-template <typename Arg>
-inline std::ostream& concat_helper(std::ostream& o, Arg a) {
-  o << a;
-  return o;
-}
-
-template <typename Arg, typename... Args>
-inline std::ostream& concat_helper(std::ostream& o, Arg a, Args... args) {
-  o << a;
-  concat_helper(o, args...);
-  return o;
-}
-
-template <typename... Args>
-inline std::string concat(Args... args) {
-  std::ostringstream str;
-  concat_helper(str, args...);
-  return str.str();
-}
-
-enum class DeviceSourceType {
-  kFile,
-  kUnixSocketClient,
-  kUnixSocketServer,
-};
-
-// Basic VM configuration.
-// This section configures name, basic resource allocation and response to
-// events.
-void ConfigureVM(xmlNode* root, const std::string& instance_name, int cpus,
-                 int mem_mb, const std::string& uuid) {
-  xmlNewChild(root, nullptr, xc("name"), xc(instance_name.c_str()));
-
-  // TODO(ender): should this all be 'restart'?
-  xmlNewChild(root, nullptr, xc("on_poweroff"), xc("destroy"));
-  xmlNewChild(root, nullptr, xc("on_reboot"), xc("restart"));
-  xmlNewChild(root, nullptr, xc("on_crash"), xc("restart"));
-  xmlNewChild(root, nullptr, xc("vcpu"), xc(concat(cpus).c_str()));
-  xmlNewChild(root, nullptr, xc("memory"), xc(concat(mem_mb << 10).c_str()));
-  if (uuid.size()) {
-    xmlNewChild(root, nullptr, xc("uuid"), xc(uuid.c_str()));
-  }
-}
-
-// Configure VM features.
-// This section takes care of the <features> section of the target XML file.
-void ConfigureVMFeatures(xmlNode* root,
-                         const std::initializer_list<std::string>& features) {
-  auto ch = xmlNewChild(root, nullptr, xc("features"), nullptr);
-  for (const auto& str : features) {
-    xmlNewChild(ch, nullptr, xc(str.c_str()), nullptr);
-  }
-}
-
-// Configure VM OS.
-// This section configures target os (<os>).
-void ConfigureOperatingSystem(xmlNode* root, const std::string& kernel,
-                              const std::string& initrd,
-                              const std::string& args, const std::string& dtb) {
-  auto os = xmlNewChild(root, nullptr, xc("os"), nullptr);
-
-  auto type = xmlNewChild(os, nullptr, xc("type"), xc("hvm"));
-  xmlNewProp(type, xc("arch"), xc("x86_64"));
-  xmlNewProp(type, xc("machine"), xc("pc"));
-
-  xmlNewChild(os, nullptr, xc("kernel"), xc(kernel.c_str()));
-  if (!initrd.empty()) {
-    xmlNewChild(os, nullptr, xc("initrd"), xc(initrd.c_str()));
-  }
-  xmlNewChild(os, nullptr, xc("cmdline"), xc(args.c_str()));
-  xmlNewChild(os, nullptr, xc("dtb"), xc(dtb.c_str()));
-}
-
-// Configure QEmu specific arguments.
-// This section adds the <qemu:commandline> node.
-xmlNodePtr ConfigureQEmuSpecificOptions(
-    xmlNode* root, std::initializer_list<std::string> qemu_args,
-    xmlNode* existing_options = nullptr) {
-  xmlNs* qemu_ns{xmlNewNs(
-      root, xc("http://libvirt.org/schemas/domain/qemu/1.0"), xc("qemu"))};
-
-  xmlNode* cmd;
-  if (existing_options) {
-    cmd = existing_options;
-  } else {
-    cmd = xmlNewChild(root, qemu_ns, xc("commandline"), nullptr);
-  }
-
-  for (const auto& str : qemu_args) {
-    auto arg = xmlNewChild(cmd, qemu_ns, xc("arg"), nullptr);
-    xmlNewProp(arg, xc("value"), xc(str.c_str()));
-  }
-  return cmd;
-}
-
-void ConfigureDeviceSource(xmlNode* device, DeviceSourceType type,
-                           const std::string& path) {
-  auto source = xmlNewChild(device, nullptr, xc("source"), nullptr);
-  xmlNewProp(source, xc("path"), xc(path.c_str()));
-
-  switch (type) {
-    case DeviceSourceType::kFile:
-      xmlNewProp(device, xc("type"), xc("file"));
-      break;
-
-    case DeviceSourceType::kUnixSocketClient:
-      xmlNewProp(device, xc("type"), xc("unix"));
-      xmlNewProp(source, xc("mode"), xc("connect"));
-      break;
-
-    case DeviceSourceType::kUnixSocketServer:
-      xmlNewProp(device, xc("type"), xc("unix"));
-      xmlNewProp(source, xc("mode"), xc("bind"));
-      break;
-  }
-}
-
-// Configure serial port.
-// This section adds <serial> elements to <device> node.
-void ConfigureSerialPort(xmlNode* devices, int port, DeviceSourceType type,
-                         const std::string& path) {
-  auto tty = xmlNewChild(devices, nullptr, xc("serial"), nullptr);
-  ConfigureDeviceSource(tty, type, path);
-
-  auto tgt = xmlNewChild(tty, nullptr, xc("target"), nullptr);
-  xmlNewProp(tgt, xc("port"), xc(concat(port).c_str()));
-}
-
-// Configure disk partition.
-// This section adds <disk> elements to <devices> node.
-void ConfigureDisk(xmlNode* devices, const std::string& name,
-                   const std::string& path) {
-  auto ch = xmlNewChild(devices, nullptr, xc("disk"), nullptr);
-  xmlNewProp(ch, xc("type"), xc("file"));
-
-  auto dr = xmlNewChild(ch, nullptr, xc("driver"), nullptr);
-  xmlNewProp(dr, xc("name"), xc("qemu"));
-  xmlNewProp(dr, xc("type"), xc("raw"));
-  xmlNewProp(dr, xc("io"), xc("threads"));
-
-  auto tg = xmlNewChild(ch, nullptr, xc("target"), nullptr);
-  xmlNewProp(tg, xc("dev"), xc(name.c_str()));
-  xmlNewProp(tg, xc("bus"), xc("virtio"));
-
-  auto sr = xmlNewChild(ch, nullptr, xc("source"), nullptr);
-  xmlNewProp(sr, xc("file"), xc(path.c_str()));
-}
-
-// Configure virtio channel.
-// This section adds <channel> elements to <devices> node.
-void ConfigureVirtioChannel(xmlNode* devices, int port, const std::string& name,
-                            DeviceSourceType type, const std::string& path) {
-  if (path.empty()) {
-    return;
-  }
-  auto vch = xmlNewChild(devices, nullptr, xc("channel"), nullptr);
-  ConfigureDeviceSource(vch, type, path);
-
-  auto tgt = xmlNewChild(vch, nullptr, xc("target"), nullptr);
-  xmlNewProp(tgt, xc("type"), xc("virtio"));
-  xmlNewProp(tgt, xc("name"), xc(name.c_str()));
-
-  auto adr = xmlNewChild(vch, nullptr, xc("address"), nullptr);
-  xmlNewProp(adr, xc("type"), xc("virtio-serial"));
-  xmlNewProp(adr, xc("controller"), xc("0"));
-  xmlNewProp(adr, xc("bus"), xc("0"));
-  xmlNewProp(adr, xc("port"), xc(concat(port).c_str()));
-}
-
-// Configure network interface.
-// This section adds <interface> elements to <devices> node.
-void ConfigureNIC(xmlNode* devices, const std::string& name,
-                  const std::string& bridge, int guest_id, int nic_id) {
-  auto nic = xmlNewChild(devices, nullptr, xc("interface"), nullptr);
-  xmlNewProp(nic, xc("type"), xc("bridge"));
-
-  auto brg = xmlNewChild(nic, nullptr, xc("source"), nullptr);
-  xmlNewProp(brg, xc("bridge"), xc(bridge.c_str()));
-
-  auto mac = xmlNewChild(nic, nullptr, xc("mac"), nullptr);
-  xmlNewProp(mac, xc("address"),
-             xc(concat("00:43:56:44:", std::setfill('0'), std::hex,
-                       std::setw(2), guest_id, ':', std::setw(2), nic_id)
-                    .c_str()));
-
-  auto mdl = xmlNewChild(nic, nullptr, xc("model"), nullptr);
-  xmlNewProp(mdl, xc("type"), xc("virtio"));
-
-  auto tgt = xmlNewChild(nic, nullptr, xc("target"), nullptr);
-  xmlNewProp(tgt, xc("dev"), xc(name.c_str()));
-}
-
-// Configure Harwdare Random Number Generator.
-// This section adds <rng> element to <devices> node.
-void ConfigureHWRNG(xmlNode* devices, const std::string& entsrc) {
-  auto rng = xmlNewChild(devices, nullptr, xc("rng"), nullptr);
-  xmlNewProp(rng, xc("model"), xc("virtio"));
-
-  auto rate = xmlNewChild(rng, nullptr, xc("rate"), nullptr);
-  xmlNewProp(rate, xc("period"), xc("2000"));
-  xmlNewProp(rate, xc("bytes"), xc("1024"));
-
-  auto bend = xmlNewChild(rng, nullptr, xc("backend"), xc(entsrc.c_str()));
-  xmlNewProp(bend, xc("model"), xc("random"));
-}
-
-static std::string GetLibvirtCommand(const vsoc::CuttlefishConfig* config) {
-  std::string cmd = "virsh";
-  if (!config->hypervisor_uri().empty()) {
-    cmd += " -c " + config->hypervisor_uri();
-  }
-  return cmd;
-}
-
-std::string BuildXmlConfig(const vsoc::CuttlefishConfig* config) {
-  std::string instance_name = config->instance_name();
-
-  std::unique_ptr<xmlDoc, void (*)(xmlDocPtr)> xml{xmlNewDoc(xc("1.0")),
-                                                   xmlFreeDoc};
-  auto root{xmlNewNode(nullptr, xc("domain"))};
-  xmlDocSetRootElement(xml.get(), root);
-  xmlNewProp(root, xc("type"), xc("kvm"));
-
-  ConfigureVM(root, instance_name, config->cpus(), config->memory_mb(),
-              config->uuid());
-  ConfigureVMFeatures(root, {"acpi", "apic", "hap"});
-  ConfigureOperatingSystem(root, config->kernel_image_path(),
-                           config->ramdisk_image_path(),
-                           config->kernel_cmdline_as_string(),
-                           config->dtb_path());
-  auto qemu_options = ConfigureQEmuSpecificOptions(
-      root, {"-chardev",
-             concat("socket,path=", config->ivshmem_qemu_socket_path(),
-                    ",id=ivsocket"),
-             "-device",
-             concat("ivshmem-doorbell,chardev=ivsocket,vectors=",
-                    config->ivshmem_vector_count()),
-             "-cpu", "host"});
-  if (config->gdb_flag().size()) {
-    ConfigureQEmuSpecificOptions(root, {"-gdb", config->gdb_flag().c_str(),
-                                        "-S"},
-                                 qemu_options);
-  }
-
-  if (config->disable_app_armor_security()) {
-    auto seclabel = xmlNewChild(root, nullptr, xc("seclabel"), nullptr);
-    xmlNewProp(seclabel, xc("type"), xc("none"));
-    xmlNewProp(seclabel, xc("model"), xc("apparmor"));
-  }
-  if (config->disable_dac_security()) {
-    auto seclabel = xmlNewChild(root, nullptr, xc("seclabel"), nullptr);
-    xmlNewProp(seclabel, xc("type"), xc("none"));
-    xmlNewProp(seclabel, xc("model"), xc("dac"));
-  }
-
-  auto devices = xmlNewChild(root, nullptr, xc("devices"), nullptr);
-
-  ConfigureSerialPort(devices, 0, DeviceSourceType::kUnixSocketClient,
-                      config->kernel_log_socket_name());
-  ConfigureSerialPort(devices, 1, DeviceSourceType::kUnixSocketServer,
-                      config->console_path());
-  ConfigureVirtioChannel(devices, 1, "cf-logcat", DeviceSourceType::kFile,
-                         config->logcat_path());
-  ConfigureVirtioChannel(devices, 2, "cf-gadget-usb-v1",
-                         DeviceSourceType::kUnixSocketClient,
-                         config->usb_v1_socket_name());
-
-  ConfigureDisk(devices, "vda", config->system_image_path());
-  ConfigureDisk(devices, "vdb", config->data_image_path());
-  ConfigureDisk(devices, "vdc", config->cache_image_path());
-  ConfigureDisk(devices, "vdd", config->vendor_image_path());
-
-  ConfigureNIC(devices, config->wifi_tap_name(), config->wifi_bridge_name(),
-	       vsoc::GetInstance(), 1);
-  ConfigureNIC(devices, config->mobile_tap_name(), config->mobile_bridge_name(),
-               vsoc::GetInstance(), 2);
-  ConfigureHWRNG(devices, config->entropy_source());
-
-  xmlChar* tgt;
-  int tgt_len;
-
-  xmlDocDumpFormatMemoryEnc(xml.get(), &tgt, &tgt_len, "utf-8", true);
-  std::string out((const char*)(tgt), tgt_len);
-  xmlFree(tgt);
-  return out;
-}
-}  // namespace
-
-const std::string LibvirtManager::name() { return "libvirt"; }
-
-LibvirtManager::LibvirtManager(const vsoc::CuttlefishConfig* config)
-  : VmManager(config) {}
-
-bool LibvirtManager::Start() {
-  std::string start_command = GetLibvirtCommand(config_);
-  start_command += " create /dev/fd/0";
-
-  std::string xml = BuildXmlConfig(config_);
-  if (config_->log_xml()) {
-    LOG(INFO) << "Using XML:\n" << xml;
-  }
-
-  FILE* launch = popen(start_command.c_str(), "w");
-  if (!launch) {
-    LOG(ERROR) << "Unable to execute " << start_command;
-    return false;
-  }
-  int rval = fputs(xml.c_str(), launch);
-  if (rval == EOF) {
-    LOG(ERROR) << "Launch command exited while accepting XML";
-    return false;
-  }
-  int exit_code = pclose(launch);
-  if (exit_code != 0) {
-    LOG(ERROR) << "Launch command exited with status " << exit_code;
-    return false;
-  }
-  return true;
-}
-
-bool LibvirtManager::Stop() {
-  auto stop_command = GetLibvirtCommand(config_);
-  stop_command += " destroy " + config_->instance_name();
-  return std::system(stop_command.c_str()) == 0;
-}
-
-bool LibvirtManager::EnsureInstanceDirExists(const std::string& instance_dir) {
-  if (!cvd::DirectoryExists(instance_dir)) {
-    LOG(INFO) << "Setting up " << instance_dir;
-    cvd::execute({"/usr/bin/sudo", "/bin/mkdir", "-m", "0775", instance_dir});
-
-    // When created with sudo the owner and group is root.
-    std::string user_group = getenv("USER");
-    user_group += ":libvirt-qemu";
-    cvd::execute({"/usr/bin/sudo", "/bin/chown", user_group, instance_dir});
-  }
-  return true;
-}
-
-bool LibvirtManager::ValidateHostConfiguration(
-    std::vector<std::string>* config_commands) const {
-  return VmManager::UserInGroup("libvirt", config_commands);
-}
-}  // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 8a46825..bb8e0af 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -51,39 +51,6 @@
   LOG(INFO) << key << "=" << value;
 }
 
-cvd::Subprocess BuildAndRunQemuCmd(const vsoc::CuttlefishConfig* config) {
-  // Set the config values in the environment
-  LogAndSetEnv("qemu_binary", config->qemu_binary());
-  LogAndSetEnv("instance_name", config->instance_name());
-  LogAndSetEnv("memory_mb", std::to_string(config->memory_mb()));
-  LogAndSetEnv("cpus", std::to_string(config->cpus()));
-  LogAndSetEnv("uuid", config->uuid());
-  LogAndSetEnv("monitor_path", GetMonitorPath(config));
-  LogAndSetEnv("kernel_image_path", config->kernel_image_path());
-  LogAndSetEnv("gdb_flag", config->gdb_flag());
-  LogAndSetEnv("ramdisk_image_path", config->ramdisk_image_path());
-  LogAndSetEnv("kernel_cmdline", config->kernel_cmdline_as_string());
-  LogAndSetEnv("dtb_path", config->dtb_path());
-  LogAndSetEnv("system_image_path", config->system_image_path());
-  LogAndSetEnv("data_image_path", config->data_image_path());
-  LogAndSetEnv("cache_image_path", config->cache_image_path());
-  LogAndSetEnv("vendor_image_path", config->vendor_image_path());
-  LogAndSetEnv("wifi_tap_name", config->wifi_tap_name());
-  LogAndSetEnv("mobile_tap_name", config->mobile_tap_name());
-  LogAndSetEnv("kernel_log_socket_name",
-                      config->kernel_log_socket_name());
-  LogAndSetEnv("console_path", config->console_path());
-  LogAndSetEnv("logcat_path", config->logcat_path());
-  LogAndSetEnv("ivshmem_qemu_socket_path",
-                      config->ivshmem_qemu_socket_path());
-  LogAndSetEnv("ivshmem_vector_count",
-                      std::to_string(config->ivshmem_vector_count()));
-  LogAndSetEnv("usb_v1_socket_name", config->usb_v1_socket_name());
-
-  cvd::Command qemu_cmd(vsoc::DefaultHostArtifactsPath("bin/cf_qemu.sh"));
-  return qemu_cmd.Start();
-}
-
 }  // namespace
 
 const std::string QemuManager::name() { return "qemu_cli"; }
@@ -91,51 +58,64 @@
 QemuManager::QemuManager(const vsoc::CuttlefishConfig* config)
   : VmManager(config) {}
 
-bool QemuManager::Start() {
-  if (monitor_conn_->IsOpen()) {
-    LOG(ERROR) << "Already started, should call Stop() first";
-    return false;
-  }
-  auto monitor_path = GetMonitorPath(config_);
-  auto monitor_sock = cvd::SharedFD::SocketLocalServer(
-      monitor_path.c_str(), false, SOCK_STREAM, 0666);
+cvd::Command QemuManager::StartCommand(){
+  // Set the config values in the environment
+  LogAndSetEnv("qemu_binary", config_->qemu_binary());
+  LogAndSetEnv("instance_name", config_->instance_name());
+  LogAndSetEnv("memory_mb", std::to_string(config_->memory_mb()));
+  LogAndSetEnv("cpus", std::to_string(config_->cpus()));
+  LogAndSetEnv("uuid", config_->uuid());
+  LogAndSetEnv("monitor_path", GetMonitorPath(config_));
+  LogAndSetEnv("kernel_image_path", config_->GetKernelImageToUse());
+  LogAndSetEnv("gdb_flag", config_->gdb_flag());
+  LogAndSetEnv("ramdisk_image_path", config_->ramdisk_image_path());
+  LogAndSetEnv("kernel_cmdline", config_->kernel_cmdline_as_string());
+  LogAndSetEnv("dtb_path", config_->dtb_path());
+  LogAndSetEnv("system_image_path", config_->system_image_path());
+  LogAndSetEnv("data_image_path", config_->data_image_path());
+  LogAndSetEnv("cache_image_path", config_->cache_image_path());
+  LogAndSetEnv("vendor_image_path", config_->vendor_image_path());
+  LogAndSetEnv("metadata_image_path", config_->metadata_image_path());
+  LogAndSetEnv("product_image_path", config_->product_image_path());
+  LogAndSetEnv("wifi_tap_name", config_->wifi_tap_name());
+  LogAndSetEnv("mobile_tap_name", config_->mobile_tap_name());
+  LogAndSetEnv("kernel_log_socket_name",
+                      config_->kernel_log_socket_name());
+  LogAndSetEnv("console_path", config_->console_path());
+  LogAndSetEnv("logcat_path", config_->logcat_path());
+  LogAndSetEnv("ivshmem_qemu_socket_path",
+                      config_->ivshmem_qemu_socket_path());
+  LogAndSetEnv("ivshmem_vector_count",
+                      std::to_string(config_->ivshmem_vector_count()));
+  LogAndSetEnv("usb_v1_socket_name", config_->usb_v1_socket_name());
+  LogAndSetEnv("vsock_guest_cid", std::to_string(config_->vsock_guest_cid()));
+  LogAndSetEnv("logcat_mode", config_->logcat_mode());
 
-  BuildAndRunQemuCmd(config_);
-
-  cvd::SharedFDSet fdset;
-  fdset.Set(monitor_sock);
-  struct timeval timeout {5, 0}; // Wait at most 5 seconds for qemu to connect
-  int select_result = cvd::Select(&fdset, 0, 0, &timeout);
-  if (select_result < 0) {
-    LOG(ERROR) << "Error when calling seletct: " << strerror(errno);
-    return false;
-  }
-  if (select_result == 0) {
-    LOG(ERROR) << "Timed out waiting for qemu to connect to monitor";
-    return false;
-  }
-  monitor_conn_ = cvd::SharedFD::Accept(*monitor_sock);
-  monitor_conn_->Fcntl(F_SETFD, FD_CLOEXEC);
-  return monitor_conn_->IsOpen();
+  cvd::Command qemu_cmd(vsoc::DefaultHostArtifactsPath("bin/cf_qemu.sh"));
+  return qemu_cmd;
 }
 bool QemuManager::Stop() {
-  if (!monitor_conn_->IsOpen()) {
+  auto monitor_path = GetMonitorPath(config_);
+  auto monitor_sock = cvd::SharedFD::SocketLocalClient(
+      monitor_path.c_str(), false, SOCK_STREAM);
+
+  if (!monitor_sock->IsOpen()) {
     LOG(ERROR) << "The connection to qemu is closed, is it still running?";
     return false;
   }
   char msg[] = "{\"execute\":\"qmp_capabilities\"}{\"execute\":\"quit\"}";
   ssize_t len = sizeof(msg) - 1;
   while (len > 0) {
-    int tmp = monitor_conn_->Write(msg, len);
+    int tmp = monitor_sock->Write(msg, len);
     if (tmp < 0) {
-      LOG(ERROR) << "Error writing to socket: " << monitor_conn_->StrError();
+      LOG(ERROR) << "Error writing to socket: " << monitor_sock->StrError();
       return false;
     }
     len -= tmp;
   }
   // Log the reply
   char buff[1000];
-  while ((len = monitor_conn_->Read(buff, sizeof(buff) - 1)) > 0) {
+  while ((len = monitor_sock->Read(buff, sizeof(buff) - 1)) > 0) {
     buff[len] = '\0';
     LOG(INFO) << "From qemu monitor: " << buff;
   }
@@ -143,24 +123,4 @@
   return true;
 }
 
-bool QemuManager::EnsureInstanceDirExists(const std::string& instance_dir) {
-  if (!cvd::DirectoryExists(instance_dir.c_str())) {
-    LOG(INFO) << "Setting up " << instance_dir;
-    if (mkdir(instance_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
-      LOG(ERROR) << "Unable to create " << instance_dir << ". Error: " << errno;
-      return false;
-    }
-  }
-  return true;
-
-}
-
-bool QemuManager::ValidateHostConfiguration(
-    std::vector<std::string>* config_commands) const {
-  // the check for cvdnetwork needs to happen even if the user is not in kvm, so
-  // we cant just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
-  auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
-  auto in_cvdnetwork = VmManager::UserInGroup("cvdnetwork", config_commands);
-  return in_kvm && in_cvdnetwork;
-}
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.h b/host/libs/vm_manager/qemu_manager.h
index 6ae1926..a0c6540 100644
--- a/host/libs/vm_manager/qemu_manager.h
+++ b/host/libs/vm_manager/qemu_manager.h
@@ -26,18 +26,12 @@
 class QemuManager : public VmManager {
  public:
   static const std::string name();
-  static bool EnsureInstanceDirExists(const std::string& instance_dir);
 
   QemuManager(const vsoc::CuttlefishConfig* config);
   virtual ~QemuManager() = default;
 
-  bool Start() override;
+  cvd::Command StartCommand() override;
   bool Stop() override;
-
-  bool ValidateHostConfiguration(
-      std::vector<std::string>* config_commands) const override;
- private:
-  cvd::SharedFD monitor_conn_;
 };
 
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.cpp b/host/libs/vm_manager/vm_manager.cpp
index b452bf9..5433d6d 100644
--- a/host/libs/vm_manager/vm_manager.cpp
+++ b/host/libs/vm_manager/vm_manager.cpp
@@ -22,8 +22,8 @@
 
 #include "common/libs/utils/users.h"
 #include "host/libs/config/cuttlefish_config.h"
-#include "host/libs/vm_manager/libvirt_manager.h"
 #include "host/libs/vm_manager/qemu_manager.h"
+#include "host/libs/vm_manager/crosvm_manager.h"
 
 namespace vm_manager {
 
@@ -40,22 +40,17 @@
 
 std::map<std::string, VmManager::VmManagerHelper>
     VmManager::vm_manager_helpers_ = {
-        {LibvirtManager::name(),
-         {[](const vsoc::CuttlefishConfig* config) {
-            return GetManagerSingleton<LibvirtManager>(config);
-          },
-          []() { return true; },
-          [](const std::string& dir_path) {
-            return LibvirtManager::EnsureInstanceDirExists(dir_path);
-          }}},
         {QemuManager::name(),
          {[](const vsoc::CuttlefishConfig* config) {
             return GetManagerSingleton<QemuManager>(config);
           },
-          []() { return vsoc::HostSupportsQemuCli(); },
-          [](const std::string& dir_path) {
-            return QemuManager::EnsureInstanceDirExists(dir_path);
-          }}}};
+          []() { return vsoc::HostSupportsQemuCli(); }}},
+        {CrosvmManager::name(),
+         {[](const vsoc::CuttlefishConfig* config) {
+            return GetManagerSingleton<CrosvmManager>(config);
+          },
+        // Same as Qemu for the time being
+          []() { return vsoc::HostSupportsQemuCli(); }}}};
 
 VmManager* VmManager::Get(const std::string& vm_manager_name,
                           const vsoc::CuttlefishConfig* config) {
@@ -77,7 +72,7 @@
 
 std::vector<std::string> VmManager::GetValidNames() {
   std::vector<std::string> ret = {};
-  for (auto key_val: vm_manager_helpers_) {
+  for (const auto& key_val: vm_manager_helpers_) {
     ret.push_back(key_val.first);
   }
   return ret;
@@ -94,9 +89,12 @@
   return true;
 }
 
-bool VmManager::EnsureInstanceDirExists(const std::string& vm_manager_name,
-                                        const std::string& instance_dir_path) {
-  return vm_manager_helpers_[vm_manager_name].instance_dir_creator(
-      instance_dir_path);
+bool VmManager::ValidateHostConfiguration(
+    std::vector<std::string>* config_commands) const {
+  // the check for cvdnetwork needs to happen even if the user is not in kvm, so
+  // we cant just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
+  auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
+  auto in_cvdnetwork = VmManager::UserInGroup("cvdnetwork", config_commands);
+  return in_kvm && in_cvdnetwork;
 }
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
index 98dd1d3..84bf77c 100644
--- a/host/libs/vm_manager/vm_manager.h
+++ b/host/libs/vm_manager/vm_manager.h
@@ -20,6 +20,7 @@
 #include <utility>
 #include <vector>
 
+#include <common/libs/utils/subprocess.h>
 #include <host/libs/config/cuttlefish_config.h>
 
 namespace vm_manager {
@@ -36,16 +37,14 @@
   static bool IsValidName(const std::string& name);
   static bool IsVmManagerSupported(const std::string& name);
   static std::vector<std::string> GetValidNames();
-  static bool EnsureInstanceDirExists(const std::string& vm_manager_name,
-                                      const std::string& instance_dir_path);
 
   virtual ~VmManager() = default;
 
-  virtual bool Start() = 0;
+  virtual cvd::Command StartCommand() = 0;
   virtual bool Stop() = 0;
 
   virtual bool ValidateHostConfiguration(
-      std::vector<std::string>* config_commands) const = 0;
+      std::vector<std::string>* config_commands) const;
 
  protected:
   static bool UserInGroup(const std::string& group,
@@ -59,8 +58,6 @@
     std::function<VmManager*(const vsoc::CuttlefishConfig*)> builder;
     // Whether the host packages support this vm manager
     std::function<bool()> support_checker;
-    // Creates the instance directory if it doesn't exist
-    std::function<bool(const std::string&)> instance_dir_creator;
   };
   // Asociates a vm manager helper to every valid vm manager name
   static std::map<std::string, VmManagerHelper> vm_manager_helpers_;
diff --git a/tools/build-qemu-packages.sh b/tools/build-qemu-packages.sh
deleted file mode 100755
index 7d30541..0000000
--- a/tools/build-qemu-packages.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-VIRGL_PACKAGE=libvirglrenderer-dev
-
-sudo apt update
-sudo apt upgrade -y
-sudo apt install -y debhelper ubuntu-dev-tools equivs
-dpkg -l "${VIRGL_PACKAGE}" || (
-  # Try from the distribution
-  sudo apt install -y "${VIRGL_PACKAGE}"
-  dpkg -l "${VIRGL_PACKAGE}" || (
-    echo That failed. Building from source
-    curl -L -O http://http.debian.net/debian/pool/main/v/virglrenderer/virglrenderer_0.6.0-2.dsc
-    curl -L -O http://http.debian.net/debian/pool/main/v/virglrenderer/virglrenderer_0.6.0.orig.tar.bz2
-    curl -L -O http://http.debian.net/debian/pool/main/v/virglrenderer/virglrenderer_0.6.0-2.debian.tar.xz
-    sudo mk-build-deps -i virglrenderer_0.6.0-2.dsc -t "apt-get -y"
-    rm -f virglrenderer-build-deps_0.6.0-2_all.deb
-    dpkg-source -x virglrenderer_0.6.0-2.dsc
-    pushd virglrenderer*0
-    dpkg-buildpackage -uc -us
-    popd
-    sudo apt install -y ./*.deb
-  )
-)
-curl -L -O http://http.debian.net/debian/pool/main/q/qemu/qemu_2.12+dfsg.orig.tar.xz
-git clone https://salsa.debian.org/qemu-team/qemu.git
-pushd qemu
-debian/rules debian/control
-chmod +w debian/control
-popd
-sudo mk-build-deps -i qemu/debian/control  -t "apt-get -y"
-rm -f ./qemu-build-deps_2.12+dfsg-3_amd64.deb
-pushd qemu
-dpkg-buildpackage -uc -us --jobs=auto
-popd
diff --git a/tools/create_base_image_gce.sh b/tools/create_base_image_gce.sh
index 2438dab..237760e 100755
--- a/tools/create_base_image_gce.sh
+++ b/tools/create_base_image_gce.sh
@@ -33,22 +33,6 @@
 # Now gather all of the *.deb files to copy them into the image
 debs=(*.deb)
 
-# See if we are using a local build of qemu.
-if [[ -x build-qemu-packages.sh ]]; then
-  ./build-qemu-packages.sh
-  # We want only a subset of the debs that will be generated
-  for i in *.deb; do
-    case "$i" in
-      qemu-kvm_*) debs+=("$i") ;;
-      qemu-system-common_*) debs+=("$i") ;;
-      qemu-system-x86_*) debs+=("$i") ;;
-      qemu-utils_*) debs+=("$i") ;;
-      qemu-system-data_*) debs+=("$i") ;;
-      libvirglrenderer0*) debs+=("$i") ;;
-    esac
-  done                         
-fi
-
 tmp_debs=()
 for i in "${debs[@]}"; do
   tmp_debs+=(/tmp/"$(basename "$i")")
diff --git a/tools/create_base_image_hostlib.sh b/tools/create_base_image_hostlib.sh
index 26bf4f5..d8e583c 100755
--- a/tools/create_base_image_hostlib.sh
+++ b/tools/create_base_image_hostlib.sh
@@ -5,13 +5,21 @@
 # INTERNAL_extra_source may be set to a directory containing the source for
 # extra package to build.
 
+# INTERNAL_IP can be set to --internal-ip run on a GCE instance
+# The instance will need --scope compute-rw
+
 source "${ANDROID_BUILD_TOP}/external/shflags/src/shflags"
 
 DEFINE_string build_instance \
   "${USER}-build" "Instance name to create for the build" "i"
+DEFINE_string build_project "$(gcloud config get-value project)" \
+  "Project to use for scratch"
+DEFINE_string build_zone "$(gcloud config get-value compute/zone)" \
+  "Zone to use for scratch resources"
 DEFINE_string dest_image "vsoc-host-scratch-${USER}" "Image to create" "o"
 DEFINE_string dest_family "" "Image family to add the image to" "f"
-DEFINE_string dest_project "" "Project to use for the new image" "p"
+DEFINE_string dest_project "$(gcloud config get-value project)" \
+  "Project to use for the new image" "p"
 DEFINE_string launch_instance "" \
   "Name of the instance to launch with the new image" "l"
 DEFINE_string source_image_family debian-9 "Image familty to use as the base" \
@@ -26,11 +34,14 @@
 DEFINE_string variant master \
   "Variant to build: generally master or stable"
 
+
+SSH_FLAGS=(${INTERNAL_IP})
+
 wait_for_instance() {
   alive=""
   while [[ -z "${alive}" ]]; do
     sleep 5
-    alive="$(gcloud compute ssh "$@" -- uptime || true)"
+    alive="$(gcloud compute ssh "${SSH_FLAGS[@]}" "$@" -- uptime || true)"
   done
 }
 
@@ -50,11 +61,7 @@
 main() {
   set -o errexit
   set -x
-  if [[ -n "${FLAGS_dest_project}" ]]; then
-    dest_project_flag=("--project=${FLAGS_dest_project}")
-  else
-    dest_project_flag=()
-  fi
+  PZ=(--project=${FLAGS_build_project} --zone=${FLAGS_build_zone})
   if [[ -n "${FLAGS_dest_family}" ]]; then
     dest_family_flag=("--family=${FLAGS_dest_family}")
   else
@@ -69,9 +76,6 @@
     "${ANDROID_BUILD_TOP}/device/google/cuttlefish_common/tools/create_base_image_gce.sh"
     ${scratch_dir}/*
   )
-  if [[ "${FLAGS_variant}" == master ]]; then
-    source_files+=("${ANDROID_BUILD_TOP}/device/google/cuttlefish_common/tools/build-qemu-packages.sh")
-  fi
   if [[ -n "${INTERNAL_extra_source}" ]]; then
     source_files+=("${INTERNAL_extra_source}"/*)
   fi
@@ -81,50 +85,64 @@
     delete_instances+=("${FLAGS_launch_instance}")
   fi
   gcloud compute instances delete -q \
-    "${dest_project_flag[@]}" "${delete_instances[@]}" || \
+    "${PZ[@]}" "${delete_instances[@]}" || \
       echo Not running
   gcloud compute disks delete -q \
-    "${dest_project_flag[@]}" "${FLAGS_dest_image}" || echo No scratch disk
+    "${PZ[@]}" "${FLAGS_dest_image}" || echo No scratch disk
   gcloud compute images delete -q \
-    "${dest_project_flag[@]}" "${FLAGS_dest_image}" || echo Not respinning
+    --project="${FLAGS_build_project}" "${FLAGS_dest_image}" || echo Not respinning
   gcloud compute disks create \
-    "${dest_project_flag[@]}" \
+    "${PZ[@]}" \
     --image-family="${FLAGS_source_image_family}" \
     --image-project="${FLAGS_source_image_project}" \
     "${FLAGS_dest_image}"
   gcloud compute instances create \
-    "${dest_project_flag[@]}" \
+    "${PZ[@]}" \
     --machine-type=n1-standard-16 \
     --image-family="${FLAGS_source_image_family}" \
     --image-project="${FLAGS_source_image_project}" \
     --boot-disk-size=200GiB \
     "${FLAGS_build_instance}"
-  wait_for_instance "${dest_project_flag[@]}" "${FLAGS_build_instance}"
+  wait_for_instance "${PZ[@]}" "${FLAGS_build_instance}"
   # Ubuntu tends to mount the wrong disk as root, so help it by waiting until
   # it has booted before giving it access to the clean image disk
   gcloud compute instances attach-disk \
-      "${dest_project_flag[@]}" \
+      "${PZ[@]}" \
       "${FLAGS_build_instance}" --disk="${FLAGS_dest_image}"
-  gcloud compute scp "${dest_project_flag[@]}" \
+  # beta for the --internal-ip flag that may be passed via SSH_FLAGS
+  gcloud beta compute scp "${SSH_FLAGS[@]}" "${PZ[@]}" \
     "${source_files[@]}" \
     "${FLAGS_build_instance}:"
-  gcloud compute ssh \
-    "${dest_project_flag[@]}" "${FLAGS_build_instance}" -- \
+  gcloud compute ssh "${SSH_FLAGS[@]}" \
+    "${PZ[@]}" "${FLAGS_build_instance}" -- \
     ./create_base_image_gce.sh
   gcloud compute instances delete -q \
-    "${dest_project_flag[@]}" "${FLAGS_build_instance}"
-  gcloud compute images create "${dest_project_flag[@]}" \
+    "${PZ[@]}" "${FLAGS_build_instance}"
+  gcloud compute images create \
+    --project="${FLAGS_build_project}" \
     --source-disk="${FLAGS_dest_image}" \
+    --source-disk-zone="${FLAGS_build_zone}" \
     --licenses=https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx \
     "${dest_family_flag[@]}" \
     "${FLAGS_dest_image}"
-  gcloud compute disks delete -q "${dest_project_flag[@]}" \
+  gcloud compute disks delete -q "${PZ[@]}" \
     "${FLAGS_dest_image}"
   if [[ -n "${FLAGS_launch_instance}" ]]; then
-    gcloud compute instances create "${dest_project_flag[@]}" \
+    gcloud compute instances create "${PZ[@]}" \
+      --image-project="${FLAGS_build_project}" \
       --image="${FLAGS_dest_image}" \
       --machine-type=n1-standard-4 \
       --scopes storage-ro \
       "${FLAGS_launch_instance}"
   fi
+  cat <<EOF
+    echo Test and if this looks good, consider releasing it via:
+
+    gcloud compute images create \
+      --project="${FLAGS_dest_project}" \
+      --source-image="${FLAGS_dest_image}" \
+      --source-image-project="${FLAGS_build_project}" \
+      "${dest_family_flag[@]}" \
+      "${FLAGS_dest_image}"
+EOF
 }
diff --git a/tools/play_audio/.gitignore b/tools/play_audio/.gitignore
new file mode 100644
index 0000000..a6795ba
--- /dev/null
+++ b/tools/play_audio/.gitignore
@@ -0,0 +1,3 @@
+*.o
+play_audio
+opuscpp
diff --git a/tools/play_audio/Makefile b/tools/play_audio/Makefile
new file mode 100644
index 0000000..30730ef
--- /dev/null
+++ b/tools/play_audio/Makefile
@@ -0,0 +1,12 @@
+CXXFLAGS := -Wextra -Wall -pedantic-errors -std=c++17
+LDLIBS := -lopus -lgflags -lglog -lSDL2
+LINK.o := $(LINK.cc)
+
+PROG := play_audio
+OBJS := $(PROG).o client_socket.o sdl_wrapper.o opuscpp/opus_wrapper.o
+
+$(PROG): $(OBJS)
+
+clean:
+	rm -f $(OBJS) $(PROG)
+
diff --git a/tools/play_audio/README.md b/tools/play_audio/README.md
new file mode 100644
index 0000000..28c227f
--- /dev/null
+++ b/tools/play_audio/README.md
@@ -0,0 +1,30 @@
+# Play Audio
+
+Audio receiver for a cuttlefish instance. A binary to play audio from a remote
+Android virtual device.
+
+## Install Dependencies
+
+```
+git clone https://github.com/google/opuscpp
+sudo apt install libsdl2-2.0-0 libsdl2-dev libopus-dev libopus0 libgflags-dev libgoogle-glog-dev
+```
+
+## Build
+
+```
+make
+```
+
+## Run
+Use ssh port forwarding to forward `7444` from a running instance. Then run the
+`play_audio` binary.
+
+```
+./play_audio
+```
+
+If you are running multiple virtual devices on a host, you must pass the
+`device_num` flag to specify the device corresponding to the `vsoc-##` username,
+and you will have to add `1 - device_num` to the port that you forward (vsoc-02
+= port 7445).
diff --git a/tools/play_audio/client_socket.cpp b/tools/play_audio/client_socket.cpp
new file mode 100644
index 0000000..97c6b13
--- /dev/null
+++ b/tools/play_audio/client_socket.cpp
@@ -0,0 +1,113 @@
+/*
+ *
+ * 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 "client_socket.h"
+
+#include "glog/logging.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cstdint>
+#include <cstring>
+#include <vector>
+
+namespace {
+std::uint16_t HostOrderUInt16(const void* src) {
+  std::uint16_t result{};
+  std::memcpy(&result, src, sizeof result);
+  return htons(result);
+}
+
+std::uint32_t HostOrderUInt32(const void* src) {
+  std::uint32_t result{};
+  std::memcpy(&result, src, sizeof result);
+  return htonl(result);
+}
+}  // namespace
+
+using cfp::ClientSocket;
+
+ClientSocket::ClientSocket(std::uint16_t port)
+    : socket_fd_{socket(AF_INET, SOCK_STREAM, 0)} {
+  sockaddr_in server_addr{};
+
+  if (socket_fd_ < 0) {
+    LOG(ERROR) << "couldn't create socket\n";
+    return;
+  }
+  server_addr.sin_family = AF_INET;
+  server_addr.sin_port = htons(port);
+
+  if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) {
+    LOG(ERROR) << "couldn't convert localhost address\n";
+    close();
+    return;
+  }
+
+  if (connect(socket_fd_, reinterpret_cast<sockaddr*>(&server_addr),
+              sizeof server_addr) < 0) {
+    LOG(ERROR) << "connection failed\n";
+    close();
+    return;
+  }
+}
+
+ClientSocket::~ClientSocket() { close(); }
+
+ClientSocket::ClientSocket(ClientSocket&& other)
+    : socket_fd_{other.socket_fd_} {
+  other.socket_fd_ = -1;
+}
+
+ClientSocket& ClientSocket::operator=(ClientSocket&& other) {
+  close();
+  socket_fd_ = other.socket_fd_;
+  other.socket_fd_ = -1;
+  return *this;
+}
+
+std::vector<unsigned char> ClientSocket::RecvAll(ssize_t count) {
+  std::vector<unsigned char> buf(count);
+  size_t total_read = 0;
+  while (total_read < buf.size()) {
+    auto just_read =
+        read(socket_fd_, buf.data() + total_read, buf.size() - total_read);
+    if (just_read <= 0) {
+      LOG(ERROR) << "read failed";
+      return {};
+    }
+    total_read += static_cast<size_t>(just_read);
+  }
+  return buf;
+}
+
+std::uint16_t ClientSocket::RecvUInt16() {
+  return HostOrderUInt16(RecvAll(sizeof(std::uint16_t)).data());
+}
+
+std::uint32_t ClientSocket::RecvUInt32() {
+  return HostOrderUInt32(RecvAll(sizeof(std::uint32_t)).data());
+}
+
+void ClientSocket::close() {
+  if (socket_fd_ >= 0) {
+    ::close(socket_fd_);
+    socket_fd_ = -1;
+  }
+}
diff --git a/tools/play_audio/client_socket.h b/tools/play_audio/client_socket.h
new file mode 100644
index 0000000..f51cb6a
--- /dev/null
+++ b/tools/play_audio/client_socket.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * 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.
+ */
+#ifndef CFP_CLIENT_SOCKET_
+#define CFP_CLIENT_SOCKET_
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <vector>
+
+namespace cfp {
+
+class ClientSocket {
+ public:
+  ClientSocket(std::uint16_t port);
+  ~ClientSocket();
+
+  ClientSocket(ClientSocket&& other);
+  ClientSocket& operator=(ClientSocket&& other);
+
+  ClientSocket(const ClientSocket&) = delete;
+  ClientSocket& operator=(const ClientSocket&) = delete;
+
+  bool valid() const { return socket_fd_ >= 0; }
+
+  std::vector<unsigned char> RecvAll(ssize_t count);
+
+  std::uint16_t RecvUInt16();
+  std::uint32_t RecvUInt32();
+
+ private:
+  void close();
+  int socket_fd_ = -1;
+};
+
+}  // namespace cfp
+
+#endif
diff --git a/tools/play_audio/play_audio.cpp b/tools/play_audio/play_audio.cpp
new file mode 100644
index 0000000..2594396
--- /dev/null
+++ b/tools/play_audio/play_audio.cpp
@@ -0,0 +1,109 @@
+/*
+ *
+ * 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 "client_socket.h"
+#include "sdl_wrapper.h"
+
+#include "glog/logging.h"
+#include "gflags/gflags.h"
+#include "opuscpp/opus_wrapper.h"
+#include <SDL2/SDL.h>
+
+#include <cstdint>
+#include <tuple>
+#include <vector>
+
+DEFINE_int32(
+    device_num, 1,
+    "Cuttlefish device number, corresponding to username vsoc-## number");
+
+namespace {
+std::uint16_t AudioPort() {
+  constexpr std::uint16_t kAudioStreamBasePort = 7444;
+  std::uint16_t audio_port = kAudioStreamBasePort + (FLAGS_device_num - 1);
+  return audio_port;
+}
+
+cfp::ClientSocket Connect() {
+  const auto port = AudioPort();
+  auto conn = cfp::ClientSocket{port};
+  if (!conn.valid()) {
+    LOG(FATAL) << "couldn't connect on port " << port;
+  }
+  return conn;
+}
+
+std::tuple<std::uint16_t, std::uint16_t> RecvHeader(cfp::ClientSocket* conn) {
+  // creating variables because these must be received in order
+  auto num_channels = conn->RecvUInt16();
+  auto frame_rate = conn->RecvUInt16();
+  LOG(INFO) << "\nnum_channels: " << num_channels
+            << "\nframe_rate: " << frame_rate << '\n';
+  return {num_channels, frame_rate};
+}
+
+// Returns frame_size and encoded audio
+std::tuple<std::uint32_t, std::vector<unsigned char>> RecvEncodedAudio(
+    cfp::ClientSocket* conn) {
+  auto length = conn->RecvUInt32();
+  auto frame_size = conn->RecvUInt32();
+  auto encoded = conn->RecvAll(length);
+
+  if (encoded.size() < length) {
+    encoded.clear();
+  }
+  return {frame_size, std::move(encoded)};
+}
+
+void PlayDecodedAudio(cfp::SDLAudioDevice* audio_device,
+                      const std::vector<opus_int16>& audio) {
+  auto sz = audio.size() * sizeof audio[0];
+  auto ret = audio_device->QueueAudio(audio.data(), sz);
+  if (ret < 0) {
+    LOG(ERROR) << "failed to queue audio: " << SDL_GetError() << '\n';
+  }
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  ::google::InitGoogleLogging(argv[0]);
+  ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+  cfp::SDLLib sdl{};
+
+  auto conn = Connect();
+  const auto& [num_channels, frame_rate] = RecvHeader(&conn);
+
+  auto audio_device = sdl.OpenAudioDevice(frame_rate, num_channels);
+  auto dec =
+      opus::Decoder{static_cast<std::uint32_t>(frame_rate), num_channels};
+  CHECK(dec.valid()) << "Could not construct Decoder. Maybe bad frame_rate ("
+                     << frame_rate <<") or num_channels (" << num_channels
+                     << ")?";
+
+  while (true) {
+    CHECK(dec.valid()) << "decoder in invalid state";
+    const auto& [frame_size, encoded] = RecvEncodedAudio(&conn);
+    if (encoded.empty()) {
+      break;
+    }
+    auto decoded = dec.Decode(encoded, frame_size, false);
+    if (decoded.empty()) {
+      break;
+    }
+    PlayDecodedAudio(&audio_device, decoded);
+  }
+}
diff --git a/tools/play_audio/sdl_wrapper.cpp b/tools/play_audio/sdl_wrapper.cpp
new file mode 100644
index 0000000..11f76df
--- /dev/null
+++ b/tools/play_audio/sdl_wrapper.cpp
@@ -0,0 +1,75 @@
+/*
+ *
+ * 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 "sdl_wrapper.h"
+
+#include "glog/logging.h"
+#include <SDL2/SDL.h>
+
+#include <cstdint>
+
+using cfp::SDLAudioDevice;
+using cfp::SDLLib;
+
+SDLAudioDevice::SDLAudioDevice(SDLAudioDevice&& other)
+    : device_id_{other.device_id_} {
+  other.device_id_ = 0;
+}
+SDLAudioDevice& SDLAudioDevice::operator=(SDLAudioDevice&& other) {
+  close();
+  device_id_ = other.device_id_;
+  other.device_id_ = 0;
+  return *this;
+}
+
+SDLAudioDevice::~SDLAudioDevice() { close(); }
+
+int SDLAudioDevice::QueueAudio(const void* data, std::uint32_t len) {
+  return SDL_QueueAudio(device_id_, data, len);
+}
+
+SDLAudioDevice::SDLAudioDevice(SDL_AudioDeviceID device_id)
+    : device_id_{device_id} {}
+
+void SDLAudioDevice::close() {
+  if (device_id_ != 0) {
+    SDL_CloseAudioDevice(device_id_);
+  }
+}
+
+SDLLib::SDLLib() { SDL_Init(SDL_INIT_AUDIO); }
+SDLLib::~SDLLib() { SDL_Quit(); }
+
+SDLAudioDevice SDLLib::OpenAudioDevice(int freq, std::uint8_t num_channels) {
+  SDL_AudioSpec wav_spec{};
+  wav_spec.freq = freq;
+  wav_spec.format = AUDIO_S16LSB;
+  wav_spec.channels = num_channels;
+  wav_spec.silence = 0;
+  // .samples seems to work as low as 256,
+  // docs say this is 4096 when used with SDL_LoadWAV so I'm sticking with
+  // that
+  wav_spec.samples = 4096;
+  wav_spec.size = 0;
+
+  auto audio_device_id = SDL_OpenAudioDevice(nullptr, 0, &wav_spec, nullptr, 0);
+  if (audio_device_id == 0) {
+    LOG(FATAL) << "failed to open audio device: " << SDL_GetError() << '\n';
+  }
+  SDL_PauseAudioDevice(audio_device_id, false);
+  return SDLAudioDevice{audio_device_id};
+}
diff --git a/tools/play_audio/sdl_wrapper.h b/tools/play_audio/sdl_wrapper.h
new file mode 100644
index 0000000..9be1df5
--- /dev/null
+++ b/tools/play_audio/sdl_wrapper.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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 <SDL2/SDL.h>
+#include <cstdint>
+
+namespace cfp {
+
+class SDLLib;
+
+class SDLAudioDevice {
+ public:
+  SDLAudioDevice(SDLAudioDevice&& other);
+  SDLAudioDevice& operator=(SDLAudioDevice&& other);
+
+  SDLAudioDevice(const SDLAudioDevice&) = delete;
+  SDLAudioDevice& operator=(const SDLAudioDevice&) = delete;
+
+  ~SDLAudioDevice();
+
+  int QueueAudio(const void* data, std::uint32_t len);
+
+ private:
+  friend SDLLib;
+  explicit SDLAudioDevice(SDL_AudioDeviceID device_id);
+  void close();
+
+  SDL_AudioDeviceID device_id_{};
+};
+
+class SDLLib {
+ public:
+  SDLLib();
+  ~SDLLib();
+
+  SDLLib(const SDLLib&) = delete;
+  SDLLib& operator=(const SDLLib&) = delete;
+
+  SDLAudioDevice OpenAudioDevice(int freq, std::uint8_t num_channels);
+};
+
+}  // namespace cfp
diff --git a/tools/tombstone_to_line.py b/tools/tombstone_to_line.py
new file mode 100755
index 0000000..7320bd5
--- /dev/null
+++ b/tools/tombstone_to_line.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+#
+# 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.
+#
+"""Use addr2line to interpret tombstone contents
+
+The defaults should work if this is run after running lunch.
+"""
+
+from __future__ import print_function
+import argparse
+import collections
+import functools
+import multiprocessing
+import os
+import re
+import subprocess
+import sys
+
+
+# Patterns of things that we might want to match.
+
+patterns = [
+    (re.compile('(.* pc )([0-9a-f]+) +([^ ]+) .*'), 1, 3, 2),
+    (re.compile('(.*)#[0-9]+ 0x[0-9a-f]+ +\((.*)\+0x([0-9a-f]+)\)'), 1, 2, 3)]
+
+
+LookupInfo = collections.namedtuple('LookupInfo',
+                                    ['line_number', 'details', 'file_name'])
+
+
+def lookup_addr(args, object_path, address):
+  try:
+    if object_path[0] == os.path.sep:
+      object_path = object_path[1:]
+    parms = [args.addr2line, '-e',
+             os.path.join(args.symbols, object_path), address]
+    details = subprocess.check_output(parms).strip().split(':')
+    return LookupInfo(
+        line_number=details[-1],
+        details=details,
+        file_name=':'.join(details[:-1]))
+  except subprocess.CalledProcessError:
+    return None
+
+
+def simple_match(line, info, indent, out_file):
+  print('{} // From {}:{}'.format(
+      line, info.file_name, info.line_number), file=out_file)
+
+
+def source_match(line, info, indent, out_file):
+  source = ''
+  try:
+    with open(info.file_name, 'r') as f:
+      for i in range(int(info.line_number.split(' ')[0])):
+        source = f.readline()
+  # Fall back to the simple formatter on any error
+  except Exception:
+    simple_match(line, info, indent, out_file)
+    return
+  print(line, file=out_file)
+  print('{}// From {}:{}'.format(
+      ' ' * indent, info.file_name, info.line_number), file=out_file)
+  print('{}  {}'.format(' ' * indent, ' '.join(source.strip().split())),
+        file=out_file)
+
+
+def process(in_file, out_file, args):
+  for line in in_file:
+    line = line.rstrip()
+    groups = None
+    for p in patterns:
+      groups = p[0].match(line)
+      if groups:
+        break
+    info = None
+    if groups is not None:
+      info = lookup_addr(args, groups.group(p[2]), groups.group(p[3]))
+    if info is None:
+      print(line, file=out_file)
+      continue
+    if args.source:
+      source_match(line, info, len(groups.group(p[1])), out_file)
+    else:
+      simple_match(line, info, len(groups.group(p[1])), out_file)
+
+
+def process_file(path, args):
+    with open(path + args.suffix, 'w') as out_file:
+      with open(path, 'r') as in_file:
+        process(in_file, out_file, args)
+
+
+def common_arg_parser():
+  parser = argparse.ArgumentParser(description=
+                                   'Add line information to a tombstone')
+  parser.add_argument('--addr2line', type=str,
+                      help='Path to addr2line',
+                      default=os.path.join(
+                          os.environ.get('ANDROID_TOOLCHAIN', ''),
+                          'x86_64-linux-android-addr2line'))
+  parser.add_argument('files', metavar='FILE', type=str, nargs='+',
+                      help='a list of files to process')
+  parser.add_argument('--jobs', type=int, default=32,
+                      help='Number of parallel jobs to run')
+  parser.add_argument('--source', default=False, action='store_true',
+                      help='Attempt to print the source')
+  parser.add_argument('--suffix', type=str, default='.txt',
+                      help='Suffix to add to the processed file')
+  return parser
+
+
+
+def process_all(args):
+  multiprocessing.Pool(32).map(functools.partial(process_file, args=args),
+                               args.files)
+
+
+if __name__ == '__main__':
+  parser = common_arg_parser()
+  parser.add_argument('--symbols', type=str,
+                      help='Path to the symbols',
+                      default=os.path.join(
+                          os.environ.get('ANDROID_PRODUCT_OUT', ''), 'symbols'))
+  process_all(parser.parse_args())
diff --git a/tools/upload_to_gce_and_run.py b/tools/upload_to_gce_and_run.py
index 9d04b73..9ceee1f 100755
--- a/tools/upload_to_gce_and_run.py
+++ b/tools/upload_to_gce_and_run.py
@@ -9,21 +9,22 @@
 
 
 def upload_artifacts(args):
-  image_pat = os.path.join(args.dist_dir, '%s-img-*.zip' % args.product)
-  images = glob.glob(image_pat)
-  if len(images) == 0:
-    raise OSError('File not found: %s' + image_pat)
-  if len(images) > 1:
-    raise OSError('%s matches multiple images: %s' % (
-        image_pat, images))
-  subprocess.check_call(
-      'gcloud compute ssh %s@%s -- /usr/bin/install_zip.sh . < %s' % (
+  dir = os.getcwd()
+  try:
+    os.chdir(args.image_dir)
+    images = glob.glob('*.img')
+    if len(images) == 0:
+      raise OSError('File not found: %s' + image_pat)
+    subprocess.check_call(
+      'tar -c -f - --lzop -S ' + ' '.join(images) +
+        ' | gcloud compute ssh %s@%s -- tar -x -f - --lzop -S' % (
           args.user,
-          args.instance,
-          images[0]),
+          args.instance),
       shell=True)
+  finally:
+    os.chdir(dir)
 
-  host_package = os.path.join(args.dist_dir, 'cvd-host_package.tar.gz')
+  host_package = os.path.join(args.host_dir, 'cvd-host_package.tar.gz')
   # host_package
   subprocess.check_call(
       'gcloud compute ssh %s@%s -- tar -x -z -f - < %s' % (
@@ -63,20 +64,19 @@
   parser = argparse.ArgumentParser(
       description='Upload a local build to Google Compute Engine and run it')
   parser.add_argument(
-      '-dist_dir',
+      '-host_dir',
       type=str,
-      default=os.path.join(os.environ.get('ANDROID_BUILD_TOP', '.'),
-                           'out', 'dist'),
+      default=os.environ.get('ANDROID_HOST_OUT', '.'),
       help='path to the dist directory')
   parser.add_argument(
+      '-image_dir',
+      type=str,
+      default=os.environ.get('ANDROID_PRODUCT_OUT', '.'),
+      help='path to the img files')
+  parser.add_argument(
       '-instance', type=str, required=True,
       help='instance to update')
   parser.add_argument(
-      '-product',
-      type=str,
-      default=os.environ.get('TARGET_PRODUCT', 'cf_x86_phone'),
-      help='product to upload')
-  parser.add_argument(
       '-user', type=str, default='vsoc-01',
       help='user to update on the instance')
   parser.add_argument(
@@ -85,10 +85,15 @@
   parser.add_argument(
       '-blank-data-image-mb', type=int, default=4098,
       help='custom userdata image size in megabytes')
+  parser.add_argument(
+      '-launch', default=False,
+      action='store_true',
+      help='launch the device')
   args = parser.parse_args()
   stop_cvd(args)
   upload_artifacts(args)
-  launch_cvd(args)
+  if args.launch:
+    launch_cvd(args)
 
 
 if __name__ == '__main__':