Merge changes for aosp release into master

Test: Local build
Change-Id: Ie043d33328c860b952d608b1e2ced705e2500f88
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..5ad19cf
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,203 @@
+//
+// 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.
+
+subdirs = [
+    "common",
+    "guest",
+    "host",
+]
+
+cc_library_headers {
+    name: "cuttlefish_common_headers",
+    export_include_dirs: ["."],
+    host_supported: true,
+}
+
+// TODO(b/67435044) Update the include paths and remove this
+cc_library_headers {
+    name: "cuttlefish_glog",
+    export_include_dirs: ["common/libs"],
+    host_supported: true,
+}
+
+cc_defaults {
+    name: "cuttlefish_base",
+    gnu_extensions: false,
+    header_libs: [
+        "cuttlefish_common_headers",
+        "cuttlefish_kernel_headers",
+    ],
+    target: {
+        linux: {
+            host_ldlibs: ["-lrt"],
+            cflags: ["-DCUTTLEFISH_HOST"],
+        },
+        // We don't need 32 bit host-side builds
+        // TODO(ghartman): linux_glibc on master
+        linux_x86: {
+            enabled: false,
+        },
+        // We don't need Darwin host-side builds
+        darwin: {
+            enabled: false,
+        },
+    },
+    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,
+        },
+    },
+}
+
+cc_defaults {
+    name: "cuttlefish_host_only",
+    device_supported: false,
+    host_supported: true,
+    defaults: ["cuttlefish_base"],
+}
+
+cc_defaults {
+    name: "cuttlefish_host_and_guest",
+    host_supported: true,
+    defaults: ["cuttlefish_base"],
+}
+
+cc_library_shared {
+    name: "vsoc_lib",
+    srcs: [
+        "common/vsoc/lib/compat.cpp",
+        "common/vsoc/lib/e2e_test_region_layout.cpp",
+        "common/vsoc/lib/fb_bcast_layout.cpp",
+        "common/vsoc/lib/fb_bcast_region_view.cpp",
+        "common/vsoc/lib/gralloc_layout.cpp",
+        "common/vsoc/lib/lock_common.cpp",
+        "common/vsoc/lib/region_view.cpp",
+        "common/vsoc/lib/wifi_exchange_layout.cpp",
+        "common/vsoc/lib/wifi_exchange_view.cpp",
+    ],
+    header_libs: ["cuttlefish_glog"],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+        "liblog",
+    ],
+    target: {
+        //TODO(ghartman): linux_glibc on master
+        linux: {
+            srcs: [
+                "host/vsoc/lib/gralloc_buffer_region_view.cpp",
+                "host/vsoc/lib/host_lock.cpp",
+                "host/vsoc/lib/region_control.cpp",
+                "host/vsoc/lib/region_view.cpp",
+            ],
+        },
+        android: {
+            srcs: [
+                "guest/vsoc/lib/gralloc_region_view.cpp",
+                "guest/vsoc/lib/guest_lock.cpp",
+                "guest/vsoc/lib/region_control.cpp",
+                "guest/vsoc/lib/region_view.cpp",
+            ],
+        },
+    },
+    defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"],
+}
+
+cc_test_host {
+    name: "circqueue_test",
+    srcs: [
+        "common/vsoc/lib/circqueue_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libbase",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
+
+cc_test {
+    name: "vsoc_fb_bcast_region_view_test",
+    srcs: [
+        "common/vsoc/lib/fb_bcast_region_view_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libbase",
+    ],
+    target : {
+        linux_x86_64: {
+            static_libs: [
+                "libcuttlefish_host_config",
+                "libgflags",
+            ],
+        },
+    },
+    defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"],
+}
+
+cc_test_host {
+    name: "lock_test",
+    srcs: [
+        "common/vsoc/lib/lock_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libgtest_host",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
+
+cc_test_host {
+    name: "vsoc_graphics_test",
+    srcs: [
+        "common/vsoc/lib/graphics_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libbase",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
+
+cc_binary_host {
+    name: "host_region_e2e_test",
+    srcs: [
+        "host/vsoc/lib/host_region_e2e_test.cpp",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libgtest_host",
+        "libgflags",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..c38d99b
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,17 @@
+# 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.
+ifneq ($(filter vsoc_x86 vsoc_x86_64, $(TARGET_DEVICE)),)
+LOCAL_PATH:= $(call my-dir)
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/WORKSPACE b/WORKSPACE
deleted file mode 100644
index d192a98..0000000
--- a/WORKSPACE
+++ /dev/null
@@ -1 +0,0 @@
-workspace(name = "cuttlefish")
diff --git a/common/Android.bp b/common/Android.bp
new file mode 100644
index 0000000..646ec16
--- /dev/null
+++ b/common/Android.bp
@@ -0,0 +1,19 @@
+//
+// 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.
+
+subdirs = [
+    "commands",
+    "libs",
+]
diff --git a/common/commands/Android.bp b/common/commands/Android.bp
new file mode 100644
index 0000000..70311b6
--- /dev/null
+++ b/common/commands/Android.bp
@@ -0,0 +1,18 @@
+//
+// 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.
+
+subdirs = [
+    "wificlient",
+]
diff --git a/common/commands/wificlient/Android.bp b/common/commands/wificlient/Android.bp
new file mode 100644
index 0000000..067d085
--- /dev/null
+++ b/common/commands/wificlient/Android.bp
@@ -0,0 +1,19 @@
+cc_binary {
+    name: "wificlient",
+    srcs: [
+        "main.cc",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcuttlefish_wifi",
+        "liblog",
+        "libnl",
+    ],
+    static_libs: [
+        "libgflags",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"],
+}
diff --git a/common/commands/wificlient/main.cc b/common/commands/wificlient/main.cc
new file mode 100644
index 0000000..e060408
--- /dev/null
+++ b/common/commands/wificlient/main.cc
@@ -0,0 +1,46 @@
+/*
+ * 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 <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/wifi/netlink.h"
+#include "common/libs/wifi/virtual_wifi.h"
+
+DEFINE_string(router, "cvd-wifirouter", "Path to WIFI Router Unix socket.");
+DEFINE_string(iface, "cf-wlan0", "Name of the WLAN interface to create.");
+DEFINE_string(macaddr, "00:43:56:44:80:01", "MAC address for new interface");
+DEFINE_string(wifirouter_socket, "cvd-wifirouter",
+              "Name of the wifirouter unix domain socket.");
+
+int main(int argc, char* argv[]) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  std::unique_ptr<cvd::Netlink> nl(new cvd::Netlink(FLAGS_wifirouter_socket));
+  if (!nl->Init()) {
+    LOG(ERROR) << "Could not initialize netlink.";
+    exit(1);
+  }
+
+  std::unique_ptr<cvd::VirtualWIFI> radio(
+      new cvd::VirtualWIFI(nl.get(), FLAGS_iface, FLAGS_macaddr));
+  if (!radio->Init()) {
+    LOG(ERROR) << "Could not create radio.";
+    exit(1);
+  }
+
+  pause();
+}
diff --git a/common/libs/Android.bp b/common/libs/Android.bp
new file mode 100644
index 0000000..572e0fa
--- /dev/null
+++ b/common/libs/Android.bp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+subdirs = [
+    "auto_resources",
+    "fs",
+    "net",
+    "threads",
+    "time",
+    "wifi",
+]
diff --git a/common/libs/auto_resources/Android.bp b/common/libs/auto_resources/Android.bp
new file mode 100644
index 0000000..2c575ce
--- /dev/null
+++ b/common/libs/auto_resources/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "cuttlefish_auto_resources",
+    srcs: [
+        "auto_resources.cpp",
+    ],
+    defaults: ["cuttlefish_host_and_guest"],
+}
+
+cc_test_host {
+    name: "auto_free_buffer_test",
+    srcs: [
+        "auto_free_buffer_test.cpp",
+    ],
+    shared_libs: [
+        "cuttlefish_auto_resources",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/common/libs/auto_resources/BUILD b/common/libs/auto_resources/BUILD
deleted file mode 100644
index 3641e10..0000000
--- a/common/libs/auto_resources/BUILD
+++ /dev/null
@@ -1,23 +0,0 @@
-cc_library(
-    name = "auto_resources",
-    srcs = [
-        "auto_resources.cpp",
-        "auto_resources.h",
-    ],
-    hdrs = [
-        "auto_resources.h",
-    ],
-    visibility = [ "//visibility:public" ],
-)
-
-cc_test(
-    name = "auto_free_buffer_test",
-    srcs = [
-        "auto_free_buffer_test.cpp",
-    ],
-    deps = [
-        ":auto_resources",
-        "@gtest_repo//:gtest_main",
-    ],
-)
-
diff --git a/common/libs/fs/Android.bp b/common/libs/fs/Android.bp
new file mode 100644
index 0000000..1522c50
--- /dev/null
+++ b/common/libs/fs/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libcuttlefish_fs",
+    srcs: [
+        "gce_fs.cpp",
+        "shared_fd.cpp",
+    ],
+    shared_libs: [
+        "cuttlefish_auto_resources",
+        "libbase",
+        "liblog",
+    ],
+    defaults: ["cuttlefish_host_and_guest"],
+}
diff --git a/common/libs/fs/BUILD b/common/libs/fs/BUILD
deleted file mode 100644
index 2e25c4d..0000000
--- a/common/libs/fs/BUILD
+++ /dev/null
@@ -1,20 +0,0 @@
-cc_library(
-    name = "fs",
-    visibility = ["//visibility:public"],
-    srcs = [
-        "gce_fs.cpp",
-        "gce_fs.h",
-        "shared_fd.cpp",
-        "shared_fd.h",
-        "shared_select.h",
-    ],
-    hdrs = [
-        "gce_fs.h",
-        "shared_fd.h",
-        "shared_select.h",
-    ],
-    deps = [
-        "//common/libs/auto_resources",
-    ],
-)
-
diff --git a/common/libs/fs/gce_fs.cpp b/common/libs/fs/gce_fs.cpp
index b2afff5..db8ce33 100644
--- a/common/libs/fs/gce_fs.cpp
+++ b/common/libs/fs/gce_fs.cpp
@@ -32,7 +32,7 @@
 #include <stdlib.h>
 #include <dirent.h>
 
-#include <glog/logging.h>
+#include "common/libs/glog/logging.h"
 
 #define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
 #define BUF_SIZE 64
diff --git a/common/libs/fs/shared_fd.cpp b/common/libs/fs/shared_fd.cpp
index 8028bd0..6546d0a 100644
--- a/common/libs/fs/shared_fd.cpp
+++ b/common/libs/fs/shared_fd.cpp
@@ -23,17 +23,14 @@
 #include <netinet/in.h>
 #include <unistd.h>
 
-#define LOG_TAG "SharedFD"
-
-#include <glog/logging.h>
-
 #include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/glog/logging.h"
 #include "common/libs/fs/shared_select.h"
 
 // #define ENABLE_GCE_SHARED_FD_LOGGING 1
 
 namespace {
-using avd::SharedFDSet;
+using cvd::SharedFDSet;
 
 void MarkAll(const SharedFDSet& input, fd_set* dest, int* max_index) {
   for (SharedFDSet::const_iterator it = input.begin(); it != input.end();
@@ -42,17 +39,13 @@
   }
 }
 
-void CheckMarked(fd_set* in_out_mask, fd_set* error_mask,
-    SharedFDSet* in_out_set, SharedFDSet* error_set) {
+void CheckMarked(fd_set* in_out_mask, SharedFDSet* in_out_set) {
   if (!in_out_set) {
     return;
   }
   SharedFDSet save;
   save.swap(in_out_set);
   for (SharedFDSet::iterator it = save.begin(); it != save.end(); ++it) {
-    if (error_set && (*it)->IsSet(error_mask)) {
-      error_set->Set(*it);
-    }
     if ((*it)->IsSet(in_out_mask)) {
       in_out_set->Set(*it);
     }
@@ -60,7 +53,7 @@
 }
 }  // namespace
 
-namespace avd {
+namespace cvd {
 
 bool FileInstance::CopyFrom(FileInstance& in) {
   AutoFreeBuffer buffer;
@@ -119,11 +112,10 @@
 
 #if ENABLE_GCE_SHARED_FD_LOGGING
 void FileInstance::Log(const char* message) {
-  static int log_fd = open("/dev/null", O_WRONLY|O_APPEND|O_CREAT, 0666);
-  write(log_fd, message, strlen(message));
+  LOG(INFO) << message;
 }
 #else
-void FileInstance::Log(const char*) { }
+void FileInstance::Log(const char*) {}
 #endif
 
 void FileInstance::Set(fd_set* dest, int* max_index) const {
@@ -136,7 +128,6 @@
   FD_SET(fd_, dest);
 }
 
-
 int Select(SharedFDSet* read_set, SharedFDSet* write_set,
            SharedFDSet* error_set, struct timeval* timeout) {
   int max_index = 0;
@@ -152,20 +143,21 @@
   }
   fd_set errorfds;
   FD_ZERO(&errorfds);
-  int rval = TEMP_FAILURE_RETRY(select(
-      max_index, &readfds, &writefds, &errorfds, timeout));
-  FileInstance::Log("select\n");
   if (error_set) {
-    error_set->Zero();
+    MarkAll(*error_set, &errorfds, &max_index);
   }
-  CheckMarked(&readfds, &errorfds, read_set, error_set);
-  CheckMarked(&writefds, &errorfds, write_set, error_set);
+
+  int rval = TEMP_FAILURE_RETRY(
+      select(max_index, &readfds, &writefds, &errorfds, timeout));
+  FileInstance::Log("select\n");
+  CheckMarked(&readfds, read_set);
+  CheckMarked(&writefds, write_set);
+  CheckMarked(&errorfds, error_set);
   return rval;
 }
 
-static void MakeAddress(
-    const char* name, bool abstract, struct sockaddr_un* dest,
-    socklen_t* len) {
+static void MakeAddress(const char* name, bool abstract,
+                        struct sockaddr_un* dest, socklen_t* len) {
   memset(dest, 0, sizeof(*dest));
   dest->sun_family = AF_UNIX;
   // sun_path is NOT expected to be nul-terminated.
@@ -198,15 +190,30 @@
   return SocketLocalClient(name, false, SOCK_SEQPACKET);
 }
 
-SharedFD SharedFD::Accept(const FileInstance& listener,
-                struct sockaddr* addr, socklen_t *addrlen) {
-  return SharedFD(std::shared_ptr<FileInstance>(listener.Accept(addr, addrlen)));
+SharedFD SharedFD::TimerFD(int clock, int flags) {
+  int fd = timerfd_create(clock, flags);
+  if (fd == -1) {
+    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, errno)));
+  } else {
+    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, 0)));
+  }
+}
+
+SharedFD SharedFD::Accept(const FileInstance& listener, struct sockaddr* addr,
+                          socklen_t* addrlen) {
+  return SharedFD(
+      std::shared_ptr<FileInstance>(listener.Accept(addr, addrlen)));
 }
 
 SharedFD SharedFD::Accept(const FileInstance& listener) {
   return SharedFD::Accept(listener, NULL, NULL);
 }
 
+SharedFD SharedFD::Dup(int unmanaged_fd) {
+  int fd = dup(unmanaged_fd);
+  return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, errno)));
+}
+
 bool SharedFD::Pipe(SharedFD* fd0, SharedFD* fd1) {
   int fds[2];
   int rval = pipe(fds);
@@ -218,14 +225,21 @@
   return false;
 }
 
-SharedFD SharedFD::Event() {
-  return std::shared_ptr<FileInstance>(new FileInstance(eventfd(0, 0), errno));
+SharedFD SharedFD::Event(int initval, int flags) {
+  return std::shared_ptr<FileInstance>(
+      new FileInstance(eventfd(initval, flags), errno));
 }
 
-inline bool SharedFD::SocketPair(int domain, int type, int protocol, SharedFD* fd0, SharedFD* fd1){
+SharedFD SharedFD::Epoll(int flags) {
+  return std::shared_ptr<FileInstance>(
+      new FileInstance(epoll_create1(flags), errno));
+}
+
+inline 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) {
+  if (rval != -1) {
     (*fd0) = std::shared_ptr<FileInstance>(new FileInstance(fds[0], errno));
     (*fd1) = std::shared_ptr<FileInstance>(new FileInstance(fds[1], errno));
     return true;
@@ -251,49 +265,8 @@
   }
 }
 
-SharedFD SharedFD::SocketInAddrAnyServer(int in_port, int in_type) {
-  errno = 0;
-
-  // TODO(ender): this code is very similar to SocketSeqPacketServer
-  struct sockaddr_in6 addr = {
-    AF_INET6,  // sin6_family
-    htons(in_port),  // sin6_port
-    0,  // sin6_flowinfo
-    in6addr_any,  // sin6_addr
-    0,  // sin6_scope_id
-  };
-
-  SharedFD rval = SharedFD::Socket(PF_INET6, in_type, 0);
-  if (!rval->IsOpen()) {
-    return rval;
-  }
-
-  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())));
-  }
-  if (rval->Bind((struct sockaddr *) &addr, sizeof(addr)) == -1) {
-    LOG(ERROR) << "Bind failed; port=" << in_port << ": " << rval->StrError();
-    return SharedFD(std::shared_ptr<FileInstance>(
-        new FileInstance(-1, rval->GetErrno())));
-  }
-
-  if (in_type == SOCK_STREAM) {
-    // 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 rval;
-}
-
-SharedFD SharedFD::SocketLocalClient(
-    const char* name, bool abstract, int in_type) {
+SharedFD SharedFD::SocketLocalClient(const char* name, bool abstract,
+                                     int in_type) {
   struct sockaddr_un addr;
   socklen_t addrlen;
   MakeAddress(name, abstract, &addr, &addrlen);
@@ -301,16 +274,47 @@
   if (!rval->IsOpen()) {
     return rval;
   }
-  if (rval->Connect((struct sockaddr *) &addr, addrlen) == -1) {
+  if (rval->Connect((struct sockaddr*)&addr, addrlen) == -1) {
     LOG(ERROR) << "Connect failed; name=" << name << ": " << rval->StrError();
-    return SharedFD(std::shared_ptr<FileInstance>(
-        new FileInstance(-1, rval->GetErrno())));
+    return SharedFD(
+        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
   }
   return rval;
 }
 
-SharedFD SharedFD::SocketLocalServer(
-    const char* name, bool abstract, int in_type, mode_t mode) {
+SharedFD SharedFD::SocketLocalServer(int port, int type) {
+  struct sockaddr_in addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(port);
+  addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  SharedFD rval = SharedFD::Socket(AF_INET, type, 0);
+  if(!rval->IsOpen()) {
+    return rval;
+  }
+  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())));
+  }
+  if(rval->Bind((struct sockaddr *) &addr, sizeof(addr)) < 0) {
+    LOG(ERROR) << "Bind failed " << rval->StrError();
+    return SharedFD(
+        std::shared_ptr<FileInstance>(new FileInstance(-1, 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 rval;
+}
+
+SharedFD SharedFD::SocketLocalServer(const char* name, bool abstract,
+                                     int in_type, mode_t mode) {
   // DO NOT UNLINK addr.sun_path. It does NOT have to be null-terminated.
   // See man 7 unix for more details.
   if (!abstract) (void)unlink(name);
@@ -326,13 +330,13 @@
   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(
+        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
   }
-  if (rval->Bind((struct sockaddr *) &addr, addrlen) == -1) {
+  if (rval->Bind((struct 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(
+        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
   }
 
   /* Only the bottom bits are really the socket type; there are flags too. */
@@ -357,4 +361,4 @@
   return rval;
 }
 
-}  // namespace avd
+}  // namespace cvd
diff --git a/common/libs/fs/shared_fd.h b/common/libs/fs/shared_fd.h
index 1d8a956..79f5405 100644
--- a/common/libs/fs/shared_fd.h
+++ b/common/libs/fs/shared_fd.h
@@ -13,16 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+// TODO: We can't use std::shared_ptr on the older guests due to HALs.
+
 #ifndef CUTTLEFISH_COMMON_COMMON_LIBS_FS_SHARED_FD_H_
 #define CUTTLEFISH_COMMON_COMMON_LIBS_FS_SHARED_FD_H_
 
+#include <sys/epoll.h>
 #include <sys/eventfd.h>
 #include <sys/ioctl.h>
-#include <sys/socket.h>
+#include <sys/mman.h>
 #include <sys/select.h>
-#include <sys/time.h>
+#include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/timerfd.h>
 #include <sys/uio.h>
 #include <sys/un.h>
 
@@ -54,11 +60,31 @@
  * it makes it easier to convert existing code to SharedFDs and avoids the
  * possibility that new POSIX functionality will lead to large refactorings.
  */
-namespace avd {
+namespace cvd {
 
 class FileInstance;
 
 /**
+ * Describes the fields in msghdr that are honored by the *MsgAndFDs
+ * calls.
+ */
+struct InbandMessageHeader {
+  void* msg_name;
+  socklen_t msg_namelen;
+  struct iovec* msg_iov;
+  size_t msg_iovlen;
+  int msg_flags;
+
+  void Convert(struct msghdr* dest) const {
+    dest->msg_name = msg_name;
+    dest->msg_namelen = msg_namelen;
+    dest->msg_iov = msg_iov;
+    dest->msg_iovlen = msg_iovlen;
+    dest->msg_flags = msg_flags;
+  }
+};
+
+/**
  * Counted reference to a FileInstance.
  *
  * This is also the place where most new FileInstances are created. The creation
@@ -104,61 +130,47 @@
 class SharedFD {
  public:
   inline SharedFD();
-  SharedFD(const std::shared_ptr<FileInstance>& in) : value_(in) { }
+  SharedFD(const std::shared_ptr<FileInstance>& in) : value_(in) {}
   // Reference the listener as a FileInstance to make this FD type agnostic.
-  static SharedFD Accept(const FileInstance& listener,
-                                struct sockaddr* addr, socklen_t* addrlen);
+  static SharedFD Accept(const FileInstance& listener, struct sockaddr* addr,
+                         socklen_t* addrlen);
   static SharedFD Accept(const FileInstance& listener);
+  static SharedFD Dup(int unmanaged_fd);
   static SharedFD GetControlSocket(const char* name);
   // Returns false on failure, true on success.
   static SharedFD Open(const char* pathname, int flags, mode_t mode = 0);
   static bool Pipe(SharedFD* fd0, SharedFD* fd1);
-  static SharedFD Event();
-  static bool SocketPair(int domain, int type, int protocol, SharedFD* fd0, SharedFD* fd1);
+  static SharedFD Event(int initval = 0, int flags = 0);
+  static SharedFD Epoll(int flags = 0);
+  static bool SocketPair(int domain, int type, int protocol, SharedFD* fd0,
+                         SharedFD* fd1);
   static SharedFD Socket(int domain, int socket_type, int protocol);
-  static SharedFD SocketInAddrAnyServer(int in_port, int in_type);
-  static SharedFD SocketLocalClient(
-      const char* name, bool is_abstract, int in_type);
-  static SharedFD SocketLocalServer(
-      const char* name, bool is_abstract, int in_type, mode_t mode);
+  static SharedFD SocketLocalClient(const char* name, bool is_abstract,
+                                    int in_type);
+  static SharedFD SocketLocalServer(const char* name, bool is_abstract,
+                                    int in_type, mode_t mode);
+  static SharedFD SocketLocalServer(int port, int type);
   static SharedFD SocketSeqPacketServer(const char* name, mode_t mode);
   static SharedFD SocketSeqPacketClient(const char* name);
+  static SharedFD TimerFD(int clock, int flags);
 
-  bool operator==(const SharedFD& rhs) const {
-    return value_ == rhs.value_;
-  }
+  bool operator==(const SharedFD& rhs) const { return value_ == rhs.value_; }
 
-  bool operator!=(const SharedFD& rhs) const {
-    return value_ != rhs.value_;
-  }
+  bool operator!=(const SharedFD& rhs) const { return value_ != rhs.value_; }
 
-  bool operator<(const SharedFD& rhs) const {
-    return value_ < rhs.value_;
-  }
+  bool operator<(const SharedFD& rhs) const { return value_ < rhs.value_; }
 
-  bool operator<=(const SharedFD& rhs) const {
-    return value_ <= rhs.value_;
-  }
+  bool operator<=(const SharedFD& rhs) const { return value_ <= rhs.value_; }
 
-  bool operator>(const SharedFD& rhs) const {
-    return value_ > rhs.value_;
-  }
+  bool operator>(const SharedFD& rhs) const { return value_ > rhs.value_; }
 
-  bool operator>=(const SharedFD& rhs) const {
-    return value_ >= rhs.value_;
-  }
+  bool operator>=(const SharedFD& rhs) const { return value_ >= rhs.value_; }
 
-  std::shared_ptr<FileInstance> operator->() const {
-    return value_;
-  }
+  std::shared_ptr<FileInstance> operator->() const { return value_; }
 
-  const avd::FileInstance& operator*() const {
-    return *value_;
-  }
+  const cvd::FileInstance& operator*() const { return *value_; }
 
-  avd::FileInstance& operator*() {
-    return *value_;
-  }
+  cvd::FileInstance& operator*() { return *value_; }
 
  private:
   std::shared_ptr<FileInstance> value_;
@@ -180,24 +192,23 @@
 class FileInstance {
   // Give SharedFD access to the aliasing constructor.
   friend class SharedFD;
+
  public:
-  virtual ~FileInstance() {
-    Close();
-  }
+  virtual ~FileInstance() { Close(); }
 
   // This can't be a singleton because our shared_ptr's aren't thread safe.
   static std::shared_ptr<FileInstance> ClosedInstance() {
     return std::shared_ptr<FileInstance>(new FileInstance(-1, EBADF));
   }
 
-  int Bind(const struct sockaddr *addr, socklen_t addrlen) {
+  int Bind(const struct sockaddr* addr, socklen_t addrlen) {
     errno = 0;
     int rval = bind(fd_, addr, addrlen);
     errno_ = errno;
     return rval;
   }
 
-  int Connect(const struct sockaddr *addr, socklen_t addrlen) {
+  int Connect(const struct sockaddr* addr, socklen_t addrlen) {
     errno = 0;
     int rval = connect(fd_, addr, addrlen);
     errno_ = errno;
@@ -219,6 +230,22 @@
     return rval;
   }
 
+  int EpollCtl(int op, cvd::SharedFD new_fd, struct epoll_event* event) {
+    errno = 0;
+    int rval = TEMP_FAILURE_RETRY(
+        epoll_ctl(fd_, op, new_fd->fd_, event));
+    errno_ = errno;
+    return rval;
+  }
+
+  int EpollWait(struct epoll_event* events, int maxevents, int timeout) {
+    errno = 0;
+    int rval = TEMP_FAILURE_RETRY(
+        epoll_wait(fd_, events, maxevents, timeout));
+    errno_ = errno;
+    return rval;
+  }
+
   int Fchown(uid_t owner, gid_t group) {
     errno = 0;
     int rval = TEMP_FAILURE_RETRY(fchown(fd_, owner, group));
@@ -240,9 +267,7 @@
     return rval;
   }
 
-  int GetErrno() const {
-    return errno_;
-  }
+  int GetErrno() const { return errno_; }
 
   int GetSockOpt(int level, int optname, void* optval, socklen_t* optlen) {
     errno = 0;
@@ -255,16 +280,14 @@
 
   void Identify(const char* identity);
 
-  int Ioctl(int request) {
+  int Ioctl(int request, void* val = nullptr) {
     errno = 0;
-    int rval = TEMP_FAILURE_RETRY(ioctl(fd_, request));
+    int rval = TEMP_FAILURE_RETRY(ioctl(fd_, request, val));
     errno_ = errno;
     return rval;
   }
 
-  bool IsOpen() const {
-    return fd_ != -1;
-  }
+  bool IsOpen() const { return fd_ != -1; }
 
   // in probably isn't modified, but the API spec doesn't have const.
   bool IsSet(fd_set* in) const;
@@ -285,6 +308,20 @@
     return rval;
   }
 
+  void* Mmap(void* addr, size_t length, int prot, int flags, off_t offset) {
+    errno = 0;
+    void* rval = mmap(addr, length, prot, flags, fd_, offset);
+    errno_ = errno;
+    return rval;
+  }
+
+  ssize_t Pread(void* buf, size_t count, off_t offset) {
+    errno = 0;
+    ssize_t rval = TEMP_FAILURE_RETRY(pread(fd_, buf, count, offset));
+    errno_ = errno;
+    return rval;
+  }
+
   ssize_t Recv(void* buf, size_t len, int flags) {
     errno = 0;
     ssize_t rval = TEMP_FAILURE_RETRY(recv(fd_, buf, len, flags));
@@ -295,8 +332,8 @@
   ssize_t RecvFrom(void* buf, size_t len, int flags, struct sockaddr* src_addr,
                    socklen_t* addr_len) {
     errno = 0;
-    ssize_t rval = TEMP_FAILURE_RETRY(recvfrom(fd_, buf, len, flags, src_addr,
-                                               addr_len));
+    ssize_t rval =
+        TEMP_FAILURE_RETRY(recvfrom(fd_, buf, len, flags, src_addr, addr_len));
     errno_ = errno;
     return rval;
   }
@@ -308,6 +345,36 @@
     return rval;
   }
 
+  template <size_t SZ>
+  ssize_t RecvMsgAndFDs(const struct InbandMessageHeader& msg_in, int flags,
+                        SharedFD (*new_fds)[SZ]) {
+    // We need to make some modifications to land the fds. Make it clear
+    // that there are no updates to the msg being passed in during this call.
+    struct msghdr msg;
+    msg_in.Convert(&msg);
+    union {
+      char buffer[CMSG_SPACE(SZ * sizeof(int))];
+      struct cmsghdr this_aligns_buffer;
+    } u;
+    msg.msg_control = u.buffer;
+    msg.msg_controllen = sizeof(u.buffer);
+
+    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_len = CMSG_LEN(SZ * sizeof(int));
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    int* fd_array = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    for (size_t i = 0; i < SZ; ++i) {
+      fd_array[i] = -1;
+    }
+    ssize_t rval = RecvMsg(&msg, flags);
+    for (size_t i = 0; i < SZ; ++i) {
+      (*new_fds)[i] =
+          std::shared_ptr<FileInstance>(new FileInstance(fd_array[i], errno));
+    }
+    return rval;
+  }
+
   ssize_t Read(void* buf, size_t count) {
     errno = 0;
     ssize_t rval = TEMP_FAILURE_RETRY(read(fd_, buf, count));
@@ -329,18 +396,41 @@
     return rval;
   }
 
-  ssize_t SendTo(const void *buf, size_t len, int flags,
-                 const struct sockaddr *dest_addr, socklen_t addrlen) {
+  template <size_t SZ>
+  ssize_t SendMsgAndFDs(const struct InbandMessageHeader& msg_in, int flags,
+                        const SharedFD (&fds)[SZ]) {
+    struct msghdr msg;
+    msg_in.Convert(&msg);
+    union {
+      char buffer[CMSG_SPACE(SZ * sizeof(int))];
+      struct cmsghdr this_aligns_buffer;
+    } u;
+    msg.msg_control = u.buffer;
+    msg.msg_controllen = sizeof(u.buffer);
+
+    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_len = CMSG_LEN(SZ * sizeof(int));
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    int* fd_array = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    for (int i = 0; i < SZ; ++i) {
+      fd_array[i] = fds[i]->fd_;
+    }
+    return SendMsg(&msg, flags);
+  }
+
+  ssize_t SendTo(const void* buf, size_t len, int flags,
+                 const struct sockaddr* dest_addr, socklen_t addrlen) {
     errno = 0;
-    ssize_t rval = TEMP_FAILURE_RETRY(sendto(
-        fd_, buf, len, flags, dest_addr, addrlen));
+    ssize_t rval =
+        TEMP_FAILURE_RETRY(sendto(fd_, buf, len, flags, dest_addr, addrlen));
     errno_ = errno;
     return rval;
   }
 
   void Set(fd_set* dest, int* max_index) const;
 
-  int SetSockOpt(int level, int optname, const void *optval, socklen_t optlen) {
+  int SetSockOpt(int level, int optname, const void* optval, socklen_t optlen) {
     errno = 0;
     int rval = setsockopt(fd_, level, optname, optval, optlen);
     errno_ = errno;
@@ -358,11 +448,33 @@
     //  buf, or a pointer to some (immutable) static string (in which case buf
     //  is unused).
     if (out != s->strerror_buf_) {
-      strncpy(out, s->strerror_buf_, sizeof(strerror_buf_));
+      strncpy(s->strerror_buf_, out, sizeof(strerror_buf_));
     }
     return strerror_buf_;
   }
 
+  int TimerGet(struct itimerspec* curr_value) {
+    errno = 0;
+    int rval = timerfd_gettime(fd_, curr_value);
+    errno_ = errno;
+    return rval;
+  }
+
+  int TimerSet(int flags, const struct itimerspec* new_value,
+                   struct itimerspec* old_value) {
+    errno = 0;
+    int rval = timerfd_settime(fd_, flags, new_value, old_value);
+    errno_ = errno;
+    return rval;
+  }
+
+  ssize_t Truncate(off_t length) {
+    errno = 0;
+    ssize_t rval = TEMP_FAILURE_RETRY(ftruncate(fd_, length));
+    errno_ = errno;
+    return rval;
+  }
+
   ssize_t Write(const void* buf, size_t count) {
     errno = 0;
     ssize_t rval = TEMP_FAILURE_RETRY(write(fd_, buf, count));
@@ -382,7 +494,7 @@
     identity_.PrintF("fd=%d @%p", fd, this);
   }
 
-  FileInstance* Accept(struct sockaddr* addr, socklen_t *addrlen) const {
+  FileInstance* Accept(struct sockaddr* addr, socklen_t* addrlen) const {
     int fd = TEMP_FAILURE_RETRY(accept(fd_, addr, addrlen));
     if (fd == -1) {
       return new FileInstance(fd, errno);
@@ -400,8 +512,8 @@
 /* Methods that need both a fully defined SharedFD and a fully defined
    FileInstance. */
 
-SharedFD::SharedFD() : value_(FileInstance::ClosedInstance()) { }
+SharedFD::SharedFD() : value_(FileInstance::ClosedInstance()) {}
 
-}
+}  // namespace cvd
 
 #endif  // CUTTLEFISH_COMMON_COMMON_LIBS_FS_SHARED_FD_H_
diff --git a/common/libs/fs/shared_fd_test.cpp b/common/libs/fs/shared_fd_test.cpp
new file mode 100644
index 0000000..3423619
--- /dev/null
+++ b/common/libs/fs/shared_fd_test.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <gtest/gtest.h>
+
+#include <string>
+
+using cvd::InbandMessageHeader;
+using cvd::SharedFD;
+
+char hello[] = "Hello, world!";
+char pipe_message[] = "Testing the pipe";
+
+TEST(SendFD, Basic) {
+  char dirname[] = "/tmp/sfdtestXXXXXX";
+  char* socket = mkdtemp(dirname);
+  EXPECT_TRUE(socket != NULL);
+  std::string path(dirname);
+  path += "/s";
+  SharedFD server = SharedFD::SocketSeqPacketServer(path.c_str(), 0700);
+  EXPECT_TRUE(server->IsOpen());
+  int rval = fork();
+  EXPECT_NE(-1, rval);
+  if (!rval) {
+    struct iovec iov { hello, sizeof(hello) };
+    SharedFD client = SharedFD::SocketSeqPacketClient(path.c_str());
+    InbandMessageHeader hdr{};
+    hdr.msg_iov = &iov;
+    hdr.msg_iovlen = 1;
+    SharedFD fds[2];
+    SharedFD::Pipe(fds, fds + 1);
+    ssize_t rval = client->SendMsgAndFDs(hdr, 0, fds);
+    printf("SendMsg sent %zd (%s)\n", rval, client->StrError());
+    exit(0);
+  }
+  server->Listen(2);
+  SharedFD peer = SharedFD::Accept(*server);
+  EXPECT_TRUE(peer->IsOpen());
+  char buf[80];
+  struct iovec iov { buf, sizeof(buf) };
+  InbandMessageHeader hdr{};
+  hdr.msg_iov = &iov;
+  hdr.msg_iovlen = 1;
+  SharedFD fds[2];
+  peer->RecvMsgAndFDs(hdr, 0, &fds);
+  EXPECT_EQ(0, strcmp(buf, hello));
+  EXPECT_TRUE(fds[0]->IsOpen());
+  EXPECT_TRUE(fds[1]->IsOpen());
+  EXPECT_EQ(sizeof(pipe_message), fds[1]->Write(pipe_message, sizeof(pipe_message)));
+  EXPECT_EQ(sizeof(pipe_message), fds[0]->Read(buf, sizeof(buf)));
+  EXPECT_EQ(0, strcmp(buf, pipe_message));
+}
diff --git a/common/libs/fs/shared_select.h b/common/libs/fs/shared_select.h
index 1298d74..20bfb5a 100644
--- a/common/libs/fs/shared_select.h
+++ b/common/libs/fs/shared_select.h
@@ -20,7 +20,7 @@
 
 #include "common/libs/fs/shared_fd.h"
 
-namespace avd {
+namespace cvd {
 /**
  * The SharedFD version of fdset for the Select call.
  *
@@ -76,6 +76,6 @@
 int Select(SharedFDSet* read_set, SharedFDSet* write_set,
            SharedFDSet* error_set, struct timeval* timeout);
 
-}  // namespace avd
+}  // namespace cvd
 
 #endif  // CUTTLEFISH_COMMON_COMMON_LIBS_FS_SHARED_SELECT_H_
diff --git a/common/libs/metadata/gce_resource_location.h b/common/libs/glog/logging.h
similarity index 60%
rename from common/libs/metadata/gce_resource_location.h
rename to common/libs/glog/logging.h
index 5b7f38a..8cd4a8a 100644
--- a/common/libs/metadata/gce_resource_location.h
+++ b/common/libs/glog/logging.h
@@ -1,3 +1,4 @@
+#pragma once
 /*
  * Copyright (C) 2016 The Android Open Source Project
  *
@@ -13,14 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_GCE_RESOURCE_LOCATION_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_GCE_RESOURCE_LOCATION_H_
 
-class GceResourceLocation {
- public:
-  static const char* const kInitialMetadataPath;
-  static const char* const kInitialFstabPath;
-  static const char* const kDevicePersonalitiesPath;
-};
+#ifdef ANDROID
+#include <android-base/logging.h>
 
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_GCE_RESOURCE_LOCATION_H_
+#if defined(CUTTLEFISH_HOST)
+using ::android::base::VERBOSE;
+using ::android::base::INFO;
+using ::android::base::WARNING;
+using ::android::base::FATAL;
+
+#define LOG_IF(LEVEL, CONDITION) if (CONDITION) LOG(LEVEL)
+
+#endif  // CUTTLEFISH_HOST
+#else  // DEBIAN_HOST (by elimination)
+#include <glog/logging.h>
+#endif
diff --git a/common/libs/metadata/BUILD b/common/libs/metadata/BUILD
deleted file mode 100644
index 8cc8ac0..0000000
--- a/common/libs/metadata/BUILD
+++ /dev/null
@@ -1,106 +0,0 @@
-cc_library(
-    name = "constants",
-    visibility = [ "//visibility:public" ],
-    srcs = [
-        "gce_metadata_attributes.cpp",
-        "gce_metadata_attributes.h",
-        "gce_resource_location.cpp",
-        "gce_resource_location.h",
-    ],
-    hdrs = [
-        "gce_metadata_attributes.h",
-        "gce_resource_location.h",
-    ],
-)
-
-cc_library(
-    name = "initial_metadata_reader",
-    visibility = [ "//visibility:public" ],
-    srcs = [
-        "display_properties.cpp",
-        "display_properties.h",
-        "initial_metadata_reader.h",
-        "initial_metadata_reader_impl.cpp",
-        "initial_metadata_reader_impl.h",
-        "metadata_query.cpp",
-        "metadata_query.h",
-    ],
-    hdrs = [
-        "display_properties.h",
-        "initial_metadata_reader.h",
-        "metadata_query.h",
-    ],
-    copts = [
-        "-I/usr/include/jsoncpp",
-    ],
-    linkopts = [
-        "-ljsoncpp",
-    ],
-    deps = [
-        "@glog_repo//:glog",
-        ":constants",
-        "//common/libs/auto_resources",
-        "//common/libs/fs",
-    ],
-)
-
-cc_library(
-    name = "initial_metadata_reader-static",
-    visibility = [ "//visibility:public" ],
-    srcs = [
-        "display_properties.cpp",
-        "display_properties.h",
-        "initial_metadata_reader.h",
-        "initial_metadata_reader_impl.cpp",
-        "initial_metadata_reader_impl.h",
-        "metadata_query.cpp",
-        "metadata_query.h",
-    ],
-    hdrs = [
-        "display_properties.h",
-        "initial_metadata_reader.h",
-        "metadata_query.h",
-    ],
-    copts = [
-        "-I/usr/include/jsoncpp",
-    ],
-    linkopts = [
-        "-Wl,-Bstatic -ljsoncpp",
-    ],
-    linkstatic = 1,
-    deps = [
-        "@glog_repo//:glog",
-        ":constants",
-        "//common/libs/auto_resources",
-        "//common/libs/fs",
-    ],
-)
-
-
-cc_library(
-    name = "partitions",
-    visibility = [ "//visibility:public" ],
-    srcs = [
-        "get_partition_num.cpp",
-        "get_partition_num.h",
-    ],
-    hdrs = [
-        "get_partition_num.h",
-    ],
-    deps = [
-        "//common/libs/auto_resources",
-    ],
-)
-
-cc_test(
-    name = "initial_metadata_reader_test",
-    srcs = [
-        "initial_metadata_reader_test.cpp",
-    ],
-    copts = [
-        "-I/usr/include/jsoncpp",
-    ],
-    deps = [
-        ":initial_metadata_reader",
-    ],
-)
diff --git a/common/libs/metadata/display_properties.cpp b/common/libs/metadata/display_properties.cpp
deleted file mode 100644
index 587daff..0000000
--- a/common/libs/metadata/display_properties.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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/metadata/display_properties.h"
-
-#include <stdio.h>
-
-namespace avd {
-void DisplayProperties::Parse(const char* value) {
-  if (!value) {
-    return;
-  }
-  int xres, yres, unused_bits_per_pixel, dpi;
-  int rval = sscanf(
-      value, "%dx%dx%dx%d", &xres, &yres, &unused_bits_per_pixel, &dpi);
-  // bits_per_pixel isn't really controllable, so do something sensible
-  // if people stop setting it.
-  if (rval == 3) {
-    dpi = unused_bits_per_pixel;
-  } else if (rval != 4) {
-    return;
-  }
-  if ((xres < 0) || (yres < 0) || (dpi < 0)) {
-    return;
-  }
-  x_res_ = xres;
-  y_res_ = yres;
-  // Bits per pixel is fixed at 32 in our devices.
-  // bits_per_pixel_ = bits_per_pixel;
-  dpi_ = dpi;
-  default_ = false;
-  config_.SetToString(value);
-}
-}  // namespace avd
diff --git a/common/libs/metadata/display_properties.h b/common/libs/metadata/display_properties.h
deleted file mode 100644
index 0d02445..0000000
--- a/common/libs/metadata/display_properties.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.
- */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_DISPLAY_PROPERTIES_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_DISPLAY_PROPERTIES_H_
-
-#include "common/libs/auto_resources/auto_resources.h"
-
-namespace avd {
-
-class DisplayProperties {
- public:
-  DisplayProperties() :
-      x_res_(1280),
-      y_res_(720),
-      bits_per_pixel_(32),
-      dpi_(160),
-      default_(true) {
-        config_.SetToString("1280x720x32x160");
-      }
-
-  void Parse(const char* value);
-
-  int GetXRes() const { return x_res_; }
-  int GetYRes() const { return y_res_; }
-  int GetBitsPerPixel() const { return bits_per_pixel_; }
-  int GetDpi() const { return dpi_; }
-  bool IsDefault() const { return default_; }
-  const char* GetConfig() const { return config_.data(); }
-
- private:
-  // Screen width in pixels
-  int x_res_;
-  // Screen height in pixels
-  int y_res_;
-  // Depth of the screen (obsolete)
-  int bits_per_pixel_;
-  // Pixels per inch
-  int dpi_;
-  // Default
-  bool default_;
-  // Unparsed configuration
-  AutoFreeBuffer config_;
-};
-
-}  // namespace avd
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_DISPLAY_PROPERTIES_H_
diff --git a/common/libs/metadata/gce_metadata_attributes.cpp b/common/libs/metadata/gce_metadata_attributes.cpp
deleted file mode 100644
index 7ee5ac6..0000000
--- a/common/libs/metadata/gce_metadata_attributes.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * 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/metadata/gce_metadata_attributes.h"
-
-const char* const GceMetadataAttributes::kTestingPath = "testing/attributes/";
-
-const char* const GceMetadataAttributes::kInstancePath = "instance/attributes/";
-
-const char* const GceMetadataAttributes::kProjectPath = "project/attributes/";
-
-// Version of Android to boot on multiboot images.
-// Updates at boot time.
-// Example: 23_gce_x86_64_phone
-// When in doubt leave blank or use default
-const char* const GceMetadataAttributes::kAndroidVersionKey = "android_version";
-
-// Configuration of the back camera.
-// *** OBSOLETE ***
-// Updates at boot time.
-// Example: 1,768,1024,checker-sliding
-// NOTE: To escape the commas with gcloud use
-//   --metadata-from-file camera_back=<(echo 1,768,1024,checker-sliding)
-const char* const GceMetadataAttributes::kBackCameraConfigKey = "camera_back";
-
-// Content of (not a path to) an extra init file
-// Updates at boot time.
-const char* const GceMetadataAttributes::kCustomInitFileKey =
-    "custom_init_file";
-
-// Configuration of the display
-// *** OBSOLETE ***
-// Updates at boot time.
-// Example: 800x1280x16x213
-// Format is ${x_res}x${y_res}x${depth}x${dpi}
-// Note: depth is currently ignored. 32 is the forward-compatible value.
-const char* const GceMetadataAttributes::kDisplayConfigurationKey =
-    "cfg_sta_display_resolution";
-
-// Configuration of the front camera.
-// Updates at boot time.
-// Example: 1,768,1024,checker-sliding
-// Note: To escape the commas with gcloud use
-//   --metadata-from-file camera_front=<(echo 1,768,1024,checker-sliding)
-const char* const GceMetadataAttributes::kFrontCameraConfigKey = "camera_front";
-
-// Current GPS location.
-// Updates at runtime.
-// Example: -122.0840859,37.4224504,0,0,0,10
-// The fields are:
-//   Logitude in degress with decimal subdegrees
-//   Latitude in degress with decimal subdegrees
-//   Altitude in meters
-//   Heading in degress
-//   Speed in meters per second
-//   Precision in meters
-const char* const GceMetadataAttributes::kGpsCoordinatesKey = "gps_coordinates";
-
-// Initial locale of the device.
-// Updates at boot time.
-// Example: en_US
-// If this attribute is set any existing locale is forgotten and the new
-// one is set.
-// If no value is set then the existing locale, if any, remains.
-const char* const GceMetadataAttributes::kInitialLocaleKey =
-    "cfg_sta_initial_locale";
-
-// State of Wireless LAN.
-// Updates at runtime.
-// Example: ENABLED
-// Accepted values are: ( ENABLED, DISABLED ). If value is neither of these, the
-// handler falls back to default state, DISABLED.
-const char* const GceMetadataAttributes::kWlanStateKey = "cfg_sta_wlan_state";
-
-// Configuration of the physical keyboard (if any).
-// Updates at boot time.
-// If the attribute is not specified will enable physical keyboard.
-// Values accepted:
-//   1: Use a physical keyboard.
-//   All other values: disable the physical keyboard.
-const char* const GceMetadataAttributes::kPhysicalKeyboardKey =
-    "physical_keyboard";
-
-// Configuration of hardware buttons.
-// *** OBSOLETE ***
-// Updates at boot time.
-// If the attribute is not specified will enable all hardware buttons.
-// Values accepted:
-//   Empty string: disable all hardware buttons.
-//   MENU: enable only the menu button.
-//   BACK: enable only the back button.
-//   HOME: enable only the home button.
-//   Any combinion of the above values delimited by commas.
-const char* const GceMetadataAttributes::kPhysicalNavigationButtonsKey =
-    "physical_navigation_buttons";
-
-// Path to the device node to mount as a system overlay.
-// Updates at boot time.
-// If absent no overlay is mounted.
-// Example: /dev/block/sdb1
-const char* const GceMetadataAttributes::kSystemOverlayDeviceKey =
-    "system_overlay_device";
-
-// Device personality definition in JSON format.
-// Allows custom device features to be set.
-// If absent - not used.
-const char* const GceMetadataAttributes::kDevicePersonalityDefinitionKey =
-    "device_personality_definition";
-
-// Device personality name.
-// Holds name (without path or extension) of the file describing device
-// features. File describing features should be located in
-// GceResourceLocation::kDevicePersonalityPath.
-// If empty, not present, or invalid falls back to 'default'.
-const char* const GceMetadataAttributes::kDevicePersonalityNameKey =
-    "device_personality_name";
-
-// Controls which streaming protocol will be used by the remoter.
-// Values include:
-//   appstreaming
-//   vnc
-// If nothing is set the remoter behaves as if vnc is selected.
-const char* const GceMetadataAttributes::kRemotingModeKey =
-    "cfg_sta_remoting_mode";
-
-// Controls the scaling used in the VNC backend. The the device is
-// configured to 320 dpi but this is set to 160, the result will be
-// as if a scaling factor of 0.5 were used.
-const char* const GceMetadataAttributes::kConsoleDpiScalingDpiKey =
-    "cfg_sta_console_scaling_dpi";
-
-// Controls how the pixels will be scaled before they are sent over VNC.
-// Numbers are floating point and must be <= 1.0 If nothing is set then
-// 1.0 is assumed.
-// Note: VNC doesn't cope well with unusual scaling factors: it tends to
-// clip the right / bottom off the screen. 0.5 and 0.25 appear to be safe.
-// If nothing is set then 1.0 is assumed.
-const char* const GceMetadataAttributes::kConsoleScalingFactorKey =
-    "cfg_sta_console_scaling_factor";
-
-// Controls whether metadata attribute injections are permitted from
-// within Android.
-// The only legitimate purpose of this attribute is testing.
-// Not all attributes are permitted.
-const char* const GceMetadataAttributes::kTestAllowMetadataInjectionsKey =
-  "cfg_test_allow_metadata_injections";
-
-// Controls which RIL implementation will be used during next boot.
-// Values include:
-// - TESTING = use testing version of GCE RIL (if available).
-// - DEPRECATED = use previous version of GCE RIL (if available).
-// - DEFAULT / unset / other  = use current version of GCE RIL.
-const char* const GceMetadataAttributes::kRilVersionKey = "ril_version";
-
-// Controls which Hardware composer implementation will be used during next
-// boot.
-// Values include:
-// - TESTING = use testing version of GCE Hardware Composer (if available).
-// - DEPRECATED = use previous version of GCE Hardware Composer (if available).
-// - DEFAULT / unset / other  = use current version of GCE Hardware Composer.
-const char* const GceMetadataAttributes::kHWComposerVersionKey = "hwc_version";
-
-// Controls which VNC implementation will be used during next boot
-// Values include:
-// - TESTING = use testing version of the GCE VNC server (if available)
-// - DEPRECATED = use previous version of the GCE VNC server (if available).
-// - DEFAULT / unset / other  = use current version of the GCE VNC server
-const char* const GceMetadataAttributes::kVncServerVersionKey =
-    "vnc_server_version";
-
-const char* const GceMetadataAttributes::kSshKeysInstancePath =
-    "instance/attributes/sshKeys";
-const char* const GceMetadataAttributes::kSshKeysProjectPath =
-    "project/attributes/sshKeys";
-
-const char* const GceMetadataAttributes::kInjectedIntentInstancePath =
-    "instance/attributes/t_force_intent";
-const char* const GceMetadataAttributes::kInjectedIntentProjectPath =
-    "project/attributes/t_force_intent";
-
-const char* const GceMetadataAttributes::kForceCoarseOrientationChangeInstancePath =
-    "instance/attributes/t_force_orientation";
-const char* const GceMetadataAttributes::kForceCoarseOrientationChangeProjectPath =
-    "project/attributes/t_force_orientation";
-const char* const GceMetadataAttributes::kForceCoarseOrientationChangeTestingPath =
-    "testing/attributes/t_force_orientation";
-
-const char* const GceMetadataAttributes::kPropertyMapperInstancePath =
-    "instance/attributes/cfg_sta_pmap_";
-const char* const GceMetadataAttributes::kPropertyMapperProjectPath =
-    "project/attributes/cfg_sta_pmap_";
-
-const char* const GceMetadataAttributes::kAccelerometerSensorInstancePath =
-    "instance/attributes/t_sensor_accelerometer";
-const char* const GceMetadataAttributes::kAccelerometerSensorProjectPath =
-    "project/attributes/t_sensor_accelerometer";
-
-const char* const GceMetadataAttributes::kGyroscopeSensorInstancePath =
-    "instance/attributes/t_sensor_gyroscope";
-const char* const GceMetadataAttributes::kGyroscopeSensorProjectPath =
-    "project/attributes/t_sensor_gyroscope";
-
-const char* const GceMetadataAttributes::kLightSensorInstancePath =
-    "instance/attributes/t_sensor_light";
-const char* const GceMetadataAttributes::kLightSensorProjectPath =
-    "project/attributes/t_sensor_light";
-
-const char* const GceMetadataAttributes::kMagneticFieldSensorInstancePath =
-    "instance/attributes/t_sensor_magnetic_field";
-const char* const GceMetadataAttributes::kMagneticFieldSensorProjectPath =
-    "project/attributes/t_sensor_magnetic_field";
-
-const char* const GceMetadataAttributes::kPressureSensorInstancePath =
-    "instance/attributes/t_sensor_pressure";
-const char* const GceMetadataAttributes::kPressureSensorProjectPath =
-    "project/attributes/t_sensor_pressure";
-
-const char* const GceMetadataAttributes::kProximitySensorInstancePath =
-    "instance/attributes/t_sensor_proximity";
-const char* const GceMetadataAttributes::kProximitySensorProjectPath =
-    "project/attributes/t_sensor_proximity";
-
-const char* const GceMetadataAttributes::kAmbientTemperatureSensorInstancePath =
-    "instance/attributes/t_sensor_ambient_temperature";
-const char* const GceMetadataAttributes::kAmbientTemperatureSensorProjectPath =
-    "project/attributes/t_sensor_ambient_temperature";
-
-const char* const GceMetadataAttributes::kDeviceTemperatureSensorInstancePath =
-    "instance/attributes/t_sensor_device_temperature";
-const char* const GceMetadataAttributes::kDeviceTemperatureSensorProjectPath =
-    "project/attributes/t_sensor_device_temperature";
-
-const char* const GceMetadataAttributes::kRelativeHumiditySensorInstancePath =
-    "instance/attributes/t_sensor_relative_humidity";
-const char* const GceMetadataAttributes::kRelativeHumiditySensorProjectPath =
-    "project/attributes/t_sensor_relative_humidity";
-const char* const GceMetadataAttributes::kAutoScreenshotFrequencyInstancePath =
-    "instance/attributes/t_auto_screenshot_frequency";
-const char* const GceMetadataAttributes::kAutoScreenshotPrefixInstancePath =
-    "instance/attributes/t_auto_screenshot_prefix";
-const char* const GceMetadataAttributes::kRebootIfMissingInstancePath =
-    "instance/attributes/t_reboot_if_missing_path";
-
-const char* const GceMetadataAttributes::kPowerBatteryConfigPath =
-    "instance/attributes/power_battery_config";
-
-const char* const GceMetadataAttributes::kMobileDataNetworkingConfigPath =
-    "instance/attributes/mobile_data_networking_config";
-// IMEI value which must be set before the system starts to boot. If not,
-// a randomly generated value will be used by the GCE RIL module.
-const char* const GceMetadataAttributes::kIMEIConfigKey = "imei";
-
-const char* const GceMetadataAttributes::kScreenshotsDirectoryInstancePath =
-    "instance/attributes/t_screenshot_dir_path";
-const char* const GceMetadataAttributes::kScreenshotsDirectoryProjectPath =
-    "project/attributes/t_screenshot_dir_path";
diff --git a/common/libs/metadata/gce_metadata_attributes.h b/common/libs/metadata/gce_metadata_attributes.h
deleted file mode 100644
index ee65735..0000000
--- a/common/libs/metadata/gce_metadata_attributes.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.
- */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_GCE_METADATA_ATTRIBUTES_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_GCE_METADATA_ATTRIBUTES_H_
-
-// Constant string declarations for GCE metadata attributes used by the Remoter.
-class GceMetadataAttributes {
-public:
-  static const char* const kTestingPath;
-  static const char* const kInstancePath;
-  static const char* const kProjectPath;
-
-  // The InitialMetadataReader reasons in terms of keys, while the
-  // MetadataService uses full paths.
-  // A key represents two paths, one for the instance and one for the project.
-  // When both paths are present the instance value takes precedence.
-  //
-  // TODO(ghartman): Refactor the MetadataService to use keys as well.
-  static const char* const kAndroidVersionKey;
-  static const char* const kBackCameraConfigKey;
-  static const char* const kConsoleDpiScalingDpiKey;
-  static const char* const kConsoleScalingFactorKey;
-  static const char* const kCustomInitFileKey;
-  static const char* const kDevicePersonalityDefinitionKey;
-  static const char* const kDevicePersonalityNameKey;
-  static const char* const kDisplayConfigurationKey;
-  static const char* const kFrontCameraConfigKey;
-  static const char* const kGpsCoordinatesKey;
-  static const char* const kInitialLocaleKey;
-  static const char* const kWlanStateKey;
-  static const char* const kPhysicalKeyboardKey;
-  static const char* const kPhysicalNavigationButtonsKey;
-  static const char* const kRemotingModeKey;
-  static const char* const kSystemOverlayDeviceKey;
-  static const char* const kRilVersionKey;
-  static const char* const kHWComposerVersionKey;
-  static const char* const kVncServerVersionKey;
-
-  static const char* const kTestAllowMetadataInjectionsKey;
-
-  static const char* const kSshKeysInstancePath;
-  static const char* const kSshKeysProjectPath;
-  static const char* const kInjectedIntentInstancePath;
-  static const char* const kInjectedIntentProjectPath;
-  static const char* const kForceCoarseOrientationChangeTestingPath;
-  static const char* const kForceCoarseOrientationChangeInstancePath;
-  static const char* const kForceCoarseOrientationChangeProjectPath;
-  static const char* const kPropertyMapperInstancePath;
-  static const char* const kPropertyMapperProjectPath;
-
-  static const char* const kAccelerometerSensorInstancePath;
-  static const char* const kAccelerometerSensorProjectPath;
-  static const char* const kGyroscopeSensorInstancePath;
-  static const char* const kGyroscopeSensorProjectPath;
-  static const char* const kLightSensorInstancePath;
-  static const char* const kLightSensorProjectPath;
-  static const char* const kMagneticFieldSensorInstancePath;
-  static const char* const kMagneticFieldSensorProjectPath;
-  static const char* const kPressureSensorInstancePath;
-  static const char* const kPressureSensorProjectPath;
-  static const char* const kProximitySensorInstancePath;
-  static const char* const kProximitySensorProjectPath;
-  static const char* const kDeviceTemperatureSensorInstancePath;
-  static const char* const kDeviceTemperatureSensorProjectPath;
-  static const char* const kAmbientTemperatureSensorInstancePath;
-  static const char* const kAmbientTemperatureSensorProjectPath;
-  static const char* const kRelativeHumiditySensorInstancePath;
-  static const char* const kRelativeHumiditySensorProjectPath;
-  static const char* const kAutoScreenshotFrequencyInstancePath;
-  static const char* const kAutoScreenshotPrefixInstancePath;
-  static const char* const kRebootIfMissingInstancePath;
-
-  static const char* const kPowerBatteryConfigPath;
-
-  static const char* const kMobileDataNetworkingConfigPath;
-  static const char* const kIMEIConfigKey;
-
-  static const char* const kScreenshotsDirectoryInstancePath;
-  static const char* const kScreenshotsDirectoryProjectPath;
-};
-
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_GCE_METADATA_ATTRIBUTES_H_
diff --git a/common/libs/metadata/gce_resource_location.cpp b/common/libs/metadata/gce_resource_location.cpp
deleted file mode 100644
index b9405a9..0000000
--- a/common/libs/metadata/gce_resource_location.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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/metadata/gce_resource_location.h"
-
-// Location of the file containing initial metadata.
-// Contains device configuration acquired from metadata server in json format.
-const char* const GceResourceLocation::kInitialMetadataPath =
-    "/initial.metadata";
-
-// Location of the fstab file.
-const char* const GceResourceLocation::kInitialFstabPath = "/fstab.gce_x86";
-
-// Location of the folder containing device personality files.
-const char* const GceResourceLocation::kDevicePersonalitiesPath =
-    "/system/etc/device_personalities";
-
diff --git a/common/libs/metadata/get_partition_num.cpp b/common/libs/metadata/get_partition_num.cpp
deleted file mode 100644
index 891b9c8..0000000
--- a/common/libs/metadata/get_partition_num.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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/metadata/get_partition_num.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "common/libs/auto_resources/auto_resources.h"
-
-namespace {
-const char kDefaultPartitionsPath[] = "/partitions";
-}  // namespace
-
-// Looks up the partition number for a given name.
-// Returns -1 if the partition number cannot be found for any reason.
-long GetPartitionNum(const char* name, const char* path) {
-  if (!path) {
-    path = kDefaultPartitionsPath;
-  }
-  size_t name_len = strlen(name);
-  AutoCloseFILE f(fopen(path, "r"));
-  if (f.IsError()) {
-    printf("%s: fopen(%s) failed %s:%d (%s)", __FUNCTION__,
-               path, __FILE__, __LINE__, strerror(errno));
-    return -1;
-  }
-  char line[160];
-  while (!f.IsEOF()) {
-    if (!fgets(line, sizeof(line), f)) {
-      if (!f.IsEOF()) {
-        printf("%s: fgets failed %s:%d (%s)", __FUNCTION__,
-                   __FILE__, __LINE__, strerror(errno));
-      }
-      return -1;
-    }
-    if (!strncmp(name, line, name_len) && (line[name_len] == ' ')) {
-      char* end;
-      const char* base = line + name_len + 1;
-      long rval = strtol(base, &end, 10);
-      if (base != end) {
-        return rval;
-      } else {
-        printf("%s: parse failed line=%s %s:%d", __FUNCTION__,
-                   line, __FILE__, __LINE__);
-      }
-    }
-  }
-  printf("%s: Could not find name=%s %s:%d", __FUNCTION__, name,
-             __FILE__, __LINE__);
-  return -1;
-}
diff --git a/common/libs/metadata/get_partition_num.h b/common/libs/metadata/get_partition_num.h
deleted file mode 100644
index c8dc1e6..0000000
--- a/common/libs/metadata/get_partition_num.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_GET_PARTITION_NUM_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_GET_PARTITION_NUM_H_
-
-// Looks up the partition number for a given name.
-// Path can be a full path to a partition number file. If null is provided the
-// system default partition file is used.
-// Returns -1 if the partition number cannot be found for any reason.
-long GetPartitionNum(const char* name, const char* path = 0);
-
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_GET_PARTITION_NUM_H_
diff --git a/common/libs/metadata/initial_metadata_reader.h b/common/libs/metadata/initial_metadata_reader.h
deleted file mode 100644
index 5c10dde..0000000
--- a/common/libs/metadata/initial_metadata_reader.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_INITIAL_METADATA_READER_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_INITIAL_METADATA_READER_H_
-
-namespace avd {
-
-class DisplayProperties;
-
-// See the comments in MetadataService.h for the rules that apply to
-// project and instance values.
-class InitialMetadataReader {
- public:
-  // Describes the configuration of a screen. This is here because it is shared
-  // with gce_init, which can't handle some of the dependencies in the full
-  // framebuffer configuration.
-  static InitialMetadataReader* getInstance();
-  virtual const DisplayProperties& GetDisplay() const = 0;
-  virtual const char* GetInstanceHostname() const = 0;
-  virtual const char* GetValueForKey(const char* key) const = 0;
-
- protected:
-  InitialMetadataReader() {}
-  virtual ~InitialMetadataReader() {}
-  InitialMetadataReader(const InitialMetadataReader&);
-  InitialMetadataReader& operator= (const InitialMetadataReader&);
-};
-
-}
-
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_INITIAL_METADATA_READER_H_
diff --git a/common/libs/metadata/initial_metadata_reader_impl.cpp b/common/libs/metadata/initial_metadata_reader_impl.cpp
deleted file mode 100644
index 03fdc77..0000000
--- a/common/libs/metadata/initial_metadata_reader_impl.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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/metadata/initial_metadata_reader_impl.h"
-
-#include <errno.h>
-#include <stdio.h>
-#include <iostream>
-#include <fstream>
-#include <map>
-#include <string>
-
-#include <glog/logging.h>
-#include <json/json.h>
-
-#include "common/libs/metadata/gce_metadata_attributes.h"
-#include "common/libs/metadata/gce_resource_location.h"
-
-namespace avd {
-
-InitialMetadataReaderImpl::InitialMetadataReaderImpl()
-    : is_initialized_(false) {}
-
-static std::string ValueToString(Json::Value value) {
-  if (value.isString()) {
-    return value.asString();
-  } else {
-    Json::FastWriter writer;
-    return writer.write(value);
-  }
-}
-
-void StoreValues(Json::Value source, MetadataReaderValueMap* dest) {
-  if (!source.isObject()) {
-    return;
-  }
-  Json::Value::Members members = source.getMemberNames();
-  for (Json::Value::Members::const_iterator it = members.begin();
-       it != members.end(); ++it) {
-    (*dest)[*it] = ValueToString(source[*it]);
-  }
-}
-
-bool InitialMetadataReaderImpl::Init(const char* config_path) {
-  std::ifstream ifs(config_path);
-
-  if (!ifs.good()) {
-    LOG(ERROR) << "Couldn't open initial metadata file.";
-    return false;
-  }
-  // Skip over the headers.
-  std::string response_line;
-  while (getline(ifs, response_line, '\n')) {
-    if (response_line == "\r") {
-      // End of headers.
-      break;
-    }
-  }
-  // Now parse the JSON payload.
-  Json::Reader reader;
-  Json::Value root;
-  is_initialized_ = reader.parse(ifs, root);
-  // Now we need to convert the values to strings. We do this because we need
-  // stable pointers to return, and libjsoncpp deallocates strings when the
-  // corresponding Value goes out of scope.
-  if (is_initialized_) {
-    Json::Value empty(Json::objectValue);
-    Json::Value source = root.get("project", empty).get("attributes", empty);
-    StoreValues(source, &values_);
-    source = root.get("instance", empty).get("attributes", empty);
-    StoreValues(source, &values_);
-    instance_hostname_ = ValueToString(
-        root.get("instance", empty).get("hostname", Json::stringValue));
-  } else {
-    LOG(ERROR) << "Failed to parse metadata: "
-               << reader.getFormattedErrorMessages();
-  }
-  display_.Parse(GetValueForKey(
-      GceMetadataAttributes::kDisplayConfigurationKey));
-  return is_initialized_;
-}
-
-const char* InitialMetadataReaderImpl::GetValueForKey(const char* key) const {
-  MetadataReaderValueMap::const_iterator it = values_.find(key);
-  if (it == values_.end()) {
-    return NULL;
-  }
-  return it->second.c_str();
-}
-
-InitialMetadataReader* InitialMetadataReader::getInstance() {
-  static InitialMetadataReaderImpl* instance;
-  if (!instance) {
-    instance = new InitialMetadataReaderImpl();
-    instance->Init(GceResourceLocation::kInitialMetadataPath);
-  }
-  return instance;
-}
-
-}
diff --git a/common/libs/metadata/initial_metadata_reader_impl.h b/common/libs/metadata/initial_metadata_reader_impl.h
deleted file mode 100644
index ad67d45..0000000
--- a/common/libs/metadata/initial_metadata_reader_impl.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_INITIAL_METADATA_READER_IMPL_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_INITIAL_METADATA_READER_IMPL_H_
-
-#include <map>
-#include <string>
-
-#include "common/libs/metadata/display_properties.h"
-#include "common/libs/metadata/initial_metadata_reader.h"
-
-namespace avd {
-
-typedef std::map<std::string, std::string> MetadataReaderValueMap;
-
-class InitialMetadataReaderImpl : public InitialMetadataReader {
- public:
-  InitialMetadataReaderImpl();
-
-  const DisplayProperties& GetDisplay() const {
-    return display_;
-  }
-
-  const char* GetValueForKey(const char* key) const;
-  const char* GetInstanceHostname() const { return instance_hostname_.c_str(); }
-  bool Init(const char* path);
-
- protected:
-  bool is_initialized_;
-  MetadataReaderValueMap values_;
-  std::string instance_hostname_;
-  DisplayProperties display_;
-};
-
-}  // namespace avd
-
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_INITIAL_METADATA_READER_IMPL_H_
diff --git a/common/libs/metadata/initial_metadata_reader_test.cpp b/common/libs/metadata/initial_metadata_reader_test.cpp
deleted file mode 100644
index ed543cd..0000000
--- a/common/libs/metadata/initial_metadata_reader_test.cpp
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include "common/libs/metadata/initial_metadata_reader_impl.h"
-#include "common/libs/metadata/gce_metadata_attributes.h"
-
-#include <json/json.h>
-
-// TODO(ghartman): Adopt external/gtest.
-
-const char* instance_value = "i_value";
-const char* project_value = "p_value";
-
-extern "C" {
-// Host builds have trouble because these symbols are not defined.
-// TODO(ghartman) Come up with better stubs. #include <atomic> created issues,
-// but perhaps there is something more portable than the gcc built-ins.
-int32_t android_atomic_inc(volatile int32_t* addr) {
-  return __sync_fetch_and_add(addr, 1);
-}
-
-int32_t android_atomic_dec(volatile int32_t* addr) {
-  return __sync_fetch_and_sub(addr, 1);
-}
-}
-
-void ExpectEqual(
-    const char* test,
-    const char* left_desc, const char* left,
-    const char* right_desc, const char* right,
-    const char* file, int line) {
-  if (left == right) {
-    return;
-  }
-  if (left && right && !strcmp(left, right)) {
-    return;
-  }
-  if (!left) {
-    left = "(NULL)";
-  }
-  if (!right) {
-    right = "(NULL)";
-  }
-  fprintf(stderr, "%s: FAIL: strings not equal at %s:%d\n"
-          "  %s=\"%s\"\n"
-          "  %s=\"%s\"\n", test, file, line, left_desc, left, right_desc,
-          right);
-  exit(1);
-}
-
-#define EXPECT_EQUAL(TEST, LEFT, RIGHT) ExpectEqual(TEST, #LEFT, (LEFT), #RIGHT, (RIGHT), __FILE__, __LINE__)
-
-void ExpectNotEqual(
-    const char* test,
-    const char* left_desc, const char* left,
-    const char* right_desc, const char* right,
-    const char* file, int line) {
-  if (left != right) {
-    return;
-  }
-  if (left && right && strcmp(left, right)) {
-    return;
-  }
-  if (!left) {
-    left = "(NULL)";
-  }
-  if (!right) {
-    right = "(NULL)";
-  }
-  fprintf(stderr, "%s: FAIL: strings are equal at %s:%d\n"
-          "  %s=\"%s\"\n"
-          "  %s=\"%s\"\n", test, file, line, left_desc, left, right_desc,
-          right);
-  exit(1);
-}
-
-void ExpectNotEqual(
-    const char* test,
-    const char* left_desc, int left,
-    const char* right_desc, int right,
-    const char* file, int line) {
-  if (left != right) {
-    return;
-  }
-  fprintf(stderr, "%s: FAIL: ints are equal at %s:%d\n"
-          "  %s=\"%d\"\n"
-          "  %s=\"%d\"\n", test, file, line, left_desc, left, right_desc,
-          right);
-  exit(1);
-}
-
-#define EXPECT_NOT_EQUAL(TEST, LEFT, RIGHT) ExpectNotEqual(TEST, #LEFT, (LEFT), #RIGHT, (RIGHT), __FILE__, __LINE__)
-
-void ExpectNotNull(
-    const char* test, const char* left_desc, void *left,
-    const char* file, int line) {
-  if (left) {
-    return;
-  }
-  fprintf(stderr, "%s: FAIL: null for %s at %s:%d\n",
-          test, left_desc, file, line);
-  exit(1);
-}
-
-#define EXPECT_NOT_NULL(TEST, LEFT) ExpectNotNull(TEST, #LEFT, (LEFT), __FILE__, __LINE__)
-
-class TestMetadataReader : public avd::InitialMetadataReaderImpl {
- public:
-  TestMetadataReader(const char* path) : InitialMetadataReaderImpl() {
-    Init(path);
-  }
-};
-
-struct TestLine {
-  const char* path;
-  const char* key;
-  const char* value;
-};
-
-struct TestCase {
-  const char* expected_value;
-  const char* key;
-  const TestLine * lines;
-};
-
-TestLine EmptyFileLines[] = {
-  {NULL, NULL, NULL},
-};
-
-const char* some_key = "some_key";
-
-TestCase EmptyFile = {
-  NULL,
-  some_key,
-  EmptyFileLines
-};
-
-TestLine InstanceFileLines[] =   {
-  {GceMetadataAttributes::kInstancePath, some_key, instance_value},
-  {NULL, NULL, NULL},
-};
-
-TestCase InstanceFile = {
-  instance_value,
-  some_key,
-  InstanceFileLines
-};
-
-TestLine ProjectFileLines[] =   {
-  {GceMetadataAttributes::kProjectPath, some_key, project_value},
-  {NULL, NULL, NULL},
-};
-
-TestCase ProjectFile = {
-  project_value,
-  some_key,
-  ProjectFileLines
-};
-
-TestLine InstanceBeforeProjectLines[] =   {
-  {GceMetadataAttributes::kInstancePath, some_key, instance_value},
-  {GceMetadataAttributes::kProjectPath, some_key, project_value},
-  {NULL, NULL, NULL},
-};
-
-TestCase InstanceBeforeProject = {
-  instance_value,
-  some_key,
-  InstanceBeforeProjectLines
-};
-
-TestLine ProjectBeforeInstanceLines[] =   {
-  {GceMetadataAttributes::kProjectPath, some_key, project_value},
-  {GceMetadataAttributes::kInstancePath, some_key, instance_value},
-  {NULL, NULL, NULL},
-};
-
-TestCase ProjectBeforeInstance = {
-  instance_value,
-  some_key,
-  ProjectBeforeInstanceLines
-};
-
-TestLine ProjectSetInstanceSetEmptyLines[] =   {
-  {GceMetadataAttributes::kProjectPath, some_key, project_value},
-  {GceMetadataAttributes::kInstancePath, some_key, ""},
-  {NULL, NULL, NULL},
-};
-
-TestCase ProjectSetInstanceSetEmpty = {
-  "",
-  some_key,
-  ProjectSetInstanceSetEmptyLines
-};
-
-void WriteLines(const char* name, const TestLine* data, int fd) {
-  Json::Value root(Json::objectValue);
-  while (data->path && data->key && data->value) {
-    if (data->path == GceMetadataAttributes::kProjectPath) {
-      root["project"]["attributes"][data->key] = data->value;
-    } else if (data->path == GceMetadataAttributes::kInstancePath) {
-      root["instance"]["attributes"][data->key] = data->value;
-    } else {
-      root[data->path][data->key] = data->value;
-    }
-    ++data;
-  }
-  int my_fd = dup(fd);
-  EXPECT_NOT_EQUAL(name, my_fd, -1);
-  FILE * dest = fdopen(my_fd, "w");
-  EXPECT_NOT_NULL(name, dest);
-  fputs("Metadata-Flavor: Google\r\n\r\n", dest);
-  fputs(Json::FastWriter().write(root).c_str(), dest);
-  EXPECT_NOT_EQUAL(name, fclose(dest), EOF);
-}
-
-void RunTest(const char* name, const TestCase& test) {
-  char *filename = strdup("/tmp/testXXXXXX");
-  EXPECT_NOT_NULL(name, filename);
-  int fd = mkstemp(filename);
-  EXPECT_NOT_EQUAL(name, fd, -1);
-  WriteLines(name, test.lines, fd);
-  TestMetadataReader* reader = new TestMetadataReader(filename);
-  EXPECT_NOT_NULL(name, reader);
-  EXPECT_EQUAL(name, reader->GetValueForKey(test.key), test.expected_value);
-  delete reader;
-  EXPECT_NOT_EQUAL(name, close(fd), -1);
-  EXPECT_NOT_EQUAL(name, unlink(filename), -1);
-  free(filename);
-  printf("%s: PASS\n", name);
-  fflush(stdout);
-}
-
-#define RUN_TEST(CONFIG) RunTest(#CONFIG, (CONFIG))
-
-TestLine SpuriousPathLines[] =   {
-  {"spurious_path", some_key, instance_value},
-  {NULL, NULL, NULL},
-};
-
-TestCase SpuriousPath = {
-  NULL,
-  some_key,
-  SpuriousPathLines
-};
-
-TestLine SpuriousKeyLines[] =   {
-  {GceMetadataAttributes::kInstancePath, "spurious", instance_value},
-  {NULL, NULL, NULL},
-};
-
-TestCase SpuriousKey = {
-  NULL,
-  some_key,
-  SpuriousKeyLines
-};
-
-int main(int /*argc*/, char* /*argv*/[]) {
-  RUN_TEST(EmptyFile);
-  RUN_TEST(InstanceFile);
-  RUN_TEST(ProjectFile);
-  RUN_TEST(InstanceBeforeProject);
-  RUN_TEST(ProjectBeforeInstance);
-  RUN_TEST(ProjectSetInstanceSetEmpty);
-  RUN_TEST(SpuriousPath);
-  RUN_TEST(SpuriousKey);
-  return 0;
-}
diff --git a/common/libs/metadata/metadata_query.cpp b/common/libs/metadata/metadata_query.cpp
deleted file mode 100644
index cba93f5..0000000
--- a/common/libs/metadata/metadata_query.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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/fs/shared_fd.h"
-
-#include <glog/logging.h>
-
-#include "common/libs/metadata/metadata_query.h"
-
-namespace {
-class MetadataQueryImpl : public MetadataQuery {
- public:
-  MetadataQueryImpl() {
-  }
-
-  ~MetadataQueryImpl() {}
-
-  bool QueryServer(AutoFreeBuffer* buffer) {
-    if (!client_->IsOpen()) {
-      client_ = avd::SharedFD::SocketLocalClient(
-          "gce_metadata", true, SOCK_STREAM);
-
-      if (!client_->IsOpen()) {
-        LOG(ERROR) << "Couldn't connect to metadata proxy.";
-        return false;
-      }
-    }
-
-    int32_t length;
-    client_->Read(&length, sizeof(length));
-
-    if ((length < 0) || (length > (1 << 20))) {
-      LOG(ERROR) << "Invalid metadata length: " << length;
-      client_->Close();
-      return false;
-    }
-
-    buffer->Resize(length);
-    client_->Read(buffer->data(), length);
-    buffer->Resize(length + 1);
-    return true;
-  }
-
- private:
-  avd::SharedFD client_;
-};
-}  // namespace
-
-MetadataQuery* MetadataQuery::New() {
-  return new MetadataQueryImpl();
-}
-
diff --git a/common/libs/metadata/metadata_query.h b/common/libs/metadata/metadata_query.h
deleted file mode 100644
index 77ceeb3..0000000
--- a/common/libs/metadata/metadata_query.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_METADATA_QUERY_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_METADATA_QUERY_H_
-
-#include "common/libs/auto_resources/auto_resources.h"
-
-static const int kMaxMetadataResponseBufferSize = 65536;
-
-class MetadataQuery {
- public:
-  virtual ~MetadataQuery() {}
-
-  // Request metadata from the server.
-  // On success returns true, and current metadata in supplied buffer.
-  virtual bool QueryServer(AutoFreeBuffer* buffer) = 0;
-
-  static MetadataQuery* New();
-};
-
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_METADATA_METADATA_QUERY_H_
diff --git a/common/libs/net/Android.bp b/common/libs/net/Android.bp
new file mode 100644
index 0000000..8bfebdc
--- /dev/null
+++ b/common/libs/net/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "cuttlefish_net",
+    srcs: [
+        "netlink_client.cpp",
+        "netlink_request.cpp",
+        "network_interface_manager.cpp",
+    ],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    defaults: ["cuttlefish_host_and_guest"],
+}
diff --git a/common/libs/net/netlink_client.cpp b/common/libs/net/netlink_client.cpp
new file mode 100644
index 0000000..884daca
--- /dev/null
+++ b/common/libs/net/netlink_client.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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/libs/net/netlink_client.h"
+
+#include <errno.h>
+#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+#include <sys/socket.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/glog/logging.h"
+
+namespace cvd {
+namespace {
+// NetlinkClient implementation.
+// Talks to libnetlink to apply network changes.
+class NetlinkClientImpl : public NetlinkClient {
+ public:
+  NetlinkClientImpl() = default;
+  virtual ~NetlinkClientImpl() = default;
+
+  virtual bool Send(const NetlinkRequest& message);
+
+  // Initialize NetlinkClient instance.
+  // Open netlink channel and initialize interface list.
+  // Parameter |type| specifies which netlink target to address, eg.
+  // NETLINK_ROUTE.
+  // Returns true, if initialization was successful.
+  bool OpenNetlink(int type);
+
+ private:
+  bool CheckResponse(uint32_t seq_no);
+
+  SharedFD netlink_fd_;
+  sockaddr_nl address_;
+};
+
+bool NetlinkClientImpl::CheckResponse(uint32_t seq_no) {
+  uint32_t len;
+  char buf[4096];
+  struct iovec iov = { buf, sizeof(buf) };
+  struct sockaddr_nl sa;
+  struct msghdr msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
+  struct nlmsghdr *nh;
+
+  int result = netlink_fd_->RecvMsg(&msg, 0);
+  if (result  < 0) {
+    LOG(ERROR) << "Netlink error: " << strerror(errno);
+    return false;
+  }
+
+  len = (uint32_t)result;
+  LOG(INFO) << "Received netlink response (" << len << " bytes)";
+
+  for (nh = (struct nlmsghdr*)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
+    if (nh->nlmsg_seq != seq_no) {
+      // This really shouldn't happen. If we see this, it means somebody is
+      // issuing netlink requests using the same socket as us, and ignoring
+      // responses.
+      LOG(WARNING) << "Sequence number mismatch: "
+                   << nh->nlmsg_seq << " != " << seq_no;
+      continue;
+    }
+
+    // This is the end of multi-part message.
+    // It indicates there's nothing more netlink wants to tell us.
+    // It also means we failed to find the response to our request.
+    if (nh->nlmsg_type == NLMSG_DONE)
+      break;
+
+    // This is the 'nlmsgerr' package carrying response to our request.
+    // It carries an 'error' value (errno) along with the netlink header info
+    // that caused this error.
+    if (nh->nlmsg_type == NLMSG_ERROR) {
+      nlmsgerr* err = reinterpret_cast<nlmsgerr*>(nh + 1);
+      if (err->error < 0) {
+        LOG(ERROR) << "Failed to complete netlink request: "
+                   << "Netlink error: " << err->error
+                   << ", errno: " << strerror(errno);
+        return false;
+      }
+      return true;
+    }
+  }
+
+  LOG(ERROR) << "No response from netlink.";
+  return false;
+}
+
+bool NetlinkClientImpl::Send(const NetlinkRequest& message) {
+  struct sockaddr_nl netlink_addr;
+  struct iovec netlink_iov = {
+    message.RequestData(),
+    message.RequestLength()
+  };
+  struct msghdr msg;
+  memset(&msg, 0, sizeof(msg));
+  memset(&netlink_addr, 0, sizeof(netlink_addr));
+
+  msg.msg_name = &address_;
+  msg.msg_namelen = sizeof(address_);
+  msg.msg_iov = &netlink_iov;
+  msg.msg_iovlen = sizeof(netlink_iov) / sizeof(iovec);
+
+  if (netlink_fd_->SendMsg(&msg, 0) < 0) {
+    LOG(ERROR) << "Failed to send netlink message: "
+               << strerror(errno);
+
+    return false;
+  }
+
+  return CheckResponse(message.SeqNo());
+}
+
+bool NetlinkClientImpl::OpenNetlink(int type) {
+  netlink_fd_ = SharedFD::Socket(AF_NETLINK, SOCK_RAW, type);
+  if (!netlink_fd_->IsOpen()) return false;
+
+  address_.nl_family = AF_NETLINK;
+  address_.nl_groups = 0;
+
+  netlink_fd_->Bind((struct sockaddr*)&address_, sizeof(address_));
+
+  return true;
+}
+
+class NetlinkClientFactoryImpl : public NetlinkClientFactory {
+ public:
+  NetlinkClientFactoryImpl() = default;
+  ~NetlinkClientFactoryImpl() override = default;
+
+  std::unique_ptr<NetlinkClient> New(int type) override {
+    auto client_raw = new NetlinkClientImpl();
+    // Use RVO when possible.
+    std::unique_ptr<NetlinkClient> client(client_raw);
+
+    if (!client_raw->OpenNetlink(type)) {
+      // Note: deletes client_raw.
+      client.reset();
+    }
+    return client;
+  }
+};
+
+}  // namespace
+
+NetlinkClientFactory* NetlinkClientFactory::Default() {
+  static NetlinkClientFactory &factory = *new NetlinkClientFactoryImpl();
+  return &factory;
+}
+
+}  // namespace cvd
diff --git a/common/libs/net/netlink_client.h b/common/libs/net/netlink_client.h
new file mode 100644
index 0000000..c8805c5
--- /dev/null
+++ b/common/libs/net/netlink_client.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+#ifndef COMMON_LIBS_NET_NETLINK_CLIENT_H_
+#define COMMON_LIBS_NET_NETLINK_CLIENT_H_
+
+#include <stddef.h>
+#include <memory>
+#include <string>
+#include "common/libs/net/netlink_request.h"
+
+namespace cvd {
+
+// Abstraction of Netlink client class.
+class NetlinkClient {
+ public:
+  NetlinkClient() {}
+  virtual ~NetlinkClient() {}
+
+  // Send netlink message to kernel.
+  virtual bool Send(const NetlinkRequest& message) = 0;
+
+ private:
+  NetlinkClient(const NetlinkClient&);
+  NetlinkClient& operator= (const NetlinkClient&);
+};
+
+class NetlinkClientFactory {
+ public:
+  // Create new NetlinkClient instance of a specified type.
+  // type can be any of the NETLINK_* types (eg. NETLINK_ROUTE).
+  virtual std::unique_ptr<NetlinkClient> New(int type) = 0;
+
+  static NetlinkClientFactory* Default();
+
+ protected:
+  NetlinkClientFactory() = default;
+  virtual ~NetlinkClientFactory() = default;
+};
+
+}  // namespace cvd
+
+#endif  // COMMON_LIBS_NET_NETLINK_CLIENT_H_
diff --git a/common/libs/net/netlink_request.cpp b/common/libs/net/netlink_request.cpp
new file mode 100644
index 0000000..ff5b9e9
--- /dev/null
+++ b/common/libs/net/netlink_request.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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/libs/net/netlink_request.h"
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "common/libs/glog/logging.h"
+
+namespace cvd {
+namespace {
+uint32_t kRequestSequenceNumber = 0;
+}  // namespace
+
+uint32_t NetlinkRequest::SeqNo() const {
+  return header_->nlmsg_seq;
+}
+
+void* NetlinkRequest::AppendRaw(const void* data, size_t length) {
+  void* out = request_.end();
+
+  request_.Append(data, length);
+  int pad = RTA_ALIGN(length) - length;
+  if (pad > 0) {
+    request_.Resize(request_.size() + pad);
+  }
+
+  return out;
+}
+
+void* NetlinkRequest::ReserveRaw(size_t length) {
+  void* out = request_.end();
+  request_.Resize(request_.size() + RTA_ALIGN(length));
+  return out;
+}
+
+nlattr* NetlinkRequest::AppendTag(
+    uint16_t type, const void* data, uint16_t data_length) {
+  nlattr* attr = Reserve<nlattr>();
+  attr->nla_type = type;
+  attr->nla_len = RTA_LENGTH(data_length);
+  AppendRaw(data, data_length);
+  return attr;
+}
+
+NetlinkRequest::NetlinkRequest(int32_t command, int32_t flags)
+    : request_(512),
+      header_(Reserve<nlmsghdr>()) {
+  flags |= NLM_F_ACK | NLM_F_REQUEST;
+  header_->nlmsg_flags = flags;
+  header_->nlmsg_type = command;
+  header_->nlmsg_pid = getpid();
+  header_->nlmsg_seq = kRequestSequenceNumber++;
+}
+
+NetlinkRequest::NetlinkRequest(NetlinkRequest&& other) {
+  using std::swap;
+  swap(lists_, other.lists_);
+  swap(header_, other.header_);
+  request_.Swap(other.request_);
+}
+
+void NetlinkRequest::AddString(uint16_t type, const std::string& value) {
+  AppendTag(type, value.c_str(), value.length() + 1);
+}
+
+void NetlinkRequest::AddInt32(uint16_t type, int32_t value) {
+  AppendTag(type, &value, sizeof(value));
+}
+
+void NetlinkRequest::AddInt8(uint16_t type, int8_t value) {
+  AppendTag(type, &value, sizeof(value));
+}
+
+void NetlinkRequest::AddIfInfo(int32_t if_index, bool operational) {
+  ifinfomsg* if_info = Reserve<ifinfomsg>();
+  if_info->ifi_family = AF_UNSPEC;
+  if_info->ifi_index = if_index;
+  if_info->ifi_flags = operational ? IFF_UP : 0;
+  if_info->ifi_change = IFF_UP;
+}
+
+void NetlinkRequest::AddAddrInfo(int32_t if_index) {
+  ifaddrmsg* ad_info = Reserve<ifaddrmsg>();
+  ad_info->ifa_family = AF_INET;
+  ad_info->ifa_prefixlen = 24;
+  ad_info->ifa_flags = IFA_F_PERMANENT | IFA_F_SECONDARY;
+  ad_info->ifa_scope = 0;
+  ad_info->ifa_index = if_index;
+}
+
+void NetlinkRequest::PushList(uint16_t type) {
+  int length = request_.size();
+  nlattr* list = AppendTag(type, NULL, 0);
+  lists_.push_back(std::make_pair(list, length));
+}
+
+void NetlinkRequest::PopList() {
+  if (lists_.empty()) {
+    LOG(ERROR) << "List pop with no lists left on stack.";
+    return;
+  }
+
+  std::pair<nlattr*, int> list = lists_.back();
+  lists_.pop_back();
+  list.first->nla_len = request_.size() - list.second;
+}
+
+void* NetlinkRequest::RequestData() const {
+  // Update request length before reporting raw data.
+  header_->nlmsg_len = request_.size();
+  return header_;
+}
+
+size_t NetlinkRequest::RequestLength() const {
+  return request_.size();
+}
+
+}  // namespace cvd
diff --git a/common/libs/net/netlink_request.h b/common/libs/net/netlink_request.h
new file mode 100644
index 0000000..953ea00
--- /dev/null
+++ b/common/libs/net/netlink_request.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+#ifndef COMMON_LIBS_NET_NETLINK_REQUEST_H_
+#define COMMON_LIBS_NET_NETLINK_REQUEST_H_
+
+#include <linux/netlink.h>
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "common/libs/auto_resources/auto_resources.h"
+
+namespace cvd {
+// Abstraction of Network link request.
+// Used to supply kernel with information about which interface needs to be
+// changed, and how.
+class NetlinkRequest {
+ public:
+  // Create new Netlink Request structure.
+  // Parameter |type| specifies netlink request type (eg. RTM_NEWLINK), while
+  // |flags| are netlink and request specific flags (eg. NLM_F_DUMP).
+  NetlinkRequest(int type, int flags);
+  NetlinkRequest(NetlinkRequest&& other);
+
+  ~NetlinkRequest() = default;
+
+  // Add an IFLA tag followed by a string.
+  // Returns true, if successful.
+  void AddString(uint16_t type, const std::string& value);
+
+  // Add an IFLA tag followed by int32.
+  // Returns true, if successful.
+  void AddInt32(uint16_t type, int32_t value);
+
+  // Add an IFLA tag followed by int8.
+  // Returns true, if successful.
+  void AddInt8(uint16_t type, int8_t value);
+
+  // Add an interface info structure.
+  // Parameter |if_index| specifies particular interface index to which the
+  // attributes following the IfInfo apply.
+  void AddIfInfo(int32_t if_index, bool is_operational);
+
+  // Add an address info to a specific interface.
+  // This method assumes the prefix length for address info is 24.
+  void AddAddrInfo(int32_t if_index);
+
+  // Creates new list.
+  // List mimmic recursive structures in a flat, contiuous representation.
+  // Each call to PushList() should have a corresponding call to PopList
+  // indicating end of sub-attribute list.
+  void PushList(uint16_t type);
+
+  // Indicates end of previously declared list.
+  void PopList();
+
+  // Request data.
+  void* RequestData() const;
+
+  // Request length.
+  size_t RequestLength() const;
+
+  // Request Sequence Number.
+  uint32_t SeqNo() const;
+
+  // Append raw data to buffer.
+  // data must not be null.
+  // Returns pointer to copied location.
+  void* AppendRaw(const void* data, size_t length);
+
+  // Reserve |length| number of bytes in the buffer.
+  // Returns pointer to reserved location.
+  void* ReserveRaw(size_t length);
+
+  // Append specialized data.
+  template <typename T> T* Append(const T& data) {
+    return static_cast<T*>(AppendRaw(&data, sizeof(T)));
+  }
+
+  // Reserve specialized data.
+  template <typename T> T* Reserve() {
+    return static_cast<T*>(ReserveRaw(sizeof(T)));
+  }
+
+ private:
+  nlattr* AppendTag(uint16_t type, const void* data, uint16_t length);
+
+  std::vector<std::pair<nlattr*, int32_t>> lists_;
+  AutoFreeBuffer request_;
+  nlmsghdr* header_;
+
+  NetlinkRequest(const NetlinkRequest&) = delete;
+  NetlinkRequest& operator= (const NetlinkRequest&) = delete;
+};
+}  // namespace cvd
+#endif  // COMMON_LIBS_NET_NETLINK_REQUEST_H_
diff --git a/common/libs/net/netlink_request_test.cpp b/common/libs/net/netlink_request_test.cpp
new file mode 100644
index 0000000..67b9923
--- /dev/null
+++ b/common/libs/net/netlink_request_test.cpp
@@ -0,0 +1,319 @@
+/*
+ * 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/libs/net/netlink_client.h"
+
+#include <linux/rtnetlink.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+
+#include <iostream>
+#include <memory>
+
+using ::testing::ElementsAreArray;
+using ::testing::MatchResultListener;
+using ::testing::Return;
+
+namespace cvd {
+namespace {
+extern "C" void klog_write(int /* level */, const char* /* format */, ...) {}
+
+// Dump hex buffer to test log.
+void Dump(MatchResultListener* result_listener, const char* title,
+          const uint8_t* data, size_t length) {
+  for (size_t item = 0; item < length;) {
+    *result_listener << title;
+    do {
+      result_listener->stream()->width(2);
+      result_listener->stream()->fill('0');
+      *result_listener << std::hex << +data[item] << " ";
+      ++item;
+    } while (item & 0xf);
+    *result_listener << "\n";
+  }
+}
+
+// Compare two memory areas byte by byte, print information about first
+// difference. Dumps both bufferst to user log.
+bool Compare(MatchResultListener* result_listener,
+             const uint8_t* exp, const uint8_t* act, size_t length) {
+  for (size_t index = 0; index < length; ++index) {
+    if (exp[index] != act[index]) {
+      *result_listener << "\nUnexpected data at offset " << index << "\n";
+      Dump(result_listener, "Data Expected: ", exp, length);
+      Dump(result_listener, "  Data Actual: ", act, length);
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// Matcher validating Netlink Request data.
+MATCHER_P2(RequestDataIs, data, length, "Matches expected request data") {
+  size_t offset = sizeof(nlmsghdr);
+  if (offset + length != arg.RequestLength()) {
+    *result_listener << "Unexpected request length: "
+                     << arg.RequestLength() - offset << " vs " << length;
+    return false;
+  }
+
+  // Note: Request begins with header (nlmsghdr). Header is not covered by this
+  // call.
+  const uint8_t* exp_data = static_cast<const uint8_t*>(
+      static_cast<const void*>(data));
+  const uint8_t* act_data = static_cast<const uint8_t*>(arg.RequestData());
+  return Compare(
+      result_listener, exp_data, &act_data[offset], length);
+}
+
+MATCHER_P4(RequestHeaderIs, length, type, flags, seq,
+           "Matches request header") {
+  nlmsghdr* header = static_cast<nlmsghdr*>(arg.RequestData());
+  if (arg.RequestLength() < sizeof(header)) {
+    *result_listener << "Malformed header: too short.";
+    return false;
+  }
+
+  if (header->nlmsg_len != length) {
+    *result_listener << "Invalid message length: "
+                     << header->nlmsg_len << " vs " << length;
+    return false;
+  }
+
+  if (header->nlmsg_type != type) {
+    *result_listener << "Invalid header type: "
+                     << header->nlmsg_type << " vs " << type;
+    return false;
+  }
+
+  if (header->nlmsg_flags != flags) {
+    *result_listener << "Invalid header flags: "
+                     << header->nlmsg_flags << " vs " << flags;
+    return false;
+  }
+
+  if (header->nlmsg_seq != seq) {
+    *result_listener << "Invalid header sequence number: "
+                     << header->nlmsg_seq << " vs " << seq;
+    return false;
+  }
+
+  return true;
+}
+}  // namespace
+
+class NetlinkClientTest : public ::testing::Test {
+  void SetUp() {
+    google::InstallFailureSignalHandler();
+  }
+ protected:
+  std::unique_ptr<NetlinkClient> nl_client_;
+};
+
+TEST_F(NetlinkClientTest, BasicStringNode) {
+  constexpr uint16_t kDummyTag = 0xfce2;
+  constexpr char kLongString[] = "long string";
+
+  struct {
+    // 11 bytes of text + padding 0 + 4 bytes of header.
+    const uint16_t attr_length = 0x10;
+    const uint16_t attr_type = kDummyTag;
+    char text[sizeof(kLongString)];  // sizeof includes padding 0.
+  } expected;
+
+  memcpy(&expected.text, kLongString, sizeof(kLongString));
+
+  cvd::NetlinkRequest request(RTM_SETLINK, 0);
+  request.AddString(kDummyTag, kLongString);
+  EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, BasicIntNode) {
+  // Basic { Dummy: Value } test.
+  constexpr uint16_t kDummyTag = 0xfce2;
+  constexpr int32_t kValue = 0x1badd00d;
+
+  struct {
+    const uint16_t attr_length = 0x8;  // 4 bytes of value + 4 bytes of header.
+    const uint16_t attr_type = kDummyTag;
+    const uint32_t attr_value = kValue;
+  } expected;
+
+  cvd::NetlinkRequest request(RTM_SETLINK, 0);
+  request.AddInt32(kDummyTag, kValue);
+  EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, SingleList) {
+  // List: { Dummy: Value}
+  constexpr uint16_t kDummyTag = 0xfce2;
+  constexpr uint16_t kListTag = 0xcafe;
+  constexpr int32_t kValue = 0x1badd00d;
+
+  struct {
+    const uint16_t list_length = 0xc;
+    const uint16_t list_type = kListTag;
+    const uint16_t attr_length = 0x8;  // 4 bytes of value + 4 bytes of header.
+    const uint16_t attr_type = kDummyTag;
+    const uint32_t attr_value = kValue;
+  } expected;
+
+  cvd::NetlinkRequest request(RTM_SETLINK, 0);
+  request.PushList(kListTag);
+  request.AddInt32(kDummyTag, kValue);
+  request.PopList();
+
+  EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, NestedList) {
+  // List1: { List2: { Dummy: Value}}
+  constexpr uint16_t kDummyTag = 0xfce2;
+  constexpr uint16_t kList1Tag = 0xcafe;
+  constexpr uint16_t kList2Tag = 0xfeed;
+  constexpr int32_t kValue = 0x1badd00d;
+
+  struct {
+    const uint16_t list1_length = 0x10;
+    const uint16_t list1_type = kList1Tag;
+    const uint16_t list2_length = 0xc;
+    const uint16_t list2_type = kList2Tag;
+    const uint16_t attr_length = 0x8;
+    const uint16_t attr_type = kDummyTag;
+    const uint32_t attr_value = kValue;
+  } expected;
+
+  cvd::NetlinkRequest request(RTM_SETLINK, 0);
+  request.PushList(kList1Tag);
+  request.PushList(kList2Tag);
+  request.AddInt32(kDummyTag, kValue);
+  request.PopList();
+  request.PopList();
+
+  EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, ListSequence) {
+  // List1: { Dummy1: Value1}, List2: { Dummy2: Value2 }
+  constexpr uint16_t kDummy1Tag = 0xfce2;
+  constexpr uint16_t kDummy2Tag = 0xfd38;
+  constexpr uint16_t kList1Tag = 0xcafe;
+  constexpr uint16_t kList2Tag = 0xfeed;
+  constexpr int32_t kValue1 = 0x1badd00d;
+  constexpr int32_t kValue2 = 0xfee1;
+
+  struct {
+    const uint16_t list1_length = 0xc;
+    const uint16_t list1_type = kList1Tag;
+    const uint16_t attr1_length = 0x8;
+    const uint16_t attr1_type = kDummy1Tag;
+    const uint32_t attr1_value = kValue1;
+    const uint16_t list2_length = 0xc;
+    const uint16_t list2_type = kList2Tag;
+    const uint16_t attr2_length = 0x8;
+    const uint16_t attr2_type = kDummy2Tag;
+    const uint32_t attr2_value = kValue2;
+  } expected;
+
+  cvd::NetlinkRequest request(RTM_SETLINK, 0);
+  request.PushList(kList1Tag);
+  request.AddInt32(kDummy1Tag, kValue1);
+  request.PopList();
+  request.PushList(kList2Tag);
+  request.AddInt32(kDummy2Tag, kValue2);
+  request.PopList();
+
+  EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, ComplexList) {
+  // List1: { List2: { Dummy1: Value1 }, Dummy2: Value2 }
+  constexpr uint16_t kDummy1Tag = 0xfce2;
+  constexpr uint16_t kDummy2Tag = 0xfd38;
+  constexpr uint16_t kList1Tag = 0xcafe;
+  constexpr uint16_t kList2Tag = 0xfeed;
+  constexpr int32_t kValue1 = 0x1badd00d;
+  constexpr int32_t kValue2 = 0xfee1;
+
+  struct {
+    const uint16_t list1_length = 0x18;
+    const uint16_t list1_type = kList1Tag;
+    const uint16_t list2_length = 0xc;  // Note, this only covers until kValue1.
+    const uint16_t list2_type = kList2Tag;
+    const uint16_t attr1_length = 0x8;
+    const uint16_t attr1_type = kDummy1Tag;
+    const uint32_t attr1_value = kValue1;
+    const uint16_t attr2_length = 0x8;
+    const uint16_t attr2_type = kDummy2Tag;
+    const uint32_t attr2_value = kValue2;
+  } expected;
+
+  cvd::NetlinkRequest request(RTM_SETLINK, 0);
+  request.PushList(kList1Tag);
+  request.PushList(kList2Tag);
+  request.AddInt32(kDummy1Tag, kValue1);
+  request.PopList();
+  request.AddInt32(kDummy2Tag, kValue2);
+  request.PopList();
+
+  EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, SimpleNetlinkCreateHeader) {
+  cvd::NetlinkRequest request(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL);
+  constexpr char kValue[] = "random string";
+  request.AddString(0, kValue);  // Have something to work with.
+
+  constexpr size_t kMsgLength =
+      sizeof(nlmsghdr) + sizeof(nlattr) + RTA_ALIGN(sizeof(kValue));
+  int base_seq = request.SeqNo();
+
+  EXPECT_THAT(request, RequestHeaderIs(
+      kMsgLength,
+      RTM_NEWLINK,
+      NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST,
+      base_seq));
+
+  cvd::NetlinkRequest request2(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL);
+  request2.AddString(0, kValue);  // Have something to work with.
+  EXPECT_THAT(request2, RequestHeaderIs(
+      kMsgLength,
+      RTM_NEWLINK,
+      NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST,
+      base_seq + 1));
+}
+
+TEST_F(NetlinkClientTest, SimpleNetlinkUpdateHeader) {
+  cvd::NetlinkRequest request(RTM_SETLINK, 0);
+  constexpr char kValue[] = "random string";
+  request.AddString(0, kValue);  // Have something to work with.
+
+  constexpr size_t kMsgLength =
+      sizeof(nlmsghdr) + sizeof(nlattr) + RTA_ALIGN(sizeof(kValue));
+  int base_seq = request.SeqNo();
+
+  EXPECT_THAT(request, RequestHeaderIs(
+      kMsgLength, RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK, base_seq));
+
+  cvd::NetlinkRequest request2(RTM_SETLINK, 0);
+  request2.AddString(0, kValue);  // Have something to work with.
+  EXPECT_THAT(request2, RequestHeaderIs(
+      kMsgLength, RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK, base_seq + 1));
+}
+
+}  // namespace cvd
diff --git a/common/libs/net/network_interface.h b/common/libs/net/network_interface.h
new file mode 100644
index 0000000..4005f00
--- /dev/null
+++ b/common/libs/net/network_interface.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+#ifndef GUEST_GCE_NETWORK_NETWORK_INTERFACE_H_
+#define GUEST_GCE_NETWORK_NETWORK_INTERFACE_H_
+
+#include <string>
+
+namespace cvd {
+
+// Abstraction of network interfaces.
+// This interface provides means to modify network interface parameters.
+class NetworkInterface {
+ public:
+  explicit NetworkInterface(size_t if_index)
+      : if_index_(if_index) {}
+
+  NetworkInterface() = default;
+  ~NetworkInterface() = default;
+
+  // Get network interface index.
+  size_t Index() const {
+    return if_index_;
+  }
+
+  // Set name of the network interface.
+  NetworkInterface& SetName(const std::string& new_name) {
+    name_ = new_name;
+    return *this;
+  }
+
+  // Get name of the network interface.
+  // Returns name, if previously set.
+  const std::string& Name() const {
+    return name_;
+  }
+
+  // Set operational state of the network interface (ie. whether interface is
+  // up).
+  NetworkInterface& SetOperational(bool is_operational) {
+    is_operational_ = is_operational;
+    return *this;
+  }
+
+  // Get operational state of the interface. Value of 'true' indicates interface
+  // should be 'up'.
+  bool IsOperational() const {
+    return is_operational_;
+  }
+
+  // Set IPv4 address of the network interface.
+  NetworkInterface& SetAddress(const std::string& address) {
+    ip_address_ = address;
+    return *this;
+  }
+
+  // Get IPv4 address of the network interface.
+  const std::string& Address() const {
+    return ip_address_;
+  }
+
+  // Set IPv4 broadcast address of the network interface.
+  NetworkInterface& SetBroadcastAddress(const std::string& address) {
+    bc_address_ = address;
+    return *this;
+  }
+
+  // Get IPv4 broadcast address of the network interface.
+  const std::string& BroadcastAddress() const {
+    return bc_address_;
+  }
+
+ private:
+  // Index of the network interface in the system table. 0 indicates new
+  // interface.
+  size_t if_index_ = 0;
+  // Name of the interface, e.g. "eth0".
+  std::string name_;
+  // Operational status, i.e. whether interface is up.
+  bool is_operational_ = false;
+  // IPv4 address of this interface.
+  std::string ip_address_;
+  // IPv4 broadcast address of this interface.
+  std::string bc_address_;
+
+  NetworkInterface(const NetworkInterface&);
+  NetworkInterface& operator= (const NetworkInterface&);
+};
+
+}  // namespace cvd
+
+#endif  // GUEST_GCE_NETWORK_NETWORK_INTERFACE_H_
diff --git a/common/libs/net/network_interface_manager.cpp b/common/libs/net/network_interface_manager.cpp
new file mode 100644
index 0000000..2e4d43f
--- /dev/null
+++ b/common/libs/net/network_interface_manager.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/libs/net/network_interface_manager.h"
+
+#include <arpa/inet.h>
+#include <linux/if_addr.h>
+#include <linux/if_link.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+
+#include <memory>
+
+#include "common/libs/glog/logging.h"
+#include "common/libs/net/network_interface.h"
+
+namespace cvd {
+namespace {
+NetlinkRequest BuildLinkRequest(
+    const NetworkInterface& interface) {
+  NetlinkRequest request(RTM_SETLINK, 0);
+  request.AddIfInfo(interface.Index(), interface.IsOperational());
+  if (!interface.Name().empty()) {
+    request.AddString(IFLA_IFNAME, interface.Name());
+  }
+
+  return request;
+}
+
+NetlinkRequest BuildAddrRequest(
+    const NetworkInterface& interface) {
+  NetlinkRequest request(RTM_NEWADDR, 0);
+  request.AddAddrInfo(interface.Index());
+  request.AddInt32(IFA_LOCAL, inet_addr(interface.Address().c_str()));
+  request.AddInt32(IFA_ADDRESS, inet_addr(interface.Address().c_str()));
+  request.AddInt32(IFA_BROADCAST,
+                    inet_addr(interface.BroadcastAddress().c_str()));
+
+  return request;
+}
+}  // namespace
+
+std::unique_ptr<NetworkInterfaceManager> NetworkInterfaceManager::New(
+    NetlinkClientFactory* nl_factory) {
+  std::unique_ptr<NetworkInterfaceManager> mgr;
+
+  if (nl_factory == NULL) {
+    nl_factory = NetlinkClientFactory::Default();
+  }
+
+  auto client = nl_factory->New(NETLINK_ROUTE);
+  if (client) {
+    mgr.reset(new NetworkInterfaceManager(std::move(client)));
+  }
+
+  return mgr;
+}
+
+NetworkInterfaceManager::NetworkInterfaceManager(
+    std::unique_ptr<NetlinkClient> nl_client)
+    : nl_client_(std::move(nl_client)) {}
+
+std::unique_ptr<NetworkInterface> NetworkInterfaceManager::Open(
+    const std::string& if_name) {
+  std::unique_ptr<NetworkInterface> iface;
+  // NOTE: do not replace this code with an IOCTL call.
+  // On SELinux enabled Androids, RILD is not permitted to execute an IOCTL
+  // and this call will fail.
+  const int32_t index = if_nametoindex(if_name.c_str());
+  if (index < 0) {
+    LOG(ERROR) << "Failed to get interface (" << if_name << ") index.";
+    return iface;
+  }
+
+  iface.reset(new NetworkInterface(index));
+  return iface;
+}
+
+bool NetworkInterfaceManager::ApplyChanges(const NetworkInterface& iface) {
+  if (!nl_client_->Send(BuildLinkRequest(iface))) return false;
+  // Terminate immediately if interface is down.
+  if (!iface.IsOperational()) return true;
+  return nl_client_->Send(BuildAddrRequest(iface));
+}
+
+}  // namespace cvd
+
diff --git a/common/libs/net/network_interface_manager.h b/common/libs/net/network_interface_manager.h
new file mode 100644
index 0000000..3d6ccc5
--- /dev/null
+++ b/common/libs/net/network_interface_manager.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+#ifndef COMMON_LIBS_NET_NETWORK_INTERFACE_MANAGER_H_
+#define COMMON_LIBS_NET_NETWORK_INTERFACE_MANAGER_H_
+
+#include <memory>
+#include <string>
+
+#include "common/libs/net/netlink_client.h"
+#include "common/libs/net/network_interface.h"
+
+namespace cvd {
+
+// Network interface manager class.
+// - Provides access for existing network interfaces,
+// - provides means to create new virtual interfaces.
+//
+// Example usage:
+//
+//   std::unique_ptr<NetlinkClient> client(NetlinkClient::GetDefault());
+//   NetworkInterfaceManager manager(client.get());
+//   std::unique_ptr<NetworkInterface> iface(manager.Open("eth0"));
+//
+class NetworkInterfaceManager {
+ public:
+  // Open existing network interface.
+  //
+  // NOTE: this method does not fill in any NetworkInterface details yet.
+  std::unique_ptr<NetworkInterface> Open(const std::string& if_name);
+
+  // Apply changes made to existing network interface.
+  // This method cannot be used to instantiate new network interfaces.
+  bool ApplyChanges(const NetworkInterface& interface);
+
+  // Create new connected pair of virtual (veth) interfaces.
+  // Supplied pair of interfaces describe both endpoints' properties.
+  bool CreateVethPair(const NetworkInterface& first,
+                      const NetworkInterface& second);
+
+  // Creates new NetworkInterfaceManager.
+  static std::unique_ptr<NetworkInterfaceManager> New(
+      NetlinkClientFactory* factory);
+
+ private:
+  NetworkInterfaceManager(std::unique_ptr<NetlinkClient> nl_client);
+
+  // Build (partial) netlink request.
+  bool BuildRequest(NetlinkRequest* request, const NetworkInterface& interface);
+
+  std::unique_ptr<NetlinkClient> nl_client_;
+
+  NetworkInterfaceManager(const NetworkInterfaceManager&);
+  NetworkInterfaceManager& operator= (const NetworkInterfaceManager&);
+};
+
+}  // namespace cvd
+
+#endif  // COMMON_LIBS_NET_NETWORK_INTERFACE_MANAGER_H_
diff --git a/common/libs/thread_safe_queue/thread_safe_queue.h b/common/libs/thread_safe_queue/thread_safe_queue.h
new file mode 100644
index 0000000..8662928
--- /dev/null
+++ b/common/libs/thread_safe_queue/thread_safe_queue.h
@@ -0,0 +1,80 @@
+#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 <mutex>
+#include <condition_variable>
+#include <deque>
+#include <utility>
+#include <iterator>
+
+namespace cvd {
+// Simple queue with Push and Pop capabilities.
+// If the max_elements argument is passed to the constructor, and Push is called
+// when the queue holds max_elements items, the max_elements_handler is called
+// with a pointer to the internal QueueImpl. The call is made while holding
+// the guarding mutex; operations on the QueueImpl will not interleave with
+// other threads calling Push() or Pop().
+// The QueueImpl type will be a SequenceContainer.
+template <typename T>
+class ThreadSafeQueue {
+ public:
+  using QueueImpl = std::deque<T>;
+  ThreadSafeQueue() = default;
+  explicit ThreadSafeQueue(std::size_t max_elements,
+                           std::function<void(QueueImpl*)> max_elements_handler)
+      : max_elements_{max_elements},
+        max_elements_handler_{std::move(max_elements_handler)} {}
+
+  T Pop() {
+    std::unique_lock<std::mutex> guard(m_);
+    while (items_.empty()) {
+      new_item_.wait(guard);
+    }
+    auto t = std::move(items_.front());
+    items_.pop_front();
+    return t;
+  }
+
+  void Push(T&& t) {
+    std::lock_guard<std::mutex> guard(m_);
+    DropItemsIfAtCapacity();
+    items_.push_back(std::move(t));
+    new_item_.notify_one();
+  }
+
+  void Push(const T& t) {
+    std::lock_guard<std::mutex> guard(m_);
+    DropItemsIfAtCapacity();
+    items_.push_back(t);
+    new_item_.notify_one();
+  }
+
+ private:
+  void DropItemsIfAtCapacity() {
+    if (max_elements_ && max_elements_ == items_.size()) {
+      max_elements_handler_(&items_);
+    }
+  }
+
+  std::mutex m_;
+  std::size_t max_elements_{};
+  std::function<void(QueueImpl*)> max_elements_handler_{};
+  std::condition_variable new_item_;
+  QueueImpl items_;
+};
+}  // namespace cvd
diff --git a/common/libs/threads/Android.bp b/common/libs/threads/Android.bp
new file mode 100644
index 0000000..dece9ae
--- /dev/null
+++ b/common/libs/threads/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test_host {
+    name: "cuttlefish_thread_test",
+    srcs: [
+        "cuttlefish_thread_test.cpp",
+    ],
+    shared_libs: [
+        "cuttlefish_time",
+        "libbase",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/common/libs/threads/BUILD b/common/libs/threads/BUILD
deleted file mode 100644
index ad3882e..0000000
--- a/common/libs/threads/BUILD
+++ /dev/null
@@ -1,27 +0,0 @@
-cc_library(
-    name = "threads",
-    srcs = [
-    ],
-    hdrs = [
-        "pthread.h",
-        "thunkers.h",
-    ],
-    deps = [
-        "//common/libs/time",
-    ],
-    visibility = [ "//visibility:public" ],
-)
-
-cc_test(
-    name = "threads_test",
-    srcs = [
-        "pthread_test.cpp",
-    ],
-    linkopts = [ "-lpthread" ],
-    deps = [
-        ":threads",
-        "//common/libs/time",
-        "@glog_repo//:glog",
-    ],
-)
-
diff --git a/common/libs/threads/pthread.h b/common/libs/threads/cuttlefish_thread.h
similarity index 91%
rename from common/libs/threads/pthread.h
rename to common/libs/threads/cuttlefish_thread.h
index ba774b5..54e5ceb 100644
--- a/common/libs/threads/pthread.h
+++ b/common/libs/threads/cuttlefish_thread.h
@@ -1,3 +1,4 @@
+#pragma once
 /*
  * Copyright (C) 2016 The Android Open Source Project
  *
@@ -13,10 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_PTHREAD_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_PTHREAD_H_
 
-// Concurreny classess for Cloud Android projects.
+// Concurreny classess for cuttlefish.
 //
 // These more or less mimic the interface of the C++ classes:
 //   Mutex is similar to std::mutex
@@ -32,7 +31,7 @@
 #include <pthread.h>
 #include "common/libs/time/monotonic_time.h"
 
-namespace avd {
+namespace cvd {
 
 class Mutex {
  friend class ConditionVariable;
@@ -95,7 +94,7 @@
     return pthread_cond_wait(&cond_, mutex_->GetMutex());
   }
 
-  int WaitUntil(const avd::time::MonotonicTimePoint& tp) {
+  int WaitUntil(const cvd::time::MonotonicTimePoint& tp) {
     struct timespec ts;
     tp.ToTimespec(&ts);
     return pthread_cond_timedwait(&cond_, mutex_->GetMutex(), &ts);
@@ -167,6 +166,4 @@
   ScopedThread& operator= (const ScopedThread&);
 };
 
-}  // namespace avd
-
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_PTHREAD_H_
+}  // namespace cvd
diff --git a/common/libs/threads/pthread_test.cpp b/common/libs/threads/cuttlefish_thread_test.cpp
similarity index 87%
rename from common/libs/threads/pthread_test.cpp
rename to common/libs/threads/cuttlefish_thread_test.cpp
index 5ae33db..513262e 100644
--- a/common/libs/threads/pthread_test.cpp
+++ b/common/libs/threads/cuttlefish_thread_test.cpp
@@ -13,18 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "common/libs/threads/pthread.h"
+#include "common/libs/threads/cuttlefish_thread.h"
 
-#include <glog/logging.h>
-
+#include <android-base/logging.h>
 #include "common/libs/threads/thunkers.h"
 #include "common/libs/time/monotonic_time.h"
 
-using avd::ConditionVariable;
-using avd::Mutex;
-using avd::ScopedThread;
-using avd::time::MonotonicTimePoint;
-using avd::time::Milliseconds;
+using cvd::ConditionVariable;
+using cvd::Mutex;
+using cvd::ScopedThread;
+using cvd::time::MonotonicTimePoint;
+using cvd::time::Milliseconds;
 
 static const int FINISHED = 100;
 
@@ -70,7 +69,7 @@
 
   void* FastThread() {
     mutex_.Lock();
-    LOG_IF(FATAL, busy_ != NULL);
+    CHECK(busy_ == NULL);
     busy_ = "FastThread";
     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
     stage_ = 1;
@@ -78,9 +77,9 @@
     mutex_.Unlock();
     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(10));
     mutex_.Lock();
-    LOG_IF(FATAL, busy_ != NULL);
+    CHECK(busy_ == NULL);
     busy_ = "FastThread";
-    LOG_IF(FATAL, stage_ != 2);
+    CHECK(stage_ == 2);
     stage_ = FINISHED;
     busy_ = NULL;
     mutex_.Unlock();
@@ -90,9 +89,9 @@
   void* SlowThread() {
     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(50));
     mutex_.Lock();
-    LOG_IF(FATAL, busy_ != NULL);
+    CHECK(busy_== NULL);
     busy_ = "SlowThread";
-    LOG_IF(FATAL, stage_ != 1);
+    CHECK(stage_ == 1);
     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
     stage_ = 2;
     busy_ = NULL;
@@ -135,12 +134,12 @@
     mutex_.Unlock();
     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
     mutex_.Lock();
-    LOG_IF(FATAL, signalled_ != 1);
+    CHECK(signalled_== 1);
     cond_.NotifyOne();
     mutex_.Unlock();
     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
     mutex_.Lock();
-    LOG_IF(FATAL, signalled_ != 2);
+    CHECK(signalled_ == 2);
     mutex_.Unlock();
     return NULL;
   }
@@ -186,7 +185,7 @@
     mutex_.Unlock();
     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
     mutex_.Lock();
-    LOG_IF(FATAL, signalled_ != 2);
+    CHECK(signalled_ == 2);
     mutex_.Unlock();
     return NULL;
   }
@@ -227,7 +226,7 @@
   void* SignalThread() {
     SleepUntil(start_ + Milliseconds(200));
     mutex_.Lock();
-    LOG_IF(FATAL, stage_ != 2);
+    CHECK(stage_ == 2);
     cond_.NotifyOne();
     stage_ = 3;
     mutex_.Unlock();
@@ -236,17 +235,17 @@
 
   void* WaitThread() {
     mutex_.Lock();
-    LOG_IF(FATAL, stage_ != 0);
+    CHECK(stage_ == 0);
     stage_ = 1;
     cond_.WaitUntil(start_ + Milliseconds(50));
     MonotonicTimePoint current(MonotonicTimePoint::Now());
-    LOG_IF(FATAL, Milliseconds(current - start_).count() < 50);
-    LOG_IF(FATAL, Milliseconds(current - start_).count() > 100);
+    CHECK(Milliseconds(current - start_).count() >= 50);
+    CHECK(Milliseconds(current - start_).count() <= 100);
     stage_ = 2;
     cond_.WaitUntil(start_ + Milliseconds(1000));
     current = MonotonicTimePoint::Now();
-    LOG_IF(FATAL, Milliseconds(current - start_).count() > 500);
-    LOG_IF(FATAL, stage_ != 3);
+    CHECK(Milliseconds(current - start_).count() <= 500);
+    CHECK(stage_ == 3);
     stage_ = FINISHED;
     mutex_.Unlock();
     return NULL;
@@ -258,9 +257,8 @@
   MonotonicTimePoint start_;
 };
 
-int main(int argc, char** argv) {
-  ::google::InitGoogleLogging(argv[0]);
-  ::google::LogToStderr();
+int main(int, char**argv) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
   MutexTest mt;
   mt.Run();
   NotifyOneTest nt1;
diff --git a/common/libs/threads/thread_annotations.h b/common/libs/threads/thread_annotations.h
new file mode 100644
index 0000000..3ba67e7
--- /dev/null
+++ b/common/libs/threads/thread_annotations.h
@@ -0,0 +1,79 @@
+#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.
+ */
+
+#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
+#define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x)   // no-op
+#endif
+
+#define CAPABILITY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY \
+      THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define GUARDED_BY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS \
+      THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
diff --git a/common/libs/time/Android.bp b/common/libs/time/Android.bp
new file mode 100644
index 0000000..a390f68
--- /dev/null
+++ b/common/libs/time/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "cuttlefish_time",
+    srcs: [
+        "monotonic_time.cpp",
+    ],
+    defaults: ["cuttlefish_host_and_guest"],
+}
+
+cc_test_host {
+    name: "monotonic_time_test",
+    srcs: [
+        "monotonic_time_test.cpp",
+    ],
+    shared_libs: [
+        "cuttlefish_time",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/common/libs/time/BUILD b/common/libs/time/BUILD
deleted file mode 100644
index 57c20cf..0000000
--- a/common/libs/time/BUILD
+++ /dev/null
@@ -1,23 +0,0 @@
-cc_library(
-    name = "time",
-    srcs = [
-        "monotonic_time.cpp",
-        "monotonic_time.h",
-    ],
-    hdrs = [
-        "monotonic_time.h",
-    ],
-    visibility = [ "//visibility:public" ],
-)
-
-cc_test(
-    name = "monotonic_time_test",
-    srcs = [
-        "monotonic_time_test.cpp",
-    ],
-    deps = [
-        ":time",
-        "@gtest_repo//:gtest_main",
-    ],
-)
-
diff --git a/common/libs/time/monotonic_time.cpp b/common/libs/time/monotonic_time.cpp
index b12a82f..1d7bbd8 100644
--- a/common/libs/time/monotonic_time.cpp
+++ b/common/libs/time/monotonic_time.cpp
@@ -15,7 +15,7 @@
  */
 #include "common/libs/time/monotonic_time.h"
 
-namespace avd {
+namespace cvd {
 namespace time {
 MonotonicTimePointFactory* MonotonicTimePointFactory::GetInstance() {
   static MonotonicTimePointFactory factory;
@@ -23,4 +23,4 @@
   return &factory;
 }
 }  // namespace time
-}  // namespace avd
+}  // namespace cvd
diff --git a/common/libs/time/monotonic_time.h b/common/libs/time/monotonic_time.h
index be8882d..1d61ab4 100644
--- a/common/libs/time/monotonic_time.h
+++ b/common/libs/time/monotonic_time.h
@@ -13,13 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_TIME_MONOTONIC_TIME_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_TIME_MONOTONIC_TIME_H_
+#pragma once
 
 #include <stdint.h>
 #include <time.h>
 
-namespace avd {
+namespace cvd {
 namespace time {
 
 static const int64_t kNanosecondsPerSecond = 1000000000;
@@ -280,7 +279,7 @@
 };
 
 }  // namespace time
-}  // namespace avd
+}  // namespace cvd
 
 /**
  * Legacy support for microseconds. Use MonotonicTimePoint in new code.
@@ -288,7 +287,6 @@
 static const int64_t kSecsToUsecs = static_cast<int64_t>(1000) * 1000;
 
 static inline int64_t get_monotonic_usecs() {
-  return avd::time::Microseconds(
-      avd::time::MonotonicTimePoint::Now().SinceEpoch()).count();
+  return cvd::time::Microseconds(
+      cvd::time::MonotonicTimePoint::Now().SinceEpoch()).count();
 }
-#endif  // CUTTLEFISH_COMMON_COMMON_LIBS_TIME_MONOTONIC_TIME_H_
diff --git a/common/libs/time/monotonic_time_test.cpp b/common/libs/time/monotonic_time_test.cpp
index a99be4b..b1c07c9 100644
--- a/common/libs/time/monotonic_time_test.cpp
+++ b/common/libs/time/monotonic_time_test.cpp
@@ -18,7 +18,7 @@
 #include <gtest/gtest.h>
 #include <algorithm>
 
-using avd::time::TimeDifference;
+using cvd::time::TimeDifference;
 
 class MonotonicTimeTest : public ::testing::Test {
  public:
diff --git a/common/libs/usbforward/protocol.h b/common/libs/usbforward/protocol.h
new file mode 100644
index 0000000..c55c3b6
--- /dev/null
+++ b/common/libs/usbforward/protocol.h
@@ -0,0 +1,153 @@
+/*
+ * 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 <stdint.h>
+
+namespace usb_forward {
+
+// Commands that can be executed over serial port.
+// Use magic value to avoid accidental interpretation of commonly seen numbers.
+enum Command : uint32_t {
+  // Get device list.
+  // Request format:
+  // - RequestHeader{}
+  // Response format:
+  // - ResponseHeader{}
+  // - int32_t(num_devices)
+  // - num_devices times:
+  //   - DeviceInfo{}
+  //   - DeviceInfo.num_interfaces times:
+  //     - InterfaceInfo{}
+  CmdDeviceList = 0xcfad0001,
+
+  // Attach specified device.
+  // Request format:
+  // - RequestHeader{}
+  // - AttachRequestHeader{}
+  // Response format:
+  // - ResponseHeader{}
+  CmdAttach,
+
+  // Execute command on attached USB device.
+  // Request format:
+  // - RequestHeader{}
+  // - ControlTransfer{}
+  // - if transfer direction is host -> device
+  //   - uint8_t[ControlTransfer.length] data
+  // Response format:
+  // - ResponseHeader{}
+  // - if transfer direction is device -> host
+  //   - int32_t(actual length)
+  //   - uint8_t[actual length] bytes
+  CmdControlTransfer,
+
+  // Execute transfer on attached USB device.
+  // Request format:
+  // - RequestHeader{}
+  // - DataTransfer{}
+  // - if transfer direction is host -> device
+  //   - uint8_t[DataTransfer.length] data
+  // Response format:
+  // - ResponseHeader{}
+  // - if transfer direction is host -> device
+  //   - int32_t(actual length)
+  //   - int32_t[actual length] bytes
+  CmdDataTransfer,
+
+  // Heartbeat is used to detect whether device is alive.
+  // This is a trivial request/response mechanism.
+  // Response status indicates whether server is ready.
+  // Request format:
+  // - RequestHeader{}
+  // Response format:
+  // - ResponseHeader{}
+  CmdHeartbeat,
+};
+
+// Status represents command execution result, using USB/IP compatible values.
+enum Status : uint32_t {
+  // StatusSuccess indicates successful command execution.
+  StatusSuccess = 0,
+
+  // StatusFailure indicates error during command execution.
+  StatusFailure = 1
+};
+
+struct RequestHeader {
+  Command command;
+  uint32_t tag;
+};
+
+struct ResponseHeader {
+  Status status;
+  uint32_t tag;
+};
+
+// DeviceInfo describes individual USB device that was found attached to the
+// bus.
+struct DeviceInfo {
+  uint16_t vendor_id;
+  uint16_t product_id;
+  uint16_t dev_version;
+  uint8_t dev_class;
+  uint8_t dev_subclass;
+  uint8_t dev_protocol;
+  uint8_t bus_id;
+  uint8_t dev_id;
+  uint8_t speed;
+  uint8_t num_configurations;
+  uint8_t num_interfaces;
+  uint8_t cur_configuration;
+} __attribute__((packed));
+
+// InterfaceInfo describes individual interface attached to a USB device.
+struct InterfaceInfo {
+  uint8_t if_class;
+  uint8_t if_subclass;
+  uint8_t if_protocol;
+  uint8_t if_reserved;
+} __attribute__((packed));
+
+// AttachRequest specifies which device on which bus needs to be attached.
+struct AttachRequest {
+  uint8_t bus_id;
+  uint8_t dev_id;
+} __attribute__((packed));
+
+// ControlTransfer specifies target bus and device along with USB request.
+struct ControlTransfer {
+  uint8_t bus_id;
+  uint8_t dev_id;
+  uint8_t type;
+  uint8_t cmd;
+  uint16_t value;
+  uint16_t index;
+  uint16_t length;
+  uint32_t timeout;
+} __attribute__((packed));
+
+// DataTransfer is used to exchange data between host and device.
+struct DataTransfer {
+  uint8_t bus_id;
+  uint8_t dev_id;
+  uint8_t endpoint_id;
+  uint8_t is_host_to_device;
+  int32_t length;
+  uint32_t timeout;
+} __attribute__((packed));
+
+}  // namespace usb_forward
diff --git a/common/libs/wifi/Android.bp b/common/libs/wifi/Android.bp
new file mode 100644
index 0000000..0ce06f1
--- /dev/null
+++ b/common/libs/wifi/Android.bp
@@ -0,0 +1,20 @@
+cc_library_shared {
+    name: "libcuttlefish_wifi",
+
+    srcs: [
+        "netlink.cc",
+        "nl_client.cc",
+        "cmd.cc",
+        "virtual_wifi.cc",
+        "wr_client.cc",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libbase",
+        "libnl",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"],
+}
diff --git a/common/libs/wifi/cmd.cc b/common/libs/wifi/cmd.cc
new file mode 100644
index 0000000..1a153cc
--- /dev/null
+++ b/common/libs/wifi/cmd.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/libs/wifi/cmd.h"
+
+namespace cvd {
+
+Cmd::Cmd() : msg_(nlmsg_alloc()) {}
+
+Cmd::~Cmd() {
+  for (auto& msg : responses_) {
+    nlmsg_free(msg);
+  }
+  nlmsg_free(msg_);
+}
+
+bool Cmd::OnResponse(nl_msg* msg) {
+  // nlmsg_get increases refcount on msg, but does not return the msg
+  // so we can't exactly use it as an argument to unique_ptr.
+  nlmsg_get(msg);
+  responses_.emplace_back(msg);
+  auto hdr = nlmsg_hdr(msg);
+
+  // Kernel documentation seems to be a bit misleading on this topic saying:
+  //
+  //     In multipart messages (multiple nlmsghdr headers with associated
+  //     payload in one byte stream) the first and all following headers have
+  //     the NLM_F_MULTI flag set, except for the last header which has the type
+  //     NLMSG_DONE.
+  //
+  // In theory, that would make processing multi-part messages simple, but in
+  // practice this does not seem to be true. Specifying exit criteria solely on
+  // NLM_F_MULTI flag setting will block some, if not all calls that dump
+  // NL80211 wifi interfaces for example.
+  if (!(hdr->nlmsg_flags & NLM_F_MULTI) || (hdr->nlmsg_type == NLMSG_DONE) ||
+      (hdr->nlmsg_type == NLMSG_ERROR)) {
+    std::lock_guard<std::mutex> lock(ready_mutex_);
+    ready_signal_.notify_all();
+    return true;
+  }
+
+  return false;
+}
+
+const std::vector<nl_msg*> Cmd::Responses() const {
+  std::unique_lock<std::mutex> lock(ready_mutex_);
+  ready_signal_.wait(lock, [this]() { return responses_.size() > 0; });
+  return responses_;
+}
+
+}  // namespace cvd
diff --git a/common/libs/wifi/cmd.h b/common/libs/wifi/cmd.h
new file mode 100644
index 0000000..f5d9da0
--- /dev/null
+++ b/common/libs/wifi/cmd.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <condition_variable>
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <netlink/msg.h>
+
+namespace cvd {
+constexpr int kWifiSimVersion = 1;
+
+class Cmd {
+ public:
+  Cmd();
+  ~Cmd();
+
+  // Cmd() creates netlink request to be sent to kernel.
+  // Returns netlink message header structure.
+  nl_msg* Msg() const { return msg_; }
+
+  // Responses() holds execution until netlink responds to this message.
+  // Returns all netlink replies.
+  const std::vector<nl_msg*> Responses() const;
+
+  // OnResponse() handles data response from netlink.
+  // Returns value indicating 'done' state:
+  // - false, if more data is expected, or
+  // - true, if processing is complete and instance can be disposed of.
+  bool OnResponse(nl_msg* msg);
+
+ private:
+  nl_msg* msg_;
+  std::vector<nl_msg*> responses_;
+
+  mutable std::mutex ready_mutex_;
+  mutable std::condition_variable ready_signal_;
+
+  Cmd(const Cmd&) = delete;
+  Cmd& operator=(const Cmd&) = delete;
+};
+
+}  // namespace cvd
diff --git a/common/libs/wifi/mac80211.h b/common/libs/wifi/mac80211.h
new file mode 100644
index 0000000..6435bcb
--- /dev/null
+++ b/common/libs/wifi/mac80211.h
@@ -0,0 +1,33 @@
+/*
+ * 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 <stdint.h>
+
+// The following definitions are required by uapi/mac80211_hwsim.h header.
+#define s8 int8_t
+#define u8 uint8_t
+#define BIT(x) (1 << (x))
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#include <uapi/mac80211_hwsim.h>
+
+#undef __packed
+#undef BIT
+#undef u8
+#undef s8
diff --git a/common/libs/wifi/netlink.cc b/common/libs/wifi/netlink.cc
new file mode 100644
index 0000000..143a585
--- /dev/null
+++ b/common/libs/wifi/netlink.cc
@@ -0,0 +1,95 @@
+/*
+ * 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/libs/wifi/netlink.h"
+
+#include <glog/logging.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/family.h>
+
+namespace cvd {
+namespace {
+constexpr char kWifiSimFamilyName[] = "MAC80211_HWSIM";
+constexpr char kNl80211FamilyName[] = "nl80211";
+}  // namespace
+
+Netlink::Netlink(const std::string& wifirouter_socket)
+    : genl_(NETLINK_GENERIC), rtnl_(NETLINK_ROUTE), wrcl_(wifirouter_socket) {}
+
+bool Netlink::Init() {
+  if (!genl_.Init()) {
+    LOG(ERROR) << "Could not open Netlink Generic.";
+    return false;
+  }
+
+  if (!rtnl_.Init()) {
+    LOG(ERROR) << "Could not open Netlink Route.";
+    return false;
+  }
+
+  if (!wrcl_.Init()) {
+    LOG(ERROR) << "Could not connect to Wifi Router.";
+    return false;
+  }
+
+  // Start the thread processing asynchronous netlink responses.
+  netlink_thread_.reset(new std::thread([this]() { HandleNetlinkMessages(); }));
+
+  // Query relevant netlink families:
+  // MAC80211 family allows us to create virtual radios and corresponding
+  // interfaces.
+  mac80211_hwsim_family_ = genl_ctrl_resolve(genl_.Sock(), kWifiSimFamilyName);
+  if (mac80211_hwsim_family_ < 0) {
+    LOG(ERROR) << "Could not find virtual wifi family. Please make sure module "
+               << "'mac80211_hwsim' is loaded on your system.";
+    return false;
+  }
+  LOG(INFO) << "MAC80211_HWSIM found with family id: "
+            << mac80211_hwsim_family_;
+
+  // NL80211 family allows us to find radios and corresponding interfaces.
+  nl80211_family_ = genl_ctrl_resolve(genl_.Sock(), kNl80211FamilyName);
+  if (nl80211_family_ < 0) {
+    LOG(ERROR) << "Could not find nl80211 family. WIFI stack is unavailable.";
+    return false;
+  }
+  LOG(INFO) << "NL80211 found with family id: " << nl80211_family_;
+
+  return true;
+}
+
+void Netlink::HandleNetlinkMessages() {
+  fd_set nlfds;
+  int genl_fd = nl_socket_get_fd(GeNL().Sock());
+  int rtnl_fd = nl_socket_get_fd(RtNL().Sock());
+  int wrcl_fd = wrcl_.Sock();
+
+  int max_fd = std::max({genl_fd, rtnl_fd, wrcl_fd}) + 1;
+  while (true) {
+    FD_ZERO(&nlfds);
+    FD_SET(genl_fd, &nlfds);
+    FD_SET(rtnl_fd, &nlfds);
+    FD_SET(wrcl_fd, &nlfds);
+
+    int res = select(max_fd, &nlfds, nullptr, nullptr, nullptr);
+    if (res <= 0) continue;
+
+    if (FD_ISSET(genl_fd, &nlfds)) nl_recvmsgs_default(GeNL().Sock());
+    if (FD_ISSET(rtnl_fd, &nlfds)) nl_recvmsgs_default(RtNL().Sock());
+    if (FD_ISSET(wrcl_fd, &nlfds)) wrcl_.HandleResponses();
+  }
+}
+
+}  // namespace cvd
diff --git a/common/libs/wifi/netlink.h b/common/libs/wifi/netlink.h
new file mode 100644
index 0000000..20b13c1
--- /dev/null
+++ b/common/libs/wifi/netlink.h
@@ -0,0 +1,69 @@
+/*
+ * 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 <memory>
+#include <thread>
+
+#include <netlink/genl/genl.h>
+#include "common/libs/wifi/nl_client.h"
+#include "common/libs/wifi/wr_client.h"
+
+namespace cvd {
+// Netlink provides access to relevant netlink backends and resources.
+class Netlink {
+ public:
+  Netlink(const std::string& wifirouter_socket);
+  ~Netlink() = default;
+
+  // Initialize instance of Netlink Factory.
+  bool Init();
+
+  // Getter for NETLINK_GENERIC NlClient instance.
+  NlClient& GeNL() { return genl_; }
+
+  // Getter for NETLINK_ROUTE NlClient instance.
+  NlClient& RtNL() { return rtnl_; }
+
+  WRClient& WRCL() { return wrcl_; }
+
+  // Access Family ID for MAC80211 (WIFI Simulator).
+  int FamilyMAC80211() const { return mac80211_hwsim_family_; }
+
+  // Access Family ID for NL80211 (WIFI management).
+  int FamilyNL80211() const { return nl80211_family_; }
+
+ private:
+  // Loop and process all incoming netlink messages.
+  // This function will trigger calls to NlClient's OnResponse() which handles
+  // incoming netlink messages.
+  void HandleNetlinkMessages();
+
+  NlClient genl_;
+  NlClient rtnl_;
+  WRClient wrcl_;
+
+  int mac80211_hwsim_family_ = 0;
+  int router_family_ = 0;
+  int nl80211_family_ = 0;
+
+  std::unique_ptr<std::thread> netlink_thread_;
+
+  Netlink(const Netlink&) = delete;
+  Netlink& operator=(const Netlink&) = delete;
+};
+
+}  // namespace cvd
diff --git a/common/libs/wifi/nl_client.cc b/common/libs/wifi/nl_client.cc
new file mode 100644
index 0000000..5588e3d
--- /dev/null
+++ b/common/libs/wifi/nl_client.cc
@@ -0,0 +1,95 @@
+/*
+ * 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/libs/wifi/nl_client.h"
+
+#include <glog/logging.h>
+
+namespace cvd {
+
+NlClient::NlClient(int nl_type)
+    : nl_type_(nl_type),
+      callback_(nullptr, [](nl_cb* cb) { free(cb); }),
+      sock_(nullptr, [](nl_sock* sock) { free(sock); }) {}
+
+bool NlClient::Init() {
+  // Set up netlink callbacks.
+  callback_.reset(nl_cb_alloc(NL_CB_CUSTOM));
+  if (!callback_) {
+    LOG(ERROR) << "Could not create netlink callback.";
+    return false;
+  }
+
+  // Register callback that will receive asynchronous messages from netlink.
+  nl_cb_set(callback_.get(), NL_CB_MSG_IN, NL_CB_CUSTOM,
+            [](nl_msg* msg, void* data) {
+              NlClient* self = static_cast<NlClient*>(data);
+              return self->OnResponse(msg);
+            },
+            this);
+
+  // Open Netlink target.
+  sock_.reset(nl_socket_alloc_cb(callback_.get()));
+  if (!sock_) {
+    LOG(ERROR) << "Could not create netlink socket. Are you root?";
+    return false;
+  }
+
+  if (nl_connect(sock_.get(), nl_type_) < 0) {
+    LOG(ERROR) << "Could not connect to netlink. Are you root?";
+    return false;
+  }
+
+  return true;
+}
+
+void NlClient::Send(Cmd* msg) {
+  std::lock_guard<std::mutex> guard(in_flight_mutex_);
+  // nl_send_auto sets sequence number (if defaults to NL_AUTO_SEQ).
+  // Make sure to execute this while in critical section to ensure we have time
+  // to set up callback before we receive response.
+  nl_send_auto(sock_.get(), msg->Msg());
+  auto seq = nlmsg_hdr(msg->Msg())->nlmsg_seq;
+  in_flight_[seq] = msg;
+}
+
+// Handle asynchronous messages & responses from netlink.
+int NlClient::OnResponse(nl_msg* msg) {
+  nlmsghdr* header = nlmsg_hdr(msg);
+  int seq = header->nlmsg_seq;
+
+  // Find & invoke corresponding callback, if any.
+  std::lock_guard<std::mutex> guard(in_flight_mutex_);
+  auto pos = in_flight_.find(seq);
+  if (pos != in_flight_.end()) {
+    if (pos->second->OnResponse(msg)) {
+      // Erase command if reports it's done.
+      in_flight_.erase(seq);
+    }
+  } else if (default_handler_) {
+    default_handler_(msg);
+  }
+
+  return NL_OK;
+}
+
+void NlClient::SetDefaultHandler(std::function<void(nl_msg*)> cb) {
+  std::lock_guard<std::mutex> guard(in_flight_mutex_);
+  default_handler_ = std::move(cb);
+}
+
+nl_sock* NlClient::Sock() const { return sock_.get(); }
+
+}  // namespace cvd
diff --git a/common/libs/wifi/nl_client.h b/common/libs/wifi/nl_client.h
new file mode 100644
index 0000000..2b5a57f
--- /dev/null
+++ b/common/libs/wifi/nl_client.h
@@ -0,0 +1,66 @@
+/*
+ * 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 <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include <netlink/genl/genl.h>
+
+#include "common/libs/wifi/cmd.h"
+
+namespace cvd {
+
+class NlClient {
+ public:
+  NlClient(int nl_type);
+  ~NlClient() = default;
+
+  // Init this client: set up callback & open socket.
+  bool Init();
+
+  // Get netlink socket used for sending and receiving messages.
+  nl_sock* Sock() const;
+
+  // Send message to netlink. Supplied callback will be invoked when response is
+  // received.
+  void Send(Cmd* msg);
+
+  // Set callback receiving all asynchronous messages and responses that do not
+  // have any proper recipient.
+  // This is useful in situations, where netlink sends asynchronous event
+  // notifications, such as new MAC80211 HWSIM frame.
+  void SetDefaultHandler(std::function<void(nl_msg*)> cb);
+
+ private:
+  // Receive & dispatch netlink response.
+  int OnResponse(nl_msg* msg);
+
+  int nl_type_;
+
+  std::unique_ptr<nl_cb, void (*)(nl_cb*)> callback_;
+  std::unique_ptr<nl_sock, void (*)(nl_sock*)> sock_;
+  std::mutex in_flight_mutex_;
+  std::map<uint32_t, Cmd*> in_flight_;
+  std::function<void(nl_msg*)> default_handler_;
+
+  NlClient(const NlClient&) = delete;
+  NlClient& operator=(const NlClient&) = delete;
+};
+
+}  // namespace cvd
diff --git a/common/libs/wifi/router.h b/common/libs/wifi/router.h
new file mode 100644
index 0000000..9a5ab93
--- /dev/null
+++ b/common/libs/wifi/router.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+namespace cvd {
+// Commands recognized by WIFIRouter netlink family.
+enum {
+  // WIFIROUTER_CMD_REGISTER is used by client to request notifications for
+  // packets sent from an interface with specific MAC address. Recognized
+  // attributes:
+  // - WIFIROUTER_ATTR_MAC - MAC address (byte array) of interface to receive
+  //   notifications for.
+  WIFIROUTER_CMD_REGISTER,
+
+  // WIFIROUTER_CMD_NOTIFY is issued by the server to notify clients for every
+  // new WIFIROUTER packet the client is registered for. Comes with attributes:
+  // - WIFIROUTER_ATTR_MAC - MAC address of interface that received packet,
+  // - WIFIROUTER_ATTR_PACKET - content of the MAC80211_HWSIM packet.
+  WIFIROUTER_CMD_NOTIFY,
+};
+
+// Attributes recognized by WIFIRouter netlink family.
+enum {
+  // Don't use attribute 0 to avoid parsing malformed message.
+  WIFIROUTER_ATTR_UNSPEC,
+
+  // MAC address representing interface from which the packet originated.
+  WIFIROUTER_ATTR_MAC,
+
+  // MAC80211_HWSIM packet content.
+  WIFIROUTER_ATTR_PACKET,
+
+  // Keep this last.
+  WIFIROUTER_ATTR_MAX
+};
+
+}  // namespace cvd
+
diff --git a/common/libs/wifi/virtual_wifi.cc b/common/libs/wifi/virtual_wifi.cc
new file mode 100644
index 0000000..365209e
--- /dev/null
+++ b/common/libs/wifi/virtual_wifi.cc
@@ -0,0 +1,295 @@
+/*
+ * 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/libs/wifi/virtual_wifi.h"
+
+#include <fstream>
+
+#include <glog/logging.h>
+#include <linux/nl80211.h>
+#include <netlink/genl/ctrl.h>
+
+#include "common/libs/wifi/cmd.h"
+#include "common/libs/wifi/mac80211.h"
+#include "common/libs/wifi/router.h"
+
+namespace cvd {
+namespace {
+// Create new HWSIM Radio.
+// Returns newly created HWSIM radio number, or negative errno code.
+int CreateHWSIM(Netlink* nl, const std::string& wiphy_name) {
+  Cmd msg;
+
+  if (!genlmsg_put(msg.Msg(), NL_AUTO_PID, NL_AUTO_SEQ, nl->FamilyMAC80211(), 0,
+                   NLM_F_REQUEST, HWSIM_CMD_NEW_RADIO, kWifiSimVersion) ||
+      nla_put_string(msg.Msg(), HWSIM_ATTR_RADIO_NAME, wiphy_name.c_str()) ||
+      nla_put_flag(msg.Msg(), HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE)) {
+    LOG(ERROR) << "Could not create new radio request.";
+    return -1;
+  }
+
+  nl->GeNL().Send(&msg);
+
+  // Responses() pauses until netlink responds to previously sent message.
+  for (auto* r : msg.Responses()) {
+    auto hdr = nlmsg_hdr(r);
+    if (hdr->nlmsg_type == NLMSG_ERROR) {
+      nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
+      return err->error;
+    }
+  }
+
+  LOG(ERROR) << "Unknown or no response from netlink.";
+  return -1;
+}
+
+// Destroy existing HWSIM Radio.
+int DeleteHWSIM(Netlink* nl, int hwsim_number) {
+  Cmd msg;
+
+  if (!genlmsg_put(msg.Msg(), NL_AUTO_PID, NL_AUTO_SEQ, nl->FamilyMAC80211(), 0,
+                   NLM_F_REQUEST, HWSIM_CMD_DEL_RADIO, kWifiSimVersion) ||
+      nla_put_u32(msg.Msg(), HWSIM_ATTR_RADIO_ID, hwsim_number)) {
+    LOG(ERROR) << "Could not create del radio request.";
+    return -1;
+  }
+
+  nl->GeNL().Send(&msg);
+
+  // Responses() pauses until netlink responds to previously sent message.
+  for (auto* r : msg.Responses()) {
+    auto hdr = nlmsg_hdr(r);
+    if (hdr->nlmsg_type == NLMSG_ERROR) {
+      nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
+      return err->error;
+    }
+  }
+
+  LOG(ERROR) << "Unknown or no response from netlink.";
+  return -1;
+}
+
+// Get WIPHY index number associated with a specified name.
+// Note: WIPHY number is not the same as HWSIM number:
+// - the former identifies physical radio in the system,
+// - the latter identifies simulated radio in the system.
+// TODO(ender): we can get the interface number from sysfs, but
+// the information we receive from HWSIM is not enough to get the
+// wiphy # from the system directly. Update this when there is a better
+// way to acquire radio number.
+int GetWIPHYIndex(const std::string& wiphy_name) {
+  int number;
+  std::ifstream file("/sys/class/ieee80211/" + wiphy_name + "/index");
+  file >> number;
+  return number;
+}
+
+// Get WLAN interface index associated with specific WIPHY index.
+// Returns interface index (> 0) or negative value indicating errno code.
+int GetWiphyInterface(Netlink* nl, uint32_t wiphy_index) {
+  Cmd msg;
+
+  if (!genlmsg_put(msg.Msg(), NL_AUTO_PID, NL_AUTO_SEQ, nl->FamilyNL80211(), 0,
+                   NLM_F_REQUEST | NLM_F_DUMP, NL80211_CMD_GET_INTERFACE, 0)) {
+    LOG(ERROR) << "Could not create interface query.";
+    return -1;
+  }
+
+  nl->GeNL().Send(&msg);
+
+  // Responses() pauses until netlink responds to previously sent message.
+  for (auto* r : msg.Responses()) {
+    auto hdr = nlmsg_hdr(r);
+    if (hdr->nlmsg_type == NLMSG_ERROR) {
+      nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
+      LOG(ERROR) << "Could not query wiphy: " << strerror(-err->error);
+      return err->error;
+    }
+
+    // Last message in entire series.
+    if (hdr->nlmsg_type == NLMSG_DONE) break;
+
+    // !DONE && !ERROR => content.
+    // Decode attributes supplied by netlink.
+    // the genlmsg_parse puts each attribute in a respective slot in an array,
+    // so we have to preallocate enough space.
+    struct nlattr* attrs[NL80211_ATTR_MAX + 1];
+    auto err = genlmsg_parse(hdr, 0, attrs, NL80211_ATTR_MAX, nullptr);
+
+    // Return error if response could not be parsed. This is actually quite
+    // serious.
+    if (err < 0) {
+      LOG(ERROR) << "Could not process netlink response: " << strerror(-err);
+      return err;
+    }
+
+    // Check if we have WIPHY attribute in response -- and if it's the relevant
+    // one.
+    auto wiphy = attrs[NL80211_ATTR_WIPHY];
+    if (wiphy != nullptr && nla_get_u32(wiphy) == wiphy_index) {
+      auto number = attrs[NL80211_ATTR_IFINDEX];
+
+      if (number != nullptr) {
+        return nla_get_u32(number);
+      }
+    }
+  }
+
+  LOG(INFO) << "No interfaces found for wiphy " << wiphy_index;
+  return -1;
+}
+
+// Set WLAN interface name.
+// Uses Netlink Route to alter interface attributes (currently: name).
+bool SetWLANInterface(Netlink* nl, int iface_index, const std::string& name,
+                      const uint8_t* address) {
+  Cmd msg;
+
+  ifinfomsg ifm{};
+  ifm.ifi_index = iface_index;
+
+  if (!nlmsg_put(msg.Msg(), NL_AUTO_PID, NL_AUTO_SEQ, RTM_SETLINK, 0,
+                 NLM_F_REQUEST) ||
+      nlmsg_append(msg.Msg(), &ifm, sizeof(ifm), 0) ||
+      nla_put_string(msg.Msg(), IFLA_IFNAME, name.c_str()) ||
+      nla_put(msg.Msg(), IFLA_ADDRESS, MAX_ADDR_LEN, address)) {
+    LOG(ERROR) << "Could not create interface update.";
+    return false;
+  }
+
+  nl->RtNL().Send(&msg);
+
+  // Responses() pauses until netlink responds to previously sent message.
+  for (auto* r : msg.Responses()) {
+    auto hdr = nlmsg_hdr(r);
+    if (hdr->nlmsg_type == NLMSG_ERROR) {
+      nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
+      if (err->error < 0) {
+        LOG(ERROR) << "Failed to update iface " << iface_index
+                   << ": " << strerror(-err->error);
+      }
+      return err->error == 0;
+    }
+  }
+
+  LOG(ERROR) << "Unknown or no response from netlink.";
+  return -1;
+}
+
+bool RegisterForRouterNotifications(Netlink* nl, uint8_t* mac_addr) {
+  Cmd msg;
+
+  if (!genlmsg_put(msg.Msg(), NL_AUTO_PID, NL_AUTO_SEQ, 0, 0,
+                   NLM_F_REQUEST, WIFIROUTER_CMD_REGISTER, 0) ||
+      nla_put(msg.Msg(), WIFIROUTER_ATTR_MAC, MAX_ADDR_LEN, mac_addr)) {
+    LOG(ERROR) << "Could not create wifirouter register message.";
+    return false;
+  }
+
+  nl->WRCL().Send(&msg);
+
+  // Responses() pauses until netlink responds to previously sent message.
+  for (auto* r : msg.Responses()) {
+    auto hdr = nlmsg_hdr(r);
+    if (hdr->nlmsg_type == NLMSG_ERROR) {
+      nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
+      if (err->error < 0) {
+        LOG(ERROR) << "Failed to register with wifi router: "
+                   << strerror(err->error);
+      }
+      return err->error == 0;
+    }
+  }
+
+  LOG(ERROR) << "Unknown or no response from wifi router.";
+  return -1;
+}
+
+}  // namespace
+
+VirtualWIFI::~VirtualWIFI() {
+  LOG(INFO) << "Deleting virtual wifi: " << hwsim_number_;
+  if (hwsim_number_ > 0) {
+    auto res = DeleteHWSIM(nl_, hwsim_number_);
+    if (res < 0) {
+      LOG(ERROR) << "Could not delete radio: " << strerror(-res);
+    }
+    hwsim_number_ = 0;
+  }
+  LOG(INFO) << "Done.";
+}
+
+bool VirtualWIFI::Init() {
+  // Dummy variable is used with sscanf to determine mac address is well formed
+  // (that is: there's no trailing string content).
+  char dummy;
+
+  if (sscanf(addr_.c_str(), "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx%c",
+             &mac_addr_[0], &mac_addr_[1], &mac_addr_[2], &mac_addr_[3],
+             &mac_addr_[4], &mac_addr_[5], &dummy) != 6) {
+    LOG(ERROR) << "Malformed MAC address: " << addr_;
+    return false;
+  }
+
+  std::string phy = name_ + "phy";
+  // Each WLAN device consists of two sides:
+  // - WIPHY is the "radio" side,
+  // - WLAN is the "interface" side.
+  // Radios have more physical properties, while WLAN have more logical /
+  // interface properties. Each radio can have more than one WLAN.
+
+  // 1. Create new MAC80211 HWSIM radio.
+  LOG(INFO) << "Creating virtual radio: " << phy;
+  hwsim_number_ = CreateHWSIM(nl_, phy);
+  if (hwsim_number_ <= 0) {
+    LOG(ERROR) << "Could not create HWSIM: " << strerror(-hwsim_number_);
+    return false;
+  }
+
+  // 2. Acquire the WIPHY radio number created with HWSIM radio.
+  LOG(INFO) << "Querying WIPHY number for: " << phy;
+  wiphy_number_ = GetWIPHYIndex(phy);
+  if (wiphy_number_ <= 0) {
+    LOG(ERROR) << "Could not create WIPHY.";
+    return false;
+  }
+
+  // 3. Query interface index.
+  LOG(INFO) << "Querying WIFI number for: " << wiphy_number_;
+  iface_number_ = GetWiphyInterface(nl_, wiphy_number_);
+  if (iface_number_ <= 0) {
+    LOG(ERROR) << "Could not query interface details.";
+    return false;
+  }
+
+  // 4. Apply requested interface name.
+  LOG(INFO) << "Updating interface name to: " << name_;
+  if (!SetWLANInterface(nl_, iface_number_, name_, &mac_addr_[0])) {
+    LOG(ERROR) << "Could not update wlan interface name.";
+    return false;
+  }
+
+  // 5. Register with wifi router.
+  LOG(INFO) << "Registering for notifications for: " << addr_;
+  if (!RegisterForRouterNotifications(nl_, mac_addr_)) {
+    LOG(ERROR) << "Could not register with wifi router.";
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace cvd
diff --git a/common/libs/wifi/virtual_wifi.h b/common/libs/wifi/virtual_wifi.h
new file mode 100644
index 0000000..fac70e0
--- /dev/null
+++ b/common/libs/wifi/virtual_wifi.h
@@ -0,0 +1,80 @@
+/*
+ * 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 <memory>
+#include <string>
+
+#include <netinet/in.h>
+#include <linux/netdevice.h>
+
+#include "common/libs/wifi/netlink.h"
+
+namespace cvd {
+
+// VirtualWIFI is an abstraction of an (individual) virtual WLAN device.
+// A virtual WLAN is a composition of the three elements:
+// - HWSIM RADIO, or an instance of a virtual MAC80211 device; this instance is
+//   later used to determine origin of the 802.11 frames (ie. which virtual
+//   interface was used to send them),
+// - WIPHY, or Radio that is recognized by Linux kernel; these instances are
+//   *named* representations of the HWSIM radios and can be used to identify
+//   associated WLAN interface,
+// - WLAN, or WIFI Interface, which is directly used network stack and tools.
+//
+// Typically, Cuttlefish guests will run with just one VirtualWIFI instance, but
+// the host will need (typically) one per Guest instance. This is dictated by
+// the fact that at most one user-space daemon can listen for MAC80211 packets
+// at any given time.
+class VirtualWIFI {
+ public:
+  VirtualWIFI(Netlink* nl, const std::string& name, const std::string& macaddr)
+      : nl_(nl), name_(name), addr_(macaddr) {}
+  ~VirtualWIFI();
+
+  const uint8_t* MacAddr() const { return &mac_addr_[0]; }
+  const std::string& Name() const { return name_; }
+
+  bool Init();
+
+ private:
+  Netlink* nl_;
+  std::string name_;
+
+  // MAC address associated with primary WLAN interface.
+  // This is the only way to identify origin of the packets.
+  // Sadly, if MAC Address is altered manually at runtime, we
+  // will stop working.
+  std::string addr_;
+
+  // NOTE: this has to be MAX_ADDR_LEN, even if we occupy fewer bytes.
+  // Netlink requires this to be full length.
+  uint8_t mac_addr_[MAX_ADDR_LEN];
+
+  // HWSIM number is required to identify HWSIM device that we want destroyed
+  // when we no longer need it.
+  int hwsim_number_ = 0;
+
+  // WIPHY and WIFI interface numbers. Useful for local operations, such as
+  // renaming interface.
+  int wiphy_number_ = 0;
+  int iface_number_ = 0;
+
+  VirtualWIFI(const VirtualWIFI&) = delete;
+  VirtualWIFI& operator=(const VirtualWIFI&) = delete;
+};
+
+}  // namespace cvd
diff --git a/common/libs/wifi/wr_client.cc b/common/libs/wifi/wr_client.cc
new file mode 100644
index 0000000..65fd170
--- /dev/null
+++ b/common/libs/wifi/wr_client.cc
@@ -0,0 +1,103 @@
+/*
+ * 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/libs/wifi/wr_client.h"
+
+#include <glog/logging.h>
+
+namespace cvd {
+namespace {
+const int kMaxSupportedPacketSize = getpagesize();
+}  // namespace
+WRClient::WRClient(const std::string& address) : address_(address) {}
+
+bool WRClient::Init() {
+  // Sadly, we can't use SharedFD, because we need access to raw file
+  // descriptor.
+
+  struct sockaddr_un addr {};
+  addr.sun_family = AF_UNIX;
+  memcpy(addr.sun_path + 1, address_.c_str(), address_.size());
+  socklen_t len = offsetof(struct sockaddr_un, sun_path) + address_.size() + 1;
+  socket_ = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+  if (socket_ < 0) {
+    LOG(ERROR) << "socket() failed: " << strerror(errno);
+    return false;
+  }
+
+  auto res = connect(socket_, reinterpret_cast<sockaddr*>(&addr), len);
+  if (res < 0) {
+    LOG(ERROR) << "Could not connect to wifi router: " << strerror(errno);
+    return false;
+  }
+
+  return true;
+}
+
+void WRClient::Send(Cmd* msg) {
+  std::lock_guard<std::mutex> guard(in_flight_mutex_);
+  // Make sure to execute this while in critical section to ensure we have time
+  // to set up seq number & callback before we receive response.
+  auto hdr = nlmsg_hdr(msg->Msg());
+  int seq = in_flight_last_seq_++;
+  // Do not use 0 for sequence numbers. 0 is reserved for async notifications.
+  if (!in_flight_last_seq_) in_flight_last_seq_ = 1;
+
+  hdr->nlmsg_seq = seq;
+  send(socket_, hdr, hdr->nlmsg_len, MSG_NOSIGNAL);
+  in_flight_[seq] = msg;
+}
+
+// Handle asynchronous messages & responses from netlink.
+void WRClient::HandleResponses() {
+  std::unique_ptr<uint8_t[]> buf(new uint8_t[kMaxSupportedPacketSize]);
+
+  auto size = recv(socket_, buf.get(), kMaxSupportedPacketSize, 0);
+  if (size <= 0) {
+    LOG(FATAL) << "No data from WIFI Router - likely dead: " << strerror(errno);
+    return;
+  }
+
+  auto hdr = reinterpret_cast<nlmsghdr*>(buf.get());
+  if (size != hdr->nlmsg_len) {
+    LOG(FATAL) << "Malformed message from WIFI Router.";
+    return;
+  }
+
+  int seq = hdr->nlmsg_seq;
+  std::unique_ptr<nl_msg, void (*)(nl_msg*)> nlmsg(
+      nlmsg_convert(hdr), [](nl_msg* m) { nlmsg_free(m); });
+
+  // Find & invoke corresponding callback, if any.
+  std::lock_guard<std::mutex> guard(in_flight_mutex_);
+  auto pos = in_flight_.find(seq);
+  if (pos != in_flight_.end()) {
+    if (pos->second->OnResponse(nlmsg.get())) {
+      // Erase command if reports it's done.
+      in_flight_.erase(seq);
+    }
+  } else if (default_handler_) {
+    default_handler_(nlmsg.get());
+  }
+}
+
+void WRClient::SetDefaultHandler(std::function<void(nl_msg*)> cb) {
+  std::lock_guard<std::mutex> guard(in_flight_mutex_);
+  default_handler_ = std::move(cb);
+}
+
+int WRClient::Sock() const { return socket_; }
+
+}  // namespace cvd
diff --git a/common/libs/wifi/wr_client.h b/common/libs/wifi/wr_client.h
new file mode 100644
index 0000000..71a6438
--- /dev/null
+++ b/common/libs/wifi/wr_client.h
@@ -0,0 +1,68 @@
+/*
+ * 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 <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <netlink/msg.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/wifi/cmd.h"
+
+namespace cvd {
+
+class WRClient {
+ public:
+  WRClient(const std::string& socket_address);
+  ~WRClient() = default;
+
+  // Init this client: open socket to wifi router.
+  bool Init();
+
+  // Get wifirouter socket used for sending and receiving messages.
+  int Sock() const;
+
+  // Send message to wifi router.
+  void Send(Cmd* msg);
+
+  // Handle incoming responses from wifi router.
+  void HandleResponses();
+
+  // Set callback receiving all asynchronous messages and responses that do not
+  // have any proper recipient.
+  void SetDefaultHandler(std::function<void(nl_msg*)> cb);
+
+ private:
+  // Receive & dispatch netlink response.
+
+  std::string address_;
+  int socket_ = 0;
+  std::mutex in_flight_mutex_;
+  // Do not use 0 as a sequence number. 0 is reserved for asynchronous
+  // notifications.
+  int in_flight_last_seq_ = 1;
+  std::map<uint32_t, Cmd*> in_flight_;
+  std::function<void(nl_msg*)> default_handler_;
+
+  WRClient(const WRClient&) = delete;
+  WRClient& operator=(const WRClient&) = delete;
+};
+
+}  // namespace cvd
diff --git a/common/vsoc/lib/circqueue_impl.h b/common/vsoc/lib/circqueue_impl.h
new file mode 100644
index 0000000..154dbbb
--- /dev/null
+++ b/common/vsoc/lib/circqueue_impl.h
@@ -0,0 +1,222 @@
+#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 <string.h>
+
+#include "common/vsoc/shm/circqueue.h"
+#include "common/vsoc/lib/region_signaling_interface.h"
+
+namespace {
+// Increases the given index until it is naturally aligned for T.
+template <typename T>
+uintptr_t align(uintptr_t index) {
+  return (index + sizeof(T) - 1) & ~(sizeof(T) - 1);
+}
+}  // namespace
+
+namespace vsoc {
+class RegionSignalingInterface;
+namespace layout {
+
+template <uint32_t SizeLog2>
+void CircularQueueBase<SizeLog2>::CopyInRange(const char* buffer_in,
+                                              const Range& t) {
+  size_t bytes = t.end_idx - t.start_idx;
+  uint32_t index = t.start_idx & (BufferSize - 1);
+  if (index + bytes < BufferSize) {
+    memcpy(buffer_ + index, buffer_in, bytes);
+  } else {
+    size_t part1_size = BufferSize - index;
+    size_t part2_size = bytes - part1_size;
+    memcpy(buffer_ + index, buffer_in, part1_size);
+    memcpy(buffer_, buffer_in + part1_size, part2_size);
+  }
+}
+
+template <uint32_t SizeLog2>
+void CircularQueueBase<SizeLog2>::CopyOutRange(const Range& t,
+                                               char* buffer_out) {
+  uint32_t index = t.start_idx & (BufferSize - 1);
+  size_t total_size = t.end_idx - t.start_idx;
+  if (index + total_size <= BufferSize) {
+    memcpy(buffer_out, buffer_ + index, total_size);
+  } else {
+    uint32_t part1_size = BufferSize - index;
+    uint32_t part2_size = total_size - part1_size;
+    memcpy(buffer_out, buffer_ + index, part1_size);
+    memcpy(buffer_out + part1_size, buffer_, part2_size);
+  }
+}
+
+template <uint32_t SizeLog2>
+void CircularQueueBase<SizeLog2>::WaitForDataLocked(
+    RegionSignalingInterface* r) {
+  while (1) {
+    uint32_t o_w_pub = w_pub_;
+    // We don't have data. Wait until some appears and try again
+    if (r_released_ != o_w_pub) {
+      return;
+    }
+    lock_.Unlock();
+    r->WaitForSignal(&w_pub_, o_w_pub);
+    lock_.Lock();
+  }
+}
+
+template <uint32_t SizeLog2>
+intptr_t CircularQueueBase<SizeLog2>::WriteReserveLocked(
+    RegionSignalingInterface* r,
+    size_t bytes,
+    Range* t,
+                                                         bool non_blocking) {
+  // Can't write more than the buffer will hold
+  if (bytes > BufferSize) {
+    return -ENOSPC;
+  }
+  while (true) {
+    t->start_idx = w_pub_;
+    uint32_t o_r_release = r_released_;
+    size_t available = BufferSize - t->start_idx + o_r_release;
+    if (available >= bytes) {
+      break;
+    }
+    if (non_blocking) {
+      return -EWOULDBLOCK;
+    }
+    // If we can't write at the moment wait for a reader to release
+    // some bytes.
+    lock_.Unlock();
+    r->WaitForSignal(&r_released_, o_r_release);
+    lock_.Lock();
+  }
+  t->end_idx = t->start_idx + bytes;
+  return t->end_idx - t->start_idx;
+}
+
+template <uint32_t SizeLog2>
+intptr_t CircularByteQueue<SizeLog2>::Read(RegionSignalingInterface* r, char* buffer_out,
+                                           size_t max_size) {
+  this->lock_.Lock();
+  this->WaitForDataLocked(r);
+  Range t;
+  t.start_idx = this->r_released_;
+  t.end_idx = this->w_pub_;
+  // The lock is still held here...
+  // Trim the range if we got more than the reader wanted
+  if ((t.end_idx - t.start_idx) > max_size) {
+    t.end_idx = t.start_idx + max_size;
+  }
+  this->CopyOutRange(t, buffer_out);
+  this->r_released_ = t.end_idx;
+  this->lock_.Unlock();
+  layout::Sides side;
+  side.value_ = layout::Sides::Both;
+  r->SendSignal(side, &this->r_released_);
+  return t.end_idx - t.start_idx;
+}
+
+template <uint32_t SizeLog2>
+intptr_t CircularByteQueue<SizeLog2>::Write(
+    RegionSignalingInterface* r,
+    const char* buffer_in,
+    size_t bytes,
+    bool non_blocking) {
+  Range range;
+  this->lock_.Lock();
+  intptr_t rval = this->WriteReserveLocked(r, bytes, &range, non_blocking);
+  if (rval < 0) {
+    this->lock_.Unlock();
+    return rval;
+  }
+  this->CopyInRange(buffer_in, range);
+  // We can't publish until all of the previous write allocations where
+  // published.
+  this->w_pub_ = range.end_idx;
+  this->lock_.Unlock();
+  layout::Sides side;
+  side.value_ = layout::Sides::Both;
+  r->SendSignal(side, &this->w_pub_);
+  return bytes;
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::CalculateBufferedSize(
+    size_t payload) {
+  return align<uint32_t>(sizeof(uint32_t) + payload);
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::Read(RegionSignalingInterface* r,
+                                                            char* buffer_out,
+                                                            size_t max_size) {
+  this->lock_.Lock();
+  this->WaitForDataLocked(r);
+  uint32_t packet_size = *reinterpret_cast<uint32_t*>(
+      this->buffer_ + (this->r_released_ & (this->BufferSize - 1)));
+  if (packet_size > max_size) {
+    this->lock_.Unlock();
+    return -ENOSPC;
+  }
+  Range t;
+  t.start_idx = this->r_released_ + sizeof(uint32_t);
+  t.end_idx = t.start_idx + packet_size;
+  this->CopyOutRange(t, buffer_out);
+  this->r_released_ += this->CalculateBufferedSize(packet_size);
+  this->lock_.Unlock();
+  layout::Sides side;
+  side.value_ = layout::Sides::Both;
+  r->SendSignal(side, &this->r_released_);
+  return packet_size;
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::Write(
+    RegionSignalingInterface* r,
+    const char* buffer_in,
+    uint32_t bytes,
+    bool non_blocking) {
+  if (bytes > MaxPacketSize) {
+    return -ENOSPC;
+  }
+  Range range;
+  size_t buffered_size = this->CalculateBufferedSize(bytes);
+  this->lock_.Lock();
+  intptr_t rval =
+      this->WriteReserveLocked(r, buffered_size, &range, non_blocking);
+  if (rval < 0) {
+    this->lock_.Unlock();
+    return rval;
+  }
+  Range header = range;
+  header.end_idx = header.start_idx + sizeof(uint32_t);
+  uint32_t sizeof_uint32_t = sizeof(uint32_t);
+  Range payload{static_cast<uint32_t>(range.start_idx + sizeof(uint32_t)),
+                static_cast<uint32_t>(
+                    range.start_idx + sizeof(uint32_t) + bytes)};
+  this->CopyInRange(reinterpret_cast<const char*>(&bytes), header);
+  this->CopyInRange(buffer_in, payload);
+  this->w_pub_ = range.end_idx;
+  this->lock_.Unlock();
+  layout::Sides side;
+  side.value_ = layout::Sides::Both;
+  r->SendSignal(side, &this->w_pub_);
+  return bytes;
+}
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/lib/circqueue_test.cpp b/common/vsoc/lib/circqueue_test.cpp
new file mode 100644
index 0000000..34aca33
--- /dev/null
+++ b/common/vsoc/lib/circqueue_test.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <thread>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/mock_region_view.h"
+
+#define EXPECT_BLOCK(region, tid) \
+    EXPECT_TRUE(region.IsBlocking(tid))
+
+namespace {
+
+constexpr int kQueueSizeLog2 = 16;
+constexpr int kQueueCapacity = 1 << kQueueSizeLog2;
+constexpr int kMaxPacketSize = 1024;
+
+constexpr int kNumReadingThread = 5;
+constexpr int kNumWritingThread = 5;
+
+struct CircQueueTestRegionLayout : public vsoc::layout::RegionLayout {
+  vsoc::layout::CircularByteQueue<kQueueSizeLog2> byte_queue;
+  vsoc::layout::CircularPacketQueue<kQueueSizeLog2, kMaxPacketSize> packet_queue;
+};
+
+typedef vsoc::test::MockRegionView<CircQueueTestRegionLayout> CircQueueRegionView;
+
+class CircQueueTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    region_.Open();
+  }
+
+  CircQueueRegionView region_;
+};
+
+intptr_t ReadBytes(CircQueueRegionView* region, int bytes) {
+  char buffer_out[bytes];
+  CircQueueTestRegionLayout* layout = region->data();
+  return layout->byte_queue.Read(region, buffer_out, bytes);
+}
+
+intptr_t WriteBytes(CircQueueRegionView* region, int bytes) {
+  char buffer_in[bytes];
+  CircQueueTestRegionLayout* layout = region->data();
+  return layout->byte_queue.Write(region, buffer_in, bytes);
+}
+
+intptr_t ReadPacket(CircQueueRegionView* region, int max_size) {
+  char buffer_out[max_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  return layout->packet_queue.Read(region, buffer_out, max_size);
+}
+
+intptr_t WritePacket(CircQueueRegionView* region, int packet_size) {
+  char buffer_in[packet_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  return layout->packet_queue.Write(region, buffer_in, packet_size);
+}
+
+void ReadBytesInChunk(CircQueueRegionView* region, int total_size, int chuck_size) {
+  char buffer_out[chuck_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  int total_read = 0;
+  int remaining = total_size;
+  while (remaining >= chuck_size) {
+    int ret = layout->byte_queue.Read(region, buffer_out, chuck_size);
+    total_read += ret;
+    remaining -= ret;
+  }
+  if (remaining > 0) {
+    total_read += layout->byte_queue.Write(region, buffer_out, remaining);
+  }
+  EXPECT_EQ(total_read, total_size);
+}
+
+void WriteBytesInChunk(CircQueueRegionView* region, int total_size, int chuck_size) {
+  char buffer_in[chuck_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  int total_write = 0;
+  int remaining = total_size;
+  while (remaining >= chuck_size) {
+    int ret = layout->byte_queue.Write(region, buffer_in, chuck_size);
+    total_write += ret;
+    remaining -= ret;
+  }
+  if (remaining > 0) {
+    total_write += layout->byte_queue.Write(region, buffer_in, remaining);
+  }
+  EXPECT_EQ(total_write, total_size);
+}
+
+void ReadManyPackets(CircQueueRegionView* region, int num_packets, int packet_size) {
+  char buffer_out[packet_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  int total_read = 0;
+  int remaining = num_packets;
+  while (remaining > 0) {
+    int ret = layout->packet_queue.Read(region, buffer_out, packet_size);
+    total_read += ret;
+    remaining--;
+  }
+  EXPECT_EQ(total_read, num_packets * packet_size);
+}
+
+void WriteManyPackets(CircQueueRegionView* region, int num_packets, int packet_size) {
+  char buffer_in[packet_size];
+  CircQueueTestRegionLayout* layout = region->data();
+  int total_write = 0;
+  int remaining = num_packets;
+  while (remaining > 0) {
+    int ret = layout->packet_queue.Write(region, buffer_in, packet_size);
+    total_write += ret;
+    remaining--;
+  }
+  EXPECT_EQ(total_write, num_packets * packet_size);
+}
+
+// ByteQueue Tests
+
+// Test writing bytes
+TEST_F(CircQueueTest, ByteQueueSimpleWrite) {
+  const int num_bytes = 8;
+  EXPECT_EQ(num_bytes, WriteBytes(&this->region_, num_bytes));
+}
+
+// Test reading bytes
+TEST_F(CircQueueTest, ByteQueueSimpleRead) {
+  const int num_bytes = 8;
+  EXPECT_EQ(num_bytes, WriteBytes(&this->region_, num_bytes));
+  EXPECT_EQ(num_bytes, ReadBytes(&this->region_, num_bytes));
+}
+
+// Test reading on an empty queue. Expect blocking.
+TEST_F(CircQueueTest, ByteQueueReadOnEmpty) {
+  const int num_bytes = 8;
+
+  // Spawn a thread to read from queue. Expect it to block.
+  std::thread reading_thread(ReadBytes, &this->region_, num_bytes);
+  EXPECT_BLOCK(region_, reading_thread.get_id());
+
+  // Write expected bytes in so that we can clean up properly.
+  std::thread writing_thread(WriteBytes, &this->region_, num_bytes);
+  writing_thread.join();
+
+  reading_thread.join();
+}
+
+// Test writing on a full queue. Expect blocking.
+TEST_F(CircQueueTest, ByteQueueWriteOnFull) {
+  // Fill the queue.
+  const int capacity_bytes = kQueueCapacity;
+  EXPECT_EQ(capacity_bytes, WriteBytes(&this->region_, capacity_bytes));
+
+  // Now the queue is full, any further write would block.
+  const int num_bytes = 8;
+  std::thread writing_thread(WriteBytes, &this->region_, num_bytes);
+  EXPECT_BLOCK(region_, writing_thread.get_id());
+
+  // Read the extra bytes out so that we can clean up properly.
+  std::thread reading_thread(ReadBytes, &this->region_, num_bytes);
+  reading_thread.join();
+
+  writing_thread.join();
+}
+
+// Test if bytes being read out are the same as ones being written in.
+TEST_F(CircQueueTest, ByteQueueContentIntegrity) {
+  const int num_bytes = 8;
+  CircQueueTestRegionLayout* layout = this->region_.data();
+
+  char buffer_in[num_bytes] = {'a'};
+  layout->byte_queue.Write(&this->region_, buffer_in, num_bytes);
+
+  char buffer_out[num_bytes] = {'b'};
+  layout->byte_queue.Read(&this->region_, buffer_out, num_bytes);
+
+  for (int i=0; i<num_bytes; i++) {
+    EXPECT_EQ(buffer_in[i], buffer_out[i]);
+  }
+}
+
+// Test writing more bytes than capacity
+TEST_F(CircQueueTest, ByteQueueWriteTooManyBytes) {
+  const int extra_bytes = 8;
+  const int num_bytes = kQueueCapacity + extra_bytes;
+  EXPECT_EQ(-ENOSPC, WriteBytes(&this->region_, num_bytes));
+}
+
+// Test multiple bytes read/write
+TEST_F(CircQueueTest, ByteQueueMultipleReadWrite) {
+  const int chunk_size = 7;
+  const int total_size = 3.3 * kQueueCapacity;
+  std::vector<std::thread> reading_threads;
+  std::vector<std::thread> writing_threads;
+  for (int i=0; i<kNumReadingThread; i++) {
+    reading_threads.emplace_back(
+        std::thread(ReadBytesInChunk, &this->region_, total_size, chunk_size));
+  }
+  for (int i=0; i<kNumWritingThread; i++) {
+    writing_threads.emplace_back(
+        std::thread(WriteBytesInChunk, &this->region_, total_size, chunk_size));
+  }
+  std::for_each(reading_threads.begin(), reading_threads.end(), [](std::thread& t) { t.join(); });
+  std::for_each(writing_threads.begin(), writing_threads.end(), [](std::thread& t) { t.join(); });
+}
+
+// PacketQueue Tests
+
+// Test writing packet
+TEST_F(CircQueueTest, PacketQueueSimpleWrite) {
+  const int packet_size = 8;
+  EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
+}
+
+// Test reading packet
+TEST_F(CircQueueTest, PacketQueueSimpleRead) {
+  const int packet_size = 8;
+  EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
+  EXPECT_EQ(packet_size, ReadPacket(&this->region_, packet_size));
+}
+
+// Test reading on an empty queue. Expect blocking.
+TEST_F(CircQueueTest, PacketQueueReadOnEmpty) {
+  const int packet_size = 8;
+
+  // Spawn a thread to read from queue. Expect it to block.
+  std::thread reading_thread(ReadPacket, &this->region_, packet_size);
+  EXPECT_BLOCK(region_, reading_thread.get_id());
+
+  // Write expected bytes in so that we can clean up properly.
+  std::thread writing_thread(WritePacket, &this->region_, packet_size);
+  writing_thread.join();
+
+  reading_thread.join();
+}
+
+// Test writing on a full queue. Expect blocking.
+TEST_F(CircQueueTest, PacketQueueWriteOnFull) {
+  // Fill the queue.
+  const int packet_size = kMaxPacketSize;
+  int capacity_bytes = kQueueCapacity;
+  while (capacity_bytes >= packet_size) {
+    EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
+    capacity_bytes -= (packet_size + sizeof(uint32_t));
+  }
+
+  // Now the queue is full, any further write would block.
+  std::thread writing_thread(WritePacket, &this->region_, packet_size);
+  EXPECT_BLOCK(region_, writing_thread.get_id());
+
+  // Read the extra bytes out so that we can clean up properly.
+  std::thread reading_thread(ReadPacket, &this->region_, packet_size);
+  reading_thread.join();
+
+  writing_thread.join();
+}
+
+// Test if packet being read out are the same as one being written in.
+TEST_F(CircQueueTest, PacketQueueContentIntegrity) {
+  const int packet_size = 8;
+  CircQueueTestRegionLayout* layout = this->region_.data();
+
+  char buffer_in[packet_size] = {'a'};
+  layout->packet_queue.Write(&this->region_, buffer_in, packet_size);
+
+  char buffer_out[packet_size] = {'b'};
+  layout->packet_queue.Read(&this->region_, buffer_out, packet_size);
+
+  for (int i=0; i<packet_size; i++) {
+    EXPECT_EQ(buffer_in[i], buffer_out[i]);
+  }
+}
+
+// Test writing packet larger than capacity
+TEST_F(CircQueueTest, PacketQueueWriteTooLargePacket) {
+  const int extra_bytes = 8;
+  const int packet_size = kQueueCapacity + extra_bytes;
+  EXPECT_EQ(-ENOSPC, WritePacket(&this->region_, packet_size));
+}
+
+// Test reading packet larger than can handle
+TEST_F(CircQueueTest, PacketQueueReadTooLargePacket) {
+  const int extra_bytes = 8;
+  const int small_packet = 8;
+  const int large_packet = small_packet + extra_bytes;
+
+  WritePacket(&this->region_, large_packet);
+  char buffer_out[small_packet];
+  CircQueueTestRegionLayout* layout = this->region_.data();
+  EXPECT_EQ(-ENOSPC, layout->packet_queue.Read(&this->region_, buffer_out, small_packet));
+}
+
+// Test multiple packets read/write
+TEST_F(CircQueueTest, PacketQueueMultipleReadWrite) {
+  const int packet_size = kMaxPacketSize;
+  const int num_packets = 1.5 * (kQueueCapacity / packet_size);
+  std::vector<std::thread> reading_threads;
+  std::vector<std::thread> writing_threads;
+  for (int i=0; i<kNumReadingThread; i++) {
+    reading_threads.emplace_back(
+        std::thread(ReadManyPackets, &this->region_, num_packets, packet_size));
+  }
+  for (int i=0; i<kNumWritingThread; i++) {
+    writing_threads.emplace_back(
+        std::thread(WriteManyPackets, &this->region_, num_packets, packet_size));
+  }
+  std::for_each(reading_threads.begin(), reading_threads.end(), [](std::thread& t) { t.join(); });
+  std::for_each(writing_threads.begin(), writing_threads.end(), [](std::thread& t) { t.join(); });
+}
+
+}  // namespace
diff --git a/common/vsoc/lib/compat.cpp b/common/vsoc/lib/compat.cpp
new file mode 100644
index 0000000..3b8deb2
--- /dev/null
+++ b/common/vsoc/lib/compat.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/vsoc/lib/compat.h"
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+namespace vsoc {
+
+#ifdef CUTTLEFISH_HOST
+uint32_t gettid() {
+  thread_local uint32_t tid = syscall(SYS_gettid);
+  return tid;
+}
+#endif
+}
diff --git a/common/vsoc/lib/compat.h b/common/vsoc/lib/compat.h
new file mode 100644
index 0000000..8f3355c
--- /dev/null
+++ b/common/vsoc/lib/compat.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Utilities to make the host and the guest a bit more compatible
+
+#include <stdint.h>
+
+namespace vsoc {
+#ifdef CUTTLEFISH_HOST
+// Things that are missing on the host
+uint32_t gettid();
+#endif
+}
diff --git a/common/vsoc/lib/e2e_test_region_layout.cpp b/common/vsoc/lib/e2e_test_region_layout.cpp
new file mode 100644
index 0000000..67a5435
--- /dev/null
+++ b/common/vsoc/lib/e2e_test_region_layout.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/e2e_test_region_layout.h"
+
+namespace vsoc {
+namespace layout {
+namespace e2e_test {
+
+const char* E2EPrimaryTestRegionLayout::region_name = "e2e_primary";
+const char
+    E2EPrimaryTestRegionLayout::guest_pattern[E2EMemoryFill::kOwnedFieldSize] =
+        "primary guest e2e string";
+const char
+    E2EPrimaryTestRegionLayout::host_pattern[E2EMemoryFill::kOwnedFieldSize] =
+        "primary host e2e string";
+
+const char* E2ESecondaryTestRegionLayout::region_name = "e2e_secondary";
+const char E2ESecondaryTestRegionLayout::guest_pattern
+    [E2EMemoryFill::kOwnedFieldSize] = "secondary guest e2e string";
+const char
+    E2ESecondaryTestRegionLayout::host_pattern[E2EMemoryFill::kOwnedFieldSize] =
+        "secondary host e2e string";
+
+const char* E2EUnfindableRegionLayout::region_name = "e2e_must_not_exist";
+
+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/e2e_test_region_view.h b/common/vsoc/lib/e2e_test_region_view.h
new file mode 100644
index 0000000..079eb88
--- /dev/null
+++ b/common/vsoc/lib/e2e_test_region_view.h
@@ -0,0 +1,72 @@
+#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 "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/e2e_test_region_layout.h"
+
+namespace vsoc {
+template <typename Layout>
+class E2ERegionView : public vsoc::TypedRegionView<Layout> {
+ public:
+  const char* guest_string(size_t index) const {
+    return make_nonvolatile(this->data().data[index].guest_writable);
+  }
+
+  const char* host_string(size_t index) const {
+    return make_nonvolatile(this->data().data[index].host_writable);
+  }
+
+  bool set_guest_string(size_t index, const char* value) {
+    strcpy(make_nonvolatile(this->data()->data[index].guest_writable), value);
+    return true;
+  }
+
+  bool set_host_string(size_t index, const char* value) {
+    strcpy(make_nonvolatile(this->data()->data[index].host_writable), value);
+    return true;
+  }
+
+  size_t string_size() const {
+    return Layout::NumFillRecords(this->control_->region_data_size());
+  }
+
+  void guest_status(vsoc::layout::e2e_test::E2ETestStage stage) {
+    this->data()->guest_status.set_value(stage);
+  }
+
+  void host_status(vsoc::layout::e2e_test::E2ETestStage stage) {
+    this->data()->host_status.set_value(stage);
+  }
+
+ protected:
+  /**
+   * The string functions have problems with volatile pointers, so
+   * this function casts them away.
+   */
+  template <typename T>
+  static T* make_nonvolatile(volatile T* in) {
+    return (T*)in;
+  }
+};
+
+using E2EPrimaryRegionView =
+    vsoc::E2ERegionView<layout::e2e_test::E2EPrimaryTestRegionLayout>;
+using E2ESecondaryRegionView =
+    vsoc::E2ERegionView<layout::e2e_test::E2ESecondaryTestRegionLayout>;
+using E2EUnfindableRegionView =
+    vsoc::E2ERegionView<layout::e2e_test::E2EUnfindableRegionLayout>;
+};  // namespace vsoc
diff --git a/common/vsoc/lib/fb_bcast_layout.cpp b/common/vsoc/lib/fb_bcast_layout.cpp
new file mode 100644
index 0000000..41565d2
--- /dev/null
+++ b/common/vsoc/lib/fb_bcast_layout.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/shm/fb_bcast_layout.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace framebuffer {
+
+const char* FBBroadcastLayout::region_name = "fb_broadcast";
+
+}  // framebuffer_bcast
+}  // layout
+}  // vsoc
diff --git a/common/vsoc/lib/fb_bcast_region_view.cpp b/common/vsoc/lib/fb_bcast_region_view.cpp
new file mode 100644
index 0000000..3afd9a3
--- /dev/null
+++ b/common/vsoc/lib/fb_bcast_region_view.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/vsoc/lib/fb_bcast_region_view.h"
+
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/lib/lock_guard.h"
+
+using vsoc::framebuffer::FBBroadcastRegionView;
+
+// We can use a locking protocol because we decided that the streamer should
+// have more priority than the hwcomposer, so it's OK to block the hwcomposer
+// waiting for the streamer to complete, while the streamer will only block on
+// the hwcomposer when it's ran out of work to do and needs to get more from the
+// hwcomposer.
+void FBBroadcastRegionView::BroadcastNewFrame(uint32_t seq_num,
+                                          vsoc_reg_off_t frame_offset) {
+  {
+    auto lock_guard(make_lock_guard(&data()->bcast_lock));
+    data()->seq_num = seq_num;
+    data()->frame_offset = frame_offset;
+  }
+  // Signaling after releasing the lock may cause spurious wake ups.
+  // Signaling while holding the lock may cause the just-awaken listener to
+  // block immediately trying to acquire the lock.
+  // The former is less costly and slightly less likely to happen.
+  layout::Sides side;
+  side.value_ = layout::Sides::Both;
+  SendSignal(side, &data()->seq_num);
+}
+
+vsoc_reg_off_t FBBroadcastRegionView::WaitForNewFrameSince(uint32_t* last_seq_num) {
+  static std::unique_ptr<RegionWorker> worker = StartWorker();
+  // It's ok to read seq_num here without holding the lock because the lock will
+  // be acquired immediately after so we'll block if necessary to wait for the
+  // critical section in BroadcastNewFrame to complete.
+  // Also, the call to WaitForSignal receives a pointer to seq_num (so the
+  // compiler should not optimize it out) and includes a memory barrier
+  // (FUTEX_WAIT).
+  while (data()->seq_num == *last_seq_num) {
+    // Don't hold the lock when waiting for a signal, will deadlock.
+    WaitForSignal(&data()->seq_num, *last_seq_num);
+  }
+
+  {
+    auto lock_guard(make_lock_guard(&data()->bcast_lock));
+    *last_seq_num = data()->seq_num;
+    return data()->frame_offset;
+  }
+}
diff --git a/common/vsoc/lib/fb_bcast_region_view.h b/common/vsoc/lib/fb_bcast_region_view.h
new file mode 100644
index 0000000..26bc588
--- /dev/null
+++ b/common/vsoc/lib/fb_bcast_region_view.h
@@ -0,0 +1,75 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/fb_bcast_layout.h"
+#include "common/vsoc/shm/graphics.h"
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+namespace framebuffer {
+
+class FBBroadcastRegionView : public vsoc::TypedRegionView<
+                              vsoc::layout::framebuffer::FBBroadcastLayout> {
+ public:
+  // Screen width in pixels
+  int x_res() const {
+    return data().x_res;
+  }
+
+  // Screen height in pixels
+  int y_res() const {
+    return data().y_res;
+  }
+
+  // Dots per inch
+  int dpi() const {
+    return data().dpi;
+  }
+
+  // Refresh rate in Hertz
+  int refresh_rate_hz() const {
+    // TODO(jemoreira): region_->data()->refresh_rate_hz;
+    return kFbRefreshRateHz;
+  }
+
+  uint32_t pixel_format() const {
+    return kFbPixelFormat;
+  }
+
+  uint32_t bytes_per_pixel() const {
+    return vsoc::PixelFormatProperties<kFbPixelFormat>::bytes_per_pixel;
+  }
+
+  // Broadcasts a new frame. Meant to be used exclusively by the hwcomposer.
+  // Zero is an invalid frame_num, used to indicate that there is no frame
+  // available. It's expected that frame_num increases monotonically over time,
+  // but there is no hard requirement for this.
+  // frame_offset is the offset of the current frame in the gralloc region.
+  void BroadcastNewFrame(uint32_t frame_num, vsoc_reg_off_t frame_offset);
+
+  // Waits for a new frame (one with a different seq_num than last one we saw).
+  // Returns the offset of the new frame or zero if there was an error, stores
+  // the new sequential number in *last_seq_num.
+  vsoc_reg_off_t WaitForNewFrameSince(uint32_t* last_seq_num);
+
+ private:
+  static constexpr uint32_t kFbPixelFormat = vsoc::VSOC_PIXEL_FORMAT_RGBA_8888;
+  static constexpr int kFbRefreshRateHz = 60;
+};
+} // namespace framebuffer
+} // namespace vsoc
diff --git a/common/vsoc/lib/fb_bcast_region_view_test.cpp b/common/vsoc/lib/fb_bcast_region_view_test.cpp
new file mode 100644
index 0000000..0921058
--- /dev/null
+++ b/common/vsoc/lib/fb_bcast_region_view_test.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "fb_bcast_region_view.h"
+#include "host/libs/config/host_config.h"
+#include <stdio.h>
+
+using vsoc::framebuffer::FBBroadcastRegionView;
+
+int main() {
+  uint32_t frame_num = 0;
+  vsoc_reg_off_t offset = 0;
+  FBBroadcastRegionView region;
+#if defined(CUTTLEFISH_HOST)
+  auto rval = region.Open(vsoc::GetDomain().c_str());
+#else
+  auto rval = region.Open();
+#endif
+  if (!rval) {
+    fprintf(stderr, "Error opening region\n");
+    return 1;
+  }
+
+  while (1) {
+    offset = region.WaitForNewFrameSince(&frame_num);
+    printf("Signaled frame_num = %d, offset = 0x%x\n", frame_num, offset);
+  }
+
+  return 0;
+}
diff --git a/common/vsoc/lib/gralloc_layout.cpp b/common/vsoc/lib/gralloc_layout.cpp
new file mode 100644
index 0000000..9be790a
--- /dev/null
+++ b/common/vsoc/lib/gralloc_layout.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/gralloc_layout.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace gralloc {
+
+const char* GrallocManagerLayout::region_name = "gralloc_manager";
+const char* GrallocBufferLayout::region_name = "gralloc_memory";
+
+}  // gralloc
+}  // layout
+}  // vsoc
diff --git a/common/vsoc/lib/graphics_common.h b/common/vsoc/lib/graphics_common.h
new file mode 100644
index 0000000..d2db6c4
--- /dev/null
+++ b/common/vsoc/lib/graphics_common.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/shm/graphics.h"
+
+namespace vsoc {
+
+// Returns BytesPerPixel given a PixelFormat. Code should rely on this rather
+// than accessing the mask directly.
+static inline std::size_t BytesPerPixel(PixelFormat in) {
+    return 1 + (in >> PixelFormatBase::SubformatSize) &
+        (PixelFormatBase::MaxBytesPerPixel - 1);
+}
+}  // vsoc
diff --git a/common/vsoc/lib/graphics_test.cpp b/common/vsoc/lib/graphics_test.cpp
new file mode 100644
index 0000000..b43e56e
--- /dev/null
+++ b/common/vsoc/lib/graphics_test.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/shm/graphics.h"
+
+#include <gtest/gtest.h>
+
+TEST(GraphicsTest, Basic) {
+  // This is a shared memory layout and is mostly tested via static asserts.
+  // If this can compile then the test has passed.
+}
diff --git a/common/vsoc/lib/lock_common.cpp b/common/vsoc/lib/lock_common.cpp
new file mode 100644
index 0000000..ed7962e
--- /dev/null
+++ b/common/vsoc/lib/lock_common.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/shm/lock.h"
+
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/lib/compat.h"
+#include "common/vsoc/lib/region_view.h"
+
+#include <stdlib.h>
+
+using vsoc::layout::Sides;
+
+namespace {
+
+const uint32_t LockFree = 0U;
+const uint32_t GuestWaitingFlag = (Sides::Guest << 30);
+const uint32_t HostWaitingFlag = (Sides::Host << 30);
+const uint32_t OurWaitingFlag = (Sides::OurSide << 30);
+static_assert(GuestWaitingFlag, "GuestWaitingFlag is 0");
+static_assert(HostWaitingFlag, "HostWaitingFlag is 0");
+static_assert((GuestWaitingFlag & HostWaitingFlag) == 0,
+              "Waiting flags should not share bits");
+
+// const uint32_t ReservedForRobustLocksFlag = 0x20000000U;
+
+// PID_MAX_LIMIT appears to be 0x00400000U, so we're probably ok here
+const uint32_t OwnerMask = 0x1FFFFFFFU;
+};  // namespace
+
+namespace vsoc {
+/**
+ * This is a generic synchronization primitive that provides space for the
+ * owner of the lock to write platform-specific information.
+ */
+bool vsoc::layout::WaitingLockBase::TryLock(uint32_t tid,
+                                            uint32_t* expected_out) {
+  uint32_t masked_tid = tid & OwnerMask;
+  uint32_t expected = LockFree;
+  while (1) {
+    // First try to lock assuming that the mutex is free
+    if (lock_uint32_.compare_exchange_strong(expected, masked_tid)) {
+      // We got the lock.
+      return true;
+    }
+    // We didn't get the lock and our wait flag is already set. It's safe to
+    // try to sleep
+    if (expected & OurWaitingFlag) {
+      *expected_out = expected;
+      return false;
+    }
+    // Attempt to set the wait flag. This will fail if the lock owner changes.
+    while (1) {
+      uint32_t add_wait_flag = expected | OurWaitingFlag;
+      if (lock_uint32_.compare_exchange_strong(expected, add_wait_flag)) {
+        // We set the waiting flag. Try to sleep.
+        *expected_out = expected;
+        return false;
+      }
+      // The owner changed, but we at least we got the wait flag.
+      // Try sleeping
+      if (expected & OurWaitingFlag) {
+        *expected_out = expected;
+        return false;
+      }
+      // Special case: the lock was just freed. Stop trying to set the
+      // waiting flag and try to grab the lock.
+      if (expected == LockFree) {
+        break;
+      }
+      // The owner changed and we have no wait flag
+      // Try to set the wait flag again
+    }
+    // This only happens if the lock was freed while we attempt the set the
+    // wait flag. Try to grab the lock again
+  }
+  // Never reached.
+}
+
+layout::Sides vsoc::layout::WaitingLockBase::UnlockCommon(uint32_t tid) {
+  uint32_t expected_state = lock_uint32_;
+
+  // We didn't hold the lock. This process is insane and must die before it
+  // does damage.
+  if ((tid ^ expected_state) & OwnerMask) {
+    LOG(FATAL) << tid << " unlocking " << this << " owned by "
+               << expected_state;
+  }
+  // If contention is just starting this may fail twice (once for each bit)
+  // expected_state updates on each failure. When this finishes we have
+  // one bit for each waiter
+  while (1) {
+    if (lock_uint32_.compare_exchange_strong(expected_state, LockFree)) {
+      break;
+    }
+  }
+  if ((expected_state ^ tid) & OwnerMask) {
+    LOG(FATAL) << "Lock owner of " << this << " changed from " << tid << " to "
+               << expected_state << " during unlock";
+  }
+  Sides rval;
+  rval.value_ = expected_state & (GuestWaitingFlag | HostWaitingFlag) >> 30;
+  return rval;
+}
+
+void layout::GuestAndHostLock::Lock(RegionView* region) {
+  uint32_t* uaddr = reinterpret_cast<uint32_t*>(&lock_uint32_);
+  uint32_t expected;
+  uint32_t tid = gettid();
+  while (1) {
+    if (TryLock(tid, &expected)) {
+      return;
+    }
+    region->WaitForSignal(uaddr, expected);
+  }
+}
+
+void layout::GuestAndHostLock::Unlock(RegionView* region) {
+  uint32_t* uaddr = reinterpret_cast<uint32_t*>(&lock_uint32_);
+  region->SendSignal(UnlockCommon(gettid()), uaddr);
+}
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/lock_guard.h b/common/vsoc/lib/lock_guard.h
new file mode 100644
index 0000000..371cf52
--- /dev/null
+++ b/common/vsoc/lib/lock_guard.h
@@ -0,0 +1,82 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/vsoc/shm/lock.h"
+
+namespace vsoc {
+
+class RegionView;
+
+/*
+ * Implements std::lock_guard like functionality for the vsoc locks.
+ */
+
+template <typename Lock>
+class LockGuard {
+ public:
+  explicit LockGuard(Lock* lock) : lock_(lock) { lock_->Lock(); }
+
+  LockGuard(LockGuard<Lock>&& o) noexcept {
+    lock_ = o.lock_;
+    o.lock_ = nullptr;
+  }
+
+  LockGuard(const LockGuard<Lock>&) = delete;
+  LockGuard<Lock>& operator=(const LockGuard<Lock>&) = delete;
+
+  ~LockGuard() {
+    if (lock_) {
+      lock_->Unlock();
+    }
+  }
+
+ private:
+  Lock* lock_;
+};
+
+template <>
+class LockGuard<::vsoc::layout::GuestAndHostLock> {
+  using Lock = ::vsoc::layout::GuestAndHostLock;
+
+ public:
+  LockGuard(Lock* lock, RegionView* region) : lock_(lock), region_(region) {
+    lock_->Lock(region_);
+  }
+
+  LockGuard(LockGuard<Lock>&& o) noexcept {
+    lock_ = o.lock_;
+    o.lock_ = nullptr;
+    region_ = o.region_;
+    o.region_ = nullptr;
+  }
+
+  LockGuard(const LockGuard<Lock>&) = delete;
+  LockGuard<Lock>& operator=(const LockGuard<Lock>&) = delete;
+
+  ~LockGuard() {
+    if (lock_) {
+      lock_->Unlock(region_);
+    }
+  }
+
+ private:
+  Lock* lock_;
+  RegionView* region_;
+};
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/lock_test.cpp b/common/vsoc/lib/lock_test.cpp
new file mode 100644
index 0000000..d09b5a4
--- /dev/null
+++ b/common/vsoc/lib/lock_test.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/shm/lock.h"
+
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "common/vsoc/lib/region_view.h"
+
+#ifdef CUTTLEFISH_HOST
+using MyLock = vsoc::layout::HostLock;
+#else
+using MyLock = vsoc::layout::GuestLock;
+#endif
+
+class SimpleLocker {
+ public:
+  enum State {
+    BEFORE_EXECUTION,
+    BEFORE_LOCK,
+    IN_CRITICAL_SECTION,
+    DONE,
+    JOINED
+  };
+
+  explicit SimpleLocker(MyLock* lock)
+      : lock_(lock), thread_(&SimpleLocker::Work, this) {}
+
+  void Work() {
+    state_ = BEFORE_LOCK;
+    lock_->Lock();
+    state_ = IN_CRITICAL_SECTION;
+    InCriticalSection();
+    lock_->Unlock();
+    state_ = DONE;
+  }
+
+  void InCriticalSection() {}
+
+  void Join() {
+    thread_.join();
+    state_ = JOINED;
+  }
+
+ protected:
+  MyLock* lock_;
+  volatile State state_{BEFORE_EXECUTION};
+  std::thread thread_;
+};
+
+TEST(LockTest, Basic) {
+  // In production regions are always 0 filled on allocation. That's not
+  // true on the stack, so initialize the lock when we declare it.
+  MyLock lock{};
+  SimpleLocker a(&lock);
+  SimpleLocker b(&lock);
+  a.Join();
+  b.Join();
+}
diff --git a/common/vsoc/lib/mock_region_view.h b/common/vsoc/lib/mock_region_view.h
new file mode 100644
index 0000000..b6951f7
--- /dev/null
+++ b/common/vsoc/lib/mock_region_view.h
@@ -0,0 +1,118 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+#include "common/vsoc/lib/region_signaling_interface.h"
+
+namespace vsoc {
+namespace test {
+
+/**
+ * MockRegionView mocks a region in the shared memory window by calloc and
+ * futex. It supports only one-sided signal as in it doesn't do anything on
+ * sending or waiting interrupt. This is to test if a particular layout
+ * behaves correctly when there are multiple threads accessing it.
+ */
+template <typename Layout>
+class MockRegionView : public vsoc::RegionSignalingInterface {
+ public:
+  explicit MockRegionView() {};
+  virtual ~MockRegionView() {
+    if (region_base_) {
+      free(region_base_);
+      region_base_ = nullptr;
+    }
+  };
+
+  bool Open() {
+    region_base_ = calloc(sizeof(Layout), 1);
+    return !region_base_;
+  };
+
+  virtual void SendSignal(vsoc::layout::Sides /* sides_to_signal */,
+                          uint32_t* uaddr) {
+    syscall(SYS_futex, reinterpret_cast<int32_t*>(uaddr), FUTEX_WAKE, -1,
+            nullptr, nullptr, 0);
+  }
+
+  virtual void WaitForSignal(uint32_t* uaddr, uint32_t expected_value) {
+    {
+      std::lock_guard<std::mutex> guard(mutex_);
+      if (tid_to_addr_.find(std::this_thread::get_id()) != tid_to_addr_.end()) {
+        // Same thread tries to wait
+        return;
+      }
+      tid_to_addr_.emplace(std::this_thread::get_id(), uaddr);
+      map_changed_.notify_one();
+    }
+
+    syscall(SYS_futex, uaddr, FUTEX_WAIT, expected_value, nullptr, nullptr, 0);
+
+    {
+      std::lock_guard<std::mutex> guard(mutex_);
+      tid_to_addr_.erase(std::this_thread::get_id());
+    }
+  }
+
+  // Mock methods from TypedRegionView
+  Layout* data() { return reinterpret_cast<Layout*>(region_base_); };
+
+  // Check wait status on a specificy thread
+  bool IsBlocking(std::thread::id tid) {
+    while(1) {
+      std::unique_lock<std::mutex> lock(mutex_);
+      if (tid_to_addr_.find(tid) != tid_to_addr_.end()) {
+        return true;
+      }
+      // Allow some time as tid map might not be updated yet
+      while(tid_to_addr_.find(tid) == tid_to_addr_.end()) {
+        if (map_changed_.wait_for(
+              lock, std::chrono::seconds(kWaitTimeoutInSec)) ==
+            std::cv_status::timeout) {
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+
+ private:
+  // Timeout to avoid a race on checking if a thread is blocked
+  static constexpr int kWaitTimeoutInSec = 5;
+
+  void *region_base_{};
+  std::mutex mutex_;
+  std::condition_variable map_changed_;
+  std::unordered_map<std::thread::id, uint32_t*> tid_to_addr_;
+};
+
+template <typename Layout>
+constexpr int MockRegionView<Layout>::kWaitTimeoutInSec;
+
+
+}  // namespace test
+}  // namespace vsoc
diff --git a/common/vsoc/lib/region_control.h b/common/vsoc/lib/region_control.h
new file mode 100644
index 0000000..82464aa
--- /dev/null
+++ b/common/vsoc/lib/region_control.h
@@ -0,0 +1,89 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <memory>
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+
+/**
+ * Base class for side-specific utility functions that work on regions.
+ * The methods in this class do not assume that the region is mapped in memory.
+ * This makes is appropriate for ManagedRegions and certain low-level tests
+ * of VSoC shared memory. Most other users will want to use TypedRegions with
+ * a defined RegionLayout.
+ *
+ * This class is not directly instantiable because it must be specialized with
+ * additional fields for the host and guest.
+ */
+class RegionControl {
+ public:
+  virtual ~RegionControl() {
+    if (region_base_) {
+      munmap(region_base_, region_size());
+    }
+    region_base_ = nullptr;
+  }
+
+#if defined(CUTTLEFISH_HOST)
+  static std::shared_ptr<RegionControl> Open(const char* region_name,
+                                             const char* domain);
+#else
+  static std::shared_ptr<RegionControl> Open(const char* region_name);
+#endif
+
+  const vsoc_device_region& region_desc() const { return region_desc_; }
+
+  // Returns the size of the entire region, including the signal tables.
+  uint32_t region_size() const {
+    return region_desc_.region_end_offset - region_desc_.region_begin_offset;
+  }
+
+  // Returns the size of the region that is usable for region-specific data.
+  uint32_t region_data_size() const {
+    return region_size() - region_desc_.offset_of_region_data;
+  }
+
+  // Creates a FdScopedPermission. Returns the file descriptor or -1 on
+  // failure. FdScopedPermission is not supported on the host, so -1 is
+  // always returned there.
+  virtual int CreateFdScopedPermission(const char* managed_region_name,
+                                       vsoc_reg_off_t owner_offset,
+                                       uint32_t owned_value,
+                                       vsoc_reg_off_t begin_offset,
+                                       vsoc_reg_off_t end_offset) = 0;
+
+  // Interrupt our peer, causing it to scan the outgoing_signal_table
+  virtual bool InterruptPeer() = 0;
+
+  // Wake the local signal table scanner. Primarily used during shutdown
+  virtual void InterruptSelf() = 0;
+
+  // Maps the entire region at an address, returning a pointer to the mapping
+  virtual void* Map() = 0;
+
+  // Wait for an interrupt from our peer
+  virtual void WaitForInterrupt() = 0;
+
+ protected:
+  RegionControl() {}
+  void* region_base_{};
+  vsoc_device_region region_desc_{};
+};
+}  // namespace vsoc
diff --git a/common/vsoc/lib/region_signaling_interface.h b/common/vsoc/lib/region_signaling_interface.h
new file mode 100644
index 0000000..9adffa5
--- /dev/null
+++ b/common/vsoc/lib/region_signaling_interface.h
@@ -0,0 +1,54 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include "common/vsoc/shm/base.h"
+
+namespace vsoc {
+
+/**
+ * Interface that defines signaling and waiting for signal.
+ */
+class RegionSignalingInterface {
+ public:
+  virtual ~RegionSignalingInterface() {};
+
+  // Post a signal to the guest, the host, or both.
+  // See futex(2) FUTEX_WAKE for details.
+  //
+  //   sides_to_signal: controls where the signal is sent
+  //
+  //   signal_addr: the memory location to signal. Must be within the region.
+  virtual void SendSignal(layout::Sides sides_to_signal, uint32_t* signal_addr) = 0;
+
+  // This implements the following:
+  // if (*signal_addr == last_observed_value)
+  //   wait_for_signal_at(signal_addr);
+  //
+  // Note: the caller still needs to check the value at signal_addr because
+  // this function may return early for reasons that are implementation-defined.
+  // See futex(2) FUTEX_WAIT for details.
+  //
+  //   signal_addr: the memory that will be signaled. Must be within the region.
+  //
+  //   last_observed_value: the value that motivated the calling code to wait.
+  virtual void WaitForSignal(uint32_t* signal_addr, uint32_t last_observed_value) = 0;
+};
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/region_view.cpp b/common/vsoc/lib/region_view.cpp
new file mode 100644
index 0000000..fbd0393
--- /dev/null
+++ b/common/vsoc/lib/region_view.cpp
@@ -0,0 +1,185 @@
+#include "common/vsoc/lib/region_view.h"
+
+#include <linux/futex.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+
+#include "common/libs/glog/logging.h"
+
+namespace {
+const uint32_t UADDR_OFFSET_MASK = 0xFFFFFFFC;
+const uint32_t UADDR_OFFSET_ROUND_TRIP_FLAG = 1;
+}  // namespace
+
+using vsoc::layout::Sides;
+
+vsoc::RegionWorker::RegionWorker(RegionView* region)
+    : region_(region), thread_(&vsoc::RegionWorker::Work, this) {}
+
+void vsoc::RegionWorker::Work() {
+  while (!stopping_) {
+    region_->WaitForInterrupt();
+    if (stopping_) {
+      return;
+    }
+    region_->ProcessSignalsFromPeer([](uint32_t* uaddr){
+        syscall(SYS_futex, uaddr, FUTEX_WAKE, -1, nullptr, nullptr, 0);
+      });
+  }
+}
+
+vsoc::RegionWorker::~RegionWorker() {
+  stopping_ = true;
+  region_->InterruptSelf();
+  thread_.join();
+}
+
+vsoc::RegionView::~RegionView() {
+  // region_base_ is borrowed here. It's owned by control_, which is
+  // responsible for unmapping the memory
+  region_base_ = nullptr;
+}
+
+#if defined(CUTTLEFISH_HOST)
+bool vsoc::RegionView::Open(const char* name, const char* domain) {
+  control_ = vsoc::RegionControl::Open(name, domain);
+  if (!control_) {
+    return false;
+  }
+  region_base_ = control_->Map();
+  return region_base_ != nullptr;
+}
+#else
+bool vsoc::RegionView::Open(const char* name) {
+  control_ = vsoc::RegionControl::Open(name);
+  if (!control_) {
+    return false;
+  }
+  region_base_ = control_->Map();
+  return region_base_ != nullptr;
+}
+#endif
+
+// Interrupt our peer, causing it to scan the outgoing_signal_table
+bool vsoc::RegionView::MaybeInterruptPeer() {
+  if (region_offset_to_pointer<std::atomic<uint32_t>>(
+          outgoing_signal_table().interrupt_signalled_offset)
+          ->exchange(1)) {
+    return false;
+  }
+  return control_->InterruptPeer();
+}
+
+// Wait for an interrupt from our peer
+void vsoc::RegionView::WaitForInterrupt() {
+  while (1) {
+    if (region_offset_to_pointer<std::atomic<uint32_t>>(
+            incoming_signal_table().interrupt_signalled_offset)
+            ->exchange(0)) {
+      return;
+    }
+    control_->WaitForInterrupt();
+  }
+}
+
+void vsoc::RegionView::ProcessSignalsFromPeer(
+    std::function<void(uint32_t*)> signal_handler) {
+  const vsoc_signal_table_layout& table = incoming_signal_table();
+  const size_t num_offsets = (1 << table.num_nodes_lg2);
+  std::atomic<uint32_t>* offsets =
+      region_offset_to_pointer<std::atomic<uint32_t>>(
+          table.futex_uaddr_table_offset);
+  for (size_t i = 0; i < num_offsets; ++i) {
+    uint32_t offset = offsets[i].exchange(0);
+    if (offset) {
+      bool round_trip = offset & UADDR_OFFSET_ROUND_TRIP_FLAG;
+      uint32_t* uaddr =
+        region_offset_to_pointer<uint32_t>(offset & UADDR_OFFSET_MASK);
+      signal_handler(uaddr);
+      if (round_trip) {
+        SendSignalToPeer(uaddr, false);
+      }
+    }
+  }
+}
+
+void vsoc::RegionView::SendSignal(Sides sides_to_signal, uint32_t* uaddr) {
+  if (sides_to_signal.value_ & Sides::Peer) {
+    // If we should also be signalling our side set the round trip flag on
+    // the futex signal.
+    bool round_trip = sides_to_signal.value_ & Sides::OurSide;
+    SendSignalToPeer(reinterpret_cast<uint32_t*>(uaddr), round_trip);
+    // Return without signaling our waiters to give the other side a chance
+    // to run.
+    return;
+  }
+  if (sides_to_signal.value_ & Sides::OurSide) {
+    syscall(SYS_futex, reinterpret_cast<int32_t*>(uaddr), FUTEX_WAKE, -1,
+            nullptr, nullptr, 0);
+  }
+}
+
+void vsoc::RegionView::SendSignalToPeer(uint32_t* uaddr, bool round_trip) {
+  const vsoc_signal_table_layout& table = outgoing_signal_table();
+  std::atomic<uint32_t>* offsets =
+      region_offset_to_pointer<std::atomic<uint32_t>>(
+          table.futex_uaddr_table_offset);
+  // maximum index in the node that can hold an offset;
+  const size_t max_index = (1 << table.num_nodes_lg2) - 1;
+  uint32_t offset = pointer_to_region_offset(uaddr);
+  if (offset & ~UADDR_OFFSET_MASK) {
+    LOG(FATAL) << "uaddr offset is not naturally aligned " << uaddr;
+  }
+  // Guess at where this offset should go in the table.
+  // Do this before we set the round-trip flag.
+  size_t hash = (offset >> 2) & max_index;
+  if (round_trip) {
+    offset |= UADDR_OFFSET_ROUND_TRIP_FLAG;
+  }
+  while (1) {
+    uint32_t expected = 0;
+    if (offsets[hash].compare_exchange_strong(expected, offset)) {
+      // We stored the offset. Send the interrupt.
+      this->MaybeInterruptPeer();
+      break;
+    }
+    // We didn't store, but the value was already in the table with our flag.
+    // Return without interrupting.
+    if (expected == offset) {
+      return;
+    }
+    // Hash collision. Try again in a different node
+    if ((expected & UADDR_OFFSET_MASK) != (offset & UADDR_OFFSET_MASK)) {
+      hash = (hash + 1) & max_index;
+      continue;
+    }
+    // Our offset was in the bucket, but the flags didn't match.
+    // We're done if the value in the node had the round trip flag set.
+    if (expected & UADDR_OFFSET_ROUND_TRIP_FLAG) {
+      return;
+    }
+    // We wanted the round trip flag, but the value in the bucket didn't set it.
+    // Do a second swap to try to set it.
+    if (offsets[hash].compare_exchange_strong(expected, offset)) {
+      // It worked. We're done.
+      return;
+    }
+    if (expected == offset) {
+      // expected was the offset without the flag. After the swap it has the
+      // the flag. This means that some other thread set the flag, so
+      // we're done.
+      return;
+    }
+    // Something about the offset changed. We need to go around again, because:
+    //   our peer processed the old entry
+    //   another thread may have stolen the node while we were distracted
+  }
+}
+
+std::unique_ptr<vsoc::RegionWorker> vsoc::RegionView::StartWorker() {
+  return std::unique_ptr<vsoc::RegionWorker>(new vsoc::RegionWorker(this));
+}
+
+void vsoc::RegionView::WaitForSignal(uint32_t* uaddr, uint32_t expected_value) {
+  syscall(SYS_futex, uaddr, FUTEX_WAIT, expected_value, nullptr, nullptr, 0);
+}
diff --git a/common/vsoc/lib/region_view.h b/common/vsoc/lib/region_view.h
new file mode 100644
index 0000000..5210924
--- /dev/null
+++ b/common/vsoc/lib/region_view.h
@@ -0,0 +1,206 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Object that represents a region on the Host
+
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <atomic>
+#include <cstdint>
+
+#include <functional>
+#include <thread>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/lib/lock_guard.h"
+#include "common/vsoc/lib/region_control.h"
+#include "common/vsoc/lib/region_signaling_interface.h"
+#include "common/vsoc/shm/base.h"
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+
+class RegionView;
+
+/**
+ * Represents a task that is tied to a RegionView.
+ *
+ * This is currently used for the task that forwards futexes across the
+ * shared memory window.
+ */
+class RegionWorker {
+ public:
+  explicit RegionWorker(RegionView* region);
+  ~RegionWorker();
+  void Work();
+
+ protected:
+  RegionView* region_;
+  std::thread thread_;
+  volatile bool stopping_{};
+};
+
+/**
+ * Base class to access a mapped region in VSoC shared memory.
+ * This class holds the methods that depends on the region's memory having an
+ * address. The RegionControl class holds the methods that can be invoked
+ * without mapping the region.
+ */
+class RegionView : public RegionSignalingInterface {
+ public:
+  virtual ~RegionView();
+
+#if defined(CUTTLEFISH_HOST)
+  bool Open(const char* region_name, const char* domain);
+#else
+  bool Open(const char* region_name);
+#endif
+
+  // Returns a pointer to the table that will be scanned for signals
+  const vsoc_signal_table_layout& incoming_signal_table();
+
+  // Returns a pointer to the table that will be used to post signals
+  const vsoc_signal_table_layout& outgoing_signal_table();
+
+  // Returns true iff an interrupt is queued in the signal table
+  bool HasIncomingInterrupt() {
+    return *region_offset_to_pointer<std::atomic<uint32_t>>(
+        incoming_signal_table().interrupt_signalled_offset);
+  }
+
+  // Wake any threads waiting for an interrupt. This is generally used during
+  // shutdown.
+  void InterruptSelf() { control_->InterruptSelf(); }
+
+  // Interrupt our peer if an interrupt is not already on the way.
+  // Returns true if the interrupt was sent, false if an interrupt was already
+  // pending.
+  bool MaybeInterruptPeer();
+
+  // Scan in the incoming signal table, issuing futex calls for any posted
+  // signals and then reposting them to the peer if they were round-trip
+  // signals.
+  //
+  //   signal_handler: An action to perform on every offset signalled by our
+  //   peer, usually a FUTEX_WAKE call, but can be customized for other
+  //   purposes.
+  void ProcessSignalsFromPeer(
+      std::function<void(uint32_t*)> signal_handler);
+
+  // Post a signal to the guest, the host, or both.
+  // See futex(2) FUTEX_WAKE for details.
+  //
+  //   sides_to_signal: controls where the signal is sent
+  //
+  //   signal_addr: the memory location to signal. Must be within the region.
+  void SendSignal(layout::Sides sides_to_signal, uint32_t* signal_addr);
+
+  // Post a signal to our peer for a specific memeory location.
+  // See futex(2) FUTEX_WAKE for details.
+  //
+  //   signal_addr: the memory location to signal. Must be within the region.
+  //
+  //   round_trip: true if there may be waiters on both sides of the shared
+  //               memory.
+  void SendSignalToPeer(uint32_t* signal_addr, bool round_trip);
+
+  // Waits until an interrupt appears on this region, then clears the
+  // interrupted flag and returns.
+  void WaitForInterrupt();
+
+  // This implements the following:
+  // if (*signal_addr == last_observed_value)
+  //   wait_for_signal_at(signal_addr);
+  //
+  // Note: the caller still needs to check the value at signal_addr because
+  // this function may return early for reasons that are implementation-defined.
+  // See futex(2) FUTEX_WAIT for details.
+  //
+  //   signal_addr: the memory that will be signaled. Must be within the region.
+  //
+  //   last_observed_value: the value that motivated the calling code to wait.
+  void WaitForSignal(uint32_t* signal_addr, uint32_t last_observed_value);
+
+  // Starts the signal table scanner. This must be invoked by subclasses, which
+  // must store the returned unique_ptr as a class member.
+  std::unique_ptr<RegionWorker> StartWorker();
+
+  // Returns a pointer to the start of region data that is cast to the given
+  // type.  Initializers that run in the launcher use this to get a typed view of the region. Most other cases should be handled via TypedRegionView.
+  template <typename LayoutType>
+  LayoutType* GetLayoutPointer() {
+    return this->region_offset_to_pointer<LayoutType>(
+        control_->region_desc().offset_of_region_data);
+  }
+
+  // Helper functions for lock guards. This approach has two advantages:
+  //
+  //   o Callers don't have to be refactored when lock types change
+  //   o The region pointer can be provided automatically
+  template <typename T>
+  LockGuard<T> make_lock_guard(T* lock) {
+    return LockGuard<T>(lock);
+  }
+
+  LockGuard<::vsoc::layout::GuestAndHostLock> make_lock_guard(
+      ::vsoc::layout::GuestAndHostLock* l) {
+    return LockGuard<::vsoc::layout::GuestAndHostLock>(l, this);
+  }
+
+ protected:
+  template <typename T>
+  T* region_offset_to_pointer(vsoc_reg_off_t offset) {
+    if (offset > control_->region_size()) {
+      LOG(FATAL) << __FUNCTION__ << ": " << offset << " not in region @"
+                 << region_base_;
+    }
+    return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(region_base_) +
+                                offset);
+  }
+
+  template <typename T>
+  const T& region_offset_to_reference(vsoc_reg_off_t offset) const {
+    if (offset > control_->region_size()) {
+      LOG(FATAL) << __FUNCTION__ << ": " << offset << " not in region @"
+                 << region_base_;
+    }
+    return *reinterpret_cast<const T*>(
+        reinterpret_cast<uintptr_t>(region_base_) + offset);
+  }
+
+  // Calculates an offset based on a pointer in the region. Crashes if the
+  // pointer isn't in the region.
+  // This is mostly for the RegionView's internal plumbing. Use TypedRegionView
+  // and RegionLayout to avoid this in most cases.
+  template <typename T>
+  vsoc_reg_off_t pointer_to_region_offset(T* ptr) {
+    vsoc_reg_off_t rval = reinterpret_cast<uintptr_t>(ptr) -
+                          reinterpret_cast<uintptr_t>(region_base_);
+    if (rval > control_->region_size()) {
+      LOG(FATAL) << __FUNCTION__ << ": " << ptr << " not in region @"
+                 << region_base_;
+    }
+    return rval;
+  }
+
+  std::shared_ptr<RegionControl> control_;
+  void* region_base_{};
+};
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/single_sided_signal.h b/common/vsoc/lib/single_sided_signal.h
new file mode 100644
index 0000000..c32361a
--- /dev/null
+++ b/common/vsoc/lib/single_sided_signal.h
@@ -0,0 +1,45 @@
+#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.
+ */
+
+// Signaling mechanism that allows threads to signal changes to shared
+// memory and to wait for signals.
+
+namespace vsoc {
+/**
+ * Defines the strategy for signaling among threads on a single kernel.
+ */
+namespace SingleSidedSignal {
+/**
+ * Waits for a signal, assuming the the word at addr matches expected_state.
+ * Will return immediately if the value does not match.
+ * Callers must be equipped to cope with spurious returns.
+ */
+static void AwaitSignal(uint32_t expected_state, uint32_t* uaddr) {
+  syscall(SYS_futex, uaddr, FUTEX_WAIT, expected_state, nullptr, nullptr, 0);
+}
+
+/**
+ * Sends a signal to every thread in AwaitSignal() using the address in
+ * uaddr.
+ */
+static void Signal(std::atomic<uint32_t>* uaddr) {
+  syscall(SYS_futex, reinterpret_cast<int32_t*>(uaddr), FUTEX_WAKE, -1, nullptr,
+          nullptr, 0);
+}
+}  // namespace SingleSidedSignal
+}  // namespace vsoc
diff --git a/common/vsoc/lib/typed_region_view.h b/common/vsoc/lib/typed_region_view.h
new file mode 100644
index 0000000..ec16c4d
--- /dev/null
+++ b/common/vsoc/lib/typed_region_view.h
@@ -0,0 +1,63 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Object that represents a region on the Host
+
+#include "common/vsoc/lib/region_view.h"
+
+#include <map>
+#include <mutex>
+#include <string>
+
+namespace vsoc {
+
+/**
+ * This class adds methods that depend on the Region's type.
+ * This may be directly constructed. However, it may be more effective to
+ * subclass it, adding region-specific methods.
+ *
+ * Layout should be VSoC shared memory compatible, defined in common/vsoc/shm,
+ * and should have a constant string region name.
+ */
+template <typename LayoutType>
+class TypedRegionView : public RegionView {
+ public:
+  using Layout = LayoutType;
+
+  /* Returns a pointer to the region with a type that matches the layout */
+  LayoutType* data() {
+    return this->GetLayoutPointer<LayoutType>();
+  }
+
+  const LayoutType& data() const {
+    return this->region_offset_to_reference<LayoutType>(
+        control_->region_desc().offset_of_region_data);
+  }
+
+#if defined(CUTTLEFISH_HOST)
+  bool Open(const char* domain) {
+    return RegionView::Open(LayoutType::region_name, domain);
+  }
+#else
+  bool Open() {
+    return RegionView::Open(LayoutType::region_name);
+  }
+#endif
+};
+
+}  // namespace vsoc
diff --git a/common/vsoc/lib/wifi_exchange_layout.cpp b/common/vsoc/lib/wifi_exchange_layout.cpp
new file mode 100644
index 0000000..12e9577
--- /dev/null
+++ b/common/vsoc/lib/wifi_exchange_layout.cpp
@@ -0,0 +1,24 @@
+/*
+ * 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/wifi_exchange_layout.h"
+
+namespace vsoc {
+namespace layout {
+namespace wifi {
+const char* WifiExchangeLayout::region_name = "wifi_exchange";
+}  // namespace wifi
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/lib/wifi_exchange_view.cpp b/common/vsoc/lib/wifi_exchange_view.cpp
new file mode 100644
index 0000000..23e0bc6
--- /dev/null
+++ b/common/vsoc/lib/wifi_exchange_view.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "common/vsoc/lib/circqueue_impl.h"
+
+namespace vsoc {
+namespace wifi {
+
+bool WifiExchangeView::Send(const void* buffer, size_t length) {
+#ifdef CUTTLEFISH_HOST
+  return data()->guest_ingress.Write(this, static_cast<const char*>(buffer),
+                                     length) == length;
+#else
+  return data()->guest_egress.Write(this, static_cast<const char*>(buffer),
+                                    length) == length;
+#endif
+}
+
+intptr_t WifiExchangeView::Recv(void* buffer, size_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 uint8_t* mac_address) {
+  memcpy(data()->mac_address, mac_address, ETH_ALEN);
+}
+
+void WifiExchangeView::GetGuestMACAddress(uint8_t* mac_address) {
+  memcpy(mac_address, data()->mac_address, ETH_ALEN);
+}
+
+}  // namespace wifi
+}  // namespace vsoc
diff --git a/common/vsoc/lib/wifi_exchange_view.h b/common/vsoc/lib/wifi_exchange_view.h
new file mode 100644
index 0000000..83d7e6a
--- /dev/null
+++ b/common/vsoc/lib/wifi_exchange_view.h
@@ -0,0 +1,45 @@
+/*
+ * 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/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<vsoc::layout::wifi::WifiExchangeLayout> {
+ public:
+  // Send netlink packet to peer.
+  // returns true, if operation was successful.
+  bool Send(const void* buffer, size_t length);
+
+  // Receive netlink packet from peer.
+  // Returns number of bytes read, or negative value, if failed.
+  intptr_t Recv(void* buffer, size_t max_length);
+
+  // Set guest MAC address.
+  void SetGuestMACAddress(const uint8_t* mac_address);
+  void GetGuestMACAddress(uint8_t* mac_address);
+
+  void SetConfigReady();
+  void WaitConfigReady();
+};
+
+}  // namespace wifi
+}  // namespace vsoc
diff --git a/common/vsoc/shm/base.h b/common/vsoc/shm/base.h
new file mode 100644
index 0000000..abac492
--- /dev/null
+++ b/common/vsoc/shm/base.h
@@ -0,0 +1,86 @@
+#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.
+ */
+
+// Base macros for all layout structures.
+
+#include <type_traits>
+#include "common/vsoc/shm/version.h"
+
+// ShmTypeValidator provides meaningful information about the type size
+// mismatch in compilation error messages, eg.
+//
+// error:
+//    static_assert failed "Class size changed, update the version"
+//    static_assert(Current == Expected,
+// note: in instantiation of template class
+//    'ShmTypeValidator<vsoc::layout::myclass::ClassName, 1232, 1240>'
+//    requested here ASSERT_SHM_COMPATIBLE(ClassName, myclass);
+//
+template<typename Type, size_t Current, size_t Expected>
+struct ShmTypeValidator {
+    static_assert(Current == Expected,
+                  "Class size changed, update the version");
+    static_assert(std::is_trivial<Type>(),
+                  "Class uses features that are unsafe");
+    static constexpr bool valid = (Current == Expected);
+};
+
+#define ASSERT_SHM_COMPATIBLE(T, R)                                   \
+  static_assert(                                                      \
+      ShmTypeValidator<T, sizeof(T), vsoc::layout::version_info::R::T##_size> \
+      ::valid, "Compilation error. Please fix above errors and retry.")
+
+#define ASSERT_SHM_CONSTANT_VALUE(T, R)                                 \
+  static_assert(T == vsoc::layout::version_info::R::constant_values::T, \
+                "Constant value changed")
+
+namespace vsoc {
+namespace layout {
+
+/**
+ * Memory is shared between Guest and Host kernels. In some cases we need
+ * flag to indicate which side we're on. In those cases we'll use this
+ * simple structure.
+ *
+ * These are carefully formatted to make Guest and Host a bitfield.
+ */
+struct Sides {
+  static const uint32_t NoSides = 0;
+  static const uint32_t Guest = 1;
+  static const uint32_t Host = 2;
+  static const uint32_t Both = 3;
+#ifdef CUTTLEFISH_HOST
+  static const uint32_t OurSide = Host;
+  static const uint32_t Peer = Guest;
+#else
+  static const uint32_t OurSide = Guest;
+  static const uint32_t Peer = Host;
+#endif
+
+  uint32_t value_;
+};
+ASSERT_SHM_COMPATIBLE(Sides, multi_region);
+
+/**
+ * Base class for all region layout structures.
+ */
+class RegionLayout {
+};
+ASSERT_SHM_COMPATIBLE(RegionLayout, multi_region);
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/circqueue.h b/common/vsoc/shm/circqueue.h
new file mode 100644
index 0000000..5422b8e
--- /dev/null
+++ b/common/vsoc/shm/circqueue.h
@@ -0,0 +1,170 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Memory layout for byte-oriented circular queues
+
+#include <atomic>
+#include <cstdint>
+
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/lock.h"
+
+namespace vsoc {
+class RegionSignalingInterface;
+namespace layout {
+
+/**
+ * Base classes for all spinlock protected circular queues.
+ * This class should be embedded in the per-region data structure that is used
+ * as the parameter to TypedRegion.
+ */
+template <uint32_t SizeLog2>
+class CircularQueueBase {
+  CircularQueueBase() = delete;
+  CircularQueueBase(const CircularQueueBase&) = delete;
+  CircularQueueBase& operator=(const CircularQueueBase&) = delete;
+ protected:
+  /**
+   * Specifies a part of the queue. Note, the given indexes must be masked
+   * before they can be used against buffer_
+   */
+  struct Range {
+    // Points to the first bytes that is part of the range
+    uint32_t start_idx;
+    // Points to the first byte that is not in the range. This is similar to
+    // the STL end iterator.
+    uint32_t end_idx;
+  };
+  static const uintptr_t BufferSize = (1 << SizeLog2);
+
+  /**
+   * Copy bytes from buffer_in into the part of the queue specified by Range.
+   */
+  void CopyInRange(const char* buffer_in, const Range& t);
+
+  /**
+   * Copy the bytes specified by range to the given buffer. They caller must
+   * ensure that the buffer is large enough to hold the content of the range.
+   */
+  void CopyOutRange(const Range& t, char* buffer_out);
+
+  /**
+   * Wait until data becomes available in the queue. The caller must have
+   * called Lock() before invoking this. The caller must call Unlock()
+   * after this returns.
+   */
+  void WaitForDataLocked(RegionSignalingInterface* r);
+
+  /**
+   * Reserve space in the queue for writing. The caller must have called Lock()
+   * before invoking this. The caller must call Unlock() after this returns.
+   * Indexes pointing to the reserved space will be placed in range.
+   * On success this returns bytes.
+   * On failure a negative errno indicates the problem. -ENOSPC indicates that
+   * bytes > the queue size, -EWOULDBLOCK indicates that the call would block
+   * waiting for space but was requested non bloking.
+   */
+  intptr_t WriteReserveLocked(RegionSignalingInterface* r,
+                              size_t bytes,
+                              Range* t,
+                              bool non_blocking);
+
+  // Note: Both of these fields may hold values larger than the buffer size,
+  // they should be interpreted modulo the buffer size. This fact along with the
+  // buffer size being a power of two greatly simplyfies the index calculations.
+  // Advances when a reader has finished with buffer space
+  uint32_t r_released_;
+  // Advances when buffer space is filled and ready for a reader
+  uint32_t w_pub_;
+  // Spinlock that protects the region. 0 means unlocked
+  SpinLock lock_;
+  // The actual memory in the buffer
+  char buffer_[BufferSize];
+};
+using CircularQueueBase64k = CircularQueueBase<16>;
+ASSERT_SHM_COMPATIBLE(CircularQueueBase64k, multi_region);
+
+/**
+ * Byte oriented circular queue. Reads will always return some data, but
+ * may return less data than requested. Writes will always write all of the
+ * data or return an error.
+ */
+template <uint32_t SizeLog2>
+class CircularByteQueue : public CircularQueueBase<SizeLog2> {
+ public:
+  /**
+   * Read at most max_size bytes from the qeueue, placing them in buffer_out
+   */
+  intptr_t Read(RegionSignalingInterface* r, char* buffer_out, std::size_t max_size);
+  /**
+   * Write all of the given bytes into the queue. If non_blocking isn't set the
+   * call may block until there is enough available space in the queue. On
+   * success the return value will match bytes. On failure a negative errno is
+   * returned. -ENOSPC: If the queue size is smaller than the number of bytes to
+   * write. -EWOULDBLOCK: If non_blocking is true and there is not enough free
+   * space.
+   */
+  intptr_t Write(RegionSignalingInterface* r,
+                 const char* buffer_in,
+                 std::size_t bytes,
+                 bool non_blocking = false);
+
+ protected:
+  using Range = typename CircularQueueBase<SizeLog2>::Range;
+};
+using CircularByteQueue64k = CircularByteQueue<16>;
+ASSERT_SHM_COMPATIBLE(CircularByteQueue64k, multi_region);
+
+/**
+ * Packet oriented circular queue. Reads will either return data or an error.
+ * Each return from read corresponds to a call to write and returns all of the
+ * data from that corresponding Write().
+ */
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+class CircularPacketQueue : public CircularQueueBase<SizeLog2> {
+ public:
+  /**
+   * Read a single packet from the queue, placing its data into buffer_out.
+   * If max_size indicates that buffer_out cannot hold the entire packet
+   * this function will return -ENOSPC.
+   */
+  intptr_t Read(RegionSignalingInterface* r, char* buffer_out, std::size_t max_size);
+
+  /**
+   * Writes [buffer_in, buffer_in + bytes) to the queue.
+   * If the number of bytes to be written exceeds the size of the queue
+   * -ENOSPC will be returned.
+   * If non_blocking is true and there is not enogh free space on the queue to
+   * write all the data -EWOULDBLOCK will be returned.
+   */
+  intptr_t Write(RegionSignalingInterface* r,
+                 const char* buffer_in,
+                 uint32_t bytes,
+                 bool non_blocking = false);
+
+ protected:
+  static_assert(CircularQueueBase<SizeLog2>::BufferSize >= MaxPacketSize,
+                "Buffer is too small to hold the maximum sized packet");
+  using Range = typename CircularQueueBase<SizeLog2>::Range;
+  intptr_t CalculateBufferedSize(size_t payload);
+};
+using CircularPacketQueue64k = CircularPacketQueue<16, 1024>;
+ASSERT_SHM_COMPATIBLE(CircularPacketQueue64k, multi_region);
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/e2e_test_region_layout.h b/common/vsoc/shm/e2e_test_region_layout.h
new file mode 100644
index 0000000..f0a24e9
--- /dev/null
+++ b/common/vsoc/shm/e2e_test_region_layout.h
@@ -0,0 +1,166 @@
+#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"
+#include "common/vsoc/shm/version.h"
+
+// Memory layout for a region that supports end-to-end (E2E) testing of
+// shared memory regions. This verifies that all sorts of things work along the
+// the path:
+//
+//   host libraries <-> ivshmem server <-> kernel <-> guest libraries
+//
+// This is intentionally not a unit test. The primary source of errors along
+// this path is a misunderstanding and/or inconsistency in one of the
+// interfaces. Introducing mocks would allow these errors to go undetected.
+// Another way of looking at it is that the mocks would end up being a
+// a copy-and-paste job, making a series of change-detector tests.
+//
+// These tests are actually run on every device boot to verify that things are
+// ok.
+
+namespace vsoc {
+namespace layout {
+
+namespace e2e_test {
+
+/**
+ * Flags that are used to indicate test status. Some of the latter testing
+ * stages rely on initializion that must be done on the peer.
+ */
+enum E2ETestStage {
+  // No tests have passed
+  E2E_STAGE_NONE = 0,
+  // This side has finished writing its pattern to the region
+  E2E_MEMORY_FILLED = 1,
+  // This side has confirmed that it can see its peer's writes to the region
+  E2E_PEER_MEMORY_READ = 2,
+};
+
+/**
+ * Structure that grants permission to write in the region to either the guest
+ * or the host. This size of these fields is arbitrary.
+ */
+struct E2EMemoryFill {
+  static const std::size_t kOwnedFieldSize = 32;
+
+  // The compiler must not attempt to optimize away reads and writes to the
+  // shared memory window. This is pretty typical when dealing with devices
+  // doing memory mapped I/O.
+  char host_writable[kOwnedFieldSize];
+  char guest_writable[kOwnedFieldSize];
+};
+ASSERT_SHM_COMPATIBLE(E2EMemoryFill, e2e_test);
+
+/**
+ * Structure that grants permission to write in the region to either the guest
+ * or the host. This size of these fields is arbitrary.
+ */
+class E2ETestStageRegister {
+ public:
+  E2ETestStage value() const {
+    E2ETestStage rval = static_cast<E2ETestStage>(value_);
+    return rval;
+  }
+
+  void set_value(E2ETestStage new_value) { value_ = new_value; }
+
+ protected:
+  // The compiler must not attempt to optimize away reads and writes to the
+  // shared memory window. This is pretty typical when dealing with devices
+  // doing memory mapped I/O.
+  uint32_t value_;
+};
+ASSERT_SHM_COMPATIBLE(E2ETestStageRegister, e2e_test);
+
+/**
+ * Describes the layout of the regions used for the end-to-end test. There
+ * are multiple regions: primary and secondary, so some details like the region
+ * name must wait until later.
+ */
+class E2ETestRegionLayout : public ::vsoc::layout::RegionLayout {
+ public:
+  /**
+   * Computes how many E2EMemoryFill records we need to cover the region.
+   * Covering the entire region during the test ensures that everything is
+   * mapped and coherent between guest and host.
+   */
+  static std::size_t NumFillRecords(std::size_t region_size) {
+    if (region_size < sizeof(E2ETestRegionLayout)) {
+      return 0;
+    }
+    // 1 + ... An array of size 1 is allocated in the E2ETestRegion.
+    // TODO(ghartman): AddressSanitizer may find this sort of thing to be
+    // alarming.
+    return 1 +
+           (region_size - sizeof(E2ETestRegionLayout)) / sizeof(E2EMemoryFill);
+  }
+  // The number of test stages that have completed on the guest
+  // Later host tests will wait on this
+  E2ETestStageRegister guest_status;
+  // The number of test stages that have completed on the host
+  // Later guest tests will wait on this
+  E2ETestStageRegister host_status;
+  // These fields are used to test the signaling mechanism.
+  uint32_t host_to_guest_signal;
+  uint32_t guest_to_host_signal;
+  // There rest of the region will be filled by guest_host_strings.
+  // We actually use more than one of these, but we can't know how many
+  // until we examine the region.
+  E2EMemoryFill data[1];
+};
+ASSERT_SHM_COMPATIBLE(E2ETestRegionLayout, e2e_test);
+
+struct E2EPrimaryTestRegionLayout : public E2ETestRegionLayout {
+  static const char* region_name;
+  static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize];
+  static const char host_pattern[E2EMemoryFill::kOwnedFieldSize];
+};
+ASSERT_SHM_COMPATIBLE(E2EPrimaryTestRegionLayout, e2e_test);
+
+struct E2ESecondaryTestRegionLayout : public E2ETestRegionLayout {
+  static const char* region_name;
+  static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize];
+  static const char host_pattern[E2EMemoryFill::kOwnedFieldSize];
+};
+ASSERT_SHM_COMPATIBLE(E2ESecondaryTestRegionLayout, e2e_test);
+
+/**
+ * Defines an end-to-end region with a name that should never be configured.
+ */
+struct E2EUnfindableRegionLayout : public E2ETestRegionLayout {
+  static const char* region_name;
+};
+ASSERT_SHM_COMPATIBLE(E2EUnfindableRegionLayout, e2e_test);
+
+struct E2EManagedTestRegionLayout : public RegionLayout {
+  static const char* region_name;
+  uint32_t val;  // Not needed, here only to avoid an empty struct.
+};
+ASSERT_SHM_COMPATIBLE(E2EManagedTestRegionLayout, e2e_test);
+
+struct E2EManagerTestRegionLayout : public RegionLayout {
+  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, e2e_test);
+
+}  // namespace e2e_test
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/fb_bcast_layout.h b/common/vsoc/shm/fb_bcast_layout.h
new file mode 100644
index 0000000..67c229b
--- /dev/null
+++ b/common/vsoc/shm/fb_bcast_layout.h
@@ -0,0 +1,50 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/lock.h"
+#include "common/vsoc/shm/version.h"
+
+// Memory layout for the hwcomposer and hwcomposer broadcast regions
+
+namespace vsoc {
+namespace layout {
+
+namespace framebuffer {
+
+struct FBBroadcastLayout : public RegionLayout {
+  static const char* region_name;
+  // Display properties
+  uint32_t x_res;
+  uint32_t y_res;
+  uint16_t dpi;
+  uint16_t refresh_rate_hz;
+
+  // The frame sequential number
+  uint32_t seq_num;
+  // The offset in the gralloc buffer region of the current frame buffer.
+  uint32_t frame_offset;
+  // Protects access to the frame offset and sequential number.
+  // See the region implementation for more details.
+  SpinLock bcast_lock;
+};
+ASSERT_SHM_COMPATIBLE(FBBroadcastLayout, framebuffer);
+
+}  // namespace framebuffer
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/gralloc_layout.h b/common/vsoc/shm/gralloc_layout.h
new file mode 100644
index 0000000..54f5589
--- /dev/null
+++ b/common/vsoc/shm/gralloc_layout.h
@@ -0,0 +1,70 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/graphics.h"
+#include "common/vsoc/shm/lock.h"
+#include "common/vsoc/shm/version.h"
+
+// Memory layout for the gralloc manager region.
+
+namespace vsoc {
+namespace layout {
+
+namespace gralloc {
+
+struct BufferEntry {
+  uint32_t owned_by;
+  uint32_t buffer_begin;
+  uint32_t buffer_end;
+
+  PixelFormatRegister pixel_format;
+  uint32_t stride;
+  uint32_t width;
+  uint32_t height;
+
+  // A size of 28 is causing different layouts when GrallocManagerLayout is
+  // compiled in host and guest sides
+  uint32_t padding;
+
+  uint32_t buffer_size() {
+    return buffer_end - buffer_begin;
+  }
+};
+ASSERT_SHM_COMPATIBLE(BufferEntry, gralloc);
+
+struct GrallocBufferLayout : public RegionLayout {
+  static const char* region_name;
+};
+ASSERT_SHM_COMPATIBLE(GrallocBufferLayout, gralloc);
+
+struct GrallocManagerLayout : public RegionLayout {
+  static const char* region_name;
+  typedef GrallocBufferLayout ManagedRegion;
+
+  uint32_t allocated_buffer_memory;
+  uint32_t buffer_count;
+  // Make sure this isn't the first field
+  GuestLock new_buffer_lock;
+  // Needs to be last field
+  BufferEntry buffers_table[1];
+};
+ASSERT_SHM_COMPATIBLE(GrallocManagerLayout, gralloc);
+
+} // namespace gralloc
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/graphics.h b/common/vsoc/shm/graphics.h
new file mode 100644
index 0000000..feaf7f8
--- /dev/null
+++ b/common/vsoc/shm/graphics.h
@@ -0,0 +1,170 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Memory layout for primitive graphics types.
+
+// The vsoc::layout namespace indicates that these are shared memory structure
+// definitions. The #include's given above are strictly limited, as are the
+// types that can be referenced below.
+
+#include <cstdint>
+
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/version.h"
+
+namespace vsoc {
+
+// The enumerations for VSoC pixel formats are laid out so that hardware to
+// parse bytes per pixel without relying an a exhaustive list of pixel formats.
+// These constants define the fields involved.
+namespace PixelFormatConst {
+  static const uint32_t BytesPerPixelSize = 3;
+  static const uint32_t SubformatSize = 3;
+  static const uint32_t MaxBytesPerPixel = (1 << BytesPerPixelSize);
+  static const uint32_t MaxSubformat = (1 << SubformatSize) - 1;
+};
+
+
+// Builds (statically) a new pixel format enumeration value given constant
+// bytes per pixel.
+template <uint32_t BYTES, uint32_t SUB_FORMAT>
+struct PixelFormatBuilder {
+  static_assert(BYTES > 0, "Too few bytes");
+  static_assert(BYTES <= PixelFormatConst::MaxBytesPerPixel, "Too many bytes");
+  static_assert(SUB_FORMAT <= PixelFormatConst::MaxSubformat,
+                "Too many subformats");
+  static const uint32_t value = ((BYTES - 1) << PixelFormatConst::SubformatSize) | SUB_FORMAT;
+};
+
+template <uint32_t FORMAT>
+struct PixelFormatProperties {
+  // No static asserts since all int32_t values are (technically) valid pixel formats?
+  static const uint32_t bytes_per_pixel = (FORMAT >> PixelFormatConst::SubformatSize) + 1;
+};
+
+// Contains all of the pixel formats currently supported by this VSoC. The
+// enumeration serves multiple purposes:
+//
+//   * The compile will warn (or error) if we switch on PixelFormat and don't
+//     handly all of the cases.
+//
+//   * Code can use PixelFormat to describe paramaters, making APIs a bit more
+//     self-documenting.
+//
+//   * Observant reviewers can verify that the same pixel value is not assigned
+//     to multiple formats. Keep the enums in numerical order below to
+//     make this easier.
+enum PixelFormat {
+  VSOC_PIXEL_FORMAT_UNINITIALIZED = PixelFormatBuilder<1,0>::value,
+  VSOC_PIXEL_FORMAT_BLOB =          PixelFormatBuilder<1,1>::value,
+
+  VSOC_PIXEL_FORMAT_RGB_565 =       PixelFormatBuilder<2,0>::value,
+  VSOC_PIXEL_FORMAT_YV12 =          PixelFormatBuilder<2,1>::value,
+  VSOC_PIXEL_FORMAT_YCbCr_420_888 = PixelFormatBuilder<2,2>::value,
+
+  VSOC_PIXEL_FORMAT_RGB_888 =       PixelFormatBuilder<3,0>::value,
+
+  VSOC_PIXEL_FORMAT_RGBA_8888 =     PixelFormatBuilder<4,0>::value,
+  VSOC_PIXEL_FORMAT_RGBX_8888 =     PixelFormatBuilder<4,1>::value,
+  VSOC_PIXEL_FORMAT_BGRA_8888 =     PixelFormatBuilder<4,2>::value,
+
+  // VSOC_PIXEL_FORMAT_IMPLEMENTATION_DEFINED intentionally left out. The HALs
+  // should choose one of the defined contrete types.
+  //
+  // The following formats are defined in various platform versions, but don't
+  // seem to be used. If we encounter them it's ok to add them to the table.
+  // This does not necessitate a version change.
+  //
+  // The following have been in the framework for a long time:
+  //
+  //   VSOC_PIXEL_FORMAT_YCrCb_420_SP
+  //   VSOC_PIXEL_FORMAT_YCbCr_422_SP
+  //
+  // The following were added in JB_MR2:
+  //
+  //   VSOC_PIXEL_FORMAT_YCbCr_420_888
+  //   VSOC_PIXEL_FORMAT_Y8
+  //   VSOC_PIXEL_FORMAT_Y16
+  //
+  // The following were added in L:
+  //
+  //    VSOC_PIXEL_FORMAT_RAW_OPAQUE
+  //    VSOC_PIXEL_FORMAT_RAW16 (also known as RAW_SENSOR. Define only RAW16)
+  //    VSOC_PIXEL_FORMAT_RAW10
+  //
+  // The following were added in L MR1:
+  //
+  //   VSOC_PIXEL_FORMAT_YCbCr_444_888
+  //   VSOC_PIXEL_FORMAT_YCbCr_422_888
+  //   VSOC_PIXEL_FORMAT_RAW12
+  //   VSOC_PIXEL_FORMAT_FLEX_RGBA_8888
+  //   VSOC_PIXEL_FORMAT_FLEX_RGB_888
+  //
+  // These pixel formats were removed in later framework versions. Implement
+  // only if absolutely necessary.
+  //
+  // Support was dropped in K for:
+  //
+  //   VSOC_PIXEL_FORMAT_RGBA_5551
+  //   VSOC_PIXEL_FORMAT_RGBA_4444
+  //
+  // Supported only in K, L, and LMR1:
+  //
+  //   VSOC_PIXEL_FORMAT_sRGB_X_8888
+  //   VSOC_PIXEL_FORMAT_sRGB_A_8888
+};
+ASSERT_SHM_CONSTANT_VALUE(VSOC_PIXEL_FORMAT_UNINITIALIZED, multi_region);
+ASSERT_SHM_CONSTANT_VALUE(VSOC_PIXEL_FORMAT_BLOB, multi_region);
+ASSERT_SHM_CONSTANT_VALUE(VSOC_PIXEL_FORMAT_RGB_565, multi_region);
+ASSERT_SHM_CONSTANT_VALUE(VSOC_PIXEL_FORMAT_YV12, multi_region);
+ASSERT_SHM_CONSTANT_VALUE(VSOC_PIXEL_FORMAT_YCbCr_420_888, multi_region);
+ASSERT_SHM_CONSTANT_VALUE(VSOC_PIXEL_FORMAT_RGB_888, multi_region);
+ASSERT_SHM_CONSTANT_VALUE(VSOC_PIXEL_FORMAT_RGBA_8888, multi_region);
+ASSERT_SHM_CONSTANT_VALUE(VSOC_PIXEL_FORMAT_RGBX_8888, multi_region);
+ASSERT_SHM_CONSTANT_VALUE(VSOC_PIXEL_FORMAT_BGRA_8888, multi_region);
+
+namespace layout {
+
+// VSoC memory layout for a register that accepts a single pixel format.
+// The value is volatile to ensure that the compiler does not eliminate stores.
+struct PixelFormatRegister {
+  volatile PixelFormat value_;
+};
+ASSERT_SHM_COMPATIBLE(PixelFormatRegister, multi_region);
+
+// Register layout for a mask giving different PixelFormats. Reserve enough
+// space to allow for future expansion. For example, we may well end with
+// a 12 bit per channel format in the future.
+struct PixelFormatMaskRegister {
+  volatile uint64_t value_;
+
+  bool HasValue(PixelFormat in) {
+    return !!(value_ & (uint64_t(1) << in));
+  }
+};
+ASSERT_SHM_COMPATIBLE(PixelFormatMaskRegister, multi_region);
+
+// Ensure that the mask is large enough to hold the highest encodable
+// pixel format.
+static_assert(PixelFormatBuilder<
+              PixelFormatConst::MaxBytesPerPixel,
+              PixelFormatConst::MaxSubformat>::value <
+              8 * sizeof(PixelFormatMaskRegister),
+              "Largest pixel format does not fit in mask");
+}  // layout
+}  // vsoc
diff --git a/common/vsoc/shm/lock.h b/common/vsoc/shm/lock.h
new file mode 100644
index 0000000..2cae5bc
--- /dev/null
+++ b/common/vsoc/shm/lock.h
@@ -0,0 +1,195 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Memory layout for locks of all types.
+
+// The vsoc::layout namespace indicates that these are shared memory structure
+// definitions. The #include's given above are strictly limited, as are the
+// types that can be referenced below.
+
+// For _mm_pause()
+#include <x86intrin.h>
+
+#include <atomic>
+#include <cstdint>
+
+#include <linux/futex.h>
+#include <sys/syscall.h>
+
+#include <unistd.h>
+
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/version.h"
+
+// Host userspace, guest userspace, and the guest kernel must all agree on
+// the relationship between std::atomic and atomic_t. That's hard to do without
+// examining assembly, and we can't really examing atomic_t outside of the
+// kernel tree, but we can at least assert that the host and the guest
+// agree on a size.
+static_assert(sizeof(std::atomic<uint32_t>) == 4, "std::atomic size mismatch");
+
+namespace vsoc {
+
+class RegionView;
+
+namespace layout {
+
+/**
+ * Lock that causes threads to busy loop rather than sleeping.
+ * This lock should never be used when the amount of work in the critical
+ * section cannot be bounded.
+ */
+class SpinLock {
+ public:
+  /**
+   * Acquire the spinlock on the queue. This will effectively block all
+   * readers and writers.
+   */
+  void Lock() {
+    while (lock_.exchange(1)) {
+      _mm_pause();
+    }
+  }
+
+  /**
+   * Release the spinlock.
+   */
+  void Unlock() {
+    lock_ = 0;
+  }
+
+ protected:
+  std::atomic<uint32_t> lock_;
+};
+ASSERT_SHM_COMPATIBLE(SpinLock, multi_region);
+
+/**
+ * This is a generic synchronization primitive that provides space for the
+ * owner of the lock to write platform-specific information.
+ */
+class WaitingLockBase {
+ protected:
+  // Common code to handle locking
+  // Must be called with the kernel's thread id
+  // Returns true if the lock was acquired. In this case the value in
+  // expected_vlaue is undefined.
+  // Returns false if locking failed. The value discovered in the lock word
+  // is returned in expected_value, and should probably be used in a conditional
+  // sleep.
+  bool TryLock(uint32_t tid, uint32_t* expected_value);
+
+  // Common code to handle unlocking.
+  // Must be called with the kernel's thread id
+  // Returns sides that should be signalled or 0
+  Sides UnlockCommon(uint32_t tid);
+
+  // Non-zero values in this word indicate that the lock is in use.
+  // This is 32 bits for compatibility with futex()
+  std::atomic<uint32_t> lock_uint32_;
+
+  // Pad so we line up with glib's pthread_mutex_t and can share the same queue.
+  // These fields may be redefined at any point in the future. They should not
+  // be used.
+ private:
+// These fields are known to be unused and are provided for compatibility
+// with glibc's locks.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-private-field"
+  uint32_t reserved_1_;
+  char reserved_2_[16];
+  // Provide scratch space for the owner of the lock. The content of this space
+  // is undefined when the lock is acquired. The owner may write to and read
+  // from it while it holds the lock, but must relinquish control before
+  // releasing the lock.
+  //
+  // This is intended to support Linux robust futexes. See the documentation
+  // in the kernel tree:
+  //   Documentation/robust-futex-ABI.txt
+ public:
+  int64_t owner_scratch_[2];
+#pragma clang diagnostic pop
+};
+ASSERT_SHM_COMPATIBLE(WaitingLockBase, multi_region);
+
+/**
+ * GuestLocks can be acquired and released only on the guest. They reside
+ * in the shared memory window because mutiple guest processes may need
+ * to coordinate activities in certain shared memory regions.
+ *
+ * Representing this as a concrete type allows for some optimizations when
+ * signalling on the lock.
+ */
+class GuestLock : public WaitingLockBase {
+ public:
+#ifndef CUTTLEFISH_HOST
+  void Lock();
+  void Unlock();
+#endif
+};
+ASSERT_SHM_COMPATIBLE(GuestLock, multi_region);
+
+/**
+ * HostLocks can be acquired and released only on the host. They reside
+ * in the shared memory window because mutiple host processes may need
+ * to coordinate activities in certain shared memory regions.
+ *
+ * Representing this as a concrete type allows for some optimizations when
+ * signalling on the lock.
+ */
+class HostLock : public WaitingLockBase {
+ public:
+#ifdef CUTTLEFISH_HOST
+  void Lock();
+  void Unlock();
+#endif
+};
+ASSERT_SHM_COMPATIBLE(HostLock, multi_region);
+
+/**
+ * GuestAndHostLocks can be acquired and released on either side of the
+ * shared memory window. The locks attempt to enforce fairness by using
+ * a round-trip signal:
+ *
+ *   When a guest releases a lock this code sends a signal to wake the host,
+ *   but not other guest waiters.
+ *
+ *   The wake handler on the host wakes up and local waiters and then reposts
+ *   the signal to the guest.
+ *
+ *   When the guest receives the signal from the host it then wakes ups
+ *   any waiters.
+ *
+ * A similar scenario applies when the host releases a lock with guest waiters.
+ *
+ * Signalling across the shared memory window twice has non-trivial cost.
+ * There are some optimizations in the code to prevent the full round-trip
+ * if the process releasing the lock can confirm that there are no waiters on
+ * the other side.
+ *
+ * Representing this as a concrete type allows for some optimizations when
+ * signalling on the lock.
+ */
+class GuestAndHostLock : public WaitingLockBase {
+ public:
+  void Lock(RegionView*);
+  void Unlock(RegionView*);
+};
+ASSERT_SHM_COMPATIBLE(GuestAndHostLock, multi_region);
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/version.h b/common/vsoc/shm/version.h
new file mode 100644
index 0000000..54baf34
--- /dev/null
+++ b/common/vsoc/shm/version.h
@@ -0,0 +1,147 @@
+#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.
+ */
+
+// Version information for structures that are present in VSoC shared memory
+// windows. The proper use of this file will:
+//
+//   * ensure that the guest and host builds agree on the sizes of the shared
+//     structures.
+//
+//   * provides a single version code for the entire vsoc layout, assuming
+//     that reviewers excercise some care.
+//
+//
+//  Use:
+//
+//    Every new class / structure in the shm folder needs to add a size
+//    entry here, #include the base.h file, and add a ASSERT_SHM_COMPATIBLE
+//    instantiation just below the class definition,
+//
+//    For templatized classes / structs the author should choose a fixed size,
+//    create a using alias, and instantiate the checks on the alias.
+//    See CircularByteQueue64k for an example of this usage.
+//
+//   Note to reviewers:
+//
+//     It is probably ok to approve new additions  here without forcing a
+//     a version change.
+//
+//     However, the version must increment for any change in the value of a
+//     constant.
+//
+//     #ifdef, etc is absolutely forbidden in this file and highly discouraged
+//     in the other vsoc/shm files.
+
+#include <cstdint>
+
+namespace vsoc {
+namespace layout {
+namespace version_info {
+namespace multi_region {
+
+namespace {
+// Increment this for any change in size in the fundamental structures listed
+// below.  If the size of any of these structures changes we must examine all
+// of the regions bumping the version number if they used the fields in their
+// definition.
+const uint32_t version = 0;
+}  // namespace
+
+static const std::size_t Base_size = 1;
+static const std::size_t CircularQueueBase64k_size = 65548;
+static const std::size_t CircularByteQueue64k_size = 65548;
+static const std::size_t CircularPacketQueue64k_size = 65548;
+static const std::size_t GuestLock_size = 40;
+static const std::size_t GuestAndHostLock_size = 40;
+static const std::size_t HostLock_size = 40;
+static const std::size_t PixelFormatRegister_size = 4;
+static const std::size_t PixelFormatMaskRegister_size = 8;
+static const std::size_t RegionLayout_size = 1;
+static const std::size_t Sides_size = 4;
+static const std::size_t SpinLock_size = 4;
+static const std::size_t WaitingLockBase_size = 40;
+
+namespace constant_values {
+static const uint32_t VSOC_PIXEL_FORMAT_UNINITIALIZED = 0;
+static const uint32_t VSOC_PIXEL_FORMAT_BLOB = 1;
+
+static const uint32_t VSOC_PIXEL_FORMAT_RGB_565 = 8;
+static const uint32_t VSOC_PIXEL_FORMAT_YV12 = 9;
+static const uint32_t VSOC_PIXEL_FORMAT_YCbCr_420_888 = 10;
+
+static const uint32_t VSOC_PIXEL_FORMAT_RGB_888 = 16;
+
+static const uint32_t VSOC_PIXEL_FORMAT_RGBA_8888 = 24;
+static const uint32_t VSOC_PIXEL_FORMAT_RGBX_8888 = 25;
+static const uint32_t VSOC_PIXEL_FORMAT_BGRA_8888 = 26;
+}  // namespace constant_values
+}  // namespace multi_region
+
+// Versioning information for gralloc_layout.h
+// Changes to these structures will affect only the gralloc region
+namespace gralloc {
+namespace {
+const uint32_t version = 0;
+}
+static const std::size_t BufferEntry_size = 32;
+static const std::size_t GrallocManagerLayout_size = 80;
+static const std::size_t GrallocBufferLayout_size = 1;
+}  // namespace gralloc
+
+// Versioning information for fb_bcast_layout.h
+// Changes to these structures will affect only the framebuffer broadcast region
+namespace framebuffer {
+namespace {
+const uint32_t version = 1;
+}
+static const std::size_t FBBroadcastLayout_size = 24;
+}  // namespace framebuffer
+
+// Versioning information for wifi_layout.h
+namespace wifi {
+namespace {
+constexpr uint32_t version = 0;
+}  // namespace
+constexpr size_t WifiExchangeLayout_size =
+    65548 + // sizeof(CircularPacketQueue<16, 8192>) - forward
+    65548 + // sizeof(CircularPacketQueue<16, 8192>) - reverse
+    4 +     // Lock config_lock_
+    2 +     // bool config_ready_ (and even address alignment)
+    6;      // uint8_t[6] MAC address.
+}  // namespace wifi
+
+
+// Versioning information for e2e_test_region.h
+// Changes to these structures will affect only the e2e_test_region
+namespace e2e_test {
+namespace {
+const uint32_t version = 1;
+}
+static const std::size_t E2EManagerTestRegionLayout_size = 16;
+static const std::size_t E2EMemoryFill_size = 64;
+static const std::size_t E2EPrimaryTestRegionLayout_size = 80;
+static const std::size_t E2ESecondaryTestRegionLayout_size = 80;
+static const std::size_t E2ETestRegionLayout_size = 80;
+static const std::size_t E2ETestStageRegister_size = 4;
+static const std::size_t E2EUnfindableRegionLayout_size = 80;
+static const std::size_t E2EManagedTestRegionLayout_size = 4;
+}  // namespace e2e_test
+
+}  // namespace version_info
+}  // namespace layout
+}  // namespace vsoc
diff --git a/common/vsoc/shm/wifi_exchange_layout.h b/common/vsoc/shm/wifi_exchange_layout.h
new file mode 100644
index 0000000..10888cd
--- /dev/null
+++ b/common/vsoc/shm/wifi_exchange_layout.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <linux/if_ether.h>
+
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/circqueue.h"
+#include "common/vsoc/shm/lock.h"
+#include "common/vsoc/shm/version.h"
+
+// Memory layout for wifi packet exchange region.
+namespace vsoc {
+namespace layout {
+namespace wifi {
+
+struct WifiExchangeLayout : public RegionLayout {
+  // 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;
+
+  // config_lock_ manages access to configuration section
+  SpinLock config_lock_;
+  // config_ready_ indicates whether config section is ready to be accessed.
+  bool config_ready_;
+  // Desired MAC address for guest device.
+  uint8_t mac_address[ETH_ALEN];
+
+  static const char* region_name;
+};
+
+ASSERT_SHM_COMPATIBLE(WifiExchangeLayout, wifi);
+
+}  // namespace wifi
+}  // namespace layout
+}  // namespace vsoc
diff --git a/guest/Android.bp b/guest/Android.bp
new file mode 100644
index 0000000..ee8105d
--- /dev/null
+++ b/guest/Android.bp
@@ -0,0 +1,18 @@
+//
+// 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.
+
+subdirs = [
+    "commands",
+]
diff --git a/guest/commands/Android.bp b/guest/commands/Android.bp
new file mode 100644
index 0000000..cbd09e8
--- /dev/null
+++ b/guest/commands/Android.bp
@@ -0,0 +1,19 @@
+//
+// 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.
+
+subdirs = [
+    "wifirouter",
+]
+
diff --git a/guest/commands/audio/Android.mk b/guest/commands/audio/Android.mk
new file mode 100644
index 0000000..035c7d3
--- /dev/null
+++ b/guest/commands/audio/Android.mk
@@ -0,0 +1,64 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := first
+
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    libcutils \
+    cuttlefish_auto_resources \
+    libcuttlefish_fs \
+    cuttlefish_time
+
+LOCAL_SRC_FILES := \
+    audio_hal.cpp \
+    vsoc_audio.cpp \
+    vsoc_audio_input_stream.cpp \
+    vsoc_audio_output_stream.cpp \
+    vsoc_audio_message.cpp
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    $(VSOC_STLPORT_INCLUDES) \
+    frameworks/native/include/media/hardware \
+    $(call include-path-for, audio)
+
+LOCAL_STATIC_LIBRARIES := \
+    libcutils \
+    libcuttlefish_remoter_framework \
+    $(VSOC_STLPORT_STATIC_LIBS)
+
+LOCAL_CFLAGS := \
+    -Wall -Werror -Wno-parentheses -Wno-missing-field-initializers \
+    -DLOG_TAG=\"VSoC-Audio\" \
+    $(VSOC_VERSION_CFLAGS)
+
+# Work-around for the non-standard language feautures used in
+# system/media/audio/include/system/audio.h
+LOCAL_CLANG_CFLAGS := -Wno-gnu-designator
+
+LOCAL_MODULE := audio.primary.vsoc
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/commands/audio/audio_hal.cpp b/guest/commands/audio/audio_hal.cpp
new file mode 100644
index 0000000..1b05d40
--- /dev/null
+++ b/guest/commands/audio/audio_hal.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/commands/audio/vsoc_audio.h"
+
+#include "guest/commands/audio/audio_hal.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+static hw_module_methods_t hal_module_methods = {
+  VSOC_STATIC_INITIALIZER(open) cvd::GceAudio::Open,
+};
+
+
+audio_module HAL_MODULE_INFO_SYM = {
+  VSOC_STATIC_INITIALIZER(common) {
+    VSOC_STATIC_INITIALIZER(tag) HARDWARE_MODULE_TAG,
+    VSOC_STATIC_INITIALIZER(module_api_version) AUDIO_MODULE_API_VERSION_0_1,
+    VSOC_STATIC_INITIALIZER(hal_api_version) HARDWARE_HAL_API_VERSION,
+    VSOC_STATIC_INITIALIZER(id) AUDIO_HARDWARE_MODULE_ID,
+    VSOC_STATIC_INITIALIZER(name) "GCE Audio HW HAL",
+    VSOC_STATIC_INITIALIZER(author) "The Android Open Source Project",
+    VSOC_STATIC_INITIALIZER(methods) &hal_module_methods,
+  },
+};
diff --git a/guest/commands/audio/audio_hal.h b/guest/commands/audio/audio_hal.h
new file mode 100644
index 0000000..36d47cb
--- /dev/null
+++ b/guest/commands/audio/audio_hal.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
+
+#define AUDIO_DEBUG 0
+
+#if AUDIO_DEBUG
+#  define D(...) ALOGD(__VA_ARGS__)
+#else
+#  define D(...) ((void)0)
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <cutils/log.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio.h>
diff --git a/guest/commands/audio/policy/Android.mk b/guest/commands/audio/policy/Android.mk
new file mode 100644
index 0000000..da72309
--- /dev/null
+++ b/guest/commands/audio/policy/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := first
+
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    libcutils
+
+LOCAL_SRC_FILES := \
+    vsoc_audio_policy_hal.cpp
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    $(VSOC_STLPORT_INCLUDES) \
+    frameworks/native/include/media/hardware \
+    $(call include-path-for, audio)
+
+LOCAL_CFLAGS := -Wall -DLOG_TAG=\"VSoC-AudioPolicy\"
+
+LOCAL_MODULE := audio_policy.vsoc
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/commands/audio/policy/vsoc_audio_policy_hal.cpp b/guest/commands/audio/policy/vsoc_audio_policy_hal.cpp
new file mode 100644
index 0000000..047e27d
--- /dev/null
+++ b/guest/commands/audio/policy/vsoc_audio_policy_hal.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <system/audio_policy.h>
+#include <hardware/audio_policy.h>
+
+#include "guest/commands/audio/policy/vsoc_audio_policy_hal.h"
+
+namespace cvd {
+
+int GceAudioPolicy::Create(
+    const audio_policy_device* device,
+    audio_policy_service_ops* aps_ops,
+    void* service, audio_policy** ap) {
+  D("%s", __FUNCTION__);
+  audio_policy_device* dev;
+  gce_audio_policy* dap;
+  int ret;
+
+  *ap = NULL;
+
+  if (!service || !aps_ops) {
+    return -EINVAL;
+  }
+
+  dap = (gce_audio_policy*) calloc(1, sizeof(*dap));
+  if (!dap) {
+    return -ENOMEM;
+  }
+
+  dap->policy.set_device_connection_state =
+      &GceAudioPolicy::SetDeviceConnectionState;
+  dap->policy.get_device_connection_state =
+      &GceAudioPolicy::GetDeviceConnectionState;
+  dap->policy.set_phone_state = &GceAudioPolicy::SetPhoneState;
+  dap->policy.set_ringer_mode = &GceAudioPolicy::SetRingerMode;
+  dap->policy.set_force_use = &GceAudioPolicy::SetForceUse;
+  dap->policy.get_force_use = &GceAudioPolicy::GetForceUse;
+  dap->policy.set_can_mute_enforced_audible =
+      &GceAudioPolicy::SetCanMuteEnforcedAudible;
+  dap->policy.init_check = &GceAudioPolicy::InitCheck;
+  dap->policy.get_output = &GceAudioPolicy::GetOutput;
+  dap->policy.start_output = &GceAudioPolicy::StartOutput;
+  dap->policy.stop_output = &GceAudioPolicy::StopOutput;
+  dap->policy.release_output = &GceAudioPolicy::ReleaseOutput;
+  dap->policy.get_input = &GceAudioPolicy::GetInput;
+  dap->policy.start_input = &GceAudioPolicy::StartInput;
+  dap->policy.stop_input = &GceAudioPolicy::StopInput;
+  dap->policy.release_input = &GceAudioPolicy::ReleaseInput;
+  dap->policy.init_stream_volume = &GceAudioPolicy::InitStreamVolume;
+  dap->policy.set_stream_volume_index = &GceAudioPolicy::SetStreamVolumeIndex;
+  dap->policy.get_stream_volume_index = &GceAudioPolicy::GetStreamVolumeIndex;
+  dap->policy.set_stream_volume_index_for_device =
+      &GceAudioPolicy::SetStreamVolumeIndexForDevice;
+  dap->policy.get_stream_volume_index_for_device =
+      &GceAudioPolicy::GetStreamVolumeIndexForDevice;
+  dap->policy.get_strategy_for_stream = &GceAudioPolicy::GetStrategyForStream;
+  dap->policy.get_devices_for_stream = &GceAudioPolicy::GetDevicesForStream;
+  dap->policy.get_output_for_effect = &GceAudioPolicy::GetOutputForEffect;
+  dap->policy.register_effect = &GceAudioPolicy::RegisterEffect;
+  dap->policy.unregister_effect = &GceAudioPolicy::UnregisterEffect;
+  dap->policy.set_effect_enabled = &GceAudioPolicy::SetEffectEnabled;
+  dap->policy.is_stream_active = &GceAudioPolicy::IsStreamActive;
+  dap->policy.dump = &GceAudioPolicy::Dump;
+#ifdef ENABLE_OFFLOAD
+  dap->policy.is_offload_supported = &GceAudioPolicy::IsOffloadSupported;
+#endif
+
+  dap->service = service;
+  dap->aps_ops = aps_ops;
+
+  *ap = &dap->policy;
+  return 0;
+}
+
+
+int GceAudioPolicy::Destroy(const audio_policy_device* ap_dev,
+                            audio_policy* ap) {
+  D("%s", __FUNCTION__);
+  free(ap);
+  return 0;
+}
+
+
+int GceAudioPolicy::Close(hw_device_t* device) {
+  D("%s", __FUNCTION__);
+  free(device);
+  return 0;
+}
+
+
+int GceAudioPolicy::Open(
+    const hw_module_t* module, const char* name, hw_device_t** device) {
+  D("%s", __FUNCTION__);
+  audio_policy_device* dev;
+
+  *device = NULL;
+
+  if (strcmp(name, AUDIO_POLICY_INTERFACE) != 0) {
+    return -EINVAL;
+  }
+
+  dev = (audio_policy_device*) calloc(1, sizeof(*dev));
+  if (!dev) {
+    return -ENOMEM;
+  }
+
+  dev->common.tag = HARDWARE_DEVICE_TAG;
+  dev->common.version = 0;
+  dev->common.module = (hw_module_t*) module;
+  dev->common.close = &GceAudioPolicy::Close;
+  dev->create_audio_policy = &GceAudioPolicy::Create;
+  dev->destroy_audio_policy = &GceAudioPolicy::Destroy;
+
+  *device = &dev->common;
+
+  return 0;
+}
+
+}
+
+static hw_module_methods_t gce_audio_policy_module_methods = {
+  .open = &cvd::GceAudioPolicy::Open,
+};
+
+
+audio_policy_module HAL_MODULE_INFO_SYM = {
+  .common = {
+    .tag           = HARDWARE_MODULE_TAG,
+    .version_major = 1,
+    .version_minor = 0,
+    .id            = AUDIO_POLICY_HARDWARE_MODULE_ID,
+    .name          = "GCE Audio Policy HAL",
+    .author        = "The Android Open Source Project",
+    .methods       = &gce_audio_policy_module_methods,
+  },
+};
diff --git a/guest/commands/audio/policy/vsoc_audio_policy_hal.h b/guest/commands/audio/policy/vsoc_audio_policy_hal.h
new file mode 100644
index 0000000..365d1c4
--- /dev/null
+++ b/guest/commands/audio/policy/vsoc_audio_policy_hal.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <errno.h>
+#include <string.h>
+#include <cutils/log.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio.h>
+
+#define AUDIO_DEBUG 1
+
+#if AUDIO_DEBUG
+#  define D(...) ALOGD(__VA_ARGS__)
+#else
+#  define D(...) ((void)0)
+#endif
+
+namespace cvd {
+
+struct gce_audio_policy {
+  audio_policy policy;
+
+  audio_policy_service_ops* aps_ops;
+  void* service;
+};
+
+
+class GceAudioPolicy {
+ public:
+  GceAudioPolicy() {}
+  ~GceAudioPolicy() {}
+
+  static int Open(
+      const hw_module_t* module, const char* name, hw_device_t** device);
+  static int Create(const audio_policy_device* device,
+      audio_policy_service_ops* aps_ops, void* service,
+      audio_policy** ap);
+  static int Destroy(const audio_policy_device* ap_dev,
+                     audio_policy* ap);
+  static int Close(hw_device_t* device);
+
+  static int SetDeviceConnectionState(
+      audio_policy* pol, audio_devices_t device,
+      audio_policy_dev_state_t state, const char* device_address) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static audio_policy_dev_state_t GetDeviceConnectionState(
+      const audio_policy* pol, audio_devices_t device,
+      const char* device_address) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
+  }
+
+  static void SetPhoneState(audio_policy* pol, audio_mode_t state) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static void SetRingerMode(audio_policy* pol, uint32_t mode,
+                            uint32_t mask) {
+    ALOGW("%s: deprecated", __FUNCTION__);
+  }
+
+  static void SetForceUse(
+      audio_policy* pol, audio_policy_force_use_t usage,
+    audio_policy_forced_cfg_t config) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static audio_policy_forced_cfg_t GetForceUse(
+      const audio_policy* pol, audio_policy_force_use_t usage) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return AUDIO_POLICY_FORCE_NONE;
+  }
+
+  static void SetCanMuteEnforcedAudible(
+      audio_policy* pol, bool can_mute) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static int InitCheck(const audio_policy* pol) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static audio_io_handle_t GetOutput(
+      audio_policy* pol, audio_stream_type_t stream,
+      uint32_t sampling_rate, audio_format_t format,
+      audio_channel_mask_t channelMask, audio_output_flags_t flags
+#ifdef ENABLE_OFFLOAD
+      , const audio_offload_info_t* info
+#endif
+      ) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static int StartOutput(audio_policy* pol, audio_io_handle_t output,
+                         audio_stream_type_t stream, int session) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int StopOutput(audio_policy* pol, audio_io_handle_t output,
+                        audio_stream_type_t stream, int session) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static void ReleaseOutput(
+      audio_policy* pol, audio_io_handle_t output) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static audio_io_handle_t GetInput(
+      audio_policy* pol, audio_source_t inputSource,
+      uint32_t sampling_rate, audio_format_t format,
+      audio_channel_mask_t channelMask, audio_in_acoustics_t acoustics) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static int StartInput(audio_policy* pol, audio_io_handle_t input) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int StopInput(audio_policy* pol, audio_io_handle_t input) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static void ReleaseInput(
+      audio_policy* pol, audio_io_handle_t input) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static void InitStreamVolume(audio_policy* pol,
+                               audio_stream_type_t stream, int index_min,
+                               int index_max) {
+    ALOGE("%s: not supported", __FUNCTION__);
+  }
+
+  static int SetStreamVolumeIndex(audio_policy* pol,
+                                  audio_stream_type_t stream, int index) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int GetStreamVolumeIndex(const audio_policy* pol,
+                                  audio_stream_type_t stream, int* index) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int SetStreamVolumeIndexForDevice(
+      audio_policy* pol, audio_stream_type_t stream,
+      int index, audio_devices_t device) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int GetStreamVolumeIndexForDevice(
+      const audio_policy* pol, audio_stream_type_t stream,
+      int* index, audio_devices_t device) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static uint32_t GetStrategyForStream(const audio_policy* pol,
+                                       audio_stream_type_t stream) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static audio_devices_t GetDevicesForStream(const audio_policy* pol,
+                                             audio_stream_type_t stream) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static audio_io_handle_t GetOutputForEffect(
+      audio_policy* pol, const effect_descriptor_s* desc) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return 0;
+  }
+
+  static int RegisterEffect(
+      audio_policy* pol, const effect_descriptor_s* desc,
+      audio_io_handle_t output, uint32_t strategy, int session, int id) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int UnregisterEffect(audio_policy* pol, int id) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static int SetEffectEnabled(audio_policy* pol, int id, bool enabled) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+  static bool IsStreamActive(
+      const audio_policy* pol, audio_stream_type_t stream,
+      uint32_t in_past_ms) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return false;
+  }
+
+  static int Dump(const audio_policy* pol, int fd) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return -ENOSYS;
+  }
+
+#ifdef ENABLE_OFFLOAD
+  static bool IsOffloadSupported(const audio_policy* pol,
+                                 const audio_offload_info_t* info) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return false;
+  }
+#endif
+};
+
+}
+
diff --git a/guest/commands/audio/simulated_buffer.h b/guest/commands/audio/simulated_buffer.h
new file mode 100644
index 0000000..5ea31c2
--- /dev/null
+++ b/guest/commands/audio/simulated_buffer.h
@@ -0,0 +1,315 @@
+/*
+ * 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 <stdint.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "common/libs/time/monotonic_time.h"
+
+/**
+ * This abstract class simulates a buffer that either fills or empties at
+ * a specified rate.
+ *
+ * The simulated buffer automatically fills or empties at a specific rate.
+ *
+ * An item is the thing contained in the simulated buffer. Items are moved
+ * in and out of the buffer without subdivision.
+ *
+ * An integral number of items must arrive / depart in each second.
+ * This number is stored in items_per_second_
+ *
+ * items_per_second * 2000000000 must fit within an int64_t. This
+ * works if items_per_second is represented by an int32.
+ *
+ * The base class does have the concept of capacity, but doesn't use it.
+ * It is included here to simplify unit testing.
+ *
+ * For actual use, see SimulatedInputBuffer and SimulatedOutputBuffer below.
+ */
+class SimulatedBufferBase {
+ public:
+  static inline int64_t divide_and_round_up(int64_t q, int64_t d) {
+    return q / d + ((q % d) != 0);
+  }
+
+  SimulatedBufferBase(
+      int32_t items_per_second,
+      int64_t simulated_item_capacity,
+      cvd::time::MonotonicTimePointFactory* clock =
+        cvd::time::MonotonicTimePointFactory::GetInstance()) :
+    clock_(clock),
+    current_item_num_(0),
+    base_item_num_(0),
+    simulated_item_capacity_(simulated_item_capacity),
+    items_per_second_(items_per_second),
+    initialize_(true),
+    paused_(false) { }
+
+  virtual ~SimulatedBufferBase() { }
+
+  int64_t GetCurrentItemNum() {
+    Update();
+    return current_item_num_;
+  }
+
+  const cvd::time::MonotonicTimePoint GetLastUpdatedTime() const {
+    return current_time_;
+  }
+
+  // Sleep for the given amount of time. Subclasses may override this to use
+  // different sleep calls.
+  // Sleep is best-effort. The code assumes that the acutal sleep time may be
+  // greater or less than the time requested.
+  virtual void SleepUntilTime(const cvd::time::MonotonicTimePoint& in) {
+    struct timespec ts;
+    in.ToTimespec(&ts);
+    clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
+  }
+
+  // The time counter may not start at 0. Concrete classes should call this
+  // to allow the buffer simulation to read the current time number and
+  // initialize its internal state.
+  virtual void Init() {
+    if (initialize_) {
+      clock_->FetchCurrentTime(&base_time_);
+      current_time_ = base_time_;
+      initialize_ = false;
+    }
+  }
+
+  virtual void Update() {
+    if (initialize_) {
+      Init();
+    }
+    cvd::time::MonotonicTimePoint now;
+    clock_->FetchCurrentTime(&now);
+    // We can't call FetchCurrentTime() in the constuctor because a subclass may
+    // want to override it, so we initialze the times to 0. If we detect this
+    // case go ahead and initialize to a current timestamp.
+    if (paused_) {
+      base_time_ += now - current_time_;
+      current_time_ = now;
+      return;
+    }
+    // Avoid potential overflow by limiting the scaling to one time second.
+    // There is no round-off error here because the bases are adjusted for full
+    // seconds.
+    // There is no issue with int64 overflow because 2's compliment subtraction
+    // is immune to overflow.
+    // However, this does assume that kNanosecondsPerSecond * items_per_second_
+    // fits in an int64.
+    cvd::time::Seconds seconds(now - base_time_);
+    base_time_ += seconds;
+    base_item_num_ += seconds.count() * items_per_second_;
+    current_time_ = now;
+    current_item_num_ =
+        cvd::time::Nanoseconds(now - base_time_).count() *
+        items_per_second_ / cvd::time::kNanosecondsPerSecond +
+        base_item_num_;
+  }
+
+  // If set to true new items will not be created.
+  bool SetPaused(bool new_state) {
+    bool rval = paused_;
+    Update();
+    paused_ = new_state;
+    return rval;
+  }
+
+  // Calculate the TimePoint that corresponds to an item.
+  // Caution: This may not return a correct time for items in the past.
+  cvd::time::MonotonicTimePoint CalculateItemTime(int64_t item) {
+    int64_t seconds = (item - base_item_num_) / items_per_second_;
+    int64_t new_base_item_num = base_item_num_ + seconds * items_per_second_;
+    return base_time_ + cvd::time::Seconds(seconds) +
+      cvd::time::Nanoseconds(divide_and_round_up(
+          (item - new_base_item_num) *
+          cvd::time::kNanosecondsPerSecond,
+          items_per_second_));
+  }
+
+  // Sleep until the given item number is generated. If the generator is
+  // paused unpause it to make the sleep finite.
+  void SleepUntilItem(int64_t item) {
+    if (paused_) {
+      SetPaused(false);
+    }
+    cvd::time::MonotonicTimePoint desired_time =
+        CalculateItemTime(item);
+    while (1) {
+      Update();
+      if (current_item_num_ - item >= 0) {
+        return;
+      }
+      SleepUntilTime(desired_time);
+    }
+  }
+
+ protected:
+  // Source of the timepoints.
+  cvd::time::MonotonicTimePointFactory* clock_;
+  // Time when the other values in the structure were updated.
+  cvd::time::MonotonicTimePoint current_time_;
+  // Most recent time when there was no round-off error between the clock and
+  // items.
+  cvd::time::MonotonicTimePoint base_time_;
+  // Number of the current item.
+  int64_t current_item_num_;
+  // Most recent item number where there was no round-off error between the
+  // clock and items.
+  int64_t base_item_num_;
+  // Simulated_Item_Capacity of the buffer in items.
+  int64_t simulated_item_capacity_;
+  // Number of items that are created in 1s. A typical number would be 48000.
+  int32_t items_per_second_;
+  bool initialize_;
+  // If true then don't generate new items.
+  bool paused_;
+};
+
+/**
+ * This is a simulation of an output buffer that drains at a constant rate.
+ */
+class SimulatedOutputBuffer : public SimulatedBufferBase {
+ public:
+  SimulatedOutputBuffer(
+      int64_t item_rate,
+      int64_t simulated_item_capacity,
+      cvd::time::MonotonicTimePointFactory* clock =
+        cvd::time::MonotonicTimePointFactory::GetInstance()) :
+      SimulatedBufferBase(item_rate, simulated_item_capacity, clock) {
+    output_buffer_item_num_ = current_item_num_;
+  }
+
+  void Update() override {
+    SimulatedBufferBase::Update();
+    if ((output_buffer_item_num_ - current_item_num_) < 0) {
+      // We ran out of items at some point in the past. However, the
+      // output capactiy can't be negative.
+      output_buffer_item_num_ = current_item_num_;
+    }
+  }
+
+  int64_t AddToOutputBuffer(int64_t num_new_items, bool block) {
+    Update();
+    // The easy case: num_new_items fit in the bucket.
+    if ((output_buffer_item_num_ + num_new_items - current_item_num_) <=
+        simulated_item_capacity_) {
+      output_buffer_item_num_ += num_new_items;
+      return num_new_items;
+    }
+    // If we're non-blocking accept enough items to fill the output.
+    if (!block) {
+      int64_t used = current_item_num_ + simulated_item_capacity_ -
+          output_buffer_item_num_;
+      output_buffer_item_num_ = current_item_num_ + simulated_item_capacity_;
+      return used;
+    }
+    int64_t new_output_buffer_item_num = output_buffer_item_num_ + num_new_items;
+    SleepUntilItem(new_output_buffer_item_num - simulated_item_capacity_);
+    output_buffer_item_num_ = new_output_buffer_item_num;
+    return num_new_items;
+  }
+
+  int64_t GetNextOutputBufferItemNum() {
+    Update();
+    return output_buffer_item_num_;
+  }
+
+  cvd::time::MonotonicTimePoint GetNextOutputBufferItemTime() {
+    Update();
+    return CalculateItemTime(output_buffer_item_num_);
+  }
+
+  int64_t GetOutputBufferSize() {
+    Update();
+    return output_buffer_item_num_ - current_item_num_;
+  }
+
+  void Drain() {
+    SleepUntilItem(output_buffer_item_num_);
+  }
+
+ protected:
+  int64_t output_buffer_item_num_;
+};
+
+/**
+ * Simulates an input buffer that fills at a constant rate.
+ */
+class SimulatedInputBuffer : public SimulatedBufferBase {
+ public:
+  SimulatedInputBuffer(
+      int64_t item_rate,
+      int64_t simulated_item_capacity,
+      cvd::time::MonotonicTimePointFactory* clock =
+        cvd::time::MonotonicTimePointFactory::GetInstance()) :
+      SimulatedBufferBase(item_rate, simulated_item_capacity, clock) {
+    input_buffer_item_num_ = current_item_num_;
+    lost_input_items_ = 0;
+  }
+
+  void Update() override {
+    SimulatedBufferBase::Update();
+    if ((current_item_num_ - input_buffer_item_num_) >
+        simulated_item_capacity_) {
+      // The buffer overflowed at some point in the past. Account for the lost
+      // times.
+      int64_t new_input_buffer_item_num =
+          current_item_num_ - simulated_item_capacity_;
+      lost_input_items_ +=
+          new_input_buffer_item_num - input_buffer_item_num_;
+      input_buffer_item_num_ = new_input_buffer_item_num;
+    }
+  }
+
+  int64_t RemoveFromInputBuffer(int64_t num_items_wanted, bool block) {
+    Update();
+    if (!block) {
+      int64_t num_items_available = current_item_num_ - input_buffer_item_num_;
+      if (num_items_available < num_items_wanted) {
+        input_buffer_item_num_ += num_items_available;
+        return num_items_available;
+      } else {
+        input_buffer_item_num_ += num_items_wanted;
+        return num_items_wanted;
+      }
+    }
+    // Calculate the item number that is being claimed. Sleep until it appears.
+    // Advancing input_buffer_item_num_ causes a negative value to be compared
+    // to the capacity, effectively disabling the overflow detection code
+    // in Update().
+    input_buffer_item_num_ += num_items_wanted;
+    while (input_buffer_item_num_ - current_item_num_ > 0) {
+      SleepUntilItem(input_buffer_item_num_);
+    }
+    return num_items_wanted;
+  }
+
+  int64_t GetLostInputItems() {
+    Update();
+    int64_t rval = lost_input_items_;
+    lost_input_items_ = 0;
+    return rval;
+  }
+
+ protected:
+  int64_t input_buffer_item_num_;
+  int64_t lost_input_items_;
+};
+
diff --git a/guest/commands/audio/vsoc_audio.cpp b/guest/commands/audio/vsoc_audio.cpp
new file mode 100644
index 0000000..68c896d
--- /dev/null
+++ b/guest/commands/audio/vsoc_audio.cpp
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/commands/audio/audio_hal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+extern "C" {
+#include <cutils/str_parms.h>
+}
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/fs/shared_select.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "common/libs/threads/thunkers.h"
+#include "guest/commands/audio/vsoc_audio.h"
+#include "guest/commands/audio/vsoc_audio_input_stream.h"
+#include "guest/commands/audio/vsoc_audio_output_stream.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+#include "guest/libs/remoter/remoter_framework_pkt.h"
+
+using cvd::LockGuard;
+using cvd::Mutex;
+
+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() {
+  D("GceAudio::%s", __FUNCTION__);
+  {
+    LockGuard<Mutex> guard(lock_);
+    for (std::list<GceAudioOutputStream*>::iterator it = output_list_.begin();
+         it != output_list_.end(); ++it) {
+      delete *it;
+    }
+    for (input_map_t::iterator it = input_map_.begin();
+         it != input_map_.end(); ++it) {
+      delete it->second;
+    }
+  }
+  // Make certain that the listener thread wakes up
+  cvd::SharedFD temp_client =
+      cvd::SharedFD::SocketSeqPacketClient(
+          gce_audio_message::kAudioHALSocketName);
+  uint64_t dummy_val = 1;
+  terminate_listener_thread_event_->Write(&dummy_val, sizeof dummy_val);
+  pthread_join(listener_thread_, NULL);
+  delete this;
+  return 0;
+}
+
+cvd::SharedFD GceAudio::GetAudioFd() {
+  LockGuard<Mutex> guard(lock_);
+  return audio_data_socket_;
+}
+
+size_t GceAudio::GetInputBufferSize(const audio_config*) const {
+  return IN_BUFFER_BYTES;
+}
+
+uint32_t GceAudio::GetSupportedDevices() const {
+  return AUDIO_DEVICE_OUT_EARPIECE |
+      AUDIO_DEVICE_OUT_SPEAKER |
+      AUDIO_DEVICE_OUT_DEFAULT |
+      AUDIO_DEVICE_IN_COMMUNICATION |
+      AUDIO_DEVICE_IN_BUILTIN_MIC |
+      AUDIO_DEVICE_IN_WIRED_HEADSET |
+      AUDIO_DEVICE_IN_VOICE_CALL |
+      AUDIO_DEVICE_IN_DEFAULT;
+}
+
+int GceAudio::InitCheck() const {
+  D("GceAudio::%s", __FUNCTION__);
+  return 0;
+}
+
+int GceAudio::SetMicMute(bool state) {
+  D("GceAudio::%s", __FUNCTION__);
+  LockGuard<Mutex> guard(lock_);
+  mic_muted_ = state;
+  return 0;
+}
+
+int GceAudio::GetMicMute(bool *state) const {
+  D("GceAudio::%s", __FUNCTION__);
+  LockGuard<Mutex> guard(lock_);
+  *state = mic_muted_;
+  return 0;
+}
+
+int GceAudio::OpenInputStream(audio_io_handle_t handle,
+                              audio_devices_t devices,
+                              audio_config *config,
+                              audio_stream_in **stream_in,
+                              audio_input_flags_t /*flags*/,
+                              const char * /*address*/,
+                              audio_source_t /*source*/) {
+  GceAudioInputStream* new_stream;
+  int rval = GceAudioInputStream::Open(
+      this, handle, devices, *config, &new_stream);
+  uint32_t stream_number;
+  if (new_stream) {
+    LockGuard<Mutex> guard(lock_);
+    stream_number = next_stream_number_++;
+    input_map_[stream_number] = new_stream;
+  }
+  // This should happen after the lock is released, hence the double check
+  if (new_stream) {
+    SendStreamUpdate(new_stream->GetStreamDescriptor(
+        stream_number, gce_audio_message::OPEN_INPUT_STREAM), MSG_DONTWAIT);
+  }
+  *stream_in = new_stream;
+  return rval;
+}
+
+
+void GceAudio::CloseInputStream(audio_stream_in *stream) {
+  GceAudioInputStream* astream = static_cast<GceAudioInputStream*>(stream);
+  gce_audio_message descriptor;
+  {
+    LockGuard<Mutex> guard(lock_);
+    // TODO(ghartman): This could be optimized if stream knew it's number.
+    for (input_map_t::iterator it = input_map_.begin();
+         it != input_map_.end(); ++it) {
+      if (it->second == stream) {
+        descriptor = it->second->GetStreamDescriptor(
+            it->first, gce_audio_message::CLOSE_INPUT_STREAM);
+        input_map_.erase(it);
+        break;
+      }
+    }
+  }
+  SendStreamUpdate(descriptor, MSG_DONTWAIT);
+  delete astream;
+}
+
+
+int GceAudio::OpenOutputStream(audio_io_handle_t handle,
+                               audio_devices_t devices,
+                               audio_output_flags_t flags,
+                               audio_config *config,
+                               audio_stream_out **stream_out,
+                               const char * /*address*/) {
+  GceAudioOutputStream* new_stream;
+  int rval;
+  {
+    LockGuard<Mutex> guard(lock_);
+    rval = GceAudioOutputStream::Open(
+        this, handle, devices, flags, config, next_stream_number_++,
+        &new_stream);
+    if (new_stream) {
+      output_list_.push_back(new_stream);
+    }
+  }
+  if (new_stream) {
+    SendStreamUpdate(new_stream->GetStreamDescriptor(
+        gce_audio_message::OPEN_OUTPUT_STREAM), MSG_DONTWAIT);
+  }
+  *stream_out = new_stream;
+  return rval;
+}
+
+void GceAudio::CloseOutputStream(audio_stream_out *stream) {
+  GceAudioOutputStream* astream = static_cast<GceAudioOutputStream*>(stream);
+  gce_audio_message close;
+  {
+    LockGuard<Mutex> guard(lock_);
+    output_list_.remove(astream);
+    close = astream->GetStreamDescriptor(
+        gce_audio_message::CLOSE_OUTPUT_STREAM);
+  }
+  SendStreamUpdate(close, MSG_DONTWAIT);
+  delete astream;
+}
+
+int GceAudio::Dump(int fd) const {
+  LockGuard<Mutex> guard(lock_);
+  VSOC_FDPRINTF(
+      fd,
+      "\nadev_dump:\n"
+      "\tmic_mute: %s\n"
+      "\tnum_outputs: %zu\n"
+      "\tnum_inputs: %zu\n\n",
+      mic_muted_ ? "true": "false",
+      output_list_.size(), input_map_.size());
+
+  for (std::list<GceAudioOutputStream*>::const_iterator it =
+           output_list_.begin();
+       it != output_list_.end(); ++it) {
+    (*it)->common.dump(&(*it)->common, fd);
+  }
+
+  for (input_map_t::const_iterator it = input_map_.begin();
+       it != input_map_.end(); ++it) {
+    (*it).second->common.dump(&(*it).second->common, fd);
+  }
+
+  return 0;
+}
+
+ssize_t GceAudio::SendMsg(const msghdr& msg, int flags) {
+  cvd::SharedFD fd = GetAudioFd();
+  if (!fd->IsOpen()) {
+    return 0;
+  }
+  return fd->SendMsg(&msg, flags);
+}
+
+ssize_t GceAudio::SendStreamUpdate(
+    const gce_audio_message& stream_info, int flags) {
+  msghdr msg;
+  iovec msg_iov[1];
+  msg_iov[0].iov_base = const_cast<gce_audio_message*>(&stream_info);
+  msg_iov[0].iov_len = sizeof(gce_audio_message);
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  msg.msg_iov = msg_iov;
+  msg.msg_iovlen = arraysize(msg_iov);
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+  return SendMsg(msg, flags);
+}
+
+int GceAudio::SetVoiceVolume(float volume) {
+  D("GceAudio::%s: set voice volume %f", __FUNCTION__, volume);
+  voice_volume_ = volume;
+  return 0;
+}
+
+int GceAudio::SetMasterVolume(float volume) {
+  D("GceAudio::%s: set master volume %f", __FUNCTION__, volume);
+  master_volume_ = volume;
+  return 0;
+}
+
+int GceAudio::GetMasterVolume(float* volume) {
+  D("GceAudio::%s: get master volume %f", __FUNCTION__, master_volume_);
+  *volume = master_volume_;
+  return 0;
+}
+
+int GceAudio::SetMasterMute(bool muted) {
+  D("GceAudio::%s: set master muted %d", __FUNCTION__, muted);
+  master_muted_ = muted;
+  return 0;
+}
+
+int GceAudio::GetMasterMute(bool* muted) {
+  D("GceAudio::%s: get master muted %d", __FUNCTION__, master_muted_);
+  *muted = master_muted_;
+  return 0;
+}
+
+int GceAudio::SetMode(audio_mode_t mode) {
+  D("GceAudio::%s: new mode %d", __FUNCTION__, mode);
+  mode_ = mode;
+  return 0;
+}
+
+void* GceAudio::Listener() {
+  // TODO(ghartman): Consider tightening the mode on this later.
+  audio_listener_socket_ = cvd::SharedFD::SocketSeqPacketServer(
+      gce_audio_message::kAudioHALSocketName, 0777);
+  if (!audio_listener_socket_->IsOpen()) {
+    ALOGE("GceAudio::%s: Could not listen for audio connections. (%s).",
+          __FUNCTION__, audio_listener_socket_->StrError());
+    return NULL;
+  }
+  ALOGI("GceAudio::%s: Listening for audio connections at %s",
+        __FUNCTION__, gce_audio_message::kAudioHALSocketName);
+  remoter_request_packet announce;
+  remoter_request_packet_init(&announce, kRemoterHALReady, 0);
+  announce.send_response = 0;
+  strncpy(announce.params.hal_ready_params.unix_socket,
+          gce_audio_message::kAudioHALSocketName,
+          sizeof(announce.params.hal_ready_params.unix_socket));
+  AutoCloseFileDescriptor remoter_socket(remoter_connect());
+  if (remoter_socket.IsError()) {
+    ALOGI("GceAudio::%s: Couldn't connect to remoter to register HAL (%s).",
+          __FUNCTION__, strerror(errno));
+  } else {
+    int err = remoter_do_single_request_with_socket(
+        remoter_socket, &announce, NULL);
+    if (err == -1) {
+      ALOGI("GceAudio::%s: HAL registration failed after connect (%s).",
+            __FUNCTION__, strerror(errno));
+    } else {
+      ALOGI("GceAudio::%s: HAL registered with the remoter", __FUNCTION__);
+    }
+  }
+  while (true) {
+    // Poll for new connections or the terminatation event.
+    // The listener is non-blocking. We send to at most one client. If a new
+    // client comes in disconnect the old one.
+    cvd::SharedFDSet fd_set;
+    fd_set.Set(audio_listener_socket_);
+    fd_set.Set(terminate_listener_thread_event_);
+    if (cvd::Select(&fd_set, NULL, NULL, NULL) <= 0) {
+      // There's no timeout, so 0 shouldn't happen.
+      ALOGE("GceAudio::%s: Error using shared Select", __FUNCTION__);
+      break;
+    }
+    if (fd_set.IsSet(terminate_listener_thread_event_)) {
+      break;
+    }
+    LOG_FATAL_IF(fd_set.IsSet(audio_listener_socket_),
+                 "No error in Select() but nothing ready to read");
+    cvd::SharedFD fd = cvd::SharedFD::Accept(
+        *audio_listener_socket_);
+    if (!fd->IsOpen()) {
+      continue;
+    }
+    std::list<gce_audio_message> streams;
+    {
+      LockGuard<Mutex> guard(lock_);
+      // Do not do I/O while holding the lock. It could block the HAL
+      // implementation.
+      // Register the fd before dropping the lock to ensure that every
+      // active stream will appear when we first connect.
+      // Some output streams may appear twice if an open is active
+      // during the connect.
+      audio_data_socket_ = fd;
+      for (std::list<GceAudioOutputStream*>::iterator it = output_list_.begin();
+           it != output_list_.end(); ++it) {
+        streams.push_back((*it)->GetStreamDescriptor(
+            gce_audio_message::OPEN_OUTPUT_STREAM));
+      }
+      for (input_map_t::iterator it = input_map_.begin();
+           it != input_map_.end(); ++it) {
+        streams.push_back(it->second->GetStreamDescriptor(
+            it->first, gce_audio_message::OPEN_INPUT_STREAM));
+      }
+    }
+    for (std::list<gce_audio_message>::iterator it = streams.begin();
+         it != streams.end(); ++it) {
+      // We're willing to block because this is independent of the HAL
+      // implementation. We also don't want to forget to mention the input
+      // streams.
+      if (SendStreamUpdate(*it, 0) < 0) {
+        ALOGE("GceAudio::%s: Failed to announce open stream (%s)",
+              __FUNCTION__, fd->StrError());
+      }
+    }
+  }
+  return NULL;
+}
+
+int GceAudio::Open(const hw_module_t* module, const char* name,
+                   hw_device_t** device) {
+  D("GceAudio::%s", __FUNCTION__);
+
+  if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+    ALOGE("GceAudio::%s: invalid module name %s (expected %s)",
+          __FUNCTION__, name, AUDIO_HARDWARE_INTERFACE);
+    return -EINVAL;
+  }
+
+  GceAudio* rval = new GceAudio;
+  int err = pthread_create(
+      &rval->listener_thread_, NULL,
+      AudioThreadThunker<void*()>::call<&GceAudio::Listener>, rval);
+  if (err) {
+    ALOGE("GceAudio::%s: Unable to start listener thread (%s)", __FUNCTION__,
+          strerror(err));
+  }
+  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>;
+
+#if !defined(AUDIO_DEVICE_API_VERSION_2_0)
+  // This HAL entry is supported only on AUDIO_DEVICE_API_VERSION_1_0.
+  // In fact, with version 2.0 the device numbers were orgainized in a
+  // way that makes the return value nonsense.
+  // Skipping the assignment is ok: the memset in the constructor already
+  // put a NULL here.
+  rval->get_supported_devices =
+      AudioThunker<uint32_t()>::call<&GceAudio::GetSupportedDevices>;
+#endif
+  rval->init_check = AudioThunker<int()>::call<&GceAudio::InitCheck>;
+
+  rval->set_voice_volume =
+      AudioThunker<int(float)>::call<&GceAudio::SetVoiceVolume>;
+  rval->set_master_volume =
+      AudioThunker<int(float)>::call<&GceAudio::SetMasterVolume>;
+  rval->get_master_volume =
+      AudioThunker<int(float*)>::call<&GceAudio::GetMasterVolume>;
+
+#if defined(AUDIO_DEVICE_API_VERSION_2_0)
+  rval->set_master_mute =
+      AudioThunker<int(bool)>::call<&GceAudio::SetMasterMute>;
+  rval->get_master_mute =
+      AudioThunker<int(bool*)>::call<&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_parameters =
+      AudioThunker<int(const char*)>::call<&GceAudio::SetParameters>;
+  rval->get_parameters =
+      AudioThunker<char*(const char*)>::call<&GceAudio::GetParameters>;
+
+  rval->get_input_buffer_size =
+      AudioThunker<size_t(const audio_config*)>::call<
+        &GceAudio::GetInputBufferSize>;
+
+  rval->open_input_stream =
+      AudioThunker<GceAudio::OpenInputStreamHAL_t>::call<
+        &GceAudio::OpenInputStreamCurrentHAL>;
+  rval->close_input_stream =
+      AudioThunker<void(audio_stream_in*)>::call<&GceAudio::CloseInputStream>;
+
+  rval->open_output_stream =
+      AudioThunker<GceAudio::OpenOutputStreamHAL_t>::call<
+        &GceAudio::OpenOutputStreamCurrentHAL>;
+  rval->close_output_stream =
+      AudioThunker<void(audio_stream_out*)>::call<&GceAudio::CloseOutputStream>;
+
+  rval->dump = AudioThunker<int(int)>::call<&GceAudio::Dump>;
+
+  *device = &rval->common;
+  return 0;
+}
+
+int GceAudio::SetParameters(const char *kvpairs) {
+  ALOGE("GceAudio::%s: not implemented", __FUNCTION__);
+  if (kvpairs) D("GceAudio::%s: kvpairs %s", __FUNCTION__, kvpairs);
+  return 0;
+}
+
+
+char* GceAudio::GetParameters(const char *keys) const {
+  ALOGE("GceAudio::%s: not implemented", __FUNCTION__);
+  if (keys) D("GceAudio::%s: kvpairs %s", __FUNCTION__, keys);
+  return strdup("");
+}
+
+int GceAudio::SetStreamParameters(
+    struct audio_stream *stream, const char *kv_pairs) {
+  struct str_parms *parms = str_parms_create_str(kv_pairs);
+  if (!parms) {
+    return 0;
+  }
+  int sample_rate;
+  if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
+                        &sample_rate) >= 0) {
+    stream->set_sample_rate(stream, sample_rate);
+  }
+  int format;
+  if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_FORMAT,
+                        &format) >= 0) {
+    stream->set_format(stream, static_cast<audio_format_t>(format));
+  }
+  int routing;
+  if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                        &routing) >= 0) {
+    stream->set_device(stream, static_cast<audio_devices_t>(routing));
+  }
+  if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
+                        &routing) >= 0) {
+    stream->set_device(stream, static_cast<audio_devices_t>(routing));
+  }
+  str_parms_destroy(parms);
+  return 0;
+}
+
+}
diff --git a/guest/commands/audio/vsoc_audio.h b/guest/commands/audio/vsoc_audio.h
new file mode 100644
index 0000000..7e23bf7
--- /dev/null
+++ b/guest/commands/audio/vsoc_audio.h
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <list>
+#include <map>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "guest/commands/audio/audio_hal.h"
+#include "guest/commands/audio/vsoc_audio_input_stream.h"
+#include "guest/commands/audio/vsoc_audio_message.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+namespace cvd {
+
+class GceAudioInputStream;
+class GceAudioOutputStream;
+
+class GceAudio : public audio_hw_device {
+ public:
+  // This common code manipulates the parameters of input and output streams.
+  static int SetStreamParameters(struct audio_stream *, const char *);
+
+  ~GceAudio();
+
+  // Non-HAL methods that are part of the GCE implementation.
+  // Most of these are used by the input and output streams.
+
+  // Returns true if the microphone is muted. Used by input streams.
+  bool IsMicrophoneMuted() {
+    cvd::LockGuard<cvd::Mutex> guard(lock_);
+    return mic_muted_;
+  }
+
+  // Retrieves the SharedFD of the process accepting audio data.
+  // Returns a non-open fd if no process is listening (the normal case).
+  cvd::SharedFD GetAudioFd();
+
+  // Send a message to the connected streamer.
+  // Returns:
+  //   0 if there is no streamer.
+  //   >0 if the message was sent.
+  //   -1 if there was an error.
+  ssize_t SendMsg(const msghdr&, int flags);
+
+  // Sends a stream update to the connected streamer.
+  // Stream updates have no frames. Use SendMsg if the message has frames.
+  //   0 if there is no streamer.
+  //   >0 if the message was sent.
+  //   -1 if there was an error.
+  ssize_t SendStreamUpdate(
+      const gce_audio_message& stream_info, int flags);
+
+  // Callbacks for the Android audio_module HAL interface.
+  // Most of the comments below are copied from
+  // libhardware/include/hardware/audio.h
+  //
+  // Where the is a conflict the comments there apply.
+  // By default these methods return 0 on success -<errno> for failure.
+
+  // Opens the device.
+  static int Open(const hw_module_t* module, const char* name,
+                  hw_device_t** device);
+
+  // Closes the device, closing any open input streams and output streams.
+  int Close();
+
+  // Closes the input stream, throwing away any data in the buffer.
+  void CloseInputStream(audio_stream_in* stream);
+
+  // Closes the output stream without waiting for the buffer to clear.
+  void CloseOutputStream(audio_stream_out* stream);
+
+  // Creates an audio patch between several source and sink ports.
+  // The handle is allocated by the HAL and should be unique for this
+  // audio HAL module.
+  // TODO(ghartman): Implement this as part of HAL 3.0
+  //int CreateAudioPatch(unsigned int num_sources,
+  //                     const struct audio_port_config *sources,
+  //                     unsigned int num_sinks,
+  //                     const struct audio_port_config *sinks,
+  //                     audio_patch_handle_t *handle);
+
+  // dumps the state of the audio hardware to the given fd.
+  // This information can be retrieved using the dumpsys utility.
+  int Dump(int fd) const;
+
+  // Fills the list of supported attributes for a given audio port.
+  // As input, "port" contains the information (type, role, address etc...)
+  // needed by the HAL to identify the port.
+  // As output, "port" contains possible attributes (sampling rates, formats,
+  // channel masks, gain controllers...) for this port.
+  // TODO(ghartman): Implement this as part of HAL 3.0
+  // int GetAudioPort(struct audio_port *port);
+
+  // Sets audio port configuration
+  // TODO(ghartman): Implement this as part of HAL 3.0
+  // int SetAudioPortConfig(const struct audio_port_config *config);
+
+  size_t GetInputBufferSize(const audio_config*) const;
+
+  // Gets the current master volume value for the HAL, if the HAL supports
+  // master volume control.  AudioFlinger will query this value from the
+  // primary audio HAL when the service starts and use the value for setting
+  // the initial master volume across all HALs.  HALs which do not support
+  // this method may leave it set to NULL.
+  int GetMasterVolume(float* /*volume*/);
+
+  // Get the current master mute status for the HAL, if the HAL supports
+  // master mute control.  AudioFlinger will query this value from the primary
+  // audio HAL when the service starts and use the value for setting the
+  // initial master mute across all HALs.  HALs which do not support this
+  // method may leave it set to NULL.
+  int GetMasterMute(bool* muted);
+
+  // Gets the audio mute status for the microphone.
+  int GetMicMute(bool* state) const;
+
+  // Retrieves the global audio parameters.
+  // TODO(ghartman): Implement this.
+  char* GetParameters(const char* keys) const;
+
+  // Enumerates what devices are supported by each audio_hw_device
+  // implementation.
+  // Return value is a bitmask of 1 or more values of audio_devices_t
+  // used by audio flinger.
+  // NOTE: audio HAL implementations starting with
+  // AUDIO_DEVICE_API_VERSION_2_0 do not implement this function.
+  // AUDIO_DEVICE_API_VERSION_2_0 was the current version as of JB-MR1
+  // All supported devices should be listed in audio_policy.conf
+  // file and the audio policy manager must choose the appropriate
+  // audio module based on information in this file.
+  uint32_t GetSupportedDevices() const;
+
+  // Checks to see if the audio hardware interface has been initialized.
+  // Always returns 0 to indicate success, but -ENODEV is also allowed to
+  // indicate failure.
+  int InitCheck() const;
+
+  // Creates an additional hardware input stream.
+  // Additional parameters were added in the 3.0 version of the HAL.
+  // These defaults make it easier to implement a cross-branch device.
+  int OpenInputStream(
+      audio_io_handle_t handle,
+      audio_devices_t devices, audio_config *config,
+      audio_stream_in **stream_in,
+      audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+      const char* address = 0,
+      audio_source_t source = AUDIO_SOURCE_DEFAULT);
+
+  // Creates an additional output stream.
+  // The "address" parameter qualifies the "devices" audio device type if
+  // needed. On GCE we ignore it for now because we simulate a single SoC
+  // hw devices.
+  //
+  // The format format depends on the device type:
+  //   Bluetooth devices use the MAC address of the device in the form
+  //     "00:11:22:AA:BB:CC"
+  // USB devices use the ALSA card and device numbers in the form
+  //     "card=X;device=Y"
+  // Other devices may use a number or any other string.
+  int OpenOutputStream(
+      audio_io_handle_t handle,
+      audio_devices_t devices, audio_output_flags_t flags,
+      audio_config* config, audio_stream_out** stream_out,
+      const char* address = 0);
+
+  // Releases an audio patch.
+  // TODO(ghartman): Implement this as part of HAL 3.0
+  //int ReleaseAudioPatch(audio_patch_handle_t handle);
+
+  // Sets the audio mute status for all audio activities.  If any value other
+  // than 0 is returned, the software mixer will emulate this capability.
+  // The GCE implementation always returns 0.
+  int SetMasterMute(bool muted);
+
+  // Sets the audio volume for all audio activities other than voice call.
+  // Range between 0.0 and 1.0. If any value other than 0 is returned,
+  // the software mixer will emulate this capability.
+  // The GCE implementation always returns 0.
+  int SetMasterVolume(float volume);
+
+  // Sets the audio mute status for the microphone.
+  int SetMicMute(bool state);
+
+  // set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode
+  // is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is
+  // playing, and AUDIO_MODE_IN_CALL when a call is in progress.
+  int SetMode(audio_mode_t mode);
+
+  // Sets the global audio parameters.
+  // TODO(ghartman): Create a sensible implementation.
+  int SetParameters(const char* kvpairs);
+
+  // Sets the audio volume of a voice call. Range is between 0.0 and 1.0
+  int SetVoiceVolume(float volume);
+
+
+ private:
+  // Main routine for a thread that listens for incoming streamer connections.
+  void* Listener();
+  // HAL 3.0 modifies the signatures of OpenInputStream and OpenOutputStream.
+  // We don't want to fork the implementation, and we don't want #ifdefs all
+  // over the code. The current implementation defines OpenInputStream and
+  // OpenOutputStream with default values for the paramteres that were added,
+  // and then generates a HAL-specific wrapper to be used in the function
+  // table.
+#if defined(AUDIO_DEVICE_API_VERSION_3_0)
+  typedef int OpenInputStreamHAL_t(
+      audio_io_handle_t, audio_devices_t, audio_config*, audio_stream_in**,
+      audio_input_flags_t, const char*, audio_source_t);
+
+  int OpenInputStreamCurrentHAL(
+      audio_io_handle_t a, audio_devices_t b, audio_config* c,
+      audio_stream_in** d, audio_input_flags_t e, const char* f,
+      audio_source_t g) {
+    return OpenInputStream(a, b, c, d, e, f, g);
+  }
+
+  typedef int OpenOutputStreamHAL_t(
+      audio_io_handle_t, audio_devices_t, audio_output_flags_t,
+      audio_config*, audio_stream_out**,
+      const char*);
+
+  int OpenOutputStreamCurrentHAL(
+      audio_io_handle_t a, audio_devices_t b, audio_output_flags_t c,
+      audio_config* d, audio_stream_out** e,
+      const char* f) {
+    return OpenOutputStream(a, b, c, d, e, f);
+  }
+#else
+  typedef int OpenInputStreamHAL_t(
+      audio_io_handle_t, audio_devices_t, audio_config*, audio_stream_in**);
+
+  int OpenInputStreamCurrentHAL(
+      audio_io_handle_t a, audio_devices_t b, audio_config* c,
+      audio_stream_in** d) {
+    return OpenInputStream(a, b, c, d);
+  }
+
+  typedef int OpenOutputStreamHAL_t(
+      audio_io_handle_t, audio_devices_t, audio_output_flags_t,
+      audio_config*, audio_stream_out**);
+
+  int OpenOutputStreamCurrentHAL(
+      audio_io_handle_t a, audio_devices_t b, audio_output_flags_t c,
+      audio_config* d, audio_stream_out** e) {
+    return OpenOutputStream(a, b, c, d, e);
+  }
+#endif
+
+  //TODO(ghartman): Update this when we support 3.0.
+#if defined(AUDIO_DEVICE_API_VERSION_2_0)
+  static const unsigned int version_ = AUDIO_DEVICE_API_VERSION_2_0;
+#else
+  static const unsigned int version_ = AUDIO_DEVICE_API_VERSION_1_0;
+#endif
+
+  // Thread to handle new connections.
+  pthread_t listener_thread_;
+  // Event to indicate that the listener thread should terminate.
+  cvd::SharedFD terminate_listener_thread_event_;
+  // The listener socket, which is polled for new connections.
+  // TODO(ghartman): Consider using a thread.
+  cvd::SharedFD audio_listener_socket_;
+  // Lock to protect the data below.
+  mutable cvd::Mutex lock_;
+  // The data socket for the current streamer. Typically -1.
+  // The behavior of the HAL should not be affected by the presence or absence
+  // of the streamer.
+  cvd::SharedFD audio_data_socket_;
+  // State that is managed at the device level.
+  float voice_volume_;
+  float master_volume_;
+  bool master_muted_;
+  bool mic_muted_;
+  audio_mode_t mode_;
+  // There can be multiple input and output streams. This field is used
+  // to assign each one a unique identifier.
+  // TODO(ghartman): This can wrap after 2^32 streams. Ideally we should check
+  // the output_list_ to ensure that the stream number hasn't been assigned.
+  // However, streams don't really appear and disapper that often.
+  // We use the same counter for both input and output streams to make things
+  // a little easier on the client.
+  uint32_t next_stream_number_;
+  // List of the currently active output streams.
+  // Used to clean things up Close()
+  std::list<GceAudioOutputStream *> output_list_;
+  // List of the currently active input streams.
+  // Used to clean things up Close()
+  typedef std::map<uint32_t, GceAudioInputStream *> input_map_t;
+  input_map_t input_map_;
+
+  GceAudio() :
+      audio_hw_device(),
+      terminate_listener_thread_event_(cvd::SharedFD::Event()),
+      voice_volume_(0.0),
+      master_volume_(0.0),
+      master_muted_(false),
+      mic_muted_(false),
+      mode_(AUDIO_MODE_NORMAL),
+      next_stream_number_(1) { }
+};
+
+}
+
diff --git a/guest/commands/audio/vsoc_audio_input_stream.cpp b/guest/commands/audio/vsoc_audio_input_stream.cpp
new file mode 100644
index 0000000..9802d2a
--- /dev/null
+++ b/guest/commands/audio/vsoc_audio_input_stream.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <cstdint>
+
+extern "C"{
+#include <cutils/str_parms.h>
+}
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/threads/thunkers.h"
+#include "guest/commands/audio/audio_hal.h"
+#include "guest/commands/audio/vsoc_audio.h"
+#include "guest/commands/audio/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);
+}
+#elif defined(AUDIO_DEVICE_API_VERSION_2_0)
+static inline size_t GceAudioFrameSize(const audio_stream_in* s) {
+
+  return audio_stream_frame_size(&s->common);
+}
+#else
+static inline size_t GceAudioFrameSize(audio_stream_in* s) {
+
+  return audio_stream_frame_size(&s->common);
+}
+#endif
+
+GceAudioInputStream::GceAudioInputStream(
+    cvd::GceAudio* dev, audio_devices_t devices, const audio_config& config)
+    : audio_stream_in(),
+      dev_(dev),
+      config_(config),
+      gain_(0.0),
+      device_(devices) {
+  common.get_sample_rate =
+      Thunker<uint32_t()>::call<&GceAudioInputStream::GetSampleRate>;
+  common.set_sample_rate =
+      Thunker<int(uint32_t)>::call<&GceAudioInputStream::SetSampleRate>;
+  common.get_buffer_size =
+      Thunker<size_t()>::call<&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>;
+  common.set_parameters = GceAudio::SetStreamParameters;
+  common.get_parameters =
+      Thunker<char*(const char *)>::call<&GceAudioInputStream::GetParameters>;
+  common.add_audio_effect =
+      Thunker<int(effect_handle_t)>::call<&GceAudioInputStream::AddAudioEffect>;
+  common.remove_audio_effect = Thunker<int(effect_handle_t)>::call<
+    &GceAudioInputStream::RemoveAudioEffect>;
+  set_gain = InThunker<int(float)>::call<&GceAudioInputStream::SetGain>;
+  read = InThunker<ssize_t(void*, size_t)>::call<
+    &GceAudioInputStream::Read>;
+  get_input_frames_lost = InThunker<uint32_t()>::call<
+    &GceAudioInputStream::GetInputFramesLost>;
+  frame_size_ = GceAudioFrameSize(this);
+  buffer_model_.reset(
+      new SimulatedInputBuffer(config_.sample_rate, GetBufferSize() /
+                               frame_size_));
+  reported_lost_frames_ = 0;
+}
+
+gce_audio_message GceAudioInputStream::GetStreamDescriptor(
+    uint32_t stream_number, gce_audio_message::message_t event) {
+  gce_audio_message rval;
+  rval.message_type = event;
+  rval.stream_number = stream_number;
+  rval.frame_num = buffer_model_->GetCurrentItemNum();
+  rval.time_presented =
+      buffer_model_->GetLastUpdatedTime().SinceEpoch().GetTS();
+  rval.frame_rate = config_.sample_rate;
+  rval.channel_mask = config_.channel_mask;
+  rval.format = config_.format;
+  rval.frame_size = frame_size_;
+  return rval;
+}
+
+int GceAudioInputStream::Open(GceAudio* dev,
+                              audio_io_handle_t /*handle*/,
+                              audio_devices_t devices,
+                              const audio_config& config,
+                              GceAudioInputStream** stream_in) {
+  D("GceAudioInputStream::%s", __FUNCTION__);
+  *stream_in = new GceAudioInputStream(dev, devices, config);
+  return 0;
+}
+
+int GceAudioInputStream::SetFormat(audio_format_t format) {
+  config_.format = format;
+  frame_size_ = GceAudioFrameSize(this);
+  return 0;
+}
+
+int GceAudioInputStream::Dump(int fd) const {
+  D("GceAudioInputStream::%s", __FUNCTION__);
+  VSOC_FDPRINTF(
+      fd,
+      "\tInputSteam Dump:\n"
+      "\t\tsample rate: %u\n"
+      "\t\tbuffer size: %zu\n"
+      "\t\tchannel mask: %08x\n"
+      "\t\tformat: %d\n"
+      "\t\tdevice: %08x\n"
+      "\t\taudio dev: %p\n\n",
+      GetSampleRate(), GetBufferSize(),
+      GetChannels(), GetFormat(), device_, dev_);
+  return 0;
+}
+
+int GceAudioInputStream::SetSampleRate(uint32_t sample_rate) {
+  if (sample_rate != config_.sample_rate) {
+    config_.sample_rate = sample_rate;
+    buffer_model_.reset(
+        new SimulatedInputBuffer(sample_rate, GetBufferSize() / frame_size_));
+    reported_lost_frames_ = 0;
+  }
+  return 0;
+}
+
+char* GceAudioInputStream::GetParameters(const char* keys) const {
+  D("GceAudioInputStream::%s", __FUNCTION__);
+  if (keys) D("GceAudioInputStream::%s keys %s", __FUNCTION__, keys);
+
+  str_parms* query = str_parms_create_str(keys);
+  str_parms* reply = str_parms_create();
+
+  char value[256];
+  int ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING,
+                              value, sizeof(value));
+  char* str;
+  if (ret >= 0) {
+    str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, device_);
+    str = strdup(str_parms_to_str(reply));
+  } else {
+    str = strdup(keys);
+  }
+  str_parms_destroy(query);
+  str_parms_destroy(reply);
+  return str;
+}
+
+
+ssize_t GceAudioInputStream::Read(void* buffer, size_t bytes) {
+  int64_t available = buffer_model_->RemoveFromInputBuffer(
+      bytes / frame_size_, false) * frame_size_;
+  ssize_t rval = available;
+  if ((rval != available) || (rval < 0)) {
+    ALOGE("GceAudioInputStream:%s got bad value from "
+          "RemoveFromInputBuffer %" PRId64, __FUNCTION__, available);
+    return -1;
+  }
+  memset(buffer, 0, rval);
+  return rval;
+}
+
+}
diff --git a/guest/commands/audio/vsoc_audio_input_stream.h b/guest/commands/audio/vsoc_audio_input_stream.h
new file mode 100644
index 0000000..1f8cb4d
--- /dev/null
+++ b/guest/commands/audio/vsoc_audio_input_stream.h
@@ -0,0 +1,169 @@
+/*
+ * 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 <memory>
+
+#include "guest/commands/audio/audio_hal.h"
+#include "guest/commands/audio/simulated_buffer.h"
+#include "guest/commands/audio/vsoc_audio_message.h"
+
+namespace cvd {
+
+namespace {
+static const int IN_BUFFER_BYTES = 4096;
+}
+
+class GceAudio;
+
+// Defines static callback functions for generic_stream_in HAL interface.
+class GceAudioInputStream : public audio_stream_in {
+ public:
+  // These methods are internal to the GCE audio implementation.
+  // Factory for new input streams.
+  static int Open(
+      GceAudio* dev, audio_io_handle_t handle,
+      audio_devices_t devices, const audio_config& config,
+      GceAudioInputStream** stream_in);
+
+  // Gets a description of this stream
+  gce_audio_message GetStreamDescriptor(
+      uint32_t stream_number, gce_audio_message::message_t event);
+
+  // audio_stream_in implementation. These definitions follow the ones
+  // in hardware/libhardware/include/hardware/audio.h
+
+  // Returns the sampling rate in Hz - eg. 44100.
+  uint32_t GetSampleRate() const { return config_.sample_rate; }
+
+  // Sets the sample rate
+  // no direct calls from JB and later, but called indirectly from
+  // GceAudio::SetStreamParamters when it finds
+  // AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+  int SetSampleRate(uint32_t rate);
+
+  // Returns the size of input/output buffer in bytes for this stream - eg.
+  // 4800.
+  // It should be a multiple of the frame size.  See also get_input_buffer_size
+  size_t GetBufferSize() const {
+    return IN_BUFFER_BYTES;
+  }
+
+  // Returns the channel mask -
+  //   e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+  audio_channel_mask_t GetChannels() const {
+    return config_.channel_mask;
+  }
+
+  // Returns the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+  audio_format_t GetFormat() const {
+    return config_.format;
+  }
+
+  // Sets the audio format
+  // no direct calls from JB and later, but called indirectly from
+  // GceAudio::SetStreamParamters when it finds
+  //   AUDIO_PARAMETER_STREAM_FORMAT
+  int SetFormat(audio_format_t format);
+
+  // Puts the audio hardware input/output into standby mode.
+  // Driver should exit from standby mode at the next I/O operation.
+  // Returns 0 on success and <0 on failure.
+  int Standby() { return 0; }
+
+  // Dumps the state of the audio input/output device
+  int Dump(int fd) const;
+
+  // Returns the set of device(s) which this stream is connected to
+  audio_devices_t GetDevice() const {
+    return device_;
+  }
+
+  // Sets the device this stream is connected to.
+  // no direct calls from JB and later, but called indirectly from
+  // GceAudio::SetStreamParamters when it finds
+  //   AUDIO_PARAMETER_STREAM_ROUTING for both input and output.
+  //   AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by
+  //                                       input streams only.
+  int SetDevice(audio_devices_t device) { device_ = device; return 0; }
+
+  // sets audio stream parameters. The function accepts a list of
+  // parameter key value pairs in the form: key1=value1;key2=value2;...
+  //
+  // Some keys are reserved for standard parameters (See AudioParameter class)
+  //
+  // If the implementation does not accept a parameter change while
+  // the output is active but the parameter is acceptable otherwise, it must
+  // return -ENOSYS.
+  // The audio flinger will put the stream in standby and then change the
+  // parameter value.
+  // Uses GceAudio::SetStreamParameters
+
+  // Returns a pointer to a heap allocated string. The caller is responsible
+  // for freeing the memory for it using free().
+  char* GetParameters(const char* keys) const;
+
+  int AddAudioEffect(effect_handle_t /*effect*/) const {
+    return 0;
+  }
+
+  int RemoveAudioEffect(effect_handle_t /*effect*/) const {
+    return 0;
+  }
+
+  // Input stream specific methods
+
+  // Sets the input gain for the audio driver. This method is for
+  // for future use as of M.
+  int SetGain(float gain) {
+    gain_ = gain;
+    return 0;
+  }
+
+  // Reads audio buffer in from audio driver. Returns number of bytes read, or
+  // a negative android::status_t. If at least one frame was read prior to the error,
+  //  read should return that byte count and then return an error in the
+  // subsequent call.
+  ssize_t Read(void* buffer, size_t bytes);
+
+  // Return the amount of input frames lost in the audio driver since the
+  // last call of this function.
+  // Audio driver is expected to reset the value to 0 and restart counting
+  // upon returning the current value by this function call.
+  // Such loss typically occurs when the user space process is blocked
+  // longer than the capacity of audio driver buffers.
+  //
+  // Unit: the number of input audio frames
+  uint32_t GetInputFramesLost() {
+    int64_t cur_lost_frames = buffer_model_->GetLostInputItems();
+    uint32_t rval = cur_lost_frames - reported_lost_frames_;
+    reported_lost_frames_ = cur_lost_frames;
+    return rval;
+  }
+
+ private:
+  GceAudioInputStream(cvd::GceAudio* dev, audio_devices_t devices,
+                      const audio_config& config);
+  std::unique_ptr<SimulatedInputBuffer> buffer_model_;
+  cvd::GceAudio *dev_;
+  audio_config config_;
+  float gain_;
+  audio_devices_t device_;
+  size_t frame_size_;
+  int64_t reported_lost_frames_;
+};
+
+}
diff --git a/guest/commands/audio/vsoc_audio_message.cpp b/guest/commands/audio/vsoc_audio_message.cpp
new file mode 100644
index 0000000..30ef532
--- /dev/null
+++ b/guest/commands/audio/vsoc_audio_message.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/commands/audio/vsoc_audio_message.h"
+
+const char* gce_audio_message::kAudioHALSocketName =
+    "/var/run/media/audio_hal_socket";
diff --git a/guest/commands/audio/vsoc_audio_message.h b/guest/commands/audio/vsoc_audio_message.h
new file mode 100644
index 0000000..4e02f91
--- /dev/null
+++ b/guest/commands/audio/vsoc_audio_message.h
@@ -0,0 +1,85 @@
+/*
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+#include <system/audio.h>
+
+struct gce_audio_message {
+  static const char* kAudioHALSocketName;
+  static const size_t kMaxAudioFrameLen = 65536;
+  enum message_t {
+    UNKNOWN = 0,
+    DATA_SAMPLES = 1,
+    OPEN_INPUT_STREAM = 2,
+    OPEN_OUTPUT_STREAM = 3,
+    CLOSE_INPUT_STREAM = 4,
+    CLOSE_OUTPUT_STREAM = 5,
+    CONTROL_PAUSE = 100
+  };
+  // Size of the header + data. Used to frame when we're on TCP.
+  size_t total_size;
+  // Size of the audio header
+  size_t header_size;
+  message_t message_type;
+  // Identifier for the stream.
+  uint32_t stream_number;
+  // HAL assigned frame number, starts from 0.
+  int64_t frame_num;
+  // MONOTONIC_TIME when these frames were presented to the HAL.
+  timespec time_presented;
+  // Sample rate from the audio configuration.
+  uint32_t frame_rate;
+  // Channel mask from the audio configuration.
+  audio_channel_mask_t channel_mask;
+  // Format from the audio configuration.
+  audio_format_t format;
+  // Size of each frame in bytes.
+  size_t frame_size;
+  // Number of frames that were presented to the HAL.
+  size_t num_frames_presented;
+  // Number of frames that the HAL accepted.
+  //   For blocking audio this will be the same as num_frames.
+  //   For non-blocking audio this may be less.
+  size_t num_frames_accepted;
+  // Count of the number of packets that were dropped because they would
+  // have blocked the HAL or exceeded the maximum message size.
+  size_t num_packets_dropped;
+  // Count of the number of packets that were shortened to fit within
+  // kMaxAudioFrameLen.
+  size_t num_packets_shortened;
+  // num_frames_presented (not num_frames_accepted) will follow here.
+
+  gce_audio_message() :
+      total_size(sizeof(gce_audio_message)),
+      header_size(sizeof(gce_audio_message)),
+      message_type(UNKNOWN),
+      stream_number(0),
+      frame_num(0),
+      frame_rate(0),
+      channel_mask(0),
+      format(AUDIO_FORMAT_DEFAULT),
+      frame_size(0),
+      num_frames_presented(0),
+      num_frames_accepted(0),
+      num_packets_dropped(0),
+      num_packets_shortened(0) {
+    time_presented.tv_sec = 0;
+    time_presented.tv_nsec = 0;
+  }
+};
diff --git a/guest/commands/audio/vsoc_audio_output_stream.cpp b/guest/commands/audio/vsoc_audio_output_stream.cpp
new file mode 100644
index 0000000..f32a676
--- /dev/null
+++ b/guest/commands/audio/vsoc_audio_output_stream.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/commands/audio/vsoc_audio_message.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory>
+
+#include <cutils/sockets.h>
+extern "C"{
+#include <cutils/str_parms.h>
+}
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/threads/thunkers.h"
+#include "common/libs/time/monotonic_time.h"
+#include "guest/commands/audio/audio_hal.h"
+#include "guest/commands/audio/vsoc_audio.h"
+#include "guest/commands/audio/vsoc_audio_output_stream.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+#include "guest/libs/remoter/remoter_framework_pkt.h"
+
+#if defined(AUDIO_DEVICE_API_VERSION_3_0)
+static inline size_t GceAudioFrameSize(const audio_stream_out* s) {
+  return audio_stream_out_frame_size(s);
+}
+#elif defined(AUDIO_DEVICE_API_VERSION_2_0)
+static inline size_t GceAudioFrameSize(const audio_stream_out* s) {
+
+  return audio_stream_frame_size(&s->common);
+}
+#else
+static inline size_t GceAudioFrameSize(audio_stream_out* s) {
+
+  return audio_stream_frame_size(&s->common);
+}
+#endif
+
+namespace cvd {
+
+const size_t GceAudioOutputStream::kOutBufferSize;
+const size_t GceAudioOutputStream::kOutLatency;
+
+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),
+    device_(AUDIO_DEVICE_OUT_DEFAULT),
+    frame_count_(0),
+    left_volume_(0.0),
+    right_volume_(0.0) { }
+
+int GceAudioOutputStream::Dump(int fd) const {
+  D("GceAudioOutputStream::%s", __FUNCTION__);
+  VSOC_FDPRINTF(
+      fd,
+      "\tout_dump:\n"
+      "\t\tsample rate: %u\n"
+      "\t\tbuffer size: %zu\n"
+      "\t\tchannel mask: %08x\n"
+      "\t\tformat: %d\n"
+      "\t\tdevice: %08x\n"
+      "\t\taudio dev: %p\n\n",
+      GetSampleRate(),
+      GetBufferSize(),
+      GetChannels(),
+      GetFormat(),
+      device_,
+      dev_);
+  return 0;
+}
+
+int GceAudioOutputStream::GetNextWriteTimestamp(int64_t* nstime) const {
+  *nstime = cvd::time::Nanoseconds(
+      buffer_->GetNextOutputBufferItemTime().SinceEpoch()).count();
+  return 0;
+}
+
+namespace {
+struct StrParmsDestroyer {
+  void operator()(str_parms* parms) const {
+    if (parms) {
+      str_parms_destroy(parms);
+    }
+  }
+};
+
+typedef std::unique_ptr<str_parms, StrParmsDestroyer> StrParmsPtr;
+}
+
+int GceAudioOutputStream::SetParameters(const char* kv_pairs) {
+  int err = 0;
+  StrParmsPtr parms(str_parms_create_str(kv_pairs));
+  {
+    int fmt = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_FORMAT, &fmt)
+        == 0) {
+      SetFormat(static_cast<audio_format_t>(fmt));
+    }
+  }
+  {
+    int sample_rate = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
+                          &sample_rate) == 0) {
+      SetSampleRate(static_cast<uint32_t>(sample_rate));
+    }
+  }
+  {
+    int routing = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_ROUTING,
+                          &routing) == 0) {
+      device_ = static_cast<uint32_t>(routing);
+    }
+  }
+  {
+    int channels = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_CHANNELS,
+                          &channels) == 0) {
+      message_header_.channel_mask = static_cast<audio_channel_mask_t>(channels);
+    }
+  }
+  {
+    int frame_count = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_FRAME_COUNT,
+                          &frame_count) == 0) {
+      frame_count_ = static_cast<size_t>(frame_count);
+    }
+  }
+  {
+    int input_source = 0;
+    if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
+                          &input_source) == 0){
+      ALOGE("GceAudioOutputStream::%s AUDIO_PARAMETER_STREAM_INPUT_SOURCE"
+            " passed to an output stream", __FUNCTION__);
+      err = -EINVAL;
+    }
+  }
+  return err;
+}
+
+void GceAudioOutputStream::AddIntIfKeyPresent(
+    /*const */ str_parms* query, str_parms* reply, const char* key, int value) {
+  if (str_parms_get_str(query, key, NULL, 0) >= 0) {
+    str_parms_add_int(reply, key, value);
+  }
+}
+
+
+char* GceAudioOutputStream::GetParameters(const char* keys) const {
+  D("GceAudioOutputStream::%s", __FUNCTION__);
+  if (keys) D("%s keys %s", __FUNCTION__, keys);
+
+  StrParmsPtr query(str_parms_create_str(keys));
+  StrParmsPtr reply(str_parms_create());
+
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_FORMAT,
+                     static_cast<int>(GetFormat()));
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
+                     static_cast<int>(GetSampleRate()));
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_ROUTING,
+                     static_cast<int>(device_));
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_CHANNELS,
+                     static_cast<int>(message_header_.channel_mask));
+  AddIntIfKeyPresent(query.get(), reply.get(),
+                     AUDIO_PARAMETER_STREAM_FRAME_COUNT,
+                     static_cast<int>(frame_count_));
+
+  char *str = str_parms_to_str(reply.get());
+  return str;
+}
+
+int GceAudioOutputStream::GetRenderPosition(uint32_t* dsp_frames) const {
+  *dsp_frames = buffer_->GetCurrentItemNum();
+  return 0;
+}
+
+ssize_t GceAudioOutputStream::Write(const void* buffer, size_t length) {
+  // We're always the blocking case for now.
+  static const bool blocking = true;
+  message_header_.frame_size = frame_size_;
+  frame_count_ += message_header_.num_frames_presented = length / frame_size_;
+  message_header_.message_type = gce_audio_message::DATA_SAMPLES;
+  // First do a nonblocking add
+  int64_t frames_accepted_without_blocking = buffer_->AddToOutputBuffer(
+      message_header_.num_frames_presented, false);
+  // This seems backward, but adding the items to the buffer first
+  // allows us to calculate the right frame number in the case of underflow.
+  message_header_.frame_num =
+      buffer_->GetNextOutputBufferItemNum() - frames_accepted_without_blocking;
+  message_header_.time_presented =
+      buffer_->GetLastUpdatedTime().SinceEpoch().GetTS();
+  // We want to send the message before blocking. If we're in blocking mode
+  // we will accept all of the frames.
+  if (blocking) {
+    message_header_.num_frames_accepted =
+        message_header_.num_frames_presented;
+  } else {
+    message_header_.num_frames_accepted = frames_accepted_without_blocking;
+  }
+  // Never exceed the maximum packet size, as defined by the interface.
+  // Clip off any frames that we can't transmit and increment the clipped
+  // count.
+  size_t transmitted_frame_size = length;
+  if (length > gce_audio_message::kMaxAudioFrameLen) {
+    transmitted_frame_size = gce_audio_message::kMaxAudioFrameLen;
+    message_header_.num_packets_shortened++;
+  }
+  message_header_.total_size =
+      sizeof(message_header_) + transmitted_frame_size;
+  // Now send the message. Do not block if the receiver isn't ready
+  // If this is a blocking write we will block after we have attempted to
+  // send the data to the receiver.
+  msghdr msg;
+  iovec msg_iov[2];
+  // We need a cast here because iov_base is defined non-const to support
+  // recvmsg et.al.
+  // There is no danger here:sendmsg does not write to the buffer.
+  msg_iov[0].iov_base = &message_header_;
+  msg_iov[0].iov_len = sizeof(message_header_);
+  msg_iov[1].iov_base = const_cast<void*>(buffer);
+  msg_iov[1].iov_len = transmitted_frame_size;
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  msg.msg_iov = msg_iov;
+  msg.msg_iovlen = arraysize(msg_iov);
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+  if (dev_->SendMsg(msg, MSG_DONTWAIT) < 0) {
+    message_header_.num_packets_dropped++;
+  }
+  if (!blocking) {
+    return frames_accepted_without_blocking * frame_size_;
+  }
+  if ((message_header_.num_frames_presented) >
+      static_cast<size_t>(frames_accepted_without_blocking)) {
+    buffer_->AddToOutputBuffer(
+        message_header_.num_frames_presented -
+        frames_accepted_without_blocking, true);
+  }
+  return message_header_.num_frames_presented * frame_size_;
+}
+
+int GceAudioOutputStream::Open(
+    GceAudio* dev, audio_io_handle_t /*handle*/,
+    audio_devices_t devices, audio_output_flags_t /*flags*/,
+    audio_config* config, uint32_t stream_number,
+    GceAudioOutputStream** stream_out) {
+  D("GceAudioOutputStream::%s", __FUNCTION__);
+  *stream_out = NULL;
+  // Deleted by Close(); UniquePtr holds until end of Open().
+  std::unique_ptr<GceAudioOutputStream> out(
+      new GceAudioOutputStream(dev));
+  out->message_header_.stream_number = stream_number;
+  out->message_header_.format = config->format;
+  out->message_header_.channel_mask = config->channel_mask;
+  out->message_header_.frame_rate = config->sample_rate;
+  out->frame_count_ =
+#if VSOC_PLATFORM_SDK_AFTER(K)
+      config->frame_count;
+#else
+      0;
+#endif
+  out->common.get_sample_rate =
+      Thunker<uint32_t()>::call<&GceAudioOutputStream::GetSampleRate>;
+  out->common.set_sample_rate =
+      Thunker<int(uint32_t)>::call<&GceAudioOutputStream::SetSampleRate>;
+  out->common.get_buffer_size =
+      Thunker<size_t()>::call<&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>;
+  out->common.set_parameters =
+      Thunker<int(const char*)>::call<
+      &GceAudioOutputStream::SetParameters>;
+  out->common.get_parameters =
+      Thunker<char*(const char *)>::call<
+        &GceAudioOutputStream::GetParameters>;
+  out->common.add_audio_effect =
+      Thunker<int(effect_handle_t)>::call<
+        &GceAudioOutputStream::AddAudioEffect>;
+  out->common.remove_audio_effect =
+      Thunker<int(effect_handle_t)>::call<
+        &GceAudioOutputStream::RemoveAudioEffect>;
+  out->get_latency =
+      OutThunker<uint32_t()>::call<
+        &GceAudioOutputStream::GetLatency>;
+  out->set_volume =
+      OutThunker<int(float, float)>::call<&GceAudioOutputStream::SetVolume>;
+  out->write =
+      OutThunker<ssize_t(const void*, size_t)>::call<
+        &GceAudioOutputStream::Write>;
+  out->get_render_position =
+      OutThunker<int(uint32_t*)>::call<
+        &GceAudioOutputStream::GetRenderPosition>;
+  out->get_next_write_timestamp =
+      OutThunker<int(int64_t*)>::call<
+        &GceAudioOutputStream::GetNextWriteTimestamp>;
+  out->device_ = devices;
+  out->frame_size_ = GceAudioFrameSize(out.get());
+
+  int64_t item_capacity =
+      out->frame_size_  == 0 ? 0 : out->GetBufferSize() / out->frame_size_;
+  if (item_capacity == 0) {
+    ALOGE("Attempt to create GceAudioOutputStream with frame_size_ of 0.");
+    return -EINVAL;
+  }
+  out->buffer_.reset(
+      new SimulatedOutputBuffer(
+          config->sample_rate, item_capacity));
+  *stream_out = out.release();
+  return 0;
+}
+
+}  // namespace cvd
diff --git a/guest/commands/audio/vsoc_audio_output_stream.h b/guest/commands/audio/vsoc_audio_output_stream.h
new file mode 100644
index 0000000..ecd609f
--- /dev/null
+++ b/guest/commands/audio/vsoc_audio_output_stream.h
@@ -0,0 +1,313 @@
+/*
+ * 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 <memory>
+
+#include "guest/commands/audio/audio_hal.h"
+#include "guest/commands/audio/simulated_buffer.h"
+#include "guest/commands/audio/vsoc_audio_message.h"
+
+namespace cvd {
+
+// Defines static callback functions for the audio_stream and audio_stream_out
+// interfaces in  libhardware/include/hardware/audio.h
+//
+// Where the is a conflict the comments there apply.
+// By default these methods return 0 on success -<errno> for failure.
+class GceAudioOutputStream : public audio_stream_out {
+ public:
+  // Factory method for a new output stream.
+  static int Open(GceAudio* dev, audio_io_handle_t handle,
+                  audio_devices_t devices, audio_output_flags_t flags,
+                  audio_config* config, uint32_t stream_number,
+                  GceAudioOutputStream** stream_out);
+
+  gce_audio_message GetStreamDescriptor(
+      gce_audio_message::message_t message_type) const {
+    gce_audio_message rval = message_header_;
+    rval.total_size = sizeof(rval);
+    rval.header_size = sizeof(rval);
+    rval.message_type = message_type;
+    rval.num_frames_presented = 0;
+    rval.num_frames_accepted = 0;
+    return rval;
+  }
+
+  // Method from audio_stream, listed in order of appearance.
+  // TODO(ghartman): Consider moving these if they could be shared with
+  // gce_audio_input_stream.
+
+
+  // Returns the sampling rate in Hz - eg. 44100.
+  uint32_t GetSampleRate() const {
+    return message_header_.frame_rate;
+  }
+
+  // Sets the sample rate
+  //   AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+  int SetSampleRate(uint32_t sample_rate) {
+    if (sample_rate != message_header_.frame_rate) {
+      message_header_.frame_rate = sample_rate;
+      // TODO(ghartman): The output buffer should be quantized at about 192
+      // bytes for better fidelity. Do this by passing
+      // frame_rate * frame_size / 192 and then rescaling the outputs.
+      // Or we could always create a quantized wrapper of the buffer...
+      buffer_.reset(
+          new SimulatedOutputBuffer(
+              sample_rate, GetBufferSize() / frame_size_));
+    }
+    return 0;
+  }
+
+  // Returns the size of input/output buffer in bytes for this stream.
+  // eg. 4800.
+  // It should be a multiple of the frame size.  See also GetInputBufferSize.
+  size_t GetBufferSize() const {
+    return kOutBufferSize;
+  }
+
+  // Returns the channel mask -
+  //  e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+  audio_channel_mask_t GetChannels() const {
+    return message_header_.channel_mask;
+  }
+
+  // Returns the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+  audio_format_t GetFormat() const {
+    return message_header_.format;
+  }
+
+  // Sets the audio format.
+  // Unused as of JB - use set_parameters with key
+  //   AUDIO_PARAMETER_STREAM_FORMAT
+  int SetFormat(audio_format_t format) {
+    message_header_.format = format;
+    return 0;
+  }
+
+  // Puts the audio hardware input/output into standby mode.
+  // Driver should exit from standby mode at the next I/O operation.
+  // Returns 0 on success and <0 on failure.
+  // TODO(ghartman): This should reset some of the frame counts.
+  int Standby() {
+    return 0;
+  }
+
+  // dumps the state of the audio hardware to the given fd.
+  // This information can be retrieved using the dumpsys utility.
+  int Dump(int fd) const;
+
+  // Returns the set of device(s) which this stream is connected to.
+  // TODO(ghartman): Implement this.
+  audio_devices_t GetDevice() const { return device_; }
+
+  // Not directly called from JB forward.
+  // Called indirectly from SetParameters with the key
+  //   AUDIO_PARAMETER_STREAM_ROUTING
+  int SetDevice(audio_devices_t device) { device_ = device; return 0; }
+
+  // Sets audio stream parameters. The function accepts a list of
+  // parameter key value pairs in the form: key1=value1;key2=value2;...
+  //
+  // Some keys are reserved for standard parameters (See AudioParameter class)
+  //
+  // If the implementation does not accept a parameter change while
+  // the output is active but the parameter is acceptable otherwise, it must
+  // return -ENOSYS.
+  //
+  // The audio flinger will put the stream in standby and then change the
+  // parameter value.
+  int SetParameters(const char* kv_pairs);
+
+  // Gets audio stream parameters. The function accepts a list of
+  // keys in the form: key1=value1;key2=value2;...
+  //
+  // Returns a pointer to a heap allocated string. The caller is responsible
+  // for freeing the memory for it using free().
+  // TODO(ghartman): Implement this.
+  char* GetParameters(const char* keys) const;
+
+  // TODO(ghartman): Implement this.
+  int AddAudioEffect(effect_handle_t /*effect*/) const {
+    static unsigned int printed = 0;  // printed every 2^32-th call.
+    ALOGE_IF(!printed++, "%s: not implemented", __FUNCTION__);
+    return 0;
+  }
+
+  // TODO(ghartman): Implement this.
+  int RemoveAudioEffect(effect_handle_t /*effect*/) const {
+    static unsigned int printed = 0;  // printed every 2^32-th call.
+    ALOGE_IF(!printed++, "%s: not implemented", __FUNCTION__);
+    return 0;
+  }
+
+  // Methods defined in audio_stream_out
+
+  // Returns the audio hardware driver estimated latency in milliseconds.
+  // TODO(ghartman): Calculate this based on the format and the quantum.
+  uint32_t GetLatency() const {
+    return kOutLatency;
+  }
+
+  // Use this method in situations where audio mixing is done in the
+  // hardware. This method serves as a direct interface with hardware,
+  // allowing you to directly set the volume as apposed to via the framework.
+  // This method might produce multiple PCM outputs or hardware accelerated
+  // codecs, such as MP3 or AAC.
+  //
+  // Note that GCE simulates hardware mixing.
+  int SetVolume(float left_volume, float right_volume) {
+    left_volume_ = left_volume;
+    right_volume_ = right_volume;
+    return 0;
+  }
+
+  // Write audio buffer to driver. Returns number of bytes written, or a
+  // negative android::status_t. If at least one frame was written successfully prior
+  // to the error the driver will return that successful (short) byte count
+  // and then return an error in the subsequent call.
+  //
+  // If SetCallback() has previously been called to enable non-blocking mode
+  // the Write() is not allowed to block. It must write only the number of
+  // bytes that currently fit in the driver/hardware buffer and then return
+  // this byte count. If this is less than the requested write size the
+  // callback function must be called when more space is available in the
+  // driver/hardware buffer.
+  ssize_t Write(const void* buffer, size_t bytes);
+
+  // Returns the number of audio frames written by the audio dsp to DAC since
+  // the output has exited standby
+  // TODO(ghartman): Implement zeroing this in Standby().
+  int GetRenderPosition(uint32_t* dsp_frames) const;
+
+  // Gets the local time at which the next write to the audio driver will be
+  // presented. The units are microseconds, where the epoch is decided by the
+  // local audio HAL.
+  //
+  // The GCE implementation uses CLOCK_MONOTONIC, which also happens to line
+  // up with LocalTime.
+  int GetNextWriteTimestamp(int64_t*) const;
+
+  // Turns on non-blocking mode and sets the callback function for notifying
+  // completion of non-blocking write and drain.
+  // Calling this function implies that all future Write() and Drain()
+  // must be non-blocking and use the callback to signal completion.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int SetCallback(stream_callback_t callback, void *cookie);
+
+  // Notifies to the audio driver to stop playback however the queued buffers
+  // are retained by the hardware. Useful for implementing pause/resume. Empty
+  // implementation if not supported however should be implemented for hardware
+  // with non-trivial latency. In the pause state audio hardware could still be
+  // using power. User may consider calling suspend after a timeout.
+  //
+  // Implementation of this function is mandatory for offloaded playback.
+  //
+  // TODO(ghartman): Implement this URGENTLY. There is already support in
+  // SimulatedBuffer.
+  // int Pause();
+
+  // Notifies to the audio driver to resume playback following a pause.
+  // Returns error if called without matching pause.
+  //
+  // Implementation of this function is mandatory for offloaded playback.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int Resume();
+
+  // Requests notification when data buffered by the driver/hardware has
+  // been played. If set_callback() has previously been called to enable
+  // non-blocking mode, the drain() must not block, instead it should return
+  // quickly and completion of the drain is notified through the callback.
+  // If set_callback() has not been called, the drain() must block until
+  // completion.
+  //
+  // If type==AUDIO_DRAIN_ALL, the drain completes when all previously written
+  // data has been played.
+  //
+  // If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all
+  // data for the current track has played to allow time for the framework
+  // to perform a gapless track switch.
+  //
+  // Drain must return immediately on stop() and flush() call
+  //
+  // Implementation of this function is mandatory for offloaded playback.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int Drain(audio_drain_type_t type);
+
+  // Notifies to the audio driver to flush the queued data. Stream must already
+  // be paused before calling Flush().
+  //
+  // Implementation of this function is mandatory for offloaded playback.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int Flush();
+
+  // Returns a recent count of the number of audio frames presented to an
+  // external observer.  This excludes frames which have been written but are
+  // still in the pipeline.
+  //
+  // The count is not reset to zero when output enters standby.
+  // Also returns the value of CLOCK_MONOTONIC as of this presentation count.
+  // The returned count is expected to be 'recent',
+  // but does not need to be the most recent possible value.
+  // However, the associated time should correspond to whatever count is
+  // returned.
+  //
+  // Example:  assume that N+M frames have been presented, where M is a
+  // 'small' number.
+  // Then it is permissible to return N instead of N+M,
+  // and the timestamp should correspond to N rather than N+M.
+  // The terms 'recent' and 'small' are not defined.
+  // They reflect the quality of the implementation.
+  //
+  // 3.0 and higher only.
+  //
+  // TODO(ghartman): Implement this URGENTLY.
+  //
+  // int GetPresentationPosition(uint64_t *frames, struct timespec *timestamp);
+
+ private:
+  // If key is present in query, add key=value; to reply.
+  // query should be pointer to const, but the str_parms functions aren't
+  // const-correct, so neither is this.
+  static void AddIntIfKeyPresent(
+      /*const*/ str_parms* query, str_parms* reply, const char* key, int value);
+
+
+  explicit GceAudioOutputStream(cvd::GceAudio*);
+
+  static const size_t kOutBufferSize = 3840;
+  static const size_t kOutLatency = 2;
+
+  gce_audio_message message_header_;
+  std::unique_ptr<SimulatedOutputBuffer> buffer_;
+  cvd::GceAudio *dev_;
+  audio_devices_t device_;
+  size_t frame_size_;
+  size_t frame_count_;
+  float left_volume_;
+  float right_volume_;
+};
+
+}
diff --git a/guest/commands/usbforward/Android.mk b/guest/commands/usbforward/Android.mk
new file mode 100644
index 0000000..db65f85
--- /dev/null
+++ b/guest/commands/usbforward/Android.mk
@@ -0,0 +1,40 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_MODULE := usbforward
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+    main.cpp \
+    usb_server.cpp \
+    transport_request.cpp
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_kernel
+
+LOCAL_SHARED_LIBRARIES := \
+    cuttlefish_auto_resources \
+    libcuttlefish_fs \
+    libusb \
+    libbase \
+    liblog
+
+LOCAL_CFLAGS += -DLOG_TAG=\"UsbForward\"
+
+LOCAL_MULTILIB := first
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_EXECUTABLE)
+
diff --git a/guest/commands/usbforward/main.cpp b/guest/commands/usbforward/main.cpp
new file mode 100644
index 0000000..0ccf479
--- /dev/null
+++ b/guest/commands/usbforward/main.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 <cutils/log.h>
+#include <stdio.h>
+#include <libusb/libusb.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "guest/commands/usbforward/usb_server.h"
+
+int main(int argc, char* argv[]) {
+  if (argc == 1) {
+    printf("Usage: %s <virtio_channel>\n", argv[0]);
+    return 1;
+  }
+
+  cvd::SharedFD fd = cvd::SharedFD::Open(argv[1], O_RDWR | O_NOCTTY);
+  if (!fd->IsOpen()) {
+    ALOGE("Could not open %s: %s", argv[1], fd->StrError());
+    return 1;
+  }
+
+  usb_forward::USBServer server(fd);
+  server.Serve();
+  ALOGE("Terminated.");
+
+  libusb_exit(nullptr);
+  return 1;
+}
diff --git a/guest/commands/usbforward/transport_request.cpp b/guest/commands/usbforward/transport_request.cpp
new file mode 100644
index 0000000..9f994e9
--- /dev/null
+++ b/guest/commands/usbforward/transport_request.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "guest/commands/usbforward/transport_request.h"
+
+#include <cutils/log.h>
+
+namespace usb_forward {
+
+TransportRequest::TransportRequest(std::shared_ptr<libusb_device_handle> handle,
+                                   CallbackType callback,
+                                   const ControlTransfer& transfer)
+    : handle_{std::move(handle)},
+      callback_{std::move(callback)},
+      is_control_{true},
+      transfer_{libusb_alloc_transfer(0), libusb_free_transfer} {
+  // NOTE: libusb places setup structure as part of user data!
+  buffer_.reset(new uint8_t[transfer.length + LIBUSB_CONTROL_SETUP_SIZE]);
+
+  // NOTE: libusb places a structure of size LIBUSB_CONTROL_SETUP_SIZE directly
+  // in the data buffer.
+  libusb_fill_control_setup(buffer_.get(), transfer.type, transfer.cmd,
+                            transfer.value, transfer.index, transfer.length);
+
+  // NOTE: despite libusb requires user to allocate buffer large enough to
+  // accommodate SETUP structure and actual data, it requires user to provide
+  // only data length here, while setup length is added internally.
+  libusb_fill_control_transfer(transfer_.get(), handle_.get(), buffer_.get(),
+                               OnTransferComplete, this, transfer.timeout);
+}
+
+TransportRequest::TransportRequest(std::shared_ptr<libusb_device_handle> handle,
+                                   CallbackType callback,
+                                   const DataTransfer& transfer)
+    : handle_{std::move(handle)},
+      callback_{std::move(callback)},
+      is_control_{false},
+      transfer_{libusb_alloc_transfer(0), libusb_free_transfer} {
+  buffer_.reset(new uint8_t[transfer.length]);
+  libusb_fill_bulk_transfer(
+      transfer_.get(), handle_.get(),
+      transfer.endpoint_id | (transfer.is_host_to_device ? LIBUSB_ENDPOINT_OUT
+                                                         : LIBUSB_ENDPOINT_IN),
+      buffer_.get(), transfer.length, OnTransferComplete, this,
+      transfer.timeout);
+}
+
+uint8_t* TransportRequest::Buffer() {
+  if (is_control_) {
+    return &buffer_[LIBUSB_CONTROL_SETUP_SIZE];
+  } else {
+    return buffer_.get();
+  }
+}
+
+bool TransportRequest::Submit() {
+  if (handle_) {
+    auto err = libusb_submit_transfer(transfer_.get());
+    if (err != 0) {
+      ALOGE("libusb transfer failed: %d", err);
+    }
+    return err == 0;
+  } else {
+    ALOGE("Initiated transfer, but device not opened.");
+    return false;
+  }
+}
+
+void TransportRequest::OnTransferComplete(libusb_transfer* req) {
+  auto treq = static_cast<TransportRequest*>(req->user_data);
+  treq->callback_(req->status == 0, treq->Buffer(), req->actual_length);
+}
+
+}  // namespace usb_forward
diff --git a/guest/commands/usbforward/transport_request.h b/guest/commands/usbforward/transport_request.h
new file mode 100644
index 0000000..1f76bbe
--- /dev/null
+++ b/guest/commands/usbforward/transport_request.h
@@ -0,0 +1,70 @@
+/*
+ * 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 <functional>
+#include <memory>
+
+#include <stdint.h>
+#include <libusb/libusb.h>
+#include "common/libs/usbforward/protocol.h"
+
+namespace usb_forward {
+
+// TransportRequest represents a libusb asynchronous transport request.
+// This class encapsulates everything that is necessary to complete
+// transfer.
+class TransportRequest final {
+ public:
+  // CallbackType describes what kind of function can receive call when this
+  // asynchronous call is complete.
+  // Parameters passed to callback, in order:
+  // - success indicator (true = success),
+  // - buffer with data (in or out),
+  // - actual length transferred.
+  using CallbackType = std::function<void(bool, const uint8_t*, int32_t)>;
+
+  TransportRequest(std::shared_ptr<libusb_device_handle> device,
+                   CallbackType callback, const ControlTransfer& transfer);
+  TransportRequest(std::shared_ptr<libusb_device_handle> device,
+                   CallbackType callback, const DataTransfer& transfer);
+  ~TransportRequest() = default;
+
+  uint8_t* Buffer();
+
+  // Submit sends an asynchronous data exchange requests.
+  // Returns true only if operation was successful. At this point
+  // ownership of this structure is passed to libusb and user
+  // must not release the underlying structure.
+  bool Submit();
+
+  // Executes corresponding callback with execution results.
+  // This is a static call to ensure that the callback being invoked
+  // can dispose of this instance.
+  static void OnTransferComplete(libusb_transfer* req);
+
+ private:
+  std::shared_ptr<libusb_device_handle> handle_;
+  CallbackType callback_;
+  bool is_control_;
+  std::unique_ptr<libusb_transfer, void (*)(libusb_transfer*)> transfer_;
+  std::unique_ptr<uint8_t[]> buffer_;
+
+  TransportRequest(const TransportRequest& other) = delete;
+  TransportRequest& operator=(const TransportRequest& other) = delete;
+};
+
+}  // namespace usb_forward
diff --git a/guest/commands/usbforward/usb_server.cpp b/guest/commands/usbforward/usb_server.cpp
new file mode 100644
index 0000000..119c77e
--- /dev/null
+++ b/guest/commands/usbforward/usb_server.cpp
@@ -0,0 +1,405 @@
+/*
+ * 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.
+ */
+// #undef NDEBUG
+
+#include "guest/commands/usbforward/usb_server.h"
+
+#include <string>
+#include <vector>
+#include <strings.h>
+#include <cutils/log.h>
+#include <libusb/libusb.h>
+#include "common/libs/fs/shared_select.h"
+#include "common/libs/usbforward/protocol.h"
+#include "guest/commands/usbforward/transport_request.h"
+
+namespace usb_forward {
+namespace {
+// USBServer exports device kExportedVendorID:kExportedProductID to the server.
+// We will not support exporting multiple USB devices as there's no practical
+// need for this.
+constexpr uint16_t kExportedVendorID = 0x18d1;
+constexpr uint16_t kExportedProductID = 0x4ee7;
+
+// Use default BUS and DEVICE IDs so that it's easier to attach over USB/IP.
+constexpr uint8_t kDefaultBusID = 1;
+constexpr uint8_t kDefaultDevID = 1;
+
+std::shared_ptr<libusb_device_handle> GetDevice() {
+  std::shared_ptr<libusb_device_handle> res(
+      libusb_open_device_with_vid_pid(nullptr, kExportedVendorID,
+                                      kExportedProductID),
+      [](libusb_device_handle* h) {
+        // Apparently, deleter is called even on an uninitialized shared_ptr.
+        if (h != nullptr) {
+          libusb_release_interface(h, 0);
+          libusb_close(h);
+        }
+      });
+
+  if (res) libusb_claim_interface(res.get(), 0);
+
+  return res;
+}
+
+}  // anonymous namespace
+
+bool USBServer::GetDeviceInfo(
+    DeviceInfo* info, std::vector<InterfaceInfo>* ifaces) {
+  if (!handle_) return false;
+
+  // This function does not modify the reference count of the returned device,
+  // so do not feel compelled to unreference it when you are done.
+  libusb_device* dev = libusb_get_device(handle_.get());
+
+  libusb_device_descriptor desc;
+  libusb_config_descriptor* conf;
+  memset(info, 0, sizeof(*info));
+
+  int res = libusb_get_device_descriptor(dev, &desc);
+  if (res < 0) {
+    // This shouldn't really happen.
+    ALOGE("libusb_get_device_descriptor failed %d", res);
+    return false;
+  }
+
+  res = libusb_get_active_config_descriptor(dev, &conf);
+  if (res < 0) {
+    // This shouldn't really happen.
+    ALOGE("libusb_get_active_config_descriptor failed %d", res);
+    libusb_free_config_descriptor(conf);
+    return false;
+  }
+
+  info->vendor_id = desc.idVendor;
+  info->product_id = desc.idProduct;
+  info->dev_version = desc.bcdDevice;
+  info->dev_class = desc.bDeviceClass;
+  info->dev_subclass = desc.bDeviceSubClass;
+  info->dev_protocol = desc.bDeviceProtocol;
+  info->speed = libusb_get_device_speed(dev);
+  info->num_configurations = desc.bNumConfigurations;
+  info->num_interfaces = conf->bNumInterfaces;
+  info->cur_configuration = conf->bConfigurationValue;
+  info->bus_id = kDefaultBusID;
+  info->dev_id = kDefaultDevID;
+
+  if (ifaces != nullptr) {
+    for (int ifidx = 0; ifidx < conf->bNumInterfaces; ++ifidx) {
+      const libusb_interface& iface = conf->interface[ifidx];
+      for (int altidx = 0; altidx < iface.num_altsetting; ++altidx) {
+        const libusb_interface_descriptor& alt = iface.altsetting[altidx];
+        ifaces->push_back(InterfaceInfo{alt.bInterfaceClass,
+                                        alt.bInterfaceSubClass,
+                                        alt.bInterfaceProtocol, 0});
+      }
+    }
+  }
+  libusb_free_config_descriptor(conf);
+  return true;
+}
+
+USBServer::USBServer(const cvd::SharedFD& fd)
+    : fd_{fd},
+      device_event_fd_{cvd::SharedFD::Event(0, 0)},
+      thread_event_fd_{cvd::SharedFD::Event(0, 0)} {}
+
+void USBServer::HandleDeviceList(uint32_t tag) {
+  // Iterate all devices and send structure for every found device.
+  // Write header: number of devices.
+  DeviceInfo info;
+  std::vector<InterfaceInfo> ifaces;
+  bool found = GetDeviceInfo(&info, &ifaces);
+
+  cvd::LockGuard<cvd::Mutex> lock(write_mutex_);
+  ResponseHeader rsp{StatusSuccess, tag};
+  fd_->Write(&rsp, sizeof(rsp));
+  if (found) {
+    uint32_t cnt = 1;
+    fd_->Write(&cnt, sizeof(cnt));
+    fd_->Write(&info, sizeof(info));
+    fd_->Write(ifaces.data(), ifaces.size() * sizeof(InterfaceInfo));
+  } else {
+    // No devices.
+    uint32_t cnt = 0;
+    fd_->Write(&cnt, sizeof(cnt));
+  }
+}
+
+void USBServer::HandleAttach(uint32_t tag) {
+  // We read the request, but it no longer plays any significant role here.
+  AttachRequest req;
+  if (fd_->Read(&req, sizeof(req)) != sizeof(req)) return;
+
+  cvd::LockGuard<cvd::Mutex> lock(write_mutex_);
+  ResponseHeader rsp{handle_ ? StatusSuccess : StatusFailure, tag};
+  fd_->Write(&rsp, sizeof(rsp));
+}
+
+void USBServer::HandleHeartbeat(uint32_t tag) {
+  cvd::LockGuard<cvd::Mutex> lock(write_mutex_);
+  ResponseHeader rsp{handle_ ? StatusSuccess : StatusFailure, tag};
+  fd_->Write(&rsp, sizeof(rsp));
+}
+
+void USBServer::HandleControlTransfer(uint32_t tag) {
+  ControlTransfer req;
+  // If disconnected prematurely, don't send response.
+  if (fd_->Read(&req, sizeof(req)) != sizeof(req)) return;
+
+  // Technically speaking this isn't endpoint, but names, masks, values and
+  // meaning here is exactly same.
+  bool is_data_in =
+      ((req.type & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN);
+
+  std::unique_ptr<TransportRequest> treq(new TransportRequest(
+      handle_,
+      [this, is_data_in, tag](bool is_success, const uint8_t* data,
+                              int32_t length) {
+        OnTransferComplete(tag, is_data_in, is_success, data, length);
+      },
+      req));
+
+  if (!is_data_in && req.length) {
+    // If disconnected prematurely, don't send response.
+    int32_t got = 0;
+    while (got < req.length) {
+      auto read = fd_->Read(&treq->Buffer()[got], req.length - got);
+      if (fd_->GetErrno() != 0) {
+        ALOGE("Failed to read from client: %s", fd_->StrError());
+        return;
+      } else if (read == 0) {
+        ALOGE("Failed to read from client: short read");
+        return;
+      }
+      got += read;
+    }
+  }
+
+  // At this point we store transport request internally until it completes.
+  TransportRequest* treq_ptr = treq.get();
+  {
+    cvd::LockGuard<cvd::Mutex> lock(requests_mutex_);
+    requests_in_flight_[tag] = std::move(treq);
+  }
+
+  if (!treq_ptr->Submit()) {
+    OnTransferComplete(tag, is_data_in, false, nullptr, 0);
+  }
+}
+
+void USBServer::HandleDataTransfer(uint32_t tag) {
+  DataTransfer req;
+  // If disconnected prematurely, don't send response.
+  if (fd_->Read(&req, sizeof(req)) != sizeof(req)) return;
+
+  bool is_data_in = !req.is_host_to_device;
+
+  std::unique_ptr<TransportRequest> treq(new TransportRequest(
+      handle_,
+      [this, is_data_in, tag](bool is_success, const uint8_t* data,
+                              int32_t length) {
+        OnTransferComplete(tag, is_data_in, is_success, data, length);
+      },
+      req));
+
+  if (!is_data_in && req.length) {
+    // If disconnected prematurely, don't send response.
+    int32_t got = 0;
+    while (got < req.length) {
+      auto read = fd_->Read(&treq->Buffer()[got], req.length - got);
+      if (fd_->GetErrno() != 0) {
+        ALOGE("Failed to read from client: %s", fd_->StrError());
+        return;
+      } else if (read == 0) {
+        ALOGE("Failed to read from client: short read");
+        return;
+      }
+      got += read;
+    }
+  }
+
+  // At this point we store transport request internally until it completes.
+  TransportRequest* treq_ptr = treq.get();
+  {
+    cvd::LockGuard<cvd::Mutex> lock(requests_mutex_);
+    requests_in_flight_[tag] = std::move(treq);
+  }
+
+  if (!treq_ptr->Submit()) {
+    OnTransferComplete(tag, is_data_in, false, nullptr, 0);
+  }
+}
+
+void USBServer::OnTransferComplete(uint32_t tag, bool is_data_in,
+                                   bool is_success, const uint8_t* buffer,
+                                   int32_t actual_length) {
+  ResponseHeader rsp{is_success ? StatusSuccess : StatusFailure, tag};
+
+  cvd::LockGuard<cvd::Mutex> lock(write_mutex_);
+  fd_->Write(&rsp, sizeof(rsp));
+  if (is_success && is_data_in) {
+    fd_->Write(&actual_length, sizeof(actual_length));
+    if (actual_length > 0) {
+      // NOTE: don't use buffer_ here directly, as libusb uses first few bytes
+      // to store control data there.
+      int32_t sent = 0;
+      while (sent < actual_length) {
+        int packet_size = fd_->Write(&buffer[sent], actual_length - sent);
+        sent += packet_size;
+        ALOGV("Sending response, %d / %d bytes sent", sent, actual_length);
+        if (fd_->GetErrno() != 0) {
+          ALOGE("Send failed: %s", fd_->StrError());
+          return;
+        }
+      }
+    }
+  }
+
+  {
+    cvd::LockGuard<cvd::Mutex> lock(requests_mutex_);
+    requests_in_flight_.erase(tag);
+  }
+}
+
+int USBServer::HandleDeviceEvent(libusb_context*, libusb_device*,
+                                 libusb_hotplug_event event, void* self_raw) {
+  auto self = reinterpret_cast<USBServer*>(self_raw);
+  int64_t dummy = 1;
+  self->device_event_fd_->Write(&dummy, sizeof(dummy));
+  return 0;
+}
+
+void* USBServer::ProcessLibUSBRequests(void* self_raw) {
+  USBServer* self = reinterpret_cast<USBServer*>(self_raw);
+  ALOGI("Starting hotplug thread.");
+
+  cvd::SharedFDSet rset;
+  while (true) {
+    // Do not wait if there's no event.
+    timeval select_timeout{0, 0};
+    rset.Zero();
+    rset.Set(self->thread_event_fd_);
+    int ret = cvd::Select(&rset, nullptr, nullptr, &select_timeout);
+    if (ret > 0) break;
+
+    timeval libusb_timeout{1, 0};
+    libusb_handle_events_timeout_completed(nullptr, &libusb_timeout, nullptr);
+  }
+
+  int64_t dummy;
+  self->thread_event_fd_->Read(&dummy, sizeof(dummy));
+  ALOGI("Shutting down hotplug thread.");
+  return nullptr;
+}
+
+void USBServer::InitLibUSB() {
+  if (libusb_init(nullptr) != 0) return;
+  libusb_hotplug_register_callback(
+      nullptr,
+      libusb_hotplug_event(LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
+      libusb_hotplug_flag(0), kExportedVendorID, kExportedProductID,
+      LIBUSB_HOTPLUG_MATCH_ANY, &USBServer::HandleDeviceEvent, this,
+      &hotplug_handle_);
+  handle_ = GetDevice();
+  libusb_thread_.reset(new cvd::ScopedThread(&ProcessLibUSBRequests, this));
+}
+
+void USBServer::ExitLibUSB() {
+  if (!libusb_thread_) return;
+  libusb_hotplug_deregister_callback(nullptr, hotplug_handle_);
+  int64_t dummy = 1;
+  thread_event_fd_->Write(&dummy, sizeof(dummy));
+  libusb_thread_.reset();
+  handle_.reset();
+  libusb_exit(nullptr);
+}
+
+void USBServer::Serve() {
+  cvd::SharedFDSet rset;
+  while (true) {
+    timeval retry_timeout{1, 0};
+    timeval* select_timeout = nullptr;
+    if (!handle_) {
+      select_timeout = &retry_timeout;
+    }
+
+    rset.Zero();
+    rset.Set(fd_);
+    rset.Set(device_event_fd_);
+    int ret = cvd::Select(&rset, nullptr, nullptr, select_timeout);
+
+    // device_event_fd_ is reset each time libusb notices device has re-appeared
+    // or is gone. In both cases, the existing handle is no longer valid.
+    if (rset.IsSet(device_event_fd_)) {
+      int64_t dummy;
+      device_event_fd_->Read(&dummy, sizeof(dummy));
+      handle_.reset();
+    }
+
+    if (!handle_) {
+      ExitLibUSB();
+      InitLibUSB();
+      if (handle_) {
+        ALOGI("Device present.");
+      }
+    }
+
+    if (ret < 0) continue;
+
+    if (rset.IsSet(fd_)) {
+      RequestHeader req;
+      if (fd_->Read(&req, sizeof(req)) != sizeof(req)) {
+        // There's nobody on the other side.
+        sleep(3);
+        continue;
+      }
+
+      switch (req.command) {
+        case CmdDeviceList:
+          ALOGV("Processing DeviceList command, tag=%d", req.tag);
+          HandleDeviceList(req.tag);
+          break;
+
+        case CmdAttach:
+          ALOGV("Processing Attach command, tag=%d", req.tag);
+          HandleAttach(req.tag);
+          break;
+
+        case CmdControlTransfer:
+          ALOGV("Processing ControlTransfer command, tag=%d", req.tag);
+          HandleControlTransfer(req.tag);
+          break;
+
+        case CmdDataTransfer:
+          ALOGV("Processing DataTransfer command, tag=%d", req.tag);
+          HandleDataTransfer(req.tag);
+          break;
+
+        case CmdHeartbeat:
+          ALOGV("Processing Heartbeat command, tag=%d", req.tag);
+          HandleHeartbeat(req.tag);
+          break;
+
+        default:
+          ALOGE("Discarding unknown command %08x, tag=%d", req.command,
+                req.tag);
+      }
+    }
+  }
+}
+
+}  // namespace usb_forward
diff --git a/guest/commands/usbforward/usb_server.h b/guest/commands/usbforward/usb_server.h
new file mode 100644
index 0000000..77449b9
--- /dev/null
+++ b/guest/commands/usbforward/usb_server.h
@@ -0,0 +1,98 @@
+/*
+ * 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 <map>
+#include <memory>
+#include <string>
+#include <libusb/libusb.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "guest/commands/usbforward/transport_request.h"
+
+namespace usb_forward {
+
+// USBServer exposes access to USB devices over pipe (virtio channel etc).
+// Usage:
+//
+//     cvd::SharedFD pipe = cvd::SharedFD::Open(pipe_path, O_RDWR);
+//     USBServer server(pipe);
+//     CHECK(server.Init());
+//     server.Serve();
+class USBServer final {
+ public:
+  USBServer(const cvd::SharedFD& fd);
+  ~USBServer() = default;
+
+  // Serve incoming USB requests.
+  void Serve();
+
+ private:
+  // HandleDeviceEvent opens and closes Android Gadget device, whenever it
+  // appears / disappears.
+  static int HandleDeviceEvent(libusb_context*, libusb_device*,
+                                libusb_hotplug_event event, void* self_raw);
+
+  // Handle CmdDeviceList request.
+  void HandleDeviceList(uint32_t tag);
+
+  // Handle CmdAttach request.
+  void HandleAttach(uint32_t tag);
+
+  // Handle CmdControlTransfer request.
+  void HandleControlTransfer(uint32_t tag);
+
+  // Handle CmdDataTransfer request.
+  void HandleDataTransfer(uint32_t tag);
+
+  // Handle CmdHeartbeat request.
+  void HandleHeartbeat(uint32_t tag);
+
+  // OnAsyncDataTransferComplete handles end of asynchronous data transfer cycle
+  // and sends response back to caller.
+  void OnTransferComplete(uint32_t tag, bool is_data_in, bool is_success,
+                          const uint8_t* buffer, int32_t actual_length);
+
+  // Initialize, Configure and start libusb.
+  void InitLibUSB();
+
+  // Stop, Deconfigure and Clean up libusb.
+  void ExitLibUSB();
+
+  // Extract device info, if device is available.
+  bool GetDeviceInfo(DeviceInfo* info, std::vector<InterfaceInfo>* ifaces);
+
+  // Handle asynchronous libusb events.
+  static void* ProcessLibUSBRequests(void* self_ptr);
+
+  std::shared_ptr<libusb_device_handle> handle_;
+  libusb_hotplug_callback_handle hotplug_handle_;
+
+  std::unique_ptr<cvd::ScopedThread> libusb_thread_;
+  cvd::Mutex write_mutex_;
+  cvd::SharedFD fd_;
+  cvd::SharedFD device_event_fd_;
+  cvd::SharedFD thread_event_fd_;
+
+  cvd::Mutex requests_mutex_;
+  std::map<uint32_t, std::unique_ptr<TransportRequest>> requests_in_flight_;
+
+  USBServer(const USBServer& other) = delete;
+  USBServer& operator=(const USBServer& other) = delete;
+};
+
+}  // namespace usb_forward
diff --git a/guest/commands/wifirouter/Android.bp b/guest/commands/wifirouter/Android.bp
new file mode 100644
index 0000000..1bb89af
--- /dev/null
+++ b/guest/commands/wifirouter/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+cc_binary {
+    name: "wifirouter",
+    vendor: true,
+    srcs: ["router.cc"],
+    static_libs: [
+        "libbase",
+        "libgflags",
+        "liblog",
+        "libnl",
+    ],
+    header_libs: [
+        "cuttlefish_common_headers",
+        "cuttlefish_glog",
+    ],
+}
+
diff --git a/guest/commands/wifirouter/router.cc b/guest/commands/wifirouter/router.cc
new file mode 100644
index 0000000..d01c3eb
--- /dev/null
+++ b/guest/commands/wifirouter/router.cc
@@ -0,0 +1,302 @@
+/*
+ * 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/libs/wifi/router.h"
+
+#include <cerrno>
+#include <cstddef>
+#include <map>
+#include <memory>
+#include <set>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/genl.h>
+#include <netlink/netlink.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+DEFINE_string(socket_name, "cvd-wifirouter",
+              "Name of the unix-domain socket providing access for routing. "
+              "Socket will be created in abstract namespace.");
+
+namespace cvd {
+namespace {
+using MacHash = uint64_t;
+using MacToClientsTable = std::multimap<MacHash, int>;
+using ClientsTable = std::set<int>;
+
+// Copied out of mac80211_hwsim.h header.
+constexpr int HWSIM_CMD_REGISTER = 1;
+constexpr int HWSIM_ATTR_ADDR_TRANSMITTER = 2;
+constexpr int HWSIM_ATTR_MAX = 19;
+
+// Name of the WIFI SIM Netlink Family.
+constexpr char kWifiSimFamilyName[] = "MAC80211_HWSIM";
+const int kMaxSupportedPacketSize = getpagesize();
+
+// Get hash for mac address serialized to 6 bytes of data starting at specified
+// location.
+// We don't care about byte ordering as much as we do about having all bytes
+// there. Byte order does not matter, we want to use it as a key in our map.
+uint64_t GetMacHash(const void* macaddr) {
+  auto typed = reinterpret_cast<const uint16_t*>(macaddr);
+  return (1ull * typed[0] << 32) | (typed[1] << 16) | typed[2];
+}
+
+// Enable asynchronous notifications from MAC80211_HWSIM.
+// - `sock` is a valid netlink socket connected to NETLINK_GENERIC,
+// - `family` is MAC80211_HWSIM genl family number.
+//
+// Upon failure, this function will terminate execution of the program.
+void RegisterForHWSimNotifications(nl_sock* sock, int family) {
+  std::unique_ptr<nl_msg, void (*)(nl_msg*)> msg(
+      nlmsg_alloc(), [](nl_msg* m) { nlmsg_free(m); });
+  genlmsg_put(msg.get(), NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST,
+              HWSIM_CMD_REGISTER, 0);
+  nl_send_auto(sock, msg.get());
+  auto res = nl_wait_for_ack(sock);
+  if (res < 0) {
+    LOG(ERROR) << "Could not register for notifications: " << nl_geterror(res);
+    exit(1);
+  }
+}
+
+// Create and configure WIFI Router server socket.
+// This function is guaranteed to success. If at any point an error is detected,
+// the function will terminate execution of the program.
+int CreateWifiRouterServerSocket() {
+  auto fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+  if (fd <= 0) {
+    LOG(ERROR) << "Could not create unix socket: " << strerror(-fd);
+    exit(1);
+  }
+
+  sockaddr_un addr{};
+  addr.sun_family = AF_UNIX;
+  auto len = std::min(sizeof(addr.sun_path) - 2, FLAGS_socket_name.size());
+  strncpy(&addr.sun_path[1], FLAGS_socket_name.c_str(), len);
+  len += offsetof(sockaddr_un, sun_path) + 1;  // include heading \0 byte.
+  auto res = bind(fd, reinterpret_cast<sockaddr*>(&addr), len);
+
+  if (res < 0) {
+    LOG(ERROR) << "Could not bind unix socket: " << strerror(-res);
+    exit(1);
+  }
+
+  listen(fd, 4);
+  return fd;
+}
+
+// Accept new WIFI Router client. When successful, client will be placed in
+// clients table.
+void AcceptNewClient(int server_fd, ClientsTable* clients) {
+  auto client = accept(server_fd, nullptr, nullptr);
+  if (client < 0) {
+    LOG(ERROR) << "Could not accept client: " << strerror(errno);
+    return;
+  }
+
+  clients->insert(client);
+  LOG(INFO) << "Client " << client << " added.";
+}
+
+// Disconnect and remove client from list of registered clients and recipients
+// of WLAN traffic.
+void RemoveClient(int client, ClientsTable* clients,
+                  MacToClientsTable* targets) {
+  close(client);
+  clients->erase(client);
+  for (auto iter = targets->begin(); iter != targets->end();) {
+    if (iter->second == client) {
+      iter = targets->erase(iter);
+    } else {
+      ++iter;
+    }
+  }
+  LOG(INFO) << "Client " << client << " removed.";
+}
+
+// Read MAC80211HWSIM packet, find the originating MAC address and redirect it
+// to proper sink.
+void RouteWIFIPacket(nl_sock* nl, int simfamily, ClientsTable* clients,
+                     MacToClientsTable* targets) {
+  sockaddr_nl tmp;
+  uint8_t* buf;
+
+  const auto len = nl_recv(nl, &tmp, &buf, nullptr);
+  if (len < 0) {
+    LOG(ERROR) << "Could not read from netlink: " << nl_geterror(len);
+    return;
+  }
+
+  std::unique_ptr<nlmsghdr, void (*)(nlmsghdr*)> msg(
+      reinterpret_cast<nlmsghdr*>(buf), [](nlmsghdr* m) { free(m); });
+
+  // Discard messages that originate from anything else than MAC80211_HWSIM.
+  if (msg->nlmsg_type != simfamily) return;
+
+  std::unique_ptr<nl_msg, void (*)(nl_msg*)> rep(
+      nlmsg_alloc(), [](nl_msg* m) { nlmsg_free(m); });
+  genlmsg_put(rep.get(), 0, 0, 0, 0, 0, WIFIROUTER_CMD_NOTIFY, 0);
+
+  // Note, this is generic netlink message, and uses different parsing
+  // technique.
+  nlattr* attrs[HWSIM_ATTR_MAX + 1];
+  if (genlmsg_parse(msg.get(), 0, attrs, HWSIM_ATTR_MAX, nullptr)) return;
+
+  std::set<int> pending_removals;
+  auto addr = attrs[HWSIM_ATTR_ADDR_TRANSMITTER];
+  if (addr != nullptr) {
+    nla_put(rep.get(), WIFIROUTER_ATTR_MAC, nla_len(addr), nla_data(addr));
+    nla_put(rep.get(), WIFIROUTER_ATTR_PACKET, len, buf);
+    auto hdr = nlmsg_hdr(rep.get());
+
+    auto key = GetMacHash(nla_data(attrs[HWSIM_ATTR_ADDR_TRANSMITTER]));
+    LOG(INFO) << "Received netlink packet from " << std::hex << key;
+    for (auto it = targets->find(key); it != targets->end() && it->first == key;
+         ++it) {
+      auto num_written =
+          send(it->second, hdr, hdr->nlmsg_len, MSG_NOSIGNAL);
+      if (num_written != static_cast<int64_t>(hdr->nlmsg_len)) {
+        pending_removals.insert(it->second);
+      }
+    }
+
+    for (auto client : pending_removals) {
+      RemoveClient(client, clients, targets);
+    }
+  }
+}
+
+bool HandleClientMessage(int client, MacToClientsTable* targets) {
+  std::unique_ptr<nlmsghdr, void (*)(nlmsghdr*)> msg(
+      reinterpret_cast<nlmsghdr*>(malloc(kMaxSupportedPacketSize)),
+      [](nlmsghdr* h) { free(h); });
+  int64_t size = recv(client, msg.get(), kMaxSupportedPacketSize, 0);
+
+  // Invalid message or no data -> client invalid or disconnected.
+  if (size == 0 || size != msg->nlmsg_len || size < sizeof(nlmsghdr)) {
+    return false;
+  }
+
+  int result = -EINVAL;
+  genlmsghdr* ghdr = reinterpret_cast<genlmsghdr*>(nlmsg_data(msg.get()));
+
+  switch (ghdr->cmd) {
+    case WIFIROUTER_CMD_REGISTER:
+      // Register client to receive notifications for specified MAC address.
+      nlattr* attrs[WIFIROUTER_ATTR_MAX];
+      if (!nlmsg_parse(msg.get(), sizeof(genlmsghdr), attrs,
+                       WIFIROUTER_ATTR_MAX - 1, nullptr)) {
+        if (attrs[WIFIROUTER_ATTR_MAC] != nullptr) {
+          targets->emplace(GetMacHash(nla_data(attrs[WIFIROUTER_ATTR_MAC])),
+                           client);
+          result = 0;
+        }
+      }
+      break;
+
+    default:
+      break;
+  }
+
+  nlmsgerr err{.error = result};
+  std::unique_ptr<nl_msg, void (*)(nl_msg*)> rsp(nlmsg_alloc(), nlmsg_free);
+  nlmsg_put(rsp.get(), msg->nlmsg_pid, msg->nlmsg_seq, NLMSG_ERROR, 0, 0);
+  nlmsg_append(rsp.get(), &err, sizeof(err), 0);
+  auto hdr = nlmsg_hdr(rsp.get());
+  if (send(client, hdr, hdr->nlmsg_len, MSG_NOSIGNAL) !=
+      static_cast<int64_t>(hdr->nlmsg_len)) {
+    return false;
+  }
+  return true;
+}
+
+// Process incoming requests from netlink, server or clients.
+void ServerLoop(int server_fd, nl_sock* netlink_sock, int family) {
+  ClientsTable clients;
+  MacToClientsTable targets;
+  int netlink_fd = nl_socket_get_fd(netlink_sock);
+
+  while (true) {
+    auto max_fd = server_fd;
+    fd_set reads{};
+
+    auto fdset = [&max_fd, &reads](int fd) {
+      FD_SET(fd, &reads);
+      max_fd = std::max(max_fd, fd);
+    };
+
+    fdset(server_fd);
+    fdset(netlink_fd);
+    for (int client : clients) fdset(client);
+
+    if (select(max_fd + 1, &reads, nullptr, nullptr, nullptr) <= 0) continue;
+
+    if (FD_ISSET(server_fd, &reads)) AcceptNewClient(server_fd, &clients);
+    if (FD_ISSET(netlink_fd, &reads))
+      RouteWIFIPacket(netlink_sock, family, &clients, &targets);
+
+    // Process any client messages left. Drop any client that is no longer
+    // talking with us.
+    for (auto client = clients.begin(); client != clients.end();) {
+      auto cfd = *client++;
+      // Is our client sending us data?
+      if (FD_ISSET(cfd, &reads)) {
+        if (!HandleClientMessage(cfd, &targets)) {
+          // Client should be disconnected.
+          RemoveClient(cfd, &clients, &targets);
+        }
+      }
+    }
+  }
+}
+
+}  // namespace
+}  // namespace cvd
+
+int main(int argc, char* argv[]) {
+  using namespace cvd;
+  google::ParseCommandLineFlags(&argc, &argv, true);
+#if !defined(ANDROID)
+  // We should check for legitimate google logging here.
+  google::InitGoogleLogging(argv[0]);
+  google::InstallFailureSignalHandler();
+#endif
+
+  std::unique_ptr<nl_sock, void (*)(nl_sock*)> sock(nl_socket_alloc(),
+                                                    nl_socket_free);
+
+  auto res = nl_connect(sock.get(), NETLINK_GENERIC);
+  if (res < 0) {
+    LOG(ERROR) << "Could not connect to netlink generic: " << nl_geterror(res);
+    exit(1);
+  }
+
+  auto mac80211_family = genl_ctrl_resolve(sock.get(), kWifiSimFamilyName);
+  if (mac80211_family <= 0) {
+    LOG(ERROR) << "Could not find MAC80211 HWSIM. Please make sure module "
+               << "'mac80211_hwsim' is loaded on your system.";
+    exit(1);
+  }
+
+  RegisterForHWSimNotifications(sock.get(), mac80211_family);
+  auto server_fd = CreateWifiRouterServerSocket();
+  ServerLoop(server_fd, sock.get(), mac80211_family);
+}
diff --git a/guest/frontend/vnc_server/Android.mk b/guest/frontend/vnc_server/Android.mk
new file mode 100644
index 0000000..7a01360
--- /dev/null
+++ b/guest/frontend/vnc_server/Android.mk
@@ -0,0 +1,74 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION),16 17 18 19 21 22 23)) # J K L M
+  # prior to (not including) nyc, libjpeg was used instead of libjpeg turbo
+  # the libjpeg turbo in external/ is a backport with the shared library name
+  # changed to libjpeg_turbo to avoid conflict with the system's libjpeg
+  LIBJPEG_TURBO_NAME := libjpeg_turbo
+else
+  # nyc and later use libjpeg turbo under its usual name
+  LIBJPEG_TURBO_NAME := libjpeg
+endif
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    external/libjpeg-turbo \
+    external/jsoncpp/include
+
+LOCAL_MODULE := vnc_server
+LOCAL_VENDOR_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+	blackboard.cpp \
+	frame_buffer_watcher.cpp \
+	jpeg_compressor.cpp \
+	main.cpp \
+	simulated_hw_composer.cpp \
+	tcp_socket.cpp \
+	VirtualInputDevice.cpp \
+	virtual_inputs.cpp \
+	vnc_client_connection.cpp \
+	vnc_server.cpp \
+
+LOCAL_CFLAGS := \
+	$(VSOC_VERSION_CFLAGS) \
+	-std=gnu++11 \
+	-Wall -Werror \
+	-Wno-error-unused -Wno-error=unused-parameter \
+	-Wno-attributes
+
+LOCAL_CFLAGS += -Wno-error=implicit-exception-spec-mismatch
+
+LOCAL_SHARED_LIBRARIES := \
+    $(LIBJPEG_TURBO_NAME) \
+    libbase \
+    liblog \
+    libutils \
+    libcutils \
+    cuttlefish_auto_resources \
+    libcuttlefish_fs \
+    vsoc_lib \
+    libvsocframebuffer
+
+LOCAL_STATIC_LIBRARIES := \
+    liblog \
+    libjsoncpp
+
+include $(BUILD_EXECUTABLE)
diff --git a/guest/frontend/vnc_server/VirtualInputDevice.cpp b/guest/frontend/vnc_server/VirtualInputDevice.cpp
new file mode 100644
index 0000000..c757532
--- /dev/null
+++ b/guest/frontend/vnc_server/VirtualInputDevice.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+
+#include "keysyms.h"
+
+#define LOG_TAG "RemoterVirtualInput"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "VirtualInputDevice.h"
+
+#define ARRAY_SIZE(a)           \
+  ((sizeof(a) / sizeof(*(a))) / \
+   static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+//////////////////////////
+// VirtualButton Support
+//////////////////////////
+
+namespace cvd {
+uint32_t VirtualButton::Senabled_events_[] = {EV_KEY};
+
+VirtualButton::VirtualButton(const char* name, uint32_t input_keycode)
+    : VirtualInputDevice(name, BUS_USB, 0x6006, 0x6007, 1),
+      input_keycode_(input_keycode) {
+  if (!VirtualInputDevice::Init(Senabled_events_, ARRAY_SIZE(Senabled_events_),
+                                &input_keycode_, 1, NULL, 0, NULL, 0)) {
+    LOG_FATAL("VirtualInputDevice Init() failed");
+  }
+}
+
+void VirtualButton::HandleButtonPressEvent(bool button_down) {
+  EmitEvent(EV_KEY, input_keycode_, button_down);
+  EmitEvent(EV_SYN, 0, 0);
+}
+
+//////////////////////////
+// VirtualKeyboard Support
+//////////////////////////
+
+uint32_t VirtualKeyboard::Senabled_events_[] = {EV_KEY};
+
+struct KeyEventToInput {
+  uint32_t xk;
+  uint32_t input_code;
+};
+
+static const KeyEventToInput key_table[] = {
+    {xk::AltLeft, KEY_LEFTALT},
+    {xk::ControlLeft, KEY_LEFTCTRL},
+    {xk::ShiftLeft, KEY_LEFTSHIFT},
+    {xk::AltRight, KEY_RIGHTALT},
+    {xk::ControlRight, KEY_RIGHTCTRL},
+    {xk::ShiftRight, KEY_RIGHTSHIFT},
+    {xk::MetaLeft, KEY_LEFTMETA},
+    {xk::MetaRight, KEY_RIGHTMETA},
+    {xk::MultiKey, KEY_COMPOSE},
+
+    {xk::CapsLock, KEY_CAPSLOCK},
+    {xk::NumLock, KEY_NUMLOCK},
+    {xk::ScrollLock, KEY_SCROLLLOCK},
+
+    {xk::BackSpace, KEY_BACKSPACE},
+    {xk::Tab, KEY_TAB},
+    {xk::Return, KEY_ENTER},
+    {xk::Escape, KEY_ESC},
+
+    {' ', KEY_SPACE},
+    {'!', KEY_1},
+    {'"', KEY_APOSTROPHE},
+    {'#', KEY_3},
+    {'$', KEY_4},
+    {'%', KEY_5},
+    {'^', KEY_6},
+    {'&', KEY_7},
+    {'\'', KEY_APOSTROPHE},
+    {'(', KEY_9},
+    {')', KEY_0},
+    {'*', KEY_8},
+    {'+', KEY_EQUAL},
+    {',', KEY_COMMA},
+    {'-', KEY_MINUS},
+    {'.', KEY_DOT},
+    {'/', KEY_SLASH},
+    {'0', KEY_0},
+    {'1', KEY_1},
+    {'2', KEY_2},
+    {'3', KEY_3},
+    {'4', KEY_4},
+    {'5', KEY_5},
+    {'6', KEY_6},
+    {'7', KEY_7},
+    {'8', KEY_8},
+    {'9', KEY_9},
+    {':', KEY_SEMICOLON},
+    {';', KEY_SEMICOLON},
+    {'<', KEY_COMMA},
+    {'=', KEY_EQUAL},
+    {'>', KEY_DOT},
+    {'?', KEY_SLASH},
+    {'@', KEY_2},
+    {'A', KEY_A},
+    {'B', KEY_B},
+    {'C', KEY_C},
+    {'D', KEY_D},
+    {'E', KEY_E},
+    {'F', KEY_F},
+    {'G', KEY_G},
+    {'H', KEY_H},
+    {'I', KEY_I},
+    {'J', KEY_J},
+    {'K', KEY_K},
+    {'L', KEY_L},
+    {'M', KEY_M},
+    {'N', KEY_N},
+    {'O', KEY_O},
+    {'P', KEY_P},
+    {'Q', KEY_Q},
+    {'R', KEY_R},
+    {'S', KEY_S},
+    {'T', KEY_T},
+    {'U', KEY_U},
+    {'V', KEY_V},
+    {'W', KEY_W},
+    {'X', KEY_X},
+    {'Y', KEY_Y},
+    {'Z', KEY_Z},
+    {'[', KEY_LEFTBRACE},
+    {'\\', KEY_BACKSLASH},
+    {']', KEY_RIGHTBRACE},
+    {'-', KEY_MINUS},
+    {'_', KEY_MINUS},
+    {'`', KEY_GRAVE},
+    {'a', KEY_A},
+    {'b', KEY_B},
+    {'c', KEY_C},
+    {'d', KEY_D},
+    {'e', KEY_E},
+    {'f', KEY_F},
+    {'g', KEY_G},
+    {'h', KEY_H},
+    {'i', KEY_I},
+    {'j', KEY_J},
+    {'k', KEY_K},
+    {'l', KEY_L},
+    {'m', KEY_M},
+    {'n', KEY_N},
+    {'o', KEY_O},
+    {'p', KEY_P},
+    {'q', KEY_Q},
+    {'r', KEY_R},
+    {'s', KEY_S},
+    {'t', KEY_T},
+    {'u', KEY_U},
+    {'v', KEY_V},
+    {'w', KEY_W},
+    {'x', KEY_X},
+    {'y', KEY_Y},
+    {'z', KEY_Z},
+    {'{', KEY_LEFTBRACE},
+    {'\\', KEY_BACKSLASH},
+    {'|', KEY_BACKSLASH},
+    {'}', KEY_RIGHTBRACE},
+    {'~', KEY_GRAVE},
+
+    {xk::F1, KEY_F1},
+    {xk::F2, KEY_F2},
+    {xk::F3, KEY_F3},
+    {xk::F4, KEY_F4},
+    {xk::F5, KEY_F5},
+    {xk::F6, KEY_F6},
+    {xk::F7, KEY_F7},
+    {xk::F8, KEY_F8},
+    {xk::F9, KEY_F9},
+    {xk::F10, KEY_F10},
+    {xk::F11, KEY_F11},
+    {xk::F12, KEY_F12},
+    {xk::F13, KEY_F13},
+    {xk::F14, KEY_F14},
+    {xk::F15, KEY_F15},
+    {xk::F16, KEY_F16},
+    {xk::F17, KEY_F17},
+    {xk::F18, KEY_F18},
+    {xk::F19, KEY_F19},
+    {xk::F20, KEY_F20},
+    {xk::F21, KEY_F21},
+    {xk::F22, KEY_F22},
+    {xk::F23, KEY_F23},
+    {xk::F24, KEY_F24},
+
+    {xk::Keypad0, KEY_KP0},
+    {xk::Keypad1, KEY_KP1},
+    {xk::Keypad2, KEY_KP2},
+    {xk::Keypad3, KEY_KP3},
+    {xk::Keypad4, KEY_KP4},
+    {xk::Keypad5, KEY_KP5},
+    {xk::Keypad6, KEY_KP6},
+    {xk::Keypad7, KEY_KP7},
+    {xk::Keypad8, KEY_KP8},
+    {xk::Keypad9, KEY_KP9},
+    {xk::KeypadMultiply, KEY_KPASTERISK},
+    {xk::KeypadSubtract, KEY_KPMINUS},
+    {xk::KeypadAdd, KEY_KPPLUS},
+    {xk::KeypadDecimal, KEY_KPDOT},
+    {xk::KeypadEnter, KEY_KPENTER},
+    {xk::KeypadDivide, KEY_KPSLASH},
+    {xk::KeypadEqual, KEY_KPEQUAL},
+    {xk::PlusMinus, KEY_KPPLUSMINUS},
+
+    {xk::SysReq, KEY_SYSRQ},
+    {xk::LineFeed, KEY_LINEFEED},
+    {xk::Home, KEY_HOME},
+    {xk::Up, KEY_UP},
+    {xk::PageUp, KEY_PAGEUP},
+    {xk::Left, KEY_LEFT},
+    {xk::Right, KEY_RIGHT},
+    {xk::End, KEY_END},
+    {xk::Down, KEY_DOWN},
+    {xk::PageDown, KEY_PAGEDOWN},
+    {xk::Insert, KEY_INSERT},
+    {xk::Delete, KEY_DELETE},
+    {xk::Pause, KEY_PAUSE},
+    {xk::KeypadSeparator, KEY_KPCOMMA},
+    {xk::Yen, KEY_YEN},
+    {xk::Cancel, KEY_STOP},
+    {xk::Redo, KEY_AGAIN},
+    {xk::Undo, KEY_UNDO},
+    {xk::Find, KEY_FIND},
+    {xk::Print, KEY_PRINT},
+    {xk::VolumeDown, KEY_VOLUMEDOWN},
+    {xk::Mute, KEY_MUTE},
+    {xk::VolumeUp, KEY_VOLUMEUP},
+    {xk::Menu, KEY_MENU},
+    {xk::VNCMenu, KEY_MENU},
+};
+
+VirtualKeyboard::VirtualKeyboard(const char* name)
+    : VirtualInputDevice(name, BUS_USB, 0x6006, 0x6008, 1) {
+  std::vector<uint32_t> keycodes(ARRAY_SIZE(key_table));
+  for (size_t i = 0; i < keycodes.size(); ++i) {
+    keymapping_[key_table[i].xk] = key_table[i].input_code;
+    keycodes[i] = key_table[i].input_code;
+  }
+
+  if (!VirtualInputDevice::Init(Senabled_events_, ARRAY_SIZE(Senabled_events_),
+                                &keycodes[0], keycodes.size(), NULL, 0, NULL,
+                                0)) {
+    LOG_FATAL("VirtualInputDevice Init() failed");
+  }
+}
+
+void VirtualKeyboard::GenerateKeyPressEvent(int keycode, bool button_down) {
+  if (keymapping_.count(keycode)) {
+    EmitEvent(EV_KEY, keymapping_[keycode], button_down);
+    EmitEvent(EV_SYN, 0, 0);
+  }
+  ALOGI("Unknown keycode %d", keycode);
+}
+
+//////////////////////////
+// VirtualTouchPad Support
+//////////////////////////
+
+uint32_t VirtualTouchPad::Senabled_events_[] = {EV_ABS, EV_KEY, EV_SYN};
+uint32_t VirtualTouchPad::Senabled_keys_[] = {BTN_TOUCH};
+uint32_t VirtualTouchPad::Senabled_abs_[] = {ABS_X, ABS_Y};
+uint32_t VirtualTouchPad::Senabled_props_[] = {INPUT_PROP_DIRECT};
+
+VirtualTouchPad::VirtualTouchPad(const char* name, int x_res, int y_res)
+    : VirtualInputDevice(name, BUS_USB, 0x6006, 0x6006, 1),
+      x_res_(x_res),
+      y_res_(y_res) {
+  // Customization of uinput_user_dev() must happen before calling our base
+  // Init().
+  uinput_user_dev()->absmin[ABS_X] = 0;
+  uinput_user_dev()->absmax[ABS_X] = x_res_;
+  uinput_user_dev()->absmin[ABS_Y] = 0;
+  uinput_user_dev()->absmax[ABS_Y] = y_res_;
+
+  if (!VirtualInputDevice::Init(Senabled_events_, ARRAY_SIZE(Senabled_events_),
+                                Senabled_keys_, ARRAY_SIZE(Senabled_keys_),
+                                Senabled_abs_, ARRAY_SIZE(Senabled_abs_),
+                                Senabled_props_, ARRAY_SIZE(Senabled_props_))) {
+    LOG_FATAL("VirtualInputDevice Init() failed");
+  }
+}
+
+void VirtualTouchPad::HandlePointerEvent(bool touch_down, int x, int y) {
+  EmitEvent(EV_ABS, ABS_X, x);
+  EmitEvent(EV_ABS, ABS_Y, y);
+  EmitEvent(EV_KEY, BTN_TOUCH, touch_down);
+  EmitEvent(EV_SYN, 0, 0);
+}
+
+//////////////////////////////////
+// Base VirtualInputDevice Support
+//////////////////////////////////
+VirtualInputDevice::VirtualInputDevice(const char* name, uint16_t bus_type,
+                                       uint16_t vendor, uint16_t product,
+                                       uint16_t version)
+    : fd_(-1) {
+  memset(&uinput_user_dev_, 0, sizeof(uinput_user_dev_));
+  strncpy(uinput_user_dev_.name, name, sizeof(uinput_user_dev_.name));
+  uinput_user_dev_.id.bustype = bus_type;
+  uinput_user_dev_.id.vendor = vendor;
+  uinput_user_dev_.id.product = product;
+  uinput_user_dev_.id.version = version;
+}
+
+VirtualInputDevice::~VirtualInputDevice() {
+  if (fd_ != -1) {
+    close(fd_);
+    fd_ = -1;
+  }
+}
+
+bool VirtualInputDevice::Init(uint32_t* events, int num_events, uint32_t* keys,
+                              int num_keys, uint32_t* abs, int num_abs,
+                              uint32_t* props, int num_props) {
+  if ((fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK)) < 0) {
+    SLOGE("Failed to open /dev/uinput (%s)", strerror(errno));
+    return false;
+  }
+  if (events && !EnableEventBits(events, num_events)) {
+    SLOGE("Failed to set event bits (%s)", strerror(errno));
+    return false;
+  }
+  if (keys && !EnableKeyBits(keys, num_keys)) {
+    SLOGE("Failed to set key bits (%s)", strerror(errno));
+    return false;
+  }
+  if (abs && !EnableAbsBits(abs, num_abs)) {
+    SLOGE("Failed to set abs bits (%s)", strerror(errno));
+    return false;
+  }
+  if (props && !EnablePropBits(props, num_props)) {
+    SLOGE("Failed to set prop bits (%s)", strerror(errno));
+    return false;
+  }
+  if (!FinalizeDeviceCreation()) {
+    SLOGE("Failed to finalize device creation (%s)", strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+bool VirtualInputDevice::EmitEvent(uint16_t type, uint16_t code,
+                                   uint32_t value) {
+  struct input_event ev;
+  ev.type = type;
+  ev.code = code;
+  ev.value = value;
+  if (write(fd_, &ev, sizeof(ev)) < 0) {
+    SLOGE("write() failed (%s)", strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+bool VirtualInputDevice::DoIoctls(int request, uint32_t* list,
+                                  int num_elements) {
+  for (int i = 0; i < num_elements; i++) {
+    int rc = ioctl(fd_, request, *list++);
+    if (rc < 0) {
+      SLOGE("ioctl failed (%s)", strerror(errno));
+      return false;
+    }
+  }
+  return true;
+}
+
+bool VirtualInputDevice::EnableEventBits(uint32_t* events, int num_elements) {
+  return DoIoctls(UI_SET_EVBIT, events, num_elements);
+}
+
+bool VirtualInputDevice::EnableKeyBits(uint32_t* keys, int num_elements) {
+  return DoIoctls(UI_SET_KEYBIT, keys, num_elements);
+}
+
+bool VirtualInputDevice::EnableAbsBits(uint32_t* abs, int num_elements) {
+  return DoIoctls(UI_SET_ABSBIT, abs, num_elements);
+}
+
+bool VirtualInputDevice::EnablePropBits(uint32_t* props, int num_elements) {
+// JB and ICE do not have the latest uinput headers.
+#ifndef UI_SET_PROPBIT
+#define UI_SET_PROPBIT _IOW(UINPUT_IOCTL_BASE, 110, int)
+#endif  // #ifndef UI_SET_PROPBIT
+  return DoIoctls(UI_SET_PROPBIT, props, num_elements);
+}
+
+bool VirtualInputDevice::FinalizeDeviceCreation() {
+  if (write(fd_, &uinput_user_dev_, sizeof(uinput_user_dev_)) < 0) {
+    SLOGE("Unable to set input device info (%s)", strerror(errno));
+    return false;
+  }
+  if (ioctl(fd_, UI_DEV_CREATE) < 0) {
+    SLOGE("Unable to create input device (%s)", strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/VirtualInputDevice.h b/guest/frontend/vnc_server/VirtualInputDevice.h
new file mode 100644
index 0000000..d25f45c
--- /dev/null
+++ b/guest/frontend/vnc_server/VirtualInputDevice.h
@@ -0,0 +1,93 @@
+#pragma once
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <linux/uinput.h>
+#include <map>
+
+namespace cvd {
+// Base virtual input device class which contains a bunch of boiler-plate code.
+class VirtualInputDevice {
+public:
+  VirtualInputDevice(const char* name, uint16_t bus_type, uint16_t vendor,
+                     uint16_t product, uint16_t version);
+  virtual ~VirtualInputDevice();
+
+protected:
+  bool Init(uint32_t* events, int num_events, uint32_t* keys, int num_keys,
+            uint32_t* abs, int num_abs, uint32_t* props, int num_props);
+
+  bool EmitEvent(uint16_t type, uint16_t code, uint32_t value);
+
+  struct uinput_user_dev* uinput_user_dev() { return &uinput_user_dev_; }
+
+private:
+  bool EnableEventBits(uint32_t* events, int num_elements);
+  bool EnableKeyBits(uint32_t* keys, int num_elements);
+  bool EnableAbsBits(uint32_t* abs, int num_elements);
+  bool EnablePropBits(uint32_t* props, int num_elements);
+  bool DoIoctls(int request, uint32_t* list, int num_elements);
+  bool FinalizeDeviceCreation();
+
+  int fd_;
+  struct uinput_user_dev uinput_user_dev_;
+};
+
+// Virtual touch-pad.
+class VirtualTouchPad : public VirtualInputDevice {
+public:
+  VirtualTouchPad(const char* name, int x_res, int y_res);
+  virtual ~VirtualTouchPad() {}
+
+  void HandlePointerEvent(bool touch_down, int x, int y);
+
+private:
+  static uint32_t Senabled_events_[];
+  static uint32_t Senabled_keys_[];
+  static uint32_t Senabled_abs_[];
+  static uint32_t Senabled_props_[];
+
+  int x_res_;
+  int y_res_;
+};
+
+// Virtual button.
+class VirtualButton : public VirtualInputDevice {
+public:
+  VirtualButton(const char* name, uint32_t input_keycode);
+  virtual ~VirtualButton() {}
+
+  void HandleButtonPressEvent(bool button_down);
+
+private:
+  static uint32_t Senabled_events_[];
+  uint32_t input_keycode_;
+};
+
+// Virtual keyboard.
+class VirtualKeyboard : public VirtualInputDevice {
+public:
+  VirtualKeyboard(const char* name);
+  virtual ~VirtualKeyboard() {}
+
+  void GenerateKeyPressEvent(int code, bool down);
+
+private:
+  static uint32_t Senabled_events_[];
+  std::map<uint32_t, uint32_t> keymapping_;
+};
+
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/blackboard.cpp b/guest/frontend/vnc_server/blackboard.cpp
new file mode 100644
index 0000000..b28e399
--- /dev/null
+++ b/guest/frontend/vnc_server/blackboard.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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 "blackboard.h"
+#include "frame_buffer_watcher.h"
+#include <utility>
+#include <algorithm>
+
+#define LOG_TAG ""
+#include <cutils/log.h>
+
+using cvd::vnc::BlackBoard;
+using cvd::vnc::Stripe;
+
+cvd::vnc::SeqNumberVec cvd::vnc::MakeSeqNumberVec() {
+  return SeqNumberVec(FrameBufferWatcher::StripesPerFrame());
+}
+
+void BlackBoard::NewStripeReady(int index, StripeSeqNumber seq_num) {
+  std::lock_guard<std::mutex> guard(m_);
+  D("new stripe arrived from frame watcher");
+  auto& current_seq_num = most_recent_stripe_seq_nums_[index];
+  current_seq_num = std::max(current_seq_num, seq_num);
+  for (auto& client : clients_) {
+    if (client.second.ready_to_receive) {
+      client.second.new_frame_cv.notify_one();
+    }
+  }
+}
+
+void BlackBoard::Register(const VncClientConnection* conn) {
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    ALOG_ASSERT(!clients_.count(conn));
+    clients_[conn];  // constructs new state in place
+  }
+  new_client_cv_.notify_one();
+}
+
+void BlackBoard::Unregister(const VncClientConnection* conn) {
+  std::lock_guard<std::mutex> guard(m_);
+  ALOG_ASSERT(clients_.count(conn));
+  clients_.erase(clients_.find(conn));
+}
+
+bool BlackBoard::NoNewStripesFor(const SeqNumberVec& seq_nums) const {
+  ALOG_ASSERT(seq_nums.size() == most_recent_stripe_seq_nums.size());
+  for (auto state_seq_num = seq_nums.begin(),
+            held_seq_num = most_recent_stripe_seq_nums_.begin();
+       state_seq_num != seq_nums.end(); ++state_seq_num, ++held_seq_num) {
+    if (*state_seq_num < *held_seq_num) {
+      return false;
+    }
+  }
+  return true;
+}
+
+cvd::vnc::StripePtrVec BlackBoard::WaitForSenderWork(
+    const VncClientConnection* conn) {
+  std::unique_lock<std::mutex> guard(m_);
+  auto& state = GetStateForClient(conn);
+  D("Waiting for stripe...");
+  while (!state.closed &&
+         (!state.ready_to_receive || NoNewStripesFor(state.stripe_seq_nums))) {
+    state.new_frame_cv.wait(guard);
+  }
+  D("At least one new stripe is available, should unblock %p", conn);
+  state.ready_to_receive = false;
+  auto new_stripes = frame_buffer_watcher_->StripesNewerThan(
+      state.orientation, state.stripe_seq_nums);
+  for (auto& s : new_stripes) {
+    state.stripe_seq_nums[s->index] = s->seq_number;
+  }
+  return new_stripes;
+}
+
+void BlackBoard::WaitForAtLeastOneClientConnection() {
+  std::unique_lock<std::mutex> guard(m_);
+  while (clients_.empty()) {
+    new_client_cv_.wait(guard);
+  }
+}
+
+void BlackBoard::SetOrientation(const VncClientConnection* conn,
+                                ScreenOrientation orientation) {
+  std::lock_guard<std::mutex> guard(m_);
+  auto& state = GetStateForClient(conn);
+  state.orientation = orientation;
+  // After an orientation change the vnc client will need all stripes from
+  // the new orientation, regardless of age.
+  ResetToZero(&state.stripe_seq_nums);
+}
+
+void BlackBoard::SignalClientNeedsEntireScreen(
+    const VncClientConnection* conn) {
+  std::lock_guard<std::mutex> guard(m_);
+  ResetToZero(&GetStateForClient(conn).stripe_seq_nums);
+}
+
+void BlackBoard::ResetToZero(SeqNumberVec* seq_nums) {
+  seq_nums->assign(FrameBufferWatcher::StripesPerFrame(), StripeSeqNumber{});
+}
+
+void BlackBoard::FrameBufferUpdateRequestReceived(
+    const VncClientConnection* conn) {
+  std::lock_guard<std::mutex> guard(m_);
+  D("Received frame buffer update request");
+  auto& state = GetStateForClient(conn);
+  state.ready_to_receive = true;
+  state.new_frame_cv.notify_one();
+}
+
+void BlackBoard::StopWaiting(const VncClientConnection* conn) {
+  std::lock_guard<std::mutex> guard(m_);
+  auto& state = GetStateForClient(conn);
+  state.closed = true;
+  // Wake up the thread that might be in WaitForSenderWork()
+  state.new_frame_cv.notify_one();
+}
+
+void BlackBoard::set_frame_buffer_watcher(
+    cvd::vnc::FrameBufferWatcher* frame_buffer_watcher) {
+  std::lock_guard<std::mutex> guard(m_);
+  frame_buffer_watcher_ = frame_buffer_watcher;
+}
+
+void BlackBoard::set_jpeg_quality_level(int quality_level) {
+  // NOTE all vnc clients share a common jpeg quality level because the
+  // server doesn't compress per-client. The quality level for all clients
+  // will be whatever the most recent set was by any client.
+  std::lock_guard<std::mutex> guard(m_);
+  if (quality_level < kJpegMinQualityEncoding ||
+      quality_level > kJpegMaxQualityEncoding) {
+    ALOGW("Bogus jpeg quality level: %d. Quality must be in range [%d, %d]",
+          quality_level, kJpegMinQualityEncoding, kJpegMaxQualityEncoding);
+    return;
+  }
+  jpeg_quality_level_ = 55 + (5 * (quality_level + 32));
+  D("jpeg quality level set to %d%%:", jpeg_quality_level_);
+}
+
+BlackBoard::ClientFBUState& BlackBoard::GetStateForClient(
+    const VncClientConnection* conn) {
+  ALOG_ASSERT(clients_.count(conn));
+  return clients_[conn];
+}
diff --git a/guest/frontend/vnc_server/blackboard.h b/guest/frontend/vnc_server/blackboard.h
new file mode 100644
index 0000000..dfd9257
--- /dev/null
+++ b/guest/frontend/vnc_server/blackboard.h
@@ -0,0 +1,114 @@
+#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 "vnc_utils.h"
+
+#include <android-base/thread_annotations.h>
+
+#include <unordered_map>
+#include <mutex>
+#include <condition_variable>
+#include <memory>
+
+namespace cvd {
+namespace vnc {
+
+class VncClientConnection;
+class FrameBufferWatcher;
+using StripePtrVec = std::vector<std::shared_ptr<const Stripe>>;
+using SeqNumberVec = std::vector<StripeSeqNumber>;
+
+SeqNumberVec MakeSeqNumberVec();
+
+class BlackBoard {
+ private:
+  struct ClientFBUState {
+    bool ready_to_receive{};
+    ScreenOrientation orientation{};
+    std::condition_variable new_frame_cv;
+    SeqNumberVec stripe_seq_nums = MakeSeqNumberVec();
+    bool closed{};
+  };
+
+ public:
+  class Registerer {
+   public:
+    Registerer(BlackBoard* bb, const VncClientConnection* conn)
+        : bb_{bb}, conn_{conn} {
+      bb->Register(conn);
+    }
+    ~Registerer() {
+      bb_->Unregister(conn_);
+    }
+    Registerer(const Registerer&) = delete;
+    Registerer& operator=(const Registerer&) = delete;
+
+   private:
+    BlackBoard* bb_{};
+    const VncClientConnection* conn_{};
+  };
+
+  BlackBoard() = default;
+  BlackBoard(const BlackBoard&) = delete;
+  BlackBoard& operator=(const BlackBoard&) = delete;
+
+  bool NoNewStripesFor(const SeqNumberVec& seq_nums) const REQUIRES(m_);
+  void NewStripeReady(int index, StripeSeqNumber seq_num);
+  void Register(const VncClientConnection* conn);
+  void Unregister(const VncClientConnection* conn);
+
+  StripePtrVec WaitForSenderWork(const VncClientConnection* conn);
+
+  void WaitForAtLeastOneClientConnection();
+
+  void FrameBufferUpdateRequestReceived(const VncClientConnection* conn);
+  // Setting orientation implies needing the entire screen
+  void SetOrientation(const VncClientConnection* conn,
+                      ScreenOrientation orientation);
+  void SignalClientNeedsEntireScreen(const VncClientConnection* conn);
+
+  void StopWaiting(const VncClientConnection* conn);
+
+  void set_frame_buffer_watcher(FrameBufferWatcher* frame_buffer_watcher);
+
+  // quality_level must be the value received from the client, in the range
+  // [kJpegMinQualityEncoding, kJpegMaxQualityEncoding], else it is ignored.
+  void set_jpeg_quality_level(int quality_level);
+
+  int jpeg_quality_level() const {
+    std::lock_guard<std::mutex> guard(m_);
+    return jpeg_quality_level_;
+  }
+
+ private:
+  ClientFBUState& GetStateForClient(const VncClientConnection* conn)
+      REQUIRES(m_);
+  static void ResetToZero(SeqNumberVec* seq_nums);
+
+  mutable std::mutex m_;
+  SeqNumberVec most_recent_stripe_seq_nums_ GUARDED_BY(m_) = MakeSeqNumberVec();
+  std::unordered_map<const VncClientConnection*, ClientFBUState> clients_
+      GUARDED_BY(m_);
+  int jpeg_quality_level_ GUARDED_BY(m_) = 100;
+  std::condition_variable new_client_cv_;
+  // NOTE the FrameBufferWatcher pointer itself should be
+  // guarded, but not the pointee.
+  FrameBufferWatcher* frame_buffer_watcher_ GUARDED_BY(m_){};
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/frame_buffer_watcher.cpp b/guest/frontend/vnc_server/frame_buffer_watcher.cpp
new file mode 100644
index 0000000..5f68748
--- /dev/null
+++ b/guest/frontend/vnc_server/frame_buffer_watcher.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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 "vnc_utils.h"
+#include "frame_buffer_watcher.h"
+#include <common/libs/thread_safe_queue/thread_safe_queue.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <utility>
+
+#define LOG_TAG ""
+#include <cutils/log.h>
+
+using cvd::vnc::FrameBufferWatcher;
+
+FrameBufferWatcher::FrameBufferWatcher(BlackBoard* bb)
+    : bb_{bb}, hwcomposer{bb_} {
+  for (auto& stripes_vec : stripes_) {
+    std::generate_n(std::back_inserter(stripes_vec),
+                    SimulatedHWComposer::NumberOfStripes(),
+                    std::make_shared<Stripe>);
+  }
+  bb_->set_frame_buffer_watcher(this);
+  auto num_workers = std::max(std::thread::hardware_concurrency(), 1u);
+  std::generate_n(std::back_inserter(workers_), num_workers, [this] {
+    return std::thread{&FrameBufferWatcher::Worker, this};
+  });
+}
+
+FrameBufferWatcher::~FrameBufferWatcher() {
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    closed_ = true;
+  }
+  for (auto& tid : workers_) {
+    tid.join();
+  }
+}
+
+bool FrameBufferWatcher::closed() const {
+  std::lock_guard<std::mutex> guard(m_);
+  return closed_;
+}
+
+cvd::vnc::Stripe FrameBufferWatcher::Rotated(Stripe stripe) {
+  LOG_ALWAYS_FATAL_IF(stripe.orientation == ScreenOrientation::Landscape,
+                      "Rotating a landscape stripe, this is a mistake");
+  auto w = stripe.width;
+  auto h = stripe.height;
+  const auto& raw = stripe.raw_data;
+  Message rotated(raw.size(), 0xAA);
+  for (std::uint16_t i = 0; i < w; ++i) {
+    for (std::uint16_t j = 0; j < h; ++j) {
+      auto to = (i * h + j) * BytesPerPixel();
+      auto from = (w - (i + 1) + w * j) * BytesPerPixel();
+      ALOG_ASSERT(from < raw.size());
+      ALOG_ASSERT(to < rotated.size());
+      std::memcpy(&rotated[to], &raw[from], BytesPerPixel());
+    }
+  }
+  std::swap(stripe.x, stripe.y);
+  std::swap(stripe.width, stripe.height);
+  stripe.raw_data = std::move(rotated);
+  stripe.orientation = ScreenOrientation::Landscape;
+  return stripe;
+}
+
+bool FrameBufferWatcher::StripeIsDifferentFromPrevious(
+    const Stripe& stripe) const {
+  return Stripes(stripe.orientation)[stripe.index]->raw_data != stripe.raw_data;
+}
+
+cvd::vnc::StripePtrVec FrameBufferWatcher::StripesNewerThan(
+    ScreenOrientation orientation, const SeqNumberVec& seq_numbers) const {
+  std::lock_guard<std::mutex> guard(stripes_lock_);
+  const auto& stripes = Stripes(orientation);
+  ALOG_ASSERT(seq_numbers.size() == stripes.size());
+  StripePtrVec new_stripes;
+  auto seq_number_it = seq_numbers.begin();
+  std::copy_if(stripes.begin(), stripes.end(), std::back_inserter(new_stripes),
+               [seq_number_it](const StripePtrVec::value_type& s) mutable {
+                 return *(seq_number_it++) < s->seq_number;
+               });
+  return new_stripes;
+}
+
+cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
+    ScreenOrientation orientation) {
+  return stripes_[static_cast<int>(orientation)];
+}
+
+const cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
+    ScreenOrientation orientation) const {
+  return stripes_[static_cast<int>(orientation)];
+}
+
+bool FrameBufferWatcher::UpdateMostRecentSeqNumIfStripeIsNew(
+    const Stripe& stripe) {
+  if (most_recent_identical_stripe_seq_nums_[stripe.index] <=
+      stripe.seq_number) {
+    most_recent_identical_stripe_seq_nums_[stripe.index] = stripe.seq_number;
+    return true;
+  }
+  return false;
+}
+
+bool FrameBufferWatcher::UpdateStripeIfStripeIsNew(
+    const std::shared_ptr<const Stripe>& stripe) {
+  std::lock_guard<std::mutex> guard(stripes_lock_);
+  if (UpdateMostRecentSeqNumIfStripeIsNew(*stripe)) {
+    Stripes(stripe->orientation)[stripe->index] = stripe;
+    return true;
+  }
+  return false;
+}
+
+void FrameBufferWatcher::CompressStripe(JpegCompressor* jpeg_compressor,
+                                        Stripe* stripe) {
+  stripe->jpeg_data = jpeg_compressor->Compress(
+      stripe->raw_data, bb_->jpeg_quality_level(), 0, 0, stripe->width,
+      stripe->height, stripe->width);
+}
+
+void FrameBufferWatcher::Worker() {
+  JpegCompressor jpeg_compressor;
+#ifdef FUZZ_TEST_VNC
+  std::default_random_engine e{std::random_device{}()};
+  std::uniform_int_distribution<int> random{0, 2};
+#endif
+  while (!closed()) {
+    auto portrait_stripe = hwcomposer.GetNewStripe();
+    if (closed()) {
+      break;
+    }
+    {
+      // TODO(haining) use if (with init) and else for c++17 instead of extra
+      // scope and continue
+      // if (std::lock_guard guard(stripes_lock_); /*condition*/) { }
+      std::lock_guard<std::mutex> guard(stripes_lock_);
+      if (!StripeIsDifferentFromPrevious(portrait_stripe)) {
+        UpdateMostRecentSeqNumIfStripeIsNew(portrait_stripe);
+        continue;
+      }
+    }
+    auto seq_num = portrait_stripe.seq_number;
+    auto index = portrait_stripe.index;
+    auto landscape_stripe = Rotated(portrait_stripe);
+    auto stripes = {std::make_shared<Stripe>(std::move(portrait_stripe)),
+                    std::make_shared<Stripe>(std::move(landscape_stripe))};
+    for (auto& stripe : stripes) {
+#ifdef FUZZ_TEST_VNC
+      if (random(e)) {
+        usleep(10000);
+      }
+#endif
+      CompressStripe(&jpeg_compressor, stripe.get());
+    }
+    bool any_new_stripes = false;
+    for (auto& stripe : stripes) {
+      any_new_stripes = UpdateStripeIfStripeIsNew(stripe) || any_new_stripes;
+    }
+    if (any_new_stripes) {
+      bb_->NewStripeReady(index, seq_num);
+    }
+  }
+}
+
+int FrameBufferWatcher::StripesPerFrame() {
+  return SimulatedHWComposer::NumberOfStripes();
+}
diff --git a/guest/frontend/vnc_server/frame_buffer_watcher.h b/guest/frontend/vnc_server/frame_buffer_watcher.h
new file mode 100644
index 0000000..b678b8f
--- /dev/null
+++ b/guest/frontend/vnc_server/frame_buffer_watcher.h
@@ -0,0 +1,74 @@
+#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 "blackboard.h"
+#include "jpeg_compressor.h"
+#include "simulated_hw_composer.h"
+
+#include <vector>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <utility>
+
+namespace cvd {
+namespace vnc {
+class FrameBufferWatcher {
+ public:
+  explicit FrameBufferWatcher(BlackBoard* bb);
+  FrameBufferWatcher(const FrameBufferWatcher&) = delete;
+  FrameBufferWatcher& operator=(const FrameBufferWatcher&) = delete;
+  ~FrameBufferWatcher();
+
+  StripePtrVec StripesNewerThan(ScreenOrientation orientation,
+                                const SeqNumberVec& seq_num) const;
+  static int StripesPerFrame();
+
+ private:
+  static Stripe Rotated(Stripe stripe);
+
+  bool closed() const;
+  bool StripeIsDifferentFromPrevious(const Stripe& stripe) const
+      REQUIRES(stripes_lock_);
+  // returns true if stripe is still considered new and seq number was updated
+  bool UpdateMostRecentSeqNumIfStripeIsNew(const Stripe& stripe)
+      REQUIRES(stripes_lock_);
+  // returns true if stripe is still considered new and was updated
+  bool UpdateStripeIfStripeIsNew(const std::shared_ptr<const Stripe>& stripe)
+      EXCLUDES(stripes_lock_);
+  // Compresses stripe->raw_data to stripe->jpeg_data
+  void CompressStripe(JpegCompressor* jpeg_compressor, Stripe* stripe);
+  void Worker();
+  void Updater();
+
+  StripePtrVec& Stripes(ScreenOrientation orientation) REQUIRES(stripes_lock_);
+  const StripePtrVec& Stripes(ScreenOrientation orientation) const
+      REQUIRES(stripes_lock_);
+
+  std::vector<std::thread> workers_;
+  mutable std::mutex stripes_lock_;
+  std::array<StripePtrVec, kNumOrientations> stripes_ GUARDED_BY(stripes_lock_);
+  SeqNumberVec most_recent_identical_stripe_seq_nums_
+      GUARDED_BY(stripes_lock_) = MakeSeqNumberVec();
+  mutable std::mutex m_;
+  bool closed_ GUARDED_BY(m_){};
+  BlackBoard* bb_{};
+  SimulatedHWComposer hwcomposer{bb_};
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/jpeg_compressor.cpp b/guest/frontend/vnc_server/jpeg_compressor.cpp
new file mode 100644
index 0000000..560f9d8
--- /dev/null
+++ b/guest/frontend/vnc_server/jpeg_compressor.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 "jpeg_compressor.h"
+#include "vnc_utils.h"
+
+#include <stdio.h>  // stdio.h must appear before jpeglib.h
+#include <jpeglib.h>
+
+#define LOG_TAG ""
+#include <cutils/log.h>
+
+using cvd::vnc::JpegCompressor;
+
+namespace {
+void InitCinfo(jpeg_compress_struct* cinfo, jpeg_error_mgr* err,
+               std::uint16_t width, std::uint16_t height, int jpeg_quality) {
+  cinfo->err = jpeg_std_error(err);
+  jpeg_create_compress(cinfo);
+
+  cinfo->image_width = width;
+  cinfo->image_height = height;
+  cinfo->input_components = cvd::vnc::BytesPerPixel();
+  cinfo->in_color_space = JCS_EXT_RGBX;
+
+  jpeg_set_defaults(cinfo);
+  jpeg_set_quality(cinfo, jpeg_quality, true);
+}
+}  // namespace
+
+cvd::vnc::Message JpegCompressor::Compress(const Message& frame,
+                                           int jpeg_quality, std::uint16_t x,
+                                           std::uint16_t y, std::uint16_t width,
+                                           std::uint16_t height,
+                                           int screen_width) {
+  jpeg_compress_struct cinfo{};
+  jpeg_error_mgr err{};
+  InitCinfo(&cinfo, &err, width, height, jpeg_quality);
+
+  auto* compression_buffer = buffer_.get();
+  auto compression_buffer_size = buffer_capacity_;
+  jpeg_mem_dest(&cinfo, &compression_buffer, &compression_buffer_size);
+  jpeg_start_compress(&cinfo, true);
+
+  while (cinfo.next_scanline < cinfo.image_height) {
+    auto row = static_cast<JSAMPROW>(const_cast<std::uint8_t*>(
+        &frame[(y * screen_width * BytesPerPixel()) +
+               (cinfo.next_scanline * BytesPerPixel() * screen_width) +
+               (x * BytesPerPixel())]));
+    jpeg_write_scanlines(&cinfo, &row, 1);
+  }
+  jpeg_finish_compress(&cinfo);
+  jpeg_destroy_compress(&cinfo);
+
+  UpdateBuffer(compression_buffer, compression_buffer_size);
+  return {compression_buffer, compression_buffer + compression_buffer_size};
+}
+
+void JpegCompressor::UpdateBuffer(std::uint8_t* compression_buffer,
+                                  unsigned long compression_buffer_size) {
+  if (buffer_capacity_ < compression_buffer_size) {
+    ALOG_ASSERT(buffer_ != compression_buffer);
+    buffer_capacity_ = compression_buffer_size;
+    buffer_.reset(compression_buffer);
+  } else {
+    ALOG_ASSERT(buffer_ == compression_buffer);
+  }
+}
diff --git a/guest/frontend/vnc_server/jpeg_compressor.h b/guest/frontend/vnc_server/jpeg_compressor.h
new file mode 100644
index 0000000..d9c40ee
--- /dev/null
+++ b/guest/frontend/vnc_server/jpeg_compressor.h
@@ -0,0 +1,53 @@
+#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 "vnc_utils.h"
+
+#include <memory>
+#include <cstdlib>
+#include <cstdint>
+
+namespace cvd {
+namespace vnc {
+
+// libjpeg-turbo with jpeg_mem_dest (using memory as a destination) is funky.
+// If you give it a buffer that is big enough it will use it.
+// If you give it a buffer that is too small, it will allocate a new buffer
+// but will NOT free the buffer you gave it.
+// This class keeps track of the capacity of the working buffer, and frees the
+// old buffer if libjpeg-turbo silently discards it.
+class JpegCompressor {
+ public:
+  Message Compress(const Message& frame, int jpeg_quality, std::uint16_t x,
+                   std::uint16_t y, std::uint16_t width, std::uint16_t height,
+                   int screen_width);
+
+ private:
+  void UpdateBuffer(std::uint8_t* compression_buffer,
+                    unsigned long compression_buffer_size);
+  struct Freer {
+    void operator()(void* p) const {
+      std::free(p);
+    }
+  };
+
+  std::unique_ptr<std::uint8_t, Freer> buffer_;
+  unsigned long buffer_capacity_{};
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/keysyms.h b/guest/frontend/vnc_server/keysyms.h
new file mode 100644
index 0000000..8da0117
--- /dev/null
+++ b/guest/frontend/vnc_server/keysyms.h
@@ -0,0 +1,107 @@
+#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.
+ */
+
+namespace cvd {
+namespace xk {
+
+constexpr uint32_t BackSpace = 0xff08,
+          Tab = 0xff09,
+          Return = 0xff0d,
+          Enter = Return,
+          Escape = 0xff1b,
+          MultiKey = 0xff20,
+          Insert = 0xff63,
+          Delete = 0xffff,
+          Pause = 0xff13,
+          Home = 0xff50,
+          End = 0xff57,
+          PageUp = 0xff55,
+          PageDown = 0xff56,
+          Left = 0xff51,
+          Up = 0xff52,
+          Right = 0xff53,
+          Down = 0xff54,
+          F1 = 0xffbe,
+          F2 = 0xffbf,
+          F3 = 0xffc0,
+          F4 = 0xffc1,
+          F5 = 0xffc2,
+          F6 = 0xffc3,
+          F7 = 0xffc4,
+          F8 = 0xffc5,
+          F9 = 0xffc6,
+          F10 = 0xffc7,
+          F11 = 0xffc8,
+          F12 = 0xffc9,
+          F13 = 0xffca,
+          F14 = 0xffcb,
+          F15 = 0xffcc,
+          F16 = 0xffcd,
+          F17 = 0xffce,
+          F18 = 0xffcf,
+          F19 = 0xffd0,
+          F20 = 0xffd1,
+          F21 = 0xffd2,
+          F22 = 0xffd3,
+          F23 = 0xffd4,
+          F24 = 0xffd5,
+          ShiftLeft = 0xffe1,
+          ShiftRight = 0xffe2,
+          ControlLeft = 0xffe3,
+          ControlRight = 0xffe4,
+          MetaLeft = 0xffe7,
+          MetaRight = 0xffe8,
+          AltLeft = 0xffe9,
+          AltRight = 0xffea,
+          CapsLock = 0xffe5,
+          NumLock = 0xff7f,
+          ScrollLock = 0xff14,
+          Keypad0 = 0xffb0,
+          Keypad1 = 0xffb1,
+          Keypad2 = 0xffb2,
+          Keypad3 = 0xffb3,
+          Keypad4 = 0xffb4,
+          Keypad5 = 0xffb5,
+          Keypad6 = 0xffb6,
+          Keypad7 = 0xffb7,
+          Keypad8 = 0xffb8,
+          Keypad9 = 0xffb9,
+          KeypadMultiply = 0xffaa,
+          KeypadSubtract = 0xffad,
+          KeypadAdd = 0xffab,
+          KeypadDecimal = 0xffae,
+          KeypadEnter = 0xff8d,
+          KeypadDivide = 0xffaf,
+          KeypadEqual = 0xffbd,
+          PlusMinus = 0xb1,
+          SysReq = 0xff15,
+          LineFeed = 0xff0a,
+          KeypadSeparator = 0xffac,
+          Yen = 0xa5,
+          Cancel = 0xff69,
+          Undo = 0xff65,
+          Redo = 0xff66,
+          Find = 0xff68,
+          Print = 0xff61,
+          VolumeDown = 0x1008ff11,
+          Mute = 0x1008ff12,
+          VolumeUp = 0x1008ff13,
+          Menu = 0xff67,
+          VNCMenu = 0xffed;  // VNC seems to translate MENU to this
+
+}  // namespace xk
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/main.cpp b/guest/frontend/vnc_server/main.cpp
new file mode 100644
index 0000000..f03fdcb
--- /dev/null
+++ b/guest/frontend/vnc_server/main.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "vnc_server.h"
+
+#include <signal.h>
+#include <algorithm>
+#include <string>
+
+namespace {
+constexpr int kVncServerPort = 6444;
+
+// TODO(haining) use gflags when available
+bool HasAggressiveFlag(int argc, char* argv[]) {
+  const std::string kAggressive = "--aggressive";
+  auto end = argv + argc;
+  return std::find(argv, end, kAggressive) != end;
+}
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  struct sigaction new_action, old_action;
+  memset(&new_action, 0, sizeof(new_action));
+  new_action.sa_handler = SIG_IGN;
+  sigaction(SIGPIPE, &new_action, &old_action);
+  cvd::vnc::VncServer vnc_server(kVncServerPort, HasAggressiveFlag(argc, argv));
+  vnc_server.MainLoop();
+}
diff --git a/guest/frontend/vnc_server/simulated_hw_composer.cpp b/guest/frontend/vnc_server/simulated_hw_composer.cpp
new file mode 100644
index 0000000..473a6f9
--- /dev/null
+++ b/guest/frontend/vnc_server/simulated_hw_composer.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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 "simulated_hw_composer.h"
+
+using cvd::vnc::SimulatedHWComposer;
+
+SimulatedHWComposer::SimulatedHWComposer(BlackBoard* bb)
+    :
+#ifdef FUZZ_TEST_VNC
+      engine_{std::random_device{}()},
+#endif
+      control_{VSoCFrameBufferControl::getInstance()},
+      bb_{bb},
+      stripes_(kMaxQueueElements, &SimulatedHWComposer::EraseHalfOfElements) {
+  void* p{};
+  VSoCFrameBuffer::OpenAndMapFrameBuffer(&p, &frame_buffer_fd_);
+  frame_buffer_memory_ = static_cast<char*>(p);
+  stripe_maker_ = std::thread(&SimulatedHWComposer::MakeStripes, this);
+}
+
+SimulatedHWComposer::~SimulatedHWComposer() {
+  close();
+  stripe_maker_.join();
+  VSoCFrameBuffer::UnmapAndCloseFrameBuffer(frame_buffer_memory_,
+                                           frame_buffer_fd_);
+}
+
+cvd::vnc::Stripe SimulatedHWComposer::GetNewStripe() {
+  auto s = stripes_.Pop();
+#ifdef FUZZ_TEST_VNC
+  if (random_(engine_)) {
+    usleep(7000);
+    stripes_.Push(std::move(s));
+    s = stripes_.Pop();
+  }
+#endif
+  return s;
+}
+
+bool SimulatedHWComposer::closed() {
+  std::lock_guard<std::mutex> guard(m_);
+  return closed_;
+}
+
+void SimulatedHWComposer::close() {
+  std::lock_guard<std::mutex> guard(m_);
+  closed_ = true;
+}
+
+// Assuming the number of stripes is less than half the size of the queue
+// this will be safe as the newest stripes won't be lost. In the real
+// hwcomposer, where stripes are coming in a different order, the full
+// queue case would probably need a different approach to be safe.
+void SimulatedHWComposer::EraseHalfOfElements(
+    ThreadSafeQueue<Stripe>::QueueImpl* q) {
+  q->erase(q->begin(), std::next(q->begin(), kMaxQueueElements / 2));
+}
+
+void SimulatedHWComposer::MakeStripes() {
+  std::uint32_t previous_seq_num{};
+  auto screen_height = ActualScreenHeight();
+  Message raw_screen;
+  std::uint64_t stripe_seq_num = 1;
+  while (!closed()) {
+    bb_->WaitForAtLeastOneClientConnection();
+    int y_offset{};
+    control_.WaitForFrameBufferChangeSince(previous_seq_num, &y_offset,
+                                           &previous_seq_num, nullptr);
+
+    const auto* frame_start =
+        frame_buffer_memory_ + y_offset * ActualScreenWidth() * BytesPerPixel();
+    raw_screen.assign(frame_start, frame_start + ScreenSizeInBytes());
+
+    for (int i = 0; i < kNumStripes; ++i) {
+      ++stripe_seq_num;
+      std::uint16_t y = (screen_height / kNumStripes) * i;
+
+      // Last frames on the right and/or bottom handle extra pixels
+      // when a screen dimension is not evenly divisible by Frame::kNumSlots.
+      std::uint16_t height =
+          screen_height / kNumStripes +
+          (i + 1 == kNumStripes ? screen_height % kNumStripes : 0);
+      const auto* raw_start =
+          &raw_screen[y * ActualScreenWidth() * BytesPerPixel()];
+      const auto* raw_end =
+          raw_start + (height * ActualScreenWidth() * BytesPerPixel());
+      // creating a named object and setting individual data members in order
+      // to make klp happy
+      // TODO (haining) construct this inside the call when not compiling
+      // on klp
+      Stripe s{};
+      s.index = i;
+      s.frame_id = previous_seq_num;
+      s.x = 0;
+      s.y = y;
+      s.width = ActualScreenWidth();
+      s.height = height;
+      s.raw_data.assign(raw_start, raw_end);
+      s.seq_number = StripeSeqNumber{stripe_seq_num};
+      s.orientation = ScreenOrientation::Portrait;
+      stripes_.Push(std::move(s));
+    }
+  }
+}
+
+int SimulatedHWComposer::NumberOfStripes() {
+  return kNumStripes;
+}
diff --git a/guest/frontend/vnc_server/simulated_hw_composer.h b/guest/frontend/vnc_server/simulated_hw_composer.h
new file mode 100644
index 0000000..c8a8320
--- /dev/null
+++ b/guest/frontend/vnc_server/simulated_hw_composer.h
@@ -0,0 +1,69 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "blackboard.h"
+
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer.h>
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h>
+#include <common/libs/thread_safe_queue/thread_safe_queue.h>
+
+#include <mutex>
+#include <thread>
+
+#include <condition_variable>
+#ifdef FUZZ_TEST_VNC
+#include <random>
+#endif
+
+namespace cvd {
+namespace vnc {
+class SimulatedHWComposer {
+ public:
+  SimulatedHWComposer(BlackBoard* bb);
+  SimulatedHWComposer(const SimulatedHWComposer&) = delete;
+  SimulatedHWComposer& operator=(const SimulatedHWComposer&) = delete;
+  ~SimulatedHWComposer();
+
+  Stripe GetNewStripe();
+
+  // NOTE not constexpr on purpose
+  static int NumberOfStripes();
+
+ private:
+  bool closed();
+  void close();
+  static void EraseHalfOfElements(ThreadSafeQueue<Stripe>::QueueImpl* q);
+  void MakeStripes();
+
+#ifdef FUZZ_TEST_VNC
+  std::default_random_engine engine_;
+  std::uniform_int_distribution<int> random_ =
+      std::uniform_int_distribution<int>{0, 2};
+#endif
+  static constexpr int kNumStripes = 8;
+  constexpr static std::size_t kMaxQueueElements = 64;
+  bool closed_ GUARDED_BY(m_){};
+  std::mutex m_;
+  VSoCFrameBufferControl& control_;
+  BlackBoard* bb_{};
+  ThreadSafeQueue<Stripe> stripes_;
+  std::thread stripe_maker_;
+  char* frame_buffer_memory_{};
+  int frame_buffer_fd_{};
+};
+}  // namespace vnc
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/tcp_socket.cpp b/guest/frontend/vnc_server/tcp_socket.cpp
new file mode 100644
index 0000000..c83559b
--- /dev/null
+++ b/guest/frontend/vnc_server/tcp_socket.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "tcp_socket.h"
+#include <cerrno>
+
+#include <cutils/sockets.h>
+#define LOG_TAG ""
+#include <cutils/log.h>
+
+using cvd::vnc::ClientSocket;
+using cvd::vnc::ServerSocket;
+using cvd::vnc::Message;
+
+Message ClientSocket::Recv(size_t length) {
+  Message buf(length);
+  ssize_t total_read = 0;
+  while (total_read < static_cast<ssize_t>(length)) {
+    auto just_read = read(fd_, &buf[total_read], buf.size() - total_read);
+    if (just_read <= 0) {
+      if (just_read < 0) {
+        ALOGE("read() error: %s", strerror(errno));
+      }
+      other_side_closed_ = true;
+      return Message{};
+    }
+    total_read += just_read;
+  }
+  ALOG_ASSERT(total_read == static_cast<ssize_t>(length));
+  return buf;
+}
+
+ssize_t ClientSocket::Send(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)) {
+    auto just_written = write(fd_, data + written, size - written);
+    if (just_written <= 0) {
+      ALOGI("Couldn't write to vnc client: %s", strerror(errno));
+      return just_written;
+    }
+    written += just_written;
+  }
+  return written;
+}
+
+ssize_t ClientSocket::Send(const Message& message) {
+  return Send(&message[0], message.size());
+}
+
+ServerSocket::ServerSocket(int port)
+    : fd_{socket_inaddr_any_server(port, SOCK_STREAM)} {
+  if (fd_ < 0) {
+    LOG_FATAL("Couldn't open streaming server on port %d", port);
+  }
+}
+
+ClientSocket ServerSocket::Accept() {
+  int client = accept(fd_, nullptr, nullptr);
+  if (client < 0) {
+    LOG_FATAL("Error attemping to accept: %s", strerror(errno));
+  }
+  return ClientSocket{client};
+}
diff --git a/guest/frontend/vnc_server/tcp_socket.h b/guest/frontend/vnc_server/tcp_socket.h
new file mode 100644
index 0000000..c8c1b32
--- /dev/null
+++ b/guest/frontend/vnc_server/tcp_socket.h
@@ -0,0 +1,99 @@
+#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 "vnc_utils.h"
+
+#include <mutex>
+#include <cstdint>
+#include <cstddef>
+
+#include <unistd.h>
+
+namespace cvd {
+namespace vnc {
+
+class ServerSocket;
+
+// Recv and Send wait until all data has been received or sent.
+// Send is thread safe in this regard, Recv is not.
+class ClientSocket {
+ public:
+  ClientSocket(ClientSocket&& other) : fd_{other.fd_} {
+    other.fd_ = -1;
+  }
+
+  ClientSocket& operator=(ClientSocket&& other) {
+    if (fd_ >= 0) {
+      close(fd_);
+    }
+    fd_ = other.fd_;
+    other.fd_ = -1;
+    return *this;
+  }
+
+  ClientSocket(const ClientSocket&) = delete;
+  ClientSocket& operator=(const ClientSocket&) = delete;
+
+  ~ClientSocket() {
+    if (fd_ >= 0) {
+      close(fd_);
+    }
+  }
+
+  Message Recv(std::size_t length);
+  ssize_t Send(const std::uint8_t* data, std::size_t size);
+  ssize_t Send(const Message& message);
+
+  template <std::size_t N>
+  ssize_t Send(const std::uint8_t (&data)[N]) {
+    return Send(data, N);
+  }
+
+  bool closed() const {
+    return other_side_closed_;
+  }
+
+ private:
+  friend ServerSocket;
+  explicit ClientSocket(int fd) : fd_(fd) {}
+
+  int fd_ = -1;
+  bool other_side_closed_{};
+  std::mutex send_lock_;
+};
+
+class ServerSocket {
+ public:
+  explicit ServerSocket(int port);
+
+  ServerSocket(const ServerSocket&) = delete;
+  ServerSocket& operator=(const ServerSocket&) = delete;
+
+  ~ServerSocket() {
+    if (fd_ >= 0) {
+      close(fd_);
+    }
+  }
+
+  ClientSocket Accept();
+
+ private:
+  int fd_ = -1;
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/virtual_inputs.cpp b/guest/frontend/vnc_server/virtual_inputs.cpp
new file mode 100644
index 0000000..ab4bb7a
--- /dev/null
+++ b/guest/frontend/vnc_server/virtual_inputs.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "virtual_inputs.h"
+#include <mutex>
+
+using cvd::vnc::VirtualInputs;
+
+void VirtualInputs::GenerateKeyPressEvent(int code, bool down) {
+  std::lock_guard<std::mutex> guard(m_);
+  virtual_keyboard_.GenerateKeyPressEvent(code, down);
+}
+
+void VirtualInputs::PressPowerButton(bool down) {
+  std::lock_guard<std::mutex> guard(m_);
+  virtual_power_button_.HandleButtonPressEvent(down);
+}
+
+void VirtualInputs::HandlePointerEvent(bool touch_down, int x, int y) {
+  std::lock_guard<std::mutex> guard(m_);
+  virtual_touch_pad_.HandlePointerEvent(touch_down, x, y);
+}
diff --git a/guest/frontend/vnc_server/virtual_inputs.h b/guest/frontend/vnc_server/virtual_inputs.h
new file mode 100644
index 0000000..4b24d39
--- /dev/null
+++ b/guest/frontend/vnc_server/virtual_inputs.h
@@ -0,0 +1,44 @@
+#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 "VirtualInputDevice.h"
+#include "vnc_utils.h"
+
+#include <linux/input.h>
+#include <android-base/thread_annotations.h>
+
+#include <mutex>
+
+namespace cvd {
+namespace vnc {
+
+class VirtualInputs {
+ public:
+  void GenerateKeyPressEvent(int code, bool down);
+  void PressPowerButton(bool down);
+  void HandlePointerEvent(bool touch_down, int x, int y);
+
+ private:
+  std::mutex m_;
+  VirtualKeyboard virtual_keyboard_ GUARDED_BY(m_){"remote-keyboard"};
+  VirtualTouchPad virtual_touch_pad_ GUARDED_BY(m_){
+      "remote-touchpad", ActualScreenWidth(), ActualScreenHeight()};
+  VirtualButton virtual_power_button_ GUARDED_BY(m_){"remote-power", KEY_POWER};
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/vnc_client_connection.cpp b/guest/frontend/vnc_server/vnc_client_connection.cpp
new file mode 100644
index 0000000..f6b4669
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_client_connection.cpp
@@ -0,0 +1,676 @@
+/*
+ * 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 "vnc_client_connection.h"
+#include "vnc_utils.h"
+#include "tcp_socket.h"
+#include "keysyms.h"
+
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer.h>
+
+#include <netinet/in.h>
+#include <sys/time.h>
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG ""
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+
+using cvd::vnc::Message;
+using cvd::vnc::VncClientConnection;
+using cvd::vnc::Stripe;
+using cvd::vnc::StripePtrVec;
+
+namespace {
+class BigEndianChecker {
+ public:
+  BigEndianChecker() {
+    uint32_t u = 1;
+    is_big_endian_ = *reinterpret_cast<const char*>(&u) == 0;
+  }
+  bool operator()() const {
+    return is_big_endian_;
+  }
+
+ private:
+  bool is_big_endian_{};
+};
+
+const BigEndianChecker ImBigEndian;
+
+constexpr int32_t kDesktopSizeEncoding = -223;
+constexpr int32_t kTightEncoding = 7;
+
+// These are the lengths not counting the first byte. The first byte
+// indicates the message type.
+constexpr size_t kSetPixelFormatLength = 19;
+constexpr size_t kFramebufferUpdateRequestLength = 9;
+constexpr size_t kSetEncodingsLength = 3;  // more bytes follow
+constexpr size_t kKeyEventLength = 7;
+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() {
+  return "Cuttlefish";
+}
+
+std::uint16_t uint16_tAt(const void* p) {
+  std::uint16_t u{};
+  std::memcpy(&u, p, sizeof u);
+  return ntohs(u);
+}
+
+std::uint32_t uint32_tAt(const void* p) {
+  std::uint32_t u{};
+  std::memcpy(&u, p, sizeof u);
+  return ntohl(u);
+}
+
+std::int32_t int32_tAt(const void* p) {
+  std::uint32_t u{};
+  std::memcpy(&u, p, sizeof u);
+  u = ntohl(u);
+  std::int32_t s{};
+  std::memcpy(&s, &u, sizeof s);
+  return s;
+}
+
+std::uint32_t RedVal(std::uint32_t pixel) {
+  return (pixel >> VSoCFrameBuffer::kRedShift) &
+         ((0x1 << VSoCFrameBuffer::kRedBits) - 1);
+}
+
+std::uint32_t BlueVal(std::uint32_t pixel) {
+  return (pixel >> VSoCFrameBuffer::kBlueShift) &
+         ((0x1 << VSoCFrameBuffer::kBlueBits) - 1);
+}
+
+std::uint32_t GreenVal(std::uint32_t pixel) {
+  return (pixel >> VSoCFrameBuffer::kGreenShift) &
+         ((0x1 << VSoCFrameBuffer::kGreenBits) - 1);
+}
+}
+namespace cvd {
+namespace vnc {
+bool operator==(const VncClientConnection::FrameBufferUpdateRequest& lhs,
+                const VncClientConnection::FrameBufferUpdateRequest& rhs) {
+  return lhs.x_pos == rhs.x_pos && lhs.y_pos == rhs.y_pos &&
+         lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+bool operator!=(const VncClientConnection::FrameBufferUpdateRequest& lhs,
+                const VncClientConnection::FrameBufferUpdateRequest& rhs) {
+  return !(lhs == rhs);
+}
+}  // namespace vnc
+}  // namespace cvd  // namespace
+
+VncClientConnection::VncClientConnection(ClientSocket client,
+                                         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);
+}
+
+VncClientConnection::~VncClientConnection() {
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    closed_ = true;
+  }
+  bb_->StopWaiting(this);
+  frame_buffer_request_handler_tid_.join();
+}
+
+void VncClientConnection::StartSession() {
+  SetupProtocol();
+  if (client_.closed()) {
+    return;
+  }
+  SetupSecurityType();
+  if (client_.closed()) {
+    return;
+  }
+  GetClientInit();
+  if (client_.closed()) {
+    return;
+  }
+  SendServerInit();
+  if (client_.closed()) {
+    return;
+  }
+  NormalSession();
+  ALOGI("vnc session terminated");
+}
+
+bool VncClientConnection::closed() {
+  std::lock_guard<std::mutex> guard(m_);
+  return closed_;
+}
+
+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);
+  auto client_protocol = client_.Recv(kVersionLen);
+  if (std::memcmp(&client_protocol[0], kRFBVersion,
+                  std::min(kVersionLen, client_protocol.size())) != 0) {
+    client_protocol.push_back('\0');
+    ALOGE("vnc client wants a different protocol: %s",
+          reinterpret_cast<const char*>(&client_protocol[0]));
+  }
+}
+
+void VncClientConnection::SetupSecurityType() {
+  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);
+  auto client_security = client_.Recv(1);
+  if (client_.closed()) {
+    return;
+  }
+  if (client_security.front() != kNoneSecurity) {
+    ALOGE("vnc client is asking for security type %d",
+          static_cast<int>(client_security.front()));
+  }
+  static constexpr std::uint8_t kZero[4] = {};
+  client_.Send(kZero);
+}
+
+void VncClientConnection::GetClientInit() {
+  auto client_shared = client_.Recv(1);
+}
+
+void VncClientConnection::SendServerInit() {
+  const std::string server_name = HostName();
+  std::lock_guard<std::mutex> guard(m_);
+  auto server_init = 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,
+      pixel_format_.red_max, pixel_format_.green_max, pixel_format_.blue_max,
+      pixel_format_.red_shift, pixel_format_.green_shift,
+      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);
+}
+
+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});
+}
+
+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);
+}
+
+void VncClientConnection::AppendJpegSize(Message* frame_buffer_update,
+                                         size_t jpeg_size) {
+  constexpr size_t kJpegSizeOneByteMax = 127;
+  constexpr size_t kJpegSizeTwoByteMax = 16383;
+  constexpr size_t kJpegSizeThreeByteMax = 4194303;
+
+  if (jpeg_size <= kJpegSizeOneByteMax) {
+    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));
+  } else {
+    if (jpeg_size > kJpegSizeThreeByteMax) {
+      LOG_FATAL("jpeg size is too big: %d must be under %zu", jpeg_size,
+                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));
+  }
+}
+
+void VncClientConnection::AppendRawStripe(Message* frame_buffer_update,
+                                          const Stripe& stripe) const {
+  using Pixel = VSoCFrameBuffer::Pixel;
+  auto& fbu = *frame_buffer_update;
+  AppendRawStripeHeader(&fbu, stripe);
+  auto init_size = fbu.size();
+  fbu.insert(fbu.end(), stripe.raw_data.begin(), stripe.raw_data.end());
+  for (size_t i = init_size; i < fbu.size(); i += sizeof(Pixel)) {
+    ALOG_ASSERT((i + sizeof(Pixel)) < fbu.size());
+    Pixel raw_pixel{};
+    std::memcpy(&raw_pixel, &fbu[i], sizeof raw_pixel);
+    auto red = RedVal(raw_pixel);
+    auto green = GreenVal(raw_pixel);
+    auto blue = BlueVal(raw_pixel);
+    Pixel pixel = Pixel{red} << pixel_format_.red_shift |
+                  Pixel{blue} << pixel_format_.blue_shift |
+                  Pixel{green} << pixel_format_.green_shift;
+
+    if (bool(pixel_format_.big_endian) != ImBigEndian()) {
+      // flip them bits (refactor into function)
+      auto p = reinterpret_cast<char*>(&pixel);
+      std::swap(p[0], p[3]);
+      std::swap(p[1], p[2]);
+    }
+    ALOG_ASSERT(i + sizeof pixel <= fbu.size());
+    std::memcpy(&fbu[i], &pixel, sizeof pixel);
+  }
+}
+
+Message VncClientConnection::MakeRawFrameBufferUpdate(
+    const StripePtrVec& stripes) const {
+  auto fbu =
+      MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
+  for (auto& stripe : stripes) {
+    AppendRawStripe(&fbu, *stripe);
+  }
+  return fbu;
+}
+
+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);
+  AppendJpegSize(frame_buffer_update, stripe.jpeg_data.size());
+}
+
+void VncClientConnection::AppendJpegStripe(Message* frame_buffer_update,
+                                           const Stripe& stripe) {
+  AppendJpegStripeHeader(frame_buffer_update, stripe);
+  frame_buffer_update->insert(frame_buffer_update->end(),
+                              stripe.jpeg_data.begin(), stripe.jpeg_data.end());
+}
+
+Message VncClientConnection::MakeJpegFrameBufferUpdate(
+    const StripePtrVec& stripes) {
+  auto fbu =
+      MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
+  for (auto& stripe : stripes) {
+    AppendJpegStripe(&fbu, *stripe);
+  }
+  return fbu;
+}
+
+Message VncClientConnection::MakeFrameBufferUpdate(
+    const StripePtrVec& stripes) {
+  return use_jpeg_compression_ ? MakeJpegFrameBufferUpdate(stripes)
+                               : MakeRawFrameBufferUpdate(stripes);
+}
+
+void VncClientConnection::FrameBufferUpdateRequestHandler(bool aggressive) {
+  BlackBoard::Registerer reg(bb_, this);
+  const StripeSeqNumber kBeginningOfTime{};
+
+  while (!closed()) {
+    auto stripes = bb_->WaitForSenderWork(this);
+    if (closed()) {
+      break;
+    }
+    LOG_ALWAYS_FATAL_IF(stripes.empty(), "Got 0 stripes");
+    {
+      // lock here so a portrait frame can't be sent after a landscape
+      // DesktopSize update, or vice versa.
+      std::lock_guard<std::mutex> guard(m_);
+      D("Sending update in %s mode",
+        current_orientation_ == ScreenOrientation::Portrait ? "portrait"
+                                                            : "landscape");
+      client_.Send(MakeFrameBufferUpdate(stripes));
+    }
+    if (aggressive) {
+      bb_->FrameBufferUpdateRequestReceived(this);
+    }
+  }
+}
+
+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));
+}
+
+bool VncClientConnection::IsUrgent(
+    const FrameBufferUpdateRequest& update_request) const {
+  return !update_request.incremental ||
+         update_request != previous_update_request_;
+}
+
+void VncClientConnection::HandleFramebufferUpdateRequest() {
+  auto msg = client_.Recv(kFramebufferUpdateRequestLength);
+  if (msg.size() != kFramebufferUpdateRequestLength) {
+    return;
+  }
+  FrameBufferUpdateRequest fbur{msg[1] == 0, uint16_tAt(&msg[1]),
+                                uint16_tAt(&msg[3]), uint16_tAt(&msg[5]),
+                                uint16_tAt(&msg[7])};
+  if (IsUrgent(fbur)) {
+    bb_->SignalClientNeedsEntireScreen(this);
+  }
+  bb_->FrameBufferUpdateRequestReceived(this);
+  previous_update_request_ = fbur;
+}
+
+void VncClientConnection::HandleSetEncodings() {
+  auto msg = client_.Recv(kSetEncodingsLength);
+  if (msg.size() != kSetEncodingsLength) {
+    return;
+  }
+  auto count = uint16_tAt(&msg[1]);
+  auto encodings = client_.Recv(count * sizeof(int32_t));
+  if (encodings.size() % sizeof(int32_t) != 0) {
+    return;
+  }
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    use_jpeg_compression_ = false;
+  }
+  for (size_t i = 0; i < encodings.size(); i += sizeof(int32_t)) {
+    auto enc = int32_tAt(&encodings[i]);
+    D("client requesting encoding: %d\n", enc);
+    if (enc == kTightEncoding) {
+      // This is a deviation from the spec which says that if a jpeg quality
+      // level is not specified, tight encoding won't use jpeg.
+      std::lock_guard<std::mutex> guard(m_);
+      use_jpeg_compression_ = true;
+    }
+    if (kJpegMinQualityEncoding <= enc && enc <= kJpegMaxQualityEncoding) {
+      D("jpeg compression level: %d", enc);
+      bb_->set_jpeg_quality_level(enc);
+    }
+    if (enc == kDesktopSizeEncoding) {
+      supports_desktop_size_encoding_ = true;
+    }
+  }
+}
+
+void VncClientConnection::HandleSetPixelFormat() {
+  std::lock_guard<std::mutex> guard(m_);
+  auto msg = client_.Recv(kSetPixelFormatLength);
+  if (msg.size() != kSetPixelFormatLength) {
+    return;
+  }
+  pixel_format_.bits_per_pixel = msg[3];
+  pixel_format_.depth = msg[4];
+  pixel_format_.big_endian = msg[5];
+  pixel_format_.true_color = msg[7];
+  pixel_format_.red_max = uint16_tAt(&msg[8]);
+  pixel_format_.green_max = uint16_tAt(&msg[10]);
+  pixel_format_.blue_max = uint16_tAt(&msg[12]);
+  pixel_format_.red_shift = msg[13];
+  pixel_format_.green_shift = msg[14];
+  pixel_format_.blue_shift = msg[15];
+}
+
+void VncClientConnection::HandlePointerEvent() {
+  auto msg = client_.Recv(kPointerEventLength);
+  if (msg.size() != kPointerEventLength) {
+    return;
+  }
+  std::uint8_t button_mask = msg[0];
+  auto x_pos = uint16_tAt(&msg[1]);
+  auto y_pos = uint16_tAt(&msg[3]);
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    if (current_orientation_ == ScreenOrientation::Landscape) {
+      std::tie(x_pos, y_pos) =
+          std::make_pair(ActualScreenWidth() - y_pos, x_pos);
+    }
+  }
+  virtual_inputs_->HandlePointerEvent(button_mask, x_pos, y_pos);
+}
+
+void VncClientConnection::UpdateAccelerometer(float x, float y, float z) {
+  // TODO(jemoreira): Implement when vsoc sensor hal is updated
+}
+
+VncClientConnection::Coordinates VncClientConnection::CoordinatesForOrientation(
+    ScreenOrientation orientation) const {
+  // Compute the acceleration vector that we need to send to mimic
+  // this change.
+  constexpr float g = 9.81;
+  constexpr float angle = 20.0;
+  const float cos_angle = std::cos(angle / M_PI);
+  const float sin_angle = std::sin(angle / M_PI);
+  const float z = g * sin_angle;
+  switch (orientation) {
+    case ScreenOrientation::Portrait:
+      return {0, g * cos_angle, z};
+    case ScreenOrientation::Landscape:
+      return {g * cos_angle, 0, z};
+  }
+}
+
+int VncClientConnection::ScreenWidth() const {
+  return current_orientation_ == ScreenOrientation::Portrait
+             ? ActualScreenWidth()
+             : ActualScreenHeight();
+}
+
+int VncClientConnection::ScreenHeight() const {
+  return current_orientation_ == ScreenOrientation::Portrait
+             ? ActualScreenHeight()
+             : ActualScreenWidth();
+}
+
+void VncClientConnection::SetScreenOrientation(ScreenOrientation orientation) {
+  std::lock_guard<std::mutex> guard(m_);
+  auto coords = CoordinatesForOrientation(orientation);
+  UpdateAccelerometer(coords.x, coords.y, coords.z);
+  if (supports_desktop_size_encoding_) {
+    auto previous_orientation = current_orientation_;
+    current_orientation_ = orientation;
+    if (current_orientation_ != previous_orientation &&
+        supports_desktop_size_encoding_) {
+      SendDesktopSizeUpdate();
+      bb_->SetOrientation(this, current_orientation_);
+      // TODO not sure if I should be sending a frame update along with this,
+      // or just letting the next FBUR handle it. This seems to me like it's
+      // sending one more frame buffer update than was requested, which is
+      // maybe a violation of the spec?
+    }
+  }
+}
+
+bool VncClientConnection::RotateIfIsRotationCommand(std::uint32_t key) {
+  // Due to different configurations on different platforms we're supporting
+  // a set of options for rotating the screen. These are similar to what
+  // the emulator supports and has supported.
+  // ctrl+left and ctrl+right work on windows and linux
+  // command+left and command+right work on Mac
+  // ctrl+fn+F11 and ctrl+fn+F12 work when chromoting to ubuntu from a Mac
+  if (!control_key_down_ && !meta_key_down_) {
+    return false;
+  }
+  switch (key) {
+    case cvd::xk::Right:
+    case cvd::xk::F12:
+      D("switching to portrait");
+      SetScreenOrientation(ScreenOrientation::Portrait);
+      break;
+    case cvd::xk::Left:
+    case cvd::xk::F11:
+      D("switching to landscape");
+      SetScreenOrientation(ScreenOrientation::Landscape);
+      break;
+    default:
+      return false;
+  }
+  return true;
+}
+
+void VncClientConnection::HandleKeyEvent() {
+  auto msg = client_.Recv(kKeyEventLength);
+  if (msg.size() != kKeyEventLength) {
+    return;
+  }
+
+  auto key = uint32_tAt(&msg[3]);
+  bool key_down = msg[0];
+  switch (key) {
+    case cvd::xk::ControlLeft:
+    case cvd::xk::ControlRight:
+      control_key_down_ = key_down;
+      break;
+    case cvd::xk::MetaLeft:
+    case cvd::xk::MetaRight:
+      meta_key_down_ = key_down;
+      break;
+    case cvd::xk::F5:
+      key = cvd::xk::Menu;
+      break;
+    case cvd::xk::F7:
+      virtual_inputs_->PressPowerButton(key_down);
+      return;
+    default:
+      break;
+  }
+
+  if (RotateIfIsRotationCommand(key)) {
+    return;
+  }
+
+  virtual_inputs_->GenerateKeyPressEvent(key, key_down);
+}
+
+void VncClientConnection::HandleClientCutText() {
+  auto msg = client_.Recv(kClientCutTextLength);
+  if (msg.size() != kClientCutTextLength) {
+    return;
+  }
+  auto len = uint32_tAt(&msg[3]);
+  client_.Recv(len);
+}
+
+void VncClientConnection::NormalSession() {
+  static constexpr std::uint8_t kSetPixelFormatMessage{0};
+  static constexpr std::uint8_t kSetEncodingsMessage{2};
+  static constexpr std::uint8_t kFramebufferUpdateRequestMessage{3};
+  static constexpr std::uint8_t kKeyEventMessage{4};
+  static constexpr std::uint8_t kPointerEventMessage{5};
+  static constexpr std::uint8_t kClientCutTextMessage{6};
+  while (true) {
+    if (client_.closed()) {
+      return;
+    }
+    auto msg = client_.Recv(1);
+    if (client_.closed()) {
+      return;
+    }
+    auto msg_type = msg.front();
+    D("Received message type %d\n", static_cast<int>(msg_type));
+
+    switch (msg_type) {
+      case kSetPixelFormatMessage:
+        HandleSetPixelFormat();
+        break;
+
+      case kSetEncodingsMessage:
+        HandleSetEncodings();
+        break;
+
+      case kFramebufferUpdateRequestMessage:
+        HandleFramebufferUpdateRequest();
+        break;
+
+      case kKeyEventMessage:
+        HandleKeyEvent();
+        break;
+
+      case kPointerEventMessage:
+        HandlePointerEvent();
+        break;
+
+      case kClientCutTextMessage:
+        HandleClientCutText();
+        break;
+
+      default:
+        ALOGW("message type not handled: %d", static_cast<int>(msg_type));
+        break;
+    }
+  }
+}
diff --git a/guest/frontend/vnc_server/vnc_client_connection.h b/guest/frontend/vnc_server/vnc_client_connection.h
new file mode 100644
index 0000000..b2fe6e7
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_client_connection.h
@@ -0,0 +1,169 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "blackboard.h"
+#include "virtual_inputs.h"
+#include "vnc_utils.h"
+#include "tcp_socket.h"
+
+#include <android-base/thread_annotations.h>
+#include <common/libs/fs/shared_fd.h>
+
+#include <cstdint>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+namespace cvd {
+namespace vnc {
+
+class VncClientConnection {
+ public:
+  VncClientConnection(ClientSocket client, VirtualInputs* virtual_inputs,
+                      BlackBoard* bb, bool aggressive);
+  VncClientConnection(const VncClientConnection&) = delete;
+  VncClientConnection& operator=(const VncClientConnection&) = delete;
+  ~VncClientConnection();
+
+  void StartSession();
+
+ private:
+  struct PixelFormat {
+    std::uint8_t bits_per_pixel;
+    std::uint8_t depth;
+    std::uint8_t big_endian;
+    std::uint8_t true_color;
+    std::uint16_t red_max;
+    std::uint16_t green_max;
+    std::uint16_t blue_max;
+    std::uint8_t red_shift;
+    std::uint8_t green_shift;
+    std::uint8_t blue_shift;
+  };
+
+  struct FrameBufferUpdateRequest {
+    bool incremental;
+    std::uint16_t x_pos;
+    std::uint16_t y_pos;
+    std::uint16_t width;
+    std::uint16_t height;
+  };
+
+  friend bool operator==(const FrameBufferUpdateRequest&,
+                         const FrameBufferUpdateRequest&);
+  friend bool operator!=(const FrameBufferUpdateRequest&,
+                         const FrameBufferUpdateRequest&);
+
+  bool closed();
+  void SetupProtocol();
+  void SetupSecurityType();
+
+  void GetClientInit();
+
+  void SendServerInit() EXCLUDES(m_);
+  static Message MakeFrameBufferUpdateHeader(std::uint16_t num_stripes);
+
+  static void AppendRawStripeHeader(Message* frame_buffer_update,
+                                    const Stripe& stripe);
+  void AppendRawStripe(Message* frame_buffer_update, const Stripe& stripe) const
+      REQUIRES(m_);
+  Message MakeRawFrameBufferUpdate(const StripePtrVec& stripes) const
+      REQUIRES(m_);
+
+  static void AppendJpegSize(Message* frame_buffer_update, size_t jpeg_size);
+  static void AppendJpegStripeHeader(Message* frame_buffer_update,
+                                     const Stripe& stripe);
+  static void AppendJpegStripe(Message* frame_buffer_update,
+                               const Stripe& stripe);
+  static Message MakeJpegFrameBufferUpdate(const StripePtrVec& stripes);
+
+  Message MakeFrameBufferUpdate(const StripePtrVec& frame) REQUIRES(m_);
+
+  void FrameBufferUpdateRequestHandler(bool aggressive) EXCLUDES(m_);
+
+  void SendDesktopSizeUpdate() REQUIRES(m_);
+
+  bool IsUrgent(const FrameBufferUpdateRequest& update_request) const;
+  static StripeSeqNumber MostRecentStripeSeqNumber(const StripePtrVec& stripes);
+
+  void HandleFramebufferUpdateRequest() EXCLUDES(m_);
+
+  void HandleSetEncodings();
+
+  void HandleSetPixelFormat();
+
+  void HandlePointerEvent() EXCLUDES(m_);
+
+  void UpdateAccelerometer(float x, float y, float z);
+
+  struct Coordinates {
+    float x;
+    float y;
+    float z;
+  };
+
+  Coordinates CoordinatesForOrientation(ScreenOrientation orientation) const;
+
+  int ScreenWidth() const REQUIRES(m_);
+
+  int ScreenHeight() const REQUIRES(m_);
+
+  void SetScreenOrientation(ScreenOrientation orientation) EXCLUDES(m_);
+
+  // Returns true if key is special and the screen was rotated.
+  bool RotateIfIsRotationCommand(std::uint32_t key);
+
+  void HandleKeyEvent();
+
+  void HandleClientCutText();
+
+  void NormalSession();
+
+  mutable std::mutex m_;
+  ClientSocket client_;
+  bool control_key_down_ = false;
+  bool meta_key_down_ = false;
+  VirtualInputs* virtual_inputs_{};
+
+  FrameBufferUpdateRequest previous_update_request_{};
+  BlackBoard* bb_;
+  bool use_jpeg_compression_ GUARDED_BY(m_) = false;
+
+  std::thread frame_buffer_request_handler_tid_;
+  bool closed_ GUARDED_BY(m_){};
+
+  PixelFormat pixel_format_ GUARDED_BY(m_) = {
+      std::uint8_t{32},  // bits per pixel
+      std::uint8_t{8},   // depth
+      std::uint8_t{},    // big_endian
+      std::uint8_t{},    // true_color
+      std::uint16_t{},   // red_max, (maxes not used when true color flag is 0)
+      std::uint16_t{},   // green_max
+      std::uint16_t{},   // blue_max
+      std::uint8_t{},  // red_shift (shifts not used when true color flag is 0)
+      std::uint8_t{},  // green_shift
+      std::uint8_t{},  // blue_shift
+  };
+
+  bool supports_desktop_size_encoding_ = false;
+  ScreenOrientation current_orientation_ GUARDED_BY(m_) =
+      ScreenOrientation::Portrait;
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/vnc_server.cpp b/guest/frontend/vnc_server/vnc_server.cpp
new file mode 100644
index 0000000..4b1ce89
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_server.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "blackboard.h"
+#include "frame_buffer_watcher.h"
+#include "jpeg_compressor.h"
+#include "tcp_socket.h"
+#include "virtual_inputs.h"
+#include "vnc_client_connection.h"
+#include "vnc_server.h"
+#include "vnc_utils.h"
+
+using cvd::vnc::VncServer;
+
+VncServer::VncServer(int port, bool aggressive)
+    : server_(port), frame_buffer_watcher_{&bb_}, aggressive_{aggressive} {}
+
+void VncServer::MainLoop() {
+  while (true) {
+    auto connection = server_.Accept();
+    StartClient(std::move(connection));
+  }
+}
+
+void VncServer::StartClient(ClientSocket sock) {
+  std::thread t(&VncServer::StartClientThread, this, std::move(sock));
+  t.detach();
+}
+
+void VncServer::StartClientThread(ClientSocket sock) {
+  // NOTE if VncServer is expected to be destroyed, we have a problem here.
+  // All of the client threads will be pointing to the VncServer's
+  // 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_,
+                             aggressive_);
+  client.StartSession();
+}
diff --git a/guest/frontend/vnc_server/vnc_server.h b/guest/frontend/vnc_server/vnc_server.h
new file mode 100644
index 0000000..f50947e
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_server.h
@@ -0,0 +1,55 @@
+#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 "blackboard.h"
+#include "frame_buffer_watcher.h"
+#include "jpeg_compressor.h"
+#include "tcp_socket.h"
+#include "virtual_inputs.h"
+#include "vnc_client_connection.h"
+#include "vnc_utils.h"
+
+#include <thread>
+#include <string>
+#include <utility>
+
+namespace cvd {
+namespace vnc {
+
+class VncServer {
+ public:
+  explicit VncServer(int port, bool aggressive);
+
+  VncServer(const VncServer&) = delete;
+  VncServer& operator=(const VncServer&) = delete;
+
+  [[noreturn]] void MainLoop();
+
+ private:
+  void StartClient(ClientSocket sock);
+
+  void StartClientThread(ClientSocket sock);
+
+  ServerSocket server_;
+  VirtualInputs virtual_inputs_;
+  BlackBoard bb_;
+  FrameBufferWatcher frame_buffer_watcher_;
+  bool aggressive_{};
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/guest/frontend/vnc_server/vnc_utils.h b/guest/frontend/vnc_server/vnc_utils.h
new file mode 100644
index 0000000..5a996a9
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_utils.h
@@ -0,0 +1,94 @@
+#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 <guest/libs/legacy_framebuffer/vsoc_framebuffer.h>
+
+#include <vector>
+#include <array>
+#include <utility>
+#include <cstdint>
+
+#undef D
+#ifdef VSOC_VNC_DEBUG
+#define D(...) ALOGD(__VA_ARGS__)
+#else
+#define D(...) ((void)0)
+#endif
+
+namespace cvd {
+namespace vnc {
+
+// TODO(haining) when the hwcomposer gives a sequence number type, use that
+// instead. It might just work to replace this class with a type alias
+// using StripeSeqNumber = whatever_the_hwcomposer_uses;
+class StripeSeqNumber {
+ public:
+  StripeSeqNumber() = default;
+  explicit StripeSeqNumber(std::uint64_t t) : t_{t} {}
+  bool operator<(const StripeSeqNumber& other) const {
+    return t_ < other.t_;
+  }
+
+  bool operator<=(const StripeSeqNumber& other) const {
+    return t_ <= other.t_;
+  }
+
+ private:
+  std::uint64_t t_{};
+};
+
+using Message = std::vector<std::uint8_t>;
+
+constexpr int32_t kJpegMaxQualityEncoding = -23;
+constexpr int32_t kJpegMinQualityEncoding = -32;
+
+enum class ScreenOrientation { Portrait, Landscape };
+constexpr int kNumOrientations = 2;
+
+struct Stripe {
+  int index = -1;
+  std::uint64_t frame_id{};
+  std::uint16_t x{};
+  std::uint16_t y{};
+  std::uint16_t width{};
+  std::uint16_t height{};
+  Message raw_data{};
+  Message jpeg_data{};
+  StripeSeqNumber seq_number{};
+  ScreenOrientation orientation{};
+};
+
+inline constexpr int BytesPerPixel() {
+  return sizeof(VSoCFrameBuffer::Pixel);
+}
+
+// The width of the screen regardless of orientation. Does not change.
+inline int ActualScreenWidth() {
+  return VSoCFrameBuffer::getInstance().x_res();
+}
+
+// The height of the screen regardless of orientation. Does not change.
+inline int ActualScreenHeight() {
+  return VSoCFrameBuffer::getInstance().y_res();
+}
+
+inline int ScreenSizeInBytes() {
+  return ActualScreenWidth() * ActualScreenHeight() * BytesPerPixel();
+}
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/guest/hals/camera/Android.mk b/guest/hals/camera/Android.mk
new file mode 100644
index 0000000..ced3f91
--- /dev/null
+++ b/guest/hals/camera/Android.mk
@@ -0,0 +1,154 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Emulator camera module########################################################
+
+emulator_camera_module_relative_path := hw
+emulator_camera_cflags := -fno-short-enums $(VSOC_VERSION_CFLAGS)
+emulator_camera_cflags += -Wno-unused-parameter -Wno-missing-field-initializers
+emulator_camera_clang_flags := -Wno-c++11-narrowing
+emulator_camera_shared_libraries := \
+    libbase \
+    libbinder \
+    liblog \
+    libutils \
+    libcutils \
+    libcamera_client \
+    libui \
+    libdl \
+    libjpeg \
+    libcamera_metadata \
+    libhardware
+
+emulator_camera_static_libraries := \
+    libjsoncpp
+
+emulator_camera_c_includes := \
+    device/google/cuttlefish_common \
+    frameworks/native/include/media/hardware \
+    $(call include-path-for, camera) \
+
+emulator_camera_src := \
+	CameraConfiguration.cpp \
+	EmulatedCameraHal.cpp \
+	EmulatedCameraFactory.cpp \
+	EmulatedBaseCamera.cpp \
+	EmulatedCamera.cpp \
+		EmulatedCameraDevice.cpp \
+		EmulatedFakeCamera.cpp \
+		EmulatedFakeCameraDevice.cpp \
+		Converters.cpp \
+		PreviewWindow.cpp \
+		CallbackNotifier.cpp \
+		JpegCompressor.cpp
+emulated_camera2_src := \
+	VSoCEmulatedCameraHotplugThread.cpp \
+	EmulatedCamera2.cpp \
+		EmulatedFakeCamera2.cpp \
+		fake-pipeline2/Scene.cpp \
+		fake-pipeline2/Sensor.cpp \
+		fake-pipeline2/JpegCompressor.cpp
+emulated_camera3_src := \
+	EmulatedCamera3.cpp \
+		EmulatedFakeCamera3.cpp
+
+
+emulated_camera2_stub_src := \
+	StubEmulatedCamera2.cpp \
+		StubEmulatedFakeCamera2.cpp
+
+enable_emulated_camera3 = $(shell test $(PLATFORM_SDK_VERSION) -ge 23 && echo yes)
+enable_emulated_camera2 = $(shell test $(PLATFORM_SDK_VERSION) -ge 19 && echo yes)
+
+# Emulated camera - goldfish / vbox_x86 build###################################
+#
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?))
+emulator_camera_c_includes += external/libjpeg-turbo
+else
+emulator_camera_c_includes += external/jpeg
+endif
+
+LOCAL_MODULE_RELATIVE_PATH := ${emulator_camera_module_relative_path}
+LOCAL_MULTILIB := first
+LOCAL_CFLAGS := ${emulator_camera_cflags}
+LOCAL_CLANG_CFLAGS += ${emulator_camera_clang_flags}
+
+LOCAL_SHARED_LIBRARIES := ${emulator_camera_shared_libraries}
+LOCAL_STATIC_LIBRARIES := ${emulator_camera_static_libraries}
+LOCAL_C_INCLUDES += ${emulator_camera_c_includes}
+LOCAL_SRC_FILES := ${emulator_camera_src} ${emulator_camera_ext_src} \
+	$(if $(enable_emulated_camera2),$(emulated_camera2_src),) \
+	$(if $(enable_emulated_camera3),$(emulated_camera3_src),)
+
+LOCAL_MODULE := camera.vsoc
+LOCAL_MODULE_TAGS := optional
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
+
+# JPEG stub#####################################################################
+
+include $(CLEAR_VARS)
+
+jpeg_module_relative_path := hw
+jpeg_cflags := -fno-short-enums
+jpeg_cflags += -Wno-unused-parameter
+jpeg_clang_flags += -Wno-c++11-narrowing
+jpeg_shared_libraries := \
+    libbase \
+    libcutils \
+    liblog \
+    libskia \
+    libjpeg \
+    libandroid_runtime \
+    cuttlefish_auto_resources
+jpeg_static_libraries := libyuv_static
+jpeg_c_includes := \
+    device/google/cuttlefish_common \
+    external/libyuv/files/include \
+    external/skia/include/core/ \
+    frameworks/base/core/jni/android/graphics \
+    frameworks/native/include
+jpeg_src := \
+    JpegStub.cpp \
+    ExifMetadataBuilder.cpp
+
+# JPEG stub - goldfish build####################################################
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?))
+jpeg_c_includes += external/libjpeg-turbo
+else
+jpeg_c_includes += external/jpeg
+endif
+
+LOCAL_MODULE_RELATIVE_PATH := ${emulator_camera_module_relative_path}
+LOCAL_MULTILIB := first
+LOCAL_CFLAGS += ${jpeg_cflags}
+LOCAL_CLANG_CFLAGS += ${jpeg_clangflags}
+
+LOCAL_SHARED_LIBRARIES := ${jpeg_shared_libraries}
+LOCAL_STATIC_LIBRARIES := ${jpeg_static_libraries}
+LOCAL_C_INCLUDES += ${jpeg_c_includes}
+LOCAL_SRC_FILES := ${jpeg_src}
+
+LOCAL_MODULE := camera.vsoc.jpeg
+LOCAL_MODULE_TAGS := optional
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/guest/hals/camera/CallbackNotifier.cpp b/guest/hals/camera/CallbackNotifier.cpp
new file mode 100755
index 0000000..5ac5a6b
--- /dev/null
+++ b/guest/hals/camera/CallbackNotifier.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of a class CallbackNotifier that manages callbacks set
+ * via set_callbacks, enable_msg_type, and disable_msg_type camera HAL API.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_CallbackNotifier"
+#include <cutils/log.h>
+#include <MetadataBufferType.h>
+#include "EmulatedCameraDevice.h"
+#include "CallbackNotifier.h"
+#include "ImageMetadata.h"
+#include "JpegCompressor.h"
+
+namespace android {
+
+/* String representation of camera messages. */
+static const char* lCameraMessages[] =
+{
+    "CAMERA_MSG_ERROR",
+    "CAMERA_MSG_SHUTTER",
+    "CAMERA_MSG_FOCUS",
+    "CAMERA_MSG_ZOOM",
+    "CAMERA_MSG_PREVIEW_FRAME",
+    "CAMERA_MSG_VIDEO_FRAME",
+    "CAMERA_MSG_POSTVIEW_FRAME",
+    "CAMERA_MSG_RAW_IMAGE",
+    "CAMERA_MSG_COMPRESSED_IMAGE",
+    "CAMERA_MSG_RAW_IMAGE_NOTIFY",
+    "CAMERA_MSG_PREVIEW_METADATA"
+};
+static const int lCameraMessagesNum = sizeof(lCameraMessages) / sizeof(char*);
+
+/* Builds an array of strings for the given set of messages.
+ * Param:
+ *  msg - Messages to get strings for,
+ *  strings - Array where to save strings
+ *  max - Maximum number of entries in the array.
+ * Return:
+ *  Number of strings saved into the 'strings' array.
+ */
+static int GetMessageStrings(uint32_t msg, const char** strings, int max)
+{
+    int index = 0;
+    int out = 0;
+    while (msg != 0 && out < max && index < lCameraMessagesNum) {
+        while ((msg & 0x1) == 0 && index < lCameraMessagesNum) {
+            msg >>= 1;
+            index++;
+        }
+        if ((msg & 0x1) != 0 && index < lCameraMessagesNum) {
+            strings[out] = lCameraMessages[index];
+            out++;
+            msg >>= 1;
+            index++;
+        }
+    }
+
+    return out;
+}
+
+/* Logs messages, enabled by the mask. */
+static void PrintMessages(uint32_t msg)
+{
+    const char* strs[lCameraMessagesNum];
+    const int translated = GetMessageStrings(msg, strs, lCameraMessagesNum);
+    for (int n = 0; n < translated; n++) {
+        ALOGV("    %s", strs[n]);
+    }
+}
+
+CallbackNotifier::CallbackNotifier()
+    : mNotifyCB(NULL),
+      mDataCB(NULL),
+      mDataCBTimestamp(NULL),
+      mGetMemoryCB(NULL),
+      mCBOpaque(NULL),
+      mLastFrameTimestamp(0),
+      mFrameRefreshFreq(0),
+      mMessageEnabler(0),
+      mJpegQuality(90),
+      mVideoRecEnabled(false),
+      mTakingPicture(false)
+{
+}
+
+CallbackNotifier::~CallbackNotifier()
+{
+}
+
+/****************************************************************************
+ * Camera API
+ ***************************************************************************/
+
+void CallbackNotifier::setCallbacks(camera_notify_callback notify_cb,
+                                    camera_data_callback data_cb,
+                                    camera_data_timestamp_callback data_cb_timestamp,
+                                    camera_request_memory get_memory,
+                                    void* user)
+{
+    ALOGV("%s: %p, %p, %p, %p (%p)",
+         __FUNCTION__, notify_cb, data_cb, data_cb_timestamp, get_memory, user);
+
+    Mutex::Autolock locker(&mObjectLock);
+    mNotifyCB = notify_cb;
+    mDataCB = data_cb;
+    mDataCBTimestamp = data_cb_timestamp;
+    mGetMemoryCB = get_memory;
+    mCBOpaque = user;
+}
+
+void CallbackNotifier::enableMessage(uint msg_type)
+{
+    ALOGV("%s: msg_type = 0x%x", __FUNCTION__, msg_type);
+    PrintMessages(msg_type);
+
+    Mutex::Autolock locker(&mObjectLock);
+    mMessageEnabler |= msg_type;
+    ALOGV("**** Currently enabled messages:");
+    PrintMessages(mMessageEnabler);
+}
+
+void CallbackNotifier::disableMessage(uint msg_type)
+{
+    ALOGV("%s: msg_type = 0x%x", __FUNCTION__, msg_type);
+    PrintMessages(msg_type);
+
+    Mutex::Autolock locker(&mObjectLock);
+    mMessageEnabler &= ~msg_type;
+    ALOGV("**** Currently enabled messages:");
+    PrintMessages(mMessageEnabler);
+}
+
+status_t CallbackNotifier::enableVideoRecording(int fps)
+{
+    ALOGV("%s: FPS = %d", __FUNCTION__, fps);
+
+    Mutex::Autolock locker(&mObjectLock);
+    mVideoRecEnabled = true;
+    mLastFrameTimestamp = 0;
+    mFrameRefreshFreq = 1000000000LL / fps;
+
+    return NO_ERROR;
+}
+
+void CallbackNotifier::disableVideoRecording()
+{
+    ALOGV("%s:", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    mVideoRecEnabled = false;
+    mLastFrameTimestamp = 0;
+    mFrameRefreshFreq = 0;
+}
+
+void CallbackNotifier::releaseRecordingFrame(const void* opaque)
+{
+    List<camera_memory_t*>::iterator it = mCameraMemoryTs.begin();
+    for( ; it != mCameraMemoryTs.end(); ++it ) {
+        if ( (*it)->data == opaque ) {
+            (*it)->release( *it );
+            mCameraMemoryTs.erase(it);
+            break;
+        }
+    }
+}
+
+status_t CallbackNotifier::storeMetaDataInBuffers(bool enable)
+{
+    // Return error if metadata is request, otherwise silently agree.
+    return enable ? INVALID_OPERATION : NO_ERROR;
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+void CallbackNotifier::cleanupCBNotifier()
+{
+    Mutex::Autolock locker(&mObjectLock);
+    mMessageEnabler = 0;
+    mNotifyCB = NULL;
+    mDataCB = NULL;
+    mDataCBTimestamp = NULL;
+    mGetMemoryCB = NULL;
+    mCBOpaque = NULL;
+    mLastFrameTimestamp = 0;
+    mFrameRefreshFreq = 0;
+    mJpegQuality = 90;
+    mVideoRecEnabled = false;
+    mTakingPicture = false;
+}
+
+void CallbackNotifier::onNextFrameAvailable(const void* frame,
+                                            nsecs_t timestamp,
+                                            EmulatedCameraDevice* camera_dev)
+{
+    if (isMessageEnabled(CAMERA_MSG_VIDEO_FRAME) && isVideoRecordingEnabled() &&
+            isNewVideoFrameTime(timestamp)) {
+        camera_memory_t* cam_buff =
+            mGetMemoryCB(-1, camera_dev->getFrameBufferSize(), 1, mCBOpaque);
+        if (NULL != cam_buff && NULL != cam_buff->data) {
+            memcpy(cam_buff->data, frame, camera_dev->getFrameBufferSize());
+            mDataCBTimestamp(timestamp, CAMERA_MSG_VIDEO_FRAME,
+                               cam_buff, 0, mCBOpaque);
+
+            mCameraMemoryTs.push_back( cam_buff );
+        } else {
+            ALOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
+        }
+    }
+
+    if (isMessageEnabled(CAMERA_MSG_PREVIEW_FRAME)) {
+        camera_memory_t* cam_buff =
+            mGetMemoryCB(-1, camera_dev->getFrameBufferSize(), 1, mCBOpaque);
+        if (NULL != cam_buff && NULL != cam_buff->data) {
+            memcpy(cam_buff->data, frame, camera_dev->getFrameBufferSize());
+            mDataCB(CAMERA_MSG_PREVIEW_FRAME, cam_buff, 0, NULL, mCBOpaque);
+            cam_buff->release(cam_buff);
+        } else {
+            ALOGE("%s: Memory failure in CAMERA_MSG_PREVIEW_FRAME", __FUNCTION__);
+        }
+    }
+
+    if (mTakingPicture) {
+        /* This happens just once. */
+        mTakingPicture = false;
+        /* The sequence of callbacks during picture taking is:
+         *  - CAMERA_MSG_SHUTTER
+         *  - CAMERA_MSG_RAW_IMAGE_NOTIFY
+         *  - CAMERA_MSG_COMPRESSED_IMAGE
+         */
+        if (isMessageEnabled(CAMERA_MSG_SHUTTER)) {
+            mNotifyCB(CAMERA_MSG_SHUTTER, 0, 0, mCBOpaque);
+        }
+        if (isMessageEnabled(CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
+            mNotifyCB(CAMERA_MSG_RAW_IMAGE_NOTIFY, 0, 0, mCBOpaque);
+        }
+        if (isMessageEnabled(CAMERA_MSG_COMPRESSED_IMAGE)) {
+            /* Compress the frame to JPEG. Note that when taking pictures, we
+             * have requested camera device to provide us with NV21 frames. */
+            NV21JpegCompressor compressor;
+            struct ::ImageMetadata meta;
+            status_t res =
+                camera_dev->getImageMetadata(&meta);
+            if (res == NO_ERROR) {
+                res = compressor.compressRawImage(frame, &meta, mJpegQuality);
+                if (res == NO_ERROR) {
+                    camera_memory_t* jpeg_buff =
+                        mGetMemoryCB(-1, compressor.getCompressedSize(), 1, mCBOpaque);
+                    if (NULL != jpeg_buff && NULL != jpeg_buff->data) {
+                        compressor.getCompressedImage(jpeg_buff->data);
+                        mDataCB(CAMERA_MSG_COMPRESSED_IMAGE, jpeg_buff, 0, NULL, mCBOpaque);
+                        jpeg_buff->release(jpeg_buff);
+                    } else {
+                        ALOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
+                    }
+                } else {
+                    ALOGE("%s: Compression failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
+                }
+            } else {
+                ALOGE("%s: Image Metadata acquisition failure.", __FUNCTION__);
+            }
+        }
+    }
+}
+
+void CallbackNotifier::onCameraDeviceError(int err)
+{
+    if (isMessageEnabled(CAMERA_MSG_ERROR) && mNotifyCB != NULL) {
+        mNotifyCB(CAMERA_MSG_ERROR, err, 0, mCBOpaque);
+    }
+}
+
+void CallbackNotifier::onCameraFocusAcquired() {
+    if (isMessageEnabled(CAMERA_MSG_FOCUS) && mNotifyCB != NULL) {
+        mNotifyCB(CAMERA_MSG_FOCUS, 1, 0, mCBOpaque);
+    }
+}
+
+/****************************************************************************
+ * Private API
+ ***************************************************************************/
+
+bool CallbackNotifier::isNewVideoFrameTime(nsecs_t timestamp)
+{
+    Mutex::Autolock locker(&mObjectLock);
+    if ((timestamp - mLastFrameTimestamp) >= mFrameRefreshFreq) {
+        mLastFrameTimestamp = timestamp;
+        return true;
+    }
+    return false;
+}
+
+}; /* namespace android */
diff --git a/guest/hals/camera/CallbackNotifier.h b/guest/hals/camera/CallbackNotifier.h
new file mode 100755
index 0000000..569ca57
--- /dev/null
+++ b/guest/hals/camera/CallbackNotifier.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_CALLBACK_NOTIFIER_H
+#define HW_EMULATOR_CAMERA_CALLBACK_NOTIFIER_H
+
+/*
+ * Contains declaration of a class CallbackNotifier that manages callbacks set
+ * via set_callbacks, enable_msg_type, and disable_msg_type camera HAL API.
+ */
+
+#include <utils/List.h>
+
+namespace android {
+
+class EmulatedCameraDevice;
+
+/* Manages callbacks set via set_callbacks, enable_msg_type, and disable_msg_type
+ * camera HAL API.
+ *
+ * Objects of this class are contained in EmulatedCamera objects, and handle
+ * relevant camera API callbacks.
+ * Locking considerations. Apparently, it's not allowed to call callbacks
+ * registered in this class, while holding a lock: recursion is quite possible,
+ * which will cause a deadlock.
+ */
+class CallbackNotifier {
+public:
+    /* Constructs CallbackNotifier instance. */
+    CallbackNotifier();
+
+    /* Destructs CallbackNotifier instance. */
+    ~CallbackNotifier();
+
+    /****************************************************************************
+     * Camera API
+     ***************************************************************************/
+
+public:
+    /* Actual handler for camera_device_ops_t::set_callbacks callback.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::set_callbacks callback.
+     */
+    void setCallbacks(camera_notify_callback notify_cb,
+                      camera_data_callback data_cb,
+                      camera_data_timestamp_callback data_cb_timestamp,
+                      camera_request_memory get_memory,
+                      void* user);
+
+    /* Actual handler for camera_device_ops_t::enable_msg_type callback.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::enable_msg_type callback.
+     */
+    void enableMessage(uint msg_type);
+
+    /* Actual handler for camera_device_ops_t::disable_msg_type callback.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::disable_msg_type callback.
+     */
+    void disableMessage(uint msg_type);
+
+    /* Actual handler for camera_device_ops_t::store_meta_data_in_buffers
+     * callback. This method is called by the containing emulated camera object
+     * when it is handing the camera_device_ops_t::store_meta_data_in_buffers
+     * callback.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    status_t storeMetaDataInBuffers(bool enable);
+
+    /* Enables video recording.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::start_recording callback.
+     * Param:
+     *  fps - Video frame frequency. This parameter determins when a frame
+     *      received via onNextFrameAvailable call will be pushed through the
+     *      callback.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    status_t enableVideoRecording(int fps);
+
+    /* Disables video recording.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::stop_recording callback.
+     */
+    void disableVideoRecording();
+
+    /* Releases video frame, sent to the framework.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::release_recording_frame callback.
+     */
+    void releaseRecordingFrame(const void* opaque);
+
+    /* Actual handler for camera_device_ops_t::msg_type_enabled callback.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::msg_type_enabled callback.
+     * Note: this method doesn't grab a lock while checking message status, since
+     * upon exit the status would be undefined anyway. So, grab a lock before
+     * calling this method if you care about persisting a defined message status.
+     * Return:
+     *  0 if message is disabled, or non-zero value, if message is enabled.
+     */
+    inline int isMessageEnabled(uint msg_type)
+    {
+        return mMessageEnabler & msg_type;
+    }
+
+    /* Checks id video recording is enabled.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::recording_enabled callback.
+     * Note: this method doesn't grab a lock while checking video recordin status,
+     * since upon exit the status would be undefined anyway. So, grab a lock
+     * before calling this method if you care about persisting of a defined video
+     * recording status.
+     * Return:
+     *  true if video recording is enabled, or false if it is disabled.
+     */
+    inline bool isVideoRecordingEnabled()
+    {
+        return mVideoRecEnabled;
+    }
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+public:
+    /* Resets the callback notifier. */
+    void cleanupCBNotifier();
+
+    /* Next frame is available in the camera device.
+     * This is a notification callback that is invoked by the camera device when
+     * a new frame is available.
+     * Note that most likely this method is called in context of a worker thread
+     * that camera device has created for frame capturing.
+     * Param:
+     *  frame - Captured frame, or NULL if camera device didn't pull the frame
+     *      yet. If NULL is passed in this parameter use GetCurrentFrame method
+     *      of the camera device class to obtain the next frame. Also note that
+     *      the size of the frame that is passed here (as well as the frame
+     *      returned from the GetCurrentFrame method) is defined by the current
+     *      frame settings (width + height + pixel format) for the camera device.
+     * timestamp - Frame's timestamp.
+     * camera_dev - Camera device instance that delivered the frame.
+     */
+    void onNextFrameAvailable(const void* frame,
+                              nsecs_t timestamp,
+                              EmulatedCameraDevice* camera_dev);
+
+    /* Entry point for notifications that occur in camera device.
+     * Param:
+     *  err - CAMERA_ERROR_XXX error code.
+     */
+    void onCameraDeviceError(int err);
+
+    /* Reports focus operation completion to camera client.
+     */
+    void onCameraFocusAcquired();
+
+    /* Sets, or resets taking picture state.
+     * This state control whether or not to notify the framework about compressed
+     * image, shutter, and other picture related events.
+     */
+    void setTakingPicture(bool taking)
+    {
+        mTakingPicture = taking;
+    }
+
+    /* Sets JPEG quality used to compress frame during picture taking. */
+    void setJpegQuality(int jpeg_quality)
+    {
+        mJpegQuality = jpeg_quality;
+    }
+
+    /****************************************************************************
+     * Private API
+     ***************************************************************************/
+
+protected:
+    /* Checks if it's time to push new video frame.
+     * Note that this method must be called while object is locked.
+     * Param:
+     *  timestamp - Timestamp for the new frame. */
+    bool isNewVideoFrameTime(nsecs_t timestamp);
+
+    /****************************************************************************
+     * Data members
+     ***************************************************************************/
+
+protected:
+    /* Locks this instance for data change. */
+    Mutex                           mObjectLock;
+
+    /*
+     * Callbacks, registered in set_callbacks.
+     */
+
+    camera_notify_callback          mNotifyCB;
+    camera_data_callback            mDataCB;
+    camera_data_timestamp_callback  mDataCBTimestamp;
+    camera_request_memory           mGetMemoryCB;
+    void*                           mCBOpaque;
+
+    /* video frame queue for the CameraHeapMemory destruction */
+    List<camera_memory_t*>          mCameraMemoryTs;
+
+    /* Timestamp when last frame has been delivered to the framework. */
+    nsecs_t                         mLastFrameTimestamp;
+
+    /* Video frequency in nanosec. */
+    nsecs_t                         mFrameRefreshFreq;
+
+    /* Message enabler. */
+    uint32_t                        mMessageEnabler;
+
+    /* JPEG quality used to compress frame during picture taking. */
+    int                             mJpegQuality;
+
+    /* Video recording status. */
+    bool                            mVideoRecEnabled;
+
+    /* Picture taking status. */
+    bool                            mTakingPicture;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_CALLBACK_NOTIFIER_H */
diff --git a/guest/hals/camera/CameraConfiguration.cpp b/guest/hals/camera/CameraConfiguration.cpp
new file mode 100644
index 0000000..8023045
--- /dev/null
+++ b/guest/hals/camera/CameraConfiguration.cpp
@@ -0,0 +1,305 @@
+/*
+ * 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 "CameraConfiguration.h"
+
+#define LOG_TAG "CameraConfiguration"
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <cutils/log.h>
+#include <json/json.h>
+#include <json/reader.h>
+#include <stdlib.h>
+
+namespace cvd {
+namespace {
+////////////////////// Device Personality keys //////////////////////
+//
+// **** Camera ****
+//
+// Example segment (transcribed to constants):
+//
+// kCameraDefinitionsKey: [
+//   {
+//     kCameraDefinitionOrientationKey: "front",
+//     kCameraDefinitionHalVersionKey: "1",
+//     kCameraDefinitionResolutionsKey: [
+//       {
+//         kCameraDefinitionResolutionWidthKey: "1600",
+//         kCameraDefinitionResolutionHeightKey: "1200",
+//       },
+//       {
+//         kCameraDefinitionResolutionWidthKey: "1280",
+//         kCameraDefinitionResolutionHeightKey: "800",
+//       }
+//     ]
+//   },
+//   {
+//     kCameraDefinitionOrientationKey: "back",
+//     kCameraDefinitionHalVersionKey: "1",
+//     kCameraDefinitionResolutionsKey: [
+//       {
+//         kCameraDefinitionResolutionWidthKey: "1024",
+//         kCameraDefinitionResolutionHeightKey: "768",
+//       },
+//       {
+//         kCameraDefinitionResolutionWidthKey: "800",
+//         kCameraDefinitionResolutionHeightKey: "600",
+//       }
+//     ]
+//   }
+// ]
+//
+
+// Location of the camera configuration files.
+const char* const kConfigurationFileLocation = "/vendor/etc/config/camera.json";
+
+//
+// Array of camera definitions for all cameras available on the device (array).
+// Top Level Key.
+const char* const kCameraDefinitionsKey = "camera_definitions";
+
+// Camera orientation of currently defined camera (string).
+// Currently supported values:
+// - "back",
+// - "front".
+const char* const kCameraDefinitionOrientationKey = "orientation";
+
+// Camera HAL version of currently defined camera (int).
+// Currently supported values:
+// - 1 (Camera HALv1)
+// - 2 (Camera HALv2)
+// - 3 (Camera HALv3)
+const char* const kCameraDefinitionHalVersionKey = "hal_version";
+
+// Array of resolutions supported by camera (array).
+const char* const kCameraDefinitionResolutionsKey = "resolutions";
+
+// Width of currently defined resolution (int).
+// Must be divisible by 8.
+const char* const kCameraDefinitionResolutionWidthKey = "width";
+
+// Height of currently defined resolution (int).
+// Must be divisible by 8.
+const char* const kCameraDefinitionResolutionHeightKey = "height";
+
+// Convert string value to camera orientation.
+bool ValueToCameraOrientation(
+    const std::string& value,
+    CameraDefinition::Orientation* orientation) {
+  if (value == "back") {
+    *orientation = CameraDefinition::kBack;
+    return true;
+  } else if (value == "front") {
+    *orientation = CameraDefinition::kFront;
+    return true;
+  }
+  ALOGE("%s: Invalid camera orientation: %s.",
+        __FUNCTION__, value.c_str());
+  return false;
+}
+
+// Convert string value to camera HAL version.
+bool ValueToCameraHalVersion(
+    const std::string& value,
+    CameraDefinition::HalVersion* hal_version) {
+  int temp;
+  char* endptr;
+
+  temp = strtol(value.c_str(), &endptr, 10);
+  if (endptr != value.c_str() + value.size()) {
+    ALOGE("%s: Invalid camera HAL version. Expected number, got %s.",
+          __FUNCTION__, value.c_str());
+    return false;
+  }
+
+  switch (temp) {
+    case 1:
+      *hal_version = CameraDefinition::kHalV1;
+      break;
+
+    case 2:
+      *hal_version = CameraDefinition::kHalV2;
+      break;
+
+    case 3:
+      *hal_version = CameraDefinition::kHalV3;
+      break;
+
+    default:
+      ALOGE("%s: Invalid camera HAL version. Version %d not supported.",
+            __FUNCTION__, temp);
+      return false;
+  }
+
+  return true;
+}
+
+bool ValueToCameraResolution(
+    const std::string& width, const std::string& height,
+    CameraDefinition::Resolution* resolution) {
+  char* endptr;
+
+  resolution->width = strtol(width.c_str(), &endptr, 10);
+  if (endptr != width.c_str() + width.size()) {
+    ALOGE("%s: Invalid camera resolution width. Expected number, got %s.",
+          __FUNCTION__, width.c_str());
+    return false;
+  }
+
+  resolution->height = strtol(height.c_str(), &endptr, 10);
+  if (endptr != height.c_str() + height.size()) {
+    ALOGE("%s: Invalid camera resolution height. Expected number, got %s.",
+          __FUNCTION__, height.c_str());
+    return false;
+  }
+
+  // Validate width and height parameters are sane.
+  if (resolution->width <= 0 || resolution->height <= 0) {
+    ALOGE("%s: Invalid camera resolution: %dx%d", __FUNCTION__,
+          resolution->width, resolution->height);
+    return false;
+  }
+
+  // Validate width and height divisible by 8.
+  if ((resolution->width & 7) != 0 || (resolution->height & 7) != 0) {
+    ALOGE("%s: Invalid camera resolution: width and height must be "
+          "divisible by 8, got %dx%d (%dx%d).", __FUNCTION__,
+          resolution->width, resolution->height,
+          resolution->width & 7, resolution->height & 7);
+    return false;
+  }
+
+  return true;
+}
+
+// Process camera definitions.
+// Returns true, if definitions were sane.
+bool ConfigureCameras(
+    const Json::Value& value,
+    std::vector<CameraDefinition>* cameras) {
+  if (!value.isObject()) {
+    ALOGE("%s: Configuration root is not an object", __FUNCTION__);
+    return false;
+  }
+
+  if (!value.isMember(kCameraDefinitionsKey)) return true;
+  for (Json::ValueConstIterator iter = value[kCameraDefinitionsKey].begin();
+       iter != value[kCameraDefinitionsKey].end();
+       ++iter) {
+    cameras->push_back(CameraDefinition());
+    CameraDefinition& camera = cameras->back();
+
+    if (!iter->isObject()) {
+      ALOGE("%s: Camera definition is not an object", __FUNCTION__);
+      continue;
+    }
+
+    // Camera without orientation -> invalid setting.
+    if (!iter->isMember(kCameraDefinitionOrientationKey)) {
+      ALOGE("%s: Invalid camera definition: key %s is missing.",
+            __FUNCTION__, kCameraDefinitionOrientationKey);
+      return false;
+    }
+
+    if (!ValueToCameraOrientation(
+        (*iter)[kCameraDefinitionOrientationKey].asString(),
+        &camera.orientation)) return false;
+
+    // Camera without HAL version -> invalid setting.
+    if (!(*iter).isMember(kCameraDefinitionHalVersionKey)) {
+      ALOGE("%s: Invalid camera definition: key %s is missing.",
+            __FUNCTION__, kCameraDefinitionHalVersionKey);
+      return false;
+    }
+
+    if (!ValueToCameraHalVersion(
+        (*iter)[kCameraDefinitionHalVersionKey].asString(),
+        &camera.hal_version)) return false;
+
+    // Camera without resolutions -> invalid setting.
+    if (!iter->isMember(kCameraDefinitionResolutionsKey)) {
+      ALOGE("%s: Invalid camera definition: key %s is missing.",
+            __FUNCTION__, kCameraDefinitionResolutionsKey);
+      return false;
+    }
+
+    const Json::Value& json_resolutions =
+        (*iter)[kCameraDefinitionResolutionsKey];
+
+    // Resolutions not an array, or an empty array -> invalid setting.
+    if (!json_resolutions.isArray() || json_resolutions.empty()) {
+      ALOGE("%s: Invalid camera definition: %s is not an array or is empty.",
+            __FUNCTION__, kCameraDefinitionResolutionsKey);
+      return false;
+    }
+
+    // Process all resolutions.
+    for (Json::ValueConstIterator json_res_iter = json_resolutions.begin();
+         json_res_iter != json_resolutions.end();
+         ++json_res_iter) {
+      // Check presence of width and height keys.
+      if (!json_res_iter->isObject()) {
+        ALOGE("%s: Camera resolution item is not an object", __FUNCTION__);
+        continue;
+      }
+      if (!json_res_iter->isMember(kCameraDefinitionResolutionWidthKey) ||
+          !json_res_iter->isMember(kCameraDefinitionResolutionHeightKey)) {
+        ALOGE("%s: Invalid camera resolution: keys %s and %s are both required.",
+              __FUNCTION__,
+              kCameraDefinitionResolutionWidthKey,
+              kCameraDefinitionResolutionHeightKey);
+        return false;
+      }
+
+      camera.resolutions.push_back(CameraDefinition::Resolution());
+      CameraDefinition::Resolution& resolution = camera.resolutions.back();
+
+      if (!ValueToCameraResolution(
+          (*json_res_iter)[kCameraDefinitionResolutionWidthKey].asString(),
+          (*json_res_iter)[kCameraDefinitionResolutionHeightKey].asString(),
+          &resolution)) return false;
+    }
+  }
+
+  return true;
+}
+}  // namespace
+
+bool CameraConfiguration::Init() {
+  cameras_.clear();
+  std::string config;
+  if (!android::base::ReadFileToString(
+      kConfigurationFileLocation, &config)) {
+    ALOGE("%s: Could not open configuration file: %s",
+          __FUNCTION__, kConfigurationFileLocation);
+    return false;
+  }
+
+  Json::Reader config_reader;
+  Json::Value root;
+  if (!config_reader.parse(config, root)) {
+    ALOGE("Could not parse configuration file: %s",
+          config_reader.getFormattedErrorMessages().c_str());
+    return false;
+  }
+
+  return ConfigureCameras(root, &cameras_);
+}
+
+}  // namespace cvd
+
+
diff --git a/guest/hals/camera/CameraConfiguration.h b/guest/hals/camera/CameraConfiguration.h
new file mode 100644
index 0000000..90aa087
--- /dev/null
+++ b/guest/hals/camera/CameraConfiguration.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+#ifndef GUEST_HALS_CAMERA_CAMERACONFIGURATION_H_
+#define GUEST_HALS_CAMERA_CAMERACONFIGURATION_H_
+
+#include <vector>
+
+namespace cvd {
+
+// Camera properties and features.
+struct CameraDefinition {
+  // Camera facing direction.
+  enum Orientation {
+    kFront,
+    kBack
+  };
+
+  // Camera recognized HAL versions.
+  enum HalVersion {
+    kHalV1,
+    kHalV2,
+    kHalV3
+  };
+
+  struct Resolution {
+    int width;
+    int height;
+  };
+
+  Orientation orientation;
+  HalVersion hal_version;
+  std::vector<Resolution> resolutions;
+};
+
+class CameraConfiguration {
+ public:
+  CameraConfiguration() {}
+  ~CameraConfiguration() {}
+
+  const std::vector<CameraDefinition>& cameras() const {
+    return cameras_;
+  }
+
+  bool Init();
+
+ private:
+  std::vector<CameraDefinition> cameras_;
+};
+
+}  // namespace cvd
+
+#endif  // GUEST_HALS_CAMERA_CAMERACONFIGURATION_H_
diff --git a/guest/hals/camera/Converters.cpp b/guest/hals/camera/Converters.cpp
new file mode 100755
index 0000000..f63f67f
--- /dev/null
+++ b/guest/hals/camera/Converters.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implemenation of framebuffer conversion routines.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Converter"
+#include <cutils/log.h>
+#include "Converters.h"
+
+namespace android {
+
+static void _YUV420SToRGB565(const uint8_t* Y,
+                             const uint8_t* U,
+                             const uint8_t* V,
+                             int dUV,
+                             uint16_t* rgb,
+                             int width,
+                             int height)
+{
+    const uint8_t* U_pos = U;
+    const uint8_t* V_pos = V;
+
+    for (int y = 0; y < height; y++) {
+        for (int x = 0; x < width; x += 2, U += dUV, V += dUV) {
+            const uint8_t nU = *U;
+            const uint8_t nV = *V;
+            *rgb = YUVToRGB565(*Y, nU, nV);
+            Y++; rgb++;
+            *rgb = YUVToRGB565(*Y, nU, nV);
+            Y++; rgb++;
+        }
+        if (y & 0x1) {
+            U_pos = U;
+            V_pos = V;
+        } else {
+            U = U_pos;
+            V = V_pos;
+        }
+    }
+}
+
+static void _YUV420SToRGB32(const uint8_t* Y,
+                            const uint8_t* U,
+                            const uint8_t* V,
+                            int dUV,
+                            uint32_t* rgb,
+                            int width,
+                            int height)
+{
+    const uint8_t* U_pos = U;
+    const uint8_t* V_pos = V;
+
+    for (int y = 0; y < height; y++) {
+        for (int x = 0; x < width; x += 2, U += dUV, V += dUV) {
+            const uint8_t nU = *U;
+            const uint8_t nV = *V;
+            *rgb = YUVToRGB32(*Y, nU, nV);
+            Y++; rgb++;
+            *rgb = YUVToRGB32(*Y, nU, nV);
+            Y++; rgb++;
+        }
+        if (y & 0x1) {
+            U_pos = U;
+            V_pos = V;
+        } else {
+            U = U_pos;
+            V = V_pos;
+        }
+    }
+}
+
+void YV12ToRGB565(const void* yv12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
+    const uint8_t* U = Y + pix_total;
+    const uint8_t* V = U + pix_total / 4;
+    _YUV420SToRGB565(Y, U, V, 1, reinterpret_cast<uint16_t*>(rgb), width, height);
+}
+
+void YV12ToRGB32(const void* yv12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
+    const uint8_t* V = Y + pix_total;
+    const uint8_t* U = V + pix_total / 4;
+    _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+void YU12ToRGB32(const void* yu12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yu12);
+    const uint8_t* U = Y + pix_total;
+    const uint8_t* V = U + pix_total / 4;
+    _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+/* Common converter for YUV 4:2:0 interleaved to RGB565.
+ * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
+ */
+static void _NVXXToRGB565(const uint8_t* Y,
+                          const uint8_t* U,
+                          const uint8_t* V,
+                          uint16_t* rgb,
+                          int width,
+                          int height)
+{
+    _YUV420SToRGB565(Y, U, V, 2, rgb, width, height);
+}
+
+/* Common converter for YUV 4:2:0 interleaved to RGB32.
+ * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
+ */
+static void _NVXXToRGB32(const uint8_t* Y,
+                         const uint8_t* U,
+                         const uint8_t* V,
+                         uint32_t* rgb,
+                         int width,
+                         int height)
+{
+    _YUV420SToRGB32(Y, U, V, 2, rgb, width, height);
+}
+
+void NV12ToRGB565(const void* nv12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv12);
+    _NVXXToRGB565(y, y + pix_total, y + pix_total + 1,
+                  reinterpret_cast<uint16_t*>(rgb), width, height);
+}
+
+void NV12ToRGB32(const void* nv12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv12);
+    _NVXXToRGB32(y, y + pix_total, y + pix_total + 1,
+                 reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+void NV21ToRGB565(const void* nv21, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv21);
+    _NVXXToRGB565(y, y + pix_total + 1, y + pix_total,
+                  reinterpret_cast<uint16_t*>(rgb), width, height);
+}
+
+void NV21ToRGB32(const void* nv21, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv21);
+    _NVXXToRGB32(y, y + pix_total + 1, y + pix_total,
+                 reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+}; /* namespace android */
diff --git a/guest/hals/camera/Converters.h b/guest/hals/camera/Converters.h
new file mode 100755
index 0000000..13e2a85
--- /dev/null
+++ b/guest/hals/camera/Converters.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_CONVERTERS_H
+#define HW_EMULATOR_CAMERA_CONVERTERS_H
+
+#include <endian.h>
+
+#ifndef __BYTE_ORDER
+#error "could not determine byte order"
+#endif
+
+/*
+ * Contains declaration of framebuffer conversion routines.
+ *
+ * NOTE: RGB and big/little endian considerations. Wherewer in this code RGB
+ * pixels are represented as WORD, or DWORD, the color order inside the
+ * WORD / DWORD matches the one that would occur if that WORD / DWORD would have
+ * been read from the typecasted framebuffer:
+ *
+ *      const uint32_t rgb = *reinterpret_cast<const uint32_t*>(framebuffer);
+ *
+ * So, if this code runs on the little endian CPU, red color in 'rgb' would be
+ * masked as 0x000000ff, and blue color would be masked as 0x00ff0000, while if
+ * the code runs on a big endian CPU, the red color in 'rgb' would be masked as
+ * 0xff000000, and blue color would be masked as 0x0000ff00,
+ */
+
+namespace android {
+
+/*
+ * RGB565 color masks
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static const uint16_t kRed5     = 0x001f;
+static const uint16_t kGreen6   = 0x07e0;
+static const uint16_t kBlue5    = 0xf800;
+#else   // __BYTE_ORDER
+static const uint16_t kRed5     = 0xf800;
+static const uint16_t kGreen6   = 0x07e0;
+static const uint16_t kBlue5    = 0x001f;
+#endif  // __BYTE_ORDER
+static const uint32_t kBlack16  = 0x0000;
+static const uint32_t kWhite16  = kRed5 | kGreen6 | kBlue5;
+
+/*
+ * RGB32 color masks
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static const uint32_t kRed8     = 0x000000ff;
+static const uint32_t kGreen8   = 0x0000ff00;
+static const uint32_t kBlue8    = 0x00ff0000;
+#else   // __BYTE_ORDER
+static const uint32_t kRed8     = 0x00ff0000;
+static const uint32_t kGreen8   = 0x0000ff00;
+static const uint32_t kBlue8    = 0x000000ff;
+#endif  // __BYTE_ORDER
+static const uint32_t kBlack32  = 0x00000000;
+static const uint32_t kWhite32  = kRed8 | kGreen8 | kBlue8;
+
+/*
+ * Extracting, and saving color bytes from / to WORD / DWORD RGB.
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+/* Extract red, green, and blue bytes from RGB565 word. */
+#define R16(rgb)    static_cast<uint8_t>(rgb & kRed5)
+#define G16(rgb)    static_cast<uint8_t>((rgb & kGreen6) >> 5)
+#define B16(rgb)    static_cast<uint8_t>((rgb & kBlue5) >> 11)
+/* Make 8 bits red, green, and blue, extracted from RGB565 word. */
+#define R16_32(rgb) static_cast<uint8_t>(((rgb & kRed5) << 3) | ((rgb & kRed5) >> 2))
+#define G16_32(rgb) static_cast<uint8_t>(((rgb & kGreen6) >> 3) | ((rgb & kGreen6) >> 9))
+#define B16_32(rgb) static_cast<uint8_t>(((rgb & kBlue5) >> 8) | ((rgb & kBlue5) >> 14))
+/* Extract red, green, and blue bytes from RGB32 dword. */
+#define R32(rgb)    static_cast<uint8_t>(rgb & kRed8)
+#define G32(rgb)    static_cast<uint8_t>(((rgb & kGreen8) >> 8) & 0xff)
+#define B32(rgb)    static_cast<uint8_t>(((rgb & kBlue8) >> 16) & 0xff)
+/* Build RGB565 word from red, green, and blue bytes. */
+#define RGB565(r, g, b) static_cast<uint16_t>((((static_cast<uint16_t>(b) << 6) | g) << 5) | r)
+/* Build RGB32 dword from red, green, and blue bytes. */
+#define RGB32(r, g, b) static_cast<uint32_t>((((static_cast<uint32_t>(b) << 8) | g) << 8) | r)
+#else   // __BYTE_ORDER
+/* Extract red, green, and blue bytes from RGB565 word. */
+#define R16(rgb)    static_cast<uint8_t>((rgb & kRed5) >> 11)
+#define G16(rgb)    static_cast<uint8_t>((rgb & kGreen6) >> 5)
+#define B16(rgb)    static_cast<uint8_t>(rgb & kBlue5)
+/* Make 8 bits red, green, and blue, extracted from RGB565 word. */
+#define R16_32(rgb) static_cast<uint8_t>(((rgb & kRed5) >> 8) | ((rgb & kRed5) >> 14))
+#define G16_32(rgb) static_cast<uint8_t>(((rgb & kGreen6) >> 3) | ((rgb & kGreen6) >> 9))
+#define B16_32(rgb) static_cast<uint8_t>(((rgb & kBlue5) << 3) | ((rgb & kBlue5) >> 2))
+/* Extract red, green, and blue bytes from RGB32 dword. */
+#define R32(rgb)    static_cast<uint8_t>((rgb & kRed8) >> 16)
+#define G32(rgb)    static_cast<uint8_t>((rgb & kGreen8) >> 8)
+#define B32(rgb)    static_cast<uint8_t>(rgb & kBlue8)
+/* Build RGB565 word from red, green, and blue bytes. */
+#define RGB565(r, g, b) static_cast<uint16_t>((((static_cast<uint16_t>(r) << 6) | g) << 5) | b)
+/* Build RGB32 dword from red, green, and blue bytes. */
+#define RGB32(r, g, b) static_cast<uint32_t>((((static_cast<uint32_t>(r) << 8) | g) << 8) | b)
+#endif  // __BYTE_ORDER
+
+/* An union that simplifies breaking 32 bit RGB into separate R, G, and B colors.
+ */
+typedef union RGB32_t {
+    uint32_t    color;
+    struct {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+        uint8_t r; uint8_t g; uint8_t b; uint8_t a;
+#else   // __BYTE_ORDER
+        uint8_t a; uint8_t b; uint8_t g; uint8_t r;
+#endif  // __BYTE_ORDER
+    };
+} RGB32_t;
+
+
+/* Clips a value to the unsigned 0-255 range, treating negative values as zero.
+ */
+static __inline__ int
+clamp(int x)
+{
+    if (x > 255) return 255;
+    if (x < 0)   return 0;
+    return x;
+}
+
+/********************************************************************************
+ * Basics of RGB -> YUV conversion
+ *******************************************************************************/
+
+/*
+ * RGB -> YUV conversion macros
+ */
+#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) +  16)
+#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) -  18 * (b) + 128) >> 8) + 128)
+
+/* Converts R8 G8 B8 color to YUV. */
+static __inline__ void
+R8G8B8ToYUV(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+    *y = RGB2Y((int)r, (int)g, (int)b);
+    *u = RGB2U((int)r, (int)g, (int)b);
+    *v = RGB2V((int)r, (int)g, (int)b);
+}
+
+/* Converts RGB565 color to YUV. */
+static __inline__ void
+RGB565ToYUV(uint16_t rgb, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+    R8G8B8ToYUV(R16_32(rgb), G16_32(rgb), B16_32(rgb), y, u, v);
+}
+
+/* Converts RGB32 color to YUV. */
+static __inline__ void
+RGB32ToYUV(uint32_t rgb, uint8_t* y, uint8_t* u, uint8_t* v)
+{
+    RGB32_t rgb_c;
+    rgb_c.color = rgb;
+    R8G8B8ToYUV(rgb_c.r, rgb_c.g, rgb_c.b, y, u, v);
+}
+
+/********************************************************************************
+ * Basics of YUV -> RGB conversion.
+ * Note that due to the fact that guest uses RGB only on preview window, and the
+ * RGB format that is used is RGB565, we can limit YUV -> RGB conversions to
+ * RGB565 only.
+ *******************************************************************************/
+
+/*
+ * YUV -> RGB conversion macros
+ */
+
+/* "Optimized" macros that take specialy prepared Y, U, and V values:
+ *  C = Y - 16
+ *  D = U - 128
+ *  E = V - 128
+ */
+#define YUV2RO(C, D, E) clamp((298 * (C) + 409 * (E) + 128) >> 8)
+#define YUV2GO(C, D, E) clamp((298 * (C) - 100 * (D) - 208 * (E) + 128) >> 8)
+#define YUV2BO(C, D, E) clamp((298 * (C) + 516 * (D) + 128) >> 8)
+
+/*
+ *  Main macros that take the original Y, U, and V values
+ */
+#define YUV2R(y, u, v) clamp((298 * ((y)-16) + 409 * ((v)-128) + 128) >> 8)
+#define YUV2G(y, u, v) clamp((298 * ((y)-16) - 100 * ((u)-128) - 208 * ((v)-128) + 128) >> 8)
+#define YUV2B(y, u, v) clamp((298 * ((y)-16) + 516 * ((u)-128) + 128) >> 8)
+
+
+/* Converts YUV color to RGB565. */
+static __inline__ uint16_t
+YUVToRGB565(int y, int u, int v)
+{
+    /* Calculate C, D, and E values for the optimized macro. */
+    y -= 16; u -= 128; v -= 128;
+    const uint16_t r = (YUV2RO(y,u,v) >> 3) & 0x1f;
+    const uint16_t g = (YUV2GO(y,u,v) >> 2) & 0x3f;
+    const uint16_t b = (YUV2BO(y,u,v) >> 3) & 0x1f;
+    return RGB565(r, g, b);
+}
+
+/* Converts YUV color to RGB32. */
+static __inline__ uint32_t
+YUVToRGB32(int y, int u, int v)
+{
+    /* Calculate C, D, and E values for the optimized macro. */
+    y -= 16; u -= 128; v -= 128;
+    RGB32_t rgb;
+    rgb.r = YUV2RO(y,u,v) & 0xff;
+    rgb.g = YUV2GO(y,u,v) & 0xff;
+    rgb.b = YUV2BO(y,u,v) & 0xff;
+    return rgb.color;
+}
+
+/* YUV pixel descriptor. */
+struct YUVPixel {
+    uint8_t     Y;
+    uint8_t     U;
+    uint8_t     V;
+
+    inline YUVPixel()
+        : Y(0), U(0), V(0)
+    {
+    }
+
+    inline explicit YUVPixel(uint16_t rgb565)
+    {
+        RGB565ToYUV(rgb565, &Y, &U, &V);
+    }
+
+    inline explicit YUVPixel(uint32_t rgb32)
+    {
+        RGB32ToYUV(rgb32, &Y, &U, &V);
+    }
+
+    inline void get(uint8_t* pY, uint8_t* pU, uint8_t* pV) const
+    {
+        *pY = Y; *pU = U; *pV = V;
+    }
+};
+
+/* Converts an YV12 framebuffer to RGB565 framebuffer.
+ * Param:
+ *  yv12 - YV12 framebuffer.
+ *  rgb - RGB565 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void YV12ToRGB565(const void* yv12, void* rgb, int width, int height);
+
+/* Converts an YV12 framebuffer to RGB32 framebuffer.
+ * Param:
+ *  yv12 - YV12 framebuffer.
+ *  rgb - RGB32 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void YV12ToRGB32(const void* yv12, void* rgb, int width, int height);
+
+/* Converts an YU12 framebuffer to RGB32 framebuffer.
+ * Param:
+ *  yu12 - YU12 framebuffer.
+ *  rgb - RGB32 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void YU12ToRGB32(const void* yu12, void* rgb, int width, int height);
+
+/* Converts an NV12 framebuffer to RGB565 framebuffer.
+ * Param:
+ *  nv12 - NV12 framebuffer.
+ *  rgb - RGB565 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void NV12ToRGB565(const void* nv12, void* rgb, int width, int height);
+
+/* Converts an NV12 framebuffer to RGB32 framebuffer.
+ * Param:
+ *  nv12 - NV12 framebuffer.
+ *  rgb - RGB32 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void NV12ToRGB32(const void* nv12, void* rgb, int width, int height);
+
+/* Converts an NV21 framebuffer to RGB565 framebuffer.
+ * Param:
+ *  nv21 - NV21 framebuffer.
+ *  rgb - RGB565 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void NV21ToRGB565(const void* nv21, void* rgb, int width, int height);
+
+/* Converts an NV21 framebuffer to RGB32 framebuffer.
+ * Param:
+ *  nv21 - NV21 framebuffer.
+ *  rgb - RGB32 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void NV21ToRGB32(const void* nv21, void* rgb, int width, int height);
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_CONVERTERS_H */
diff --git a/guest/hals/camera/EmulatedBaseCamera.cpp b/guest/hals/camera/EmulatedBaseCamera.cpp
new file mode 100644
index 0000000..2fbbc0b
--- /dev/null
+++ b/guest/hals/camera/EmulatedBaseCamera.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedBaseCamera that encapsulates
+ * functionality common to all emulated camera device versions ("fake",
+ * "webcam", "video file", "cam2.0" etc.).  Instances of this class (for each
+ * emulated camera) are created during the construction of the
+ * EmulatedCameraFactory instance.  This class serves as an entry point for all
+ * camera API calls that are common across all versions of the
+ * camera_device_t/camera_module_t structures.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_BaseCamera"
+#include <cutils/log.h>
+
+#include "EmulatedBaseCamera.h"
+
+namespace android {
+
+EmulatedBaseCamera::EmulatedBaseCamera(int cameraId,
+        uint32_t cameraVersion,
+        struct hw_device_t* device,
+        struct hw_module_t* module)
+        : mCameraInfo(NULL),
+          mCameraID(cameraId),
+          mCameraDeviceVersion(cameraVersion)
+{
+    /*
+     * Initialize camera_device descriptor for this object.
+     */
+
+    /* Common header */
+    device->tag = HARDWARE_DEVICE_TAG;
+    device->version = cameraVersion;
+    device->module = module;
+    device->close = NULL; // Must be filled in by child implementation
+}
+
+EmulatedBaseCamera::~EmulatedBaseCamera()
+{
+}
+
+status_t EmulatedBaseCamera::getCameraInfo(struct camera_info* info)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    info->device_version = mCameraDeviceVersion;
+#if VSOC_PLATFORM_SDK_BEFORE(O)
+    // static_camera_characteristics should be initialized if and only if two
+    // conditions hold:
+    //    CAMERA_MODULE_API_VERSION_2_0 or higher
+    //    CAMERA_DEVICE_API_VERSION_2_0 or higher
+    // See android/hardware/libhardware/include/hardware/camera_common.h
+    //
+    // The CAMERA_MODULE_API_VERSION is above 2 on all of the supported
+    // branches.
+    //
+    // The CVD supports both CAMERA_DEVICE_API_VERSION_1_0 and
+    // CAMERA_DEVICE_API_VERSION_3_0.
+    //
+    // By the spec, the framework should not look at this field on
+    // CAMERA_DEVICE_API_VERSION_1_0. However, the framework
+    // referenced them unconditionally in the M, N, and N-MR1 branches.
+    // See b/67841929 for evidence.
+    //
+    // We have to support those branches, so make initialization uconditional.
+    // However, keep the 0xcafef00d fake initiziation on O and later to ensure
+    // that we'll catch future framework changes that violate the spec.
+    info->static_camera_characteristics = mCameraInfo;
+#else
+    if (mCameraDeviceVersion >= HARDWARE_DEVICE_API_VERSION(2, 0)) {
+        info->static_camera_characteristics = mCameraInfo;
+    } else {
+        info->static_camera_characteristics = (camera_metadata_t*)0xcafef00d;
+    }
+#endif
+
+    return NO_ERROR;
+}
+
+status_t EmulatedBaseCamera::plugCamera() {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+status_t EmulatedBaseCamera::unplugCamera() {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+camera_device_status_t EmulatedBaseCamera::getHotplugStatus() {
+    return CAMERA_DEVICE_STATUS_PRESENT;
+}
+#endif
+
+status_t EmulatedBaseCamera::setTorchMode(bool /* enabled */) {
+    ALOGE("%s: not supported", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+
+} /* namespace android */
diff --git a/guest/hals/camera/EmulatedBaseCamera.h b/guest/hals/camera/EmulatedBaseCamera.h
new file mode 100644
index 0000000..ab64b45
--- /dev/null
+++ b/guest/hals/camera/EmulatedBaseCamera.h
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_BASE_CAMERA_H
+#define HW_EMULATOR_CAMERA_EMULATED_BASE_CAMERA_H
+
+#include <hardware/camera_common.h>
+#include <utils/Errors.h>
+#include "guest/libs/platform_support/api_level_fixes.h"
+#include "CameraConfiguration.h"
+#include "ImageMetadata.h"
+
+namespace android {
+
+/*
+ * Contains declaration of a class EmulatedBaseCamera that encapsulates
+ * functionality common to all emulated camera device versions ("fake",
+ * "webcam", "video file", etc.).  Instances of this class (for each emulated
+ * camera) are created during the construction of the EmulatedCameraFactory
+ * instance.  This class serves as an entry point for all camera API calls that
+ * are common across all versions of the camera_device_t/camera_module_t
+ * structures.
+ */
+
+class EmulatedBaseCamera {
+  public:
+    EmulatedBaseCamera(int cameraId,
+            uint32_t cameraVersion,
+            struct hw_device_t* device,
+            struct hw_module_t* module);
+
+    virtual ~EmulatedBaseCamera();
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+  public:
+    /* Initializes EmulatedCamera instance.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    virtual status_t Initialize(const cvd::CameraDefinition& params) = 0;
+
+    /****************************************************************************
+     * Camera API implementation
+     ***************************************************************************/
+
+  public:
+    /* Creates connection to the emulated camera device.
+     * This method is called in response to hw_module_methods_t::open callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negative EXXX statuses.
+     */
+    virtual status_t connectCamera(hw_device_t** device) = 0;
+
+
+    /* Plug the connection for the emulated camera. Until it's plugged in
+     * calls to connectCamera should fail with -ENODEV.
+     */
+    virtual status_t plugCamera();
+
+    /* Unplug the connection from underneath the emulated camera.
+     * This is similar to closing the camera, except that
+     * all function calls into the camera device will return
+     * -EPIPE errors until the camera is reopened.
+     */
+    virtual status_t unplugCamera();
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+    virtual camera_device_status_t getHotplugStatus();
+#endif
+
+    /* Closes connection to the emulated camera.
+     * This method is called in response to camera_device::close callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negative EXXX statuses.
+     */
+    virtual status_t closeCamera() = 0;
+
+    /* Gets camera information.
+     * This method is called in response to camera_module_t::get_camera_info
+     * callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negative EXXX statuses.
+     */
+    virtual status_t getCameraInfo(struct camera_info* info) = 0;
+
+    /* Gets image metadata.
+     * This method is called to collect metadata for (currently) taken picture.
+     */
+    virtual status_t getImageMetadata(struct ImageMetadata* meta) = 0;
+
+    /* Set torch mode.
+     * This method is called in response to camera_module_t::set_torch_mode
+     * callback.
+     */
+    virtual status_t setTorchMode(bool enabled);
+
+    /****************************************************************************
+     * Data members
+     ***************************************************************************/
+
+  protected:
+    /* Fixed camera information for camera2 devices. Must be valid to access if
+     * mCameraDeviceVersion is >= HARDWARE_DEVICE_API_VERSION(2,0)  */
+    camera_metadata_t *mCameraInfo;
+
+    /* Zero-based ID assigned to this camera. */
+    int mCameraID;
+
+  private:
+
+    /* Version of the camera device HAL implemented by this camera */
+    int mCameraDeviceVersion;
+};
+
+} /* namespace android */
+
+#endif /* HW_EMULATOR_CAMERA_EMULATED_BASE_CAMERA_H */
diff --git a/guest/hals/camera/EmulatedCamera.cpp b/guest/hals/camera/EmulatedCamera.cpp
new file mode 100755
index 0000000..dd0a4cb
--- /dev/null
+++ b/guest/hals/camera/EmulatedCamera.cpp
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedCamera that encapsulates
+ * functionality common to all emulated cameras ("fake", "webcam", "video file",
+ * etc.). Instances of this class (for each emulated camera) are created during
+ * the construction of the EmulatedCameraFactory instance. This class serves as
+ * an entry point for all camera API calls that defined by camera_device_ops_t
+ * API.
+ */
+
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Camera"
+#include <cutils/log.h>
+#include "EmulatedCamera.h"
+//#include "EmulatedFakeCameraDevice.h"
+#include "Converters.h"
+
+/* Defines whether we should trace parameter changes. */
+#define DEBUG_PARAM 1
+
+namespace android {
+namespace {
+const char* kSupportedFlashModes[] = {
+  CameraParameters::FLASH_MODE_OFF,
+  CameraParameters::FLASH_MODE_AUTO,
+  CameraParameters::FLASH_MODE_ON,
+  CameraParameters::FLASH_MODE_RED_EYE,
+  CameraParameters::FLASH_MODE_TORCH,
+  NULL
+};
+
+std::string BuildParameterValue(const char** value_array) {
+  std::string result;
+
+  for (int index = 0; value_array[index] != NULL; ++index) {
+    if (index) result.append(",");
+    result.append(value_array[index]);
+  }
+  return result;
+}
+
+bool CheckParameterValue(const char* value, const char** supported_values) {
+  for (int index = 0; supported_values[index] != NULL; ++index) {
+    if (!strcmp(value, supported_values[index])) return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+#if DEBUG_PARAM
+/* Calculates and logs parameter changes.
+ * Param:
+ *  current - Current set of camera parameters.
+ *  new_par - String representation of new parameters.
+ */
+static void PrintParamDiff(const CameraParameters& current, const char* new_par);
+#else
+#define PrintParamDiff(current, new_par)   (void(0))
+#endif  /* DEBUG_PARAM */
+
+/* A helper routine that adds a value to the camera parameter.
+ * Param:
+ *  param - Camera parameter to add a value to.
+ *  val - Value to add.
+ * Return:
+ *  A new string containing parameter with the added value on success, or NULL on
+ *  a failure. If non-NULL string is returned, the caller is responsible for
+ *  freeing it with 'free'.
+ */
+static char* AddValue(const char* param, const char* val);
+
+EmulatedCamera::EmulatedCamera(int cameraId,
+                               struct hw_module_t* module)
+        : EmulatedBaseCamera(cameraId,
+                HARDWARE_DEVICE_API_VERSION(1, 0),
+                &common,
+                module),
+          mPreviewWindow(),
+          mCallbackNotifier()
+{
+    /* camera_device v1 fields. */
+    common.close = EmulatedCamera::close;
+    ops = &mDeviceOps;
+    priv = this;
+}
+
+EmulatedCamera::~EmulatedCamera()
+{
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+status_t EmulatedCamera::Initialize(const cvd::CameraDefinition&)
+{
+    /* Preview formats supported by this HAL. */
+    char preview_formats[1024];
+    snprintf(preview_formats, sizeof(preview_formats), "%s,%s,%s",
+             CameraParameters::PIXEL_FORMAT_YUV420SP,
+             CameraParameters::PIXEL_FORMAT_YUV420P,
+             CameraParameters::PIXEL_FORMAT_RGBA8888);
+
+    /*
+     * Fake required parameters.
+     */
+
+    mParameters.set(CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES, "320x240,0x0");
+
+    mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, "512");
+    mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, "384");
+    mParameters.set(CameraParameters::KEY_JPEG_QUALITY, "90");
+    mParameters.set(CameraParameters::KEY_FOCAL_LENGTH, "4.31");
+    mParameters.set(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE, "54.8");
+    mParameters.set(CameraParameters::KEY_VERTICAL_VIEW_ANGLE, "42.5");
+    mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, "90");
+
+    /* Preview format settings used here are related to panoramic view only. It's
+     * not related to the preview window that works only with RGB frames, which
+     * is explicitly stated when set_buffers_geometry is called on the preview
+     * window object. */
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS,
+                    preview_formats);
+    mParameters.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP);
+
+    /* We don't relay on the actual frame rates supported by the camera device,
+     * since we will emulate them through timeouts in the emulated camera device
+     * worker thread. */
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES,
+                    "30,24,20,15,10,5");
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE,
+                    "(5000,30000),(15000,15000),(30000,30000)");
+    mParameters.set(CameraParameters::KEY_PREVIEW_FPS_RANGE,
+                    "5000,30000");
+    mParameters.setPreviewFrameRate(30000);
+
+    /* Only PIXEL_FORMAT_YUV420P is accepted by video framework in emulator! */
+    mParameters.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT,
+                    CameraParameters::PIXEL_FORMAT_YUV420P);
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS,
+                    CameraParameters::PIXEL_FORMAT_JPEG);
+    mParameters.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);
+
+    /* Set exposure compensation. */
+    mParameters.set(CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION, "6");
+    mParameters.set(CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION, "-6");
+    mParameters.set(CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP, "0.5");
+    mParameters.set(CameraParameters::KEY_EXPOSURE_COMPENSATION, "0");
+
+    /* Sets the white balance modes and the device-dependent scale factors. */
+    char supported_white_balance[1024];
+    snprintf(supported_white_balance, sizeof(supported_white_balance),
+             "%s,%s,%s,%s",
+             CameraParameters::WHITE_BALANCE_AUTO,
+             CameraParameters::WHITE_BALANCE_INCANDESCENT,
+             CameraParameters::WHITE_BALANCE_DAYLIGHT,
+             CameraParameters::WHITE_BALANCE_TWILIGHT);
+    mParameters.set(CameraParameters::KEY_SUPPORTED_WHITE_BALANCE,
+                    supported_white_balance);
+    mParameters.set(CameraParameters::KEY_WHITE_BALANCE,
+                    CameraParameters::WHITE_BALANCE_AUTO);
+    getCameraDevice()->initializeWhiteBalanceModes(
+            CameraParameters::WHITE_BALANCE_AUTO, 1.0f, 1.0f);
+    getCameraDevice()->initializeWhiteBalanceModes(
+            CameraParameters::WHITE_BALANCE_INCANDESCENT, 1.38f, 0.60f);
+    getCameraDevice()->initializeWhiteBalanceModes(
+            CameraParameters::WHITE_BALANCE_DAYLIGHT, 1.09f, 0.92f);
+    getCameraDevice()->initializeWhiteBalanceModes(
+            CameraParameters::WHITE_BALANCE_TWILIGHT, 0.92f, 1.22f);
+    getCameraDevice()->setWhiteBalanceMode(CameraParameters::WHITE_BALANCE_AUTO);
+
+    /*
+     * Not supported features
+     */
+    mParameters.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES,
+                    CameraParameters::FOCUS_MODE_FIXED);
+    mParameters.set(CameraParameters::KEY_FOCUS_MODE,
+                    CameraParameters::FOCUS_MODE_FIXED);
+    mParameters.set(CameraParameters::KEY_SUPPORTED_FLASH_MODES,
+                    BuildParameterValue(kSupportedFlashModes).c_str());
+    mParameters.set(CameraParameters::KEY_FLASH_MODE,
+                    CameraParameters::FLASH_MODE_OFF);
+    mParameters.set(CameraParameters::KEY_FOCUS_DISTANCES, "0.1,0.1,0.1");
+    mParameters.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW, "0");
+    mParameters.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW, "0");
+    mParameters.set(CameraParameters::KEY_ZOOM_RATIOS, "100");
+    mParameters.set(CameraParameters::KEY_ZOOM_SUPPORTED,
+                    CameraParameters::FALSE);
+    mParameters.set(CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED,
+                    CameraParameters::FALSE);
+    mParameters.set(CameraParameters::KEY_ZOOM, "0");
+    mParameters.set(CameraParameters::KEY_MAX_ZOOM, "0");
+
+    return NO_ERROR;
+}
+
+void EmulatedCamera::onNextFrameAvailable(const void* frame,
+                                          nsecs_t timestamp,
+                                          EmulatedCameraDevice* camera_dev)
+{
+    /* Notify the preview window first. */
+    mPreviewWindow.onNextFrameAvailable(frame, timestamp, camera_dev);
+
+    /* Notify callback notifier next. */
+    mCallbackNotifier.onNextFrameAvailable(frame, timestamp, camera_dev);
+}
+
+void EmulatedCamera::onCameraDeviceError(int err)
+{
+    /* Errors are reported through the callback notifier */
+    mCallbackNotifier.onCameraDeviceError(err);
+}
+
+void EmulatedCamera::onCameraFocusAcquired() {
+    mCallbackNotifier.onCameraFocusAcquired();
+}
+
+/****************************************************************************
+ * Camera API implementation.
+ ***************************************************************************/
+
+status_t EmulatedCamera::connectCamera(hw_device_t** device)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    status_t res = EINVAL;
+    EmulatedCameraDevice* const camera_dev = getCameraDevice();
+    ALOGE_IF(camera_dev == NULL, "%s: No camera device instance.", __FUNCTION__);
+
+    if (camera_dev != NULL) {
+        /* Connect to the camera device. */
+        res = getCameraDevice()->connectDevice();
+        if (res == NO_ERROR) {
+            *device = &common;
+        }
+    }
+
+    return -res;
+}
+
+status_t EmulatedCamera::closeCamera()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    return cleanupCamera();
+}
+
+status_t EmulatedCamera::getCameraInfo(struct camera_info* info)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    const char* valstr = NULL;
+
+    valstr = mParameters.get(EmulatedCamera::FACING_KEY);
+    if (valstr != NULL) {
+        if (strcmp(valstr, EmulatedCamera::FACING_FRONT) == 0) {
+            info->facing = CAMERA_FACING_FRONT;
+        }
+        else if (strcmp(valstr, EmulatedCamera::FACING_BACK) == 0) {
+            info->facing = CAMERA_FACING_BACK;
+        }
+    } else {
+        info->facing = CAMERA_FACING_BACK;
+    }
+
+    valstr = mParameters.get(EmulatedCamera::ORIENTATION_KEY);
+    if (valstr != NULL) {
+        info->orientation = atoi(valstr);
+    } else {
+        info->orientation = 0;
+    }
+
+#if VSOC_PLATFORM_SDK_AFTER(L_MR1)
+    info->resource_cost = 100;
+    info->conflicting_devices = NULL;
+    info->conflicting_devices_length = 0;
+#endif
+
+    return EmulatedBaseCamera::getCameraInfo(info);
+}
+
+status_t EmulatedCamera::getImageMetadata(struct ImageMetadata* meta) {
+    meta->mLensFocalLength =
+        mParameters.getFloat(CameraParameters::KEY_FOCAL_LENGTH);
+    meta->mGpsLatitude =
+        mParameters.getFloat(CameraParameters::KEY_GPS_LATITUDE);
+    meta->mGpsLongitude =
+        mParameters.getFloat(CameraParameters::KEY_GPS_LONGITUDE);
+    meta->mGpsAltitude =
+        mParameters.getFloat(CameraParameters::KEY_GPS_ALTITUDE);
+    meta->mGpsTimestamp =
+        mParameters.getInt(CameraParameters::KEY_GPS_TIMESTAMP);
+    meta->mThumbnailWidth =
+        mParameters.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH);
+    meta->mThumbnailHeight =
+        mParameters.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT);
+    const char* temp =
+        mParameters.get(CameraParameters::KEY_GPS_PROCESSING_METHOD);
+    if (temp) {
+      meta->mGpsProcessingMethod = temp;
+    }
+    return NO_ERROR;
+}
+
+status_t EmulatedCamera::setPreviewWindow(struct preview_stream_ops* window)
+{
+    /* Callback should return a negative errno. */
+    return -mPreviewWindow.setPreviewWindow(window,
+                                             mParameters.getPreviewFrameRate());
+}
+
+void EmulatedCamera::setCallbacks(camera_notify_callback notify_cb,
+                                  camera_data_callback data_cb,
+                                  camera_data_timestamp_callback data_cb_timestamp,
+                                  camera_request_memory get_memory,
+                                  void* user)
+{
+    mCallbackNotifier.setCallbacks(notify_cb, data_cb, data_cb_timestamp,
+                                    get_memory, user);
+}
+
+void EmulatedCamera::enableMsgType(int32_t msg_type)
+{
+    mCallbackNotifier.enableMessage(msg_type);
+}
+
+void EmulatedCamera::disableMsgType(int32_t msg_type)
+{
+    mCallbackNotifier.disableMessage(msg_type);
+}
+
+int EmulatedCamera::isMsgTypeEnabled(int32_t msg_type)
+{
+    return mCallbackNotifier.isMessageEnabled(msg_type);
+}
+
+status_t EmulatedCamera::startPreview()
+{
+    /* Callback should return a negative errno. */
+    return -doStartPreview();
+}
+
+void EmulatedCamera::stopPreview()
+{
+    doStopPreview();
+}
+
+int EmulatedCamera::isPreviewEnabled()
+{
+    return mPreviewWindow.isPreviewEnabled();
+}
+
+status_t EmulatedCamera::storeMetaDataInBuffers(int enable)
+{
+    /* Callback should return a negative errno. */
+    return -mCallbackNotifier.storeMetaDataInBuffers(enable);
+}
+
+status_t EmulatedCamera::startRecording()
+{
+    /* Callback should return a negative errno. */
+    return -mCallbackNotifier.enableVideoRecording(mParameters.getPreviewFrameRate());
+}
+
+void EmulatedCamera::stopRecording()
+{
+    mCallbackNotifier.disableVideoRecording();
+}
+
+int EmulatedCamera::isRecordingEnabled()
+{
+    return mCallbackNotifier.isVideoRecordingEnabled();
+}
+
+void EmulatedCamera::releaseRecordingFrame(const void* opaque)
+{
+    mCallbackNotifier.releaseRecordingFrame(opaque);
+}
+
+status_t EmulatedCamera::setAutoFocus()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    /* Trigger auto-focus. Focus response cannot be sent directly from here. */
+    getCameraDevice()->startAutoFocus();
+
+    /* TODO: Future enhancements. */
+    return NO_ERROR;
+}
+
+status_t EmulatedCamera::cancelAutoFocus()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    /* TODO: Future enhancements. */
+    return NO_ERROR;
+}
+
+status_t EmulatedCamera::takePicture()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    status_t res;
+    int width, height;
+    uint32_t org_fmt;
+
+    /* Collect frame info for the picture. */
+    mParameters.getPictureSize(&width, &height);
+    const char* pix_fmt = mParameters.getPictureFormat();
+    if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
+        org_fmt = V4L2_PIX_FMT_YUV420;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) {
+        org_fmt = V4L2_PIX_FMT_RGB32;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
+        org_fmt = V4L2_PIX_FMT_NV21;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_JPEG) == 0) {
+        /* We only have JPEG converted for NV21 format. */
+        org_fmt = V4L2_PIX_FMT_NV21;
+    } else {
+        ALOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt);
+        return EINVAL;
+    }
+    /* Get JPEG quality. */
+    int jpeg_quality = mParameters.getInt(CameraParameters::KEY_JPEG_QUALITY);
+    if (jpeg_quality <= 0) {
+        jpeg_quality = 90;  /* Fall back to default. */
+    }
+
+    /*
+     * Make sure preview is not running, and device is stopped before taking
+     * picture.
+     */
+
+    const bool preview_on = mPreviewWindow.isPreviewEnabled();
+    if (preview_on) {
+        doStopPreview();
+    }
+
+    /* Camera device should have been stopped when the shutter message has been
+     * enabled. */
+    EmulatedCameraDevice* const camera_dev = getCameraDevice();
+    if (camera_dev->isStarted()) {
+        ALOGW("%s: Camera device is started", __FUNCTION__);
+        camera_dev->stopDeliveringFrames();
+        camera_dev->stopDevice();
+    }
+
+    /* Compute target FPS rate.
+     * Pretend to simulate generation of (max_fps_rate) */
+    int min_fps_rate, max_fps_rate;
+    mParameters.getPreviewFpsRange(&min_fps_rate, &max_fps_rate);
+
+    /*
+     * Take the picture now.
+     */
+
+    /* Start camera device for the picture frame. */
+    ALOGD("Starting camera for picture: %.4s(%s)[%dx%d]",
+         reinterpret_cast<const char*>(&org_fmt), pix_fmt, width, height);
+    res = camera_dev->startDevice(width, height, org_fmt, max_fps_rate);
+    if (res != NO_ERROR) {
+        if (preview_on) {
+            doStartPreview();
+        }
+        return res;
+    }
+
+    /* Deliver one frame only. */
+    mCallbackNotifier.setJpegQuality(jpeg_quality);
+    mCallbackNotifier.setTakingPicture(true);
+    res = camera_dev->startDeliveringFrames(true);
+    if (res != NO_ERROR) {
+        mCallbackNotifier.setTakingPicture(false);
+        if (preview_on) {
+            doStartPreview();
+        }
+    }
+    return res;
+}
+
+status_t EmulatedCamera::cancelPicture()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    return NO_ERROR;
+}
+
+status_t EmulatedCamera::setParameters(const char* parms)
+{
+    ALOGV("%s", __FUNCTION__);
+    PrintParamDiff(mParameters, parms);
+
+    CameraParameters new_param;
+    String8 str8_param(parms);
+    new_param.unflatten(str8_param);
+
+    /*
+     * Check if requested dimensions are valid.
+     */
+    if (!CheckParameterValue(new_param.get(CameraParameters::KEY_FLASH_MODE),
+                             kSupportedFlashModes)) {
+      ALOGE("%s: Unsupported flash mode: %s",
+            __FUNCTION__,
+            new_param.get(CameraParameters::KEY_FLASH_MODE));
+      return -EINVAL;
+    }
+    if (strcmp(new_param.get(CameraParameters::KEY_FOCUS_MODE),
+               CameraParameters::FOCUS_MODE_FIXED)) {
+      ALOGE("%s: Unsupported flash mode: %s",
+            __FUNCTION__,
+            new_param.get(CameraParameters::KEY_FOCUS_MODE));
+      return -EINVAL;
+    }
+
+    int preview_width, preview_height;
+    new_param.getPreviewSize(&preview_width, &preview_height);
+    if (preview_width <= 0 || preview_height <= 0) return -EINVAL;
+
+
+    /*
+     * Check for new exposure compensation parameter.
+     */
+    int new_exposure_compensation = new_param.getInt(
+            CameraParameters::KEY_EXPOSURE_COMPENSATION);
+    const int min_exposure_compensation = new_param.getInt(
+            CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION);
+    const int max_exposure_compensation = new_param.getInt(
+            CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION);
+
+    // Checks if the exposure compensation change is supported.
+    if ((min_exposure_compensation != 0) || (max_exposure_compensation != 0)) {
+        if (new_exposure_compensation > max_exposure_compensation) {
+            new_exposure_compensation = max_exposure_compensation;
+        }
+        if (new_exposure_compensation < min_exposure_compensation) {
+            new_exposure_compensation = min_exposure_compensation;
+        }
+
+        const int current_exposure_compensation = mParameters.getInt(
+                CameraParameters::KEY_EXPOSURE_COMPENSATION);
+        if (current_exposure_compensation != new_exposure_compensation) {
+            const float exposure_value = new_exposure_compensation *
+                    new_param.getFloat(
+                            CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP);
+
+            getCameraDevice()->setExposureCompensation(
+                    exposure_value);
+        }
+    }
+
+    const char* new_white_balance = new_param.get(
+            CameraParameters::KEY_WHITE_BALANCE);
+    const char* supported_white_balance = new_param.get(
+            CameraParameters::KEY_SUPPORTED_WHITE_BALANCE);
+
+    if ((supported_white_balance != NULL) && (new_white_balance != NULL) &&
+        (strstr(supported_white_balance, new_white_balance) != NULL)) {
+
+        const char* current_white_balance = mParameters.get(
+                CameraParameters::KEY_WHITE_BALANCE);
+        if ((current_white_balance == NULL) ||
+            (strcmp(current_white_balance, new_white_balance) != 0)) {
+            ALOGV("Setting white balance to %s", new_white_balance);
+            getCameraDevice()->setWhiteBalanceMode(new_white_balance);
+        }
+    }
+
+    mParameters = new_param;
+
+    return NO_ERROR;
+}
+
+/* A dumb variable indicating "no params" / error on the exit from
+ * EmulatedCamera::getParameters(). */
+static char lNoParam = '\0';
+char* EmulatedCamera::getParameters()
+{
+    String8 params(mParameters.flatten());
+    char* ret_str =
+        reinterpret_cast<char*>(malloc(sizeof(char) * (params.length()+1)));
+    memset(ret_str, 0, params.length()+1);
+    if (ret_str != NULL) {
+        strncpy(ret_str, params.string(), params.length()+1);
+        return ret_str;
+    } else {
+        ALOGE("%s: Unable to allocate string for %s", __FUNCTION__, params.string());
+        /* Apparently, we can't return NULL fron this routine. */
+        return &lNoParam;
+    }
+}
+
+void EmulatedCamera::putParameters(char* params)
+{
+    /* This method simply frees parameters allocated in getParameters(). */
+    if (params != NULL && params != &lNoParam) {
+        free(params);
+    }
+}
+
+status_t EmulatedCamera::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
+{
+    ALOGV("%s: cmd = %d, arg1 = %d, arg2 = %d", __FUNCTION__, cmd, arg1, arg2);
+
+    switch (cmd) {
+      case CAMERA_CMD_START_FACE_DETECTION:
+      case CAMERA_CMD_STOP_FACE_DETECTION:
+        return -EINVAL;
+    }
+
+    /* TODO: Future enhancements. */
+    return 0;
+}
+
+void EmulatedCamera::releaseCamera()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    cleanupCamera();
+}
+
+status_t EmulatedCamera::dumpCamera(int fd)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    /* TODO: Future enhancements. */
+    return -EINVAL;
+}
+
+/****************************************************************************
+ * Preview management.
+ ***************************************************************************/
+
+status_t EmulatedCamera::doStartPreview()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    EmulatedCameraDevice* camera_dev = getCameraDevice();
+    if (camera_dev->isStarted()) {
+        camera_dev->stopDeliveringFrames();
+        camera_dev->stopDevice();
+    }
+
+    status_t res = mPreviewWindow.startPreview();
+    if (res != NO_ERROR) {
+        return res;
+    }
+
+    /* Make sure camera device is connected. */
+    if (!camera_dev->isConnected()) {
+        res = camera_dev->connectDevice();
+        if (res != NO_ERROR) {
+            mPreviewWindow.stopPreview();
+            return res;
+        }
+    }
+
+    int width, height;
+    /* Lets see what should we use for frame width, and height. */
+    if (mParameters.get(CameraParameters::KEY_VIDEO_SIZE) != NULL) {
+        mParameters.getVideoSize(&width, &height);
+    } else {
+        mParameters.getPreviewSize(&width, &height);
+    }
+    /* Lets see what should we use for the frame pixel format. Note that there
+     * are two parameters that define pixel formats for frames sent to the
+     * application via notification callbacks:
+     * - KEY_VIDEO_FRAME_FORMAT, that is used when recording video, and
+     * - KEY_PREVIEW_FORMAT, that is used for preview frame notification.
+     * We choose one or the other, depending on "recording-hint" property set by
+     * the framework that indicating intention: video, or preview. */
+    const char* pix_fmt = NULL;
+    const char* is_video = mParameters.get(EmulatedCamera::RECORDING_HINT_KEY);
+    if (is_video == NULL) {
+        is_video = CameraParameters::FALSE;
+    }
+    if (strcmp(is_video, CameraParameters::TRUE) == 0) {
+        /* Video recording is requested. Lets see if video frame format is set. */
+        pix_fmt = mParameters.get(CameraParameters::KEY_VIDEO_FRAME_FORMAT);
+    }
+    /* If this was not video recording, or video frame format is not set, lets
+     * use preview pixel format for the main framebuffer. */
+    if (pix_fmt == NULL) {
+        pix_fmt = mParameters.getPreviewFormat();
+    }
+    if (pix_fmt == NULL) {
+        ALOGE("%s: Unable to obtain video format", __FUNCTION__);
+        mPreviewWindow.stopPreview();
+        return EINVAL;
+    }
+
+    /* Convert framework's pixel format to the FOURCC one. */
+    uint32_t org_fmt;
+    if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
+        org_fmt = V4L2_PIX_FMT_YUV420;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) {
+        org_fmt = V4L2_PIX_FMT_RGB32;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
+        org_fmt = V4L2_PIX_FMT_NV21;
+    } else {
+        ALOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt);
+        mPreviewWindow.stopPreview();
+        return EINVAL;
+    }
+
+    /* Fetch the desired frame rate. */
+    int min_fps_rate, max_fps_rate;
+    mParameters.getPreviewFpsRange(&min_fps_rate, &max_fps_rate);
+
+    ALOGD("Starting camera: %dx%d -> %.4s(%s)",
+         width, height, reinterpret_cast<const char*>(&org_fmt), pix_fmt);
+    res = camera_dev->startDevice(width, height, org_fmt, max_fps_rate);
+    if (res != NO_ERROR) {
+        mPreviewWindow.stopPreview();
+        return res;
+    }
+
+    res = camera_dev->startDeliveringFrames(false);
+    if (res != NO_ERROR) {
+        camera_dev->stopDevice();
+        mPreviewWindow.stopPreview();
+    }
+
+    return res;
+}
+
+status_t EmulatedCamera::doStopPreview()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    status_t res = NO_ERROR;
+    if (mPreviewWindow.isPreviewEnabled()) {
+        /* Stop the camera. */
+        if (getCameraDevice()->isStarted()) {
+            getCameraDevice()->stopDeliveringFrames();
+            res = getCameraDevice()->stopDevice();
+        }
+
+        if (res == NO_ERROR) {
+            /* Disable preview as well. */
+            mPreviewWindow.stopPreview();
+        }
+    }
+
+    return NO_ERROR;
+}
+
+/****************************************************************************
+ * Private API.
+ ***************************************************************************/
+
+status_t EmulatedCamera::cleanupCamera()
+{
+    status_t res = NO_ERROR;
+
+    /* If preview is running - stop it. */
+    res = doStopPreview();
+    if (res != NO_ERROR) {
+        return -res;
+    }
+
+    /* Stop and disconnect the camera device. */
+    EmulatedCameraDevice* const camera_dev = getCameraDevice();
+    if (camera_dev != NULL) {
+        if (camera_dev->isStarted()) {
+            camera_dev->stopDeliveringFrames();
+            res = camera_dev->stopDevice();
+            if (res != NO_ERROR) {
+                return -res;
+            }
+        }
+        if (camera_dev->isConnected()) {
+            res = camera_dev->disconnectDevice();
+            if (res != NO_ERROR) {
+                return -res;
+            }
+        }
+    }
+
+    mCallbackNotifier.cleanupCBNotifier();
+
+    return NO_ERROR;
+}
+
+/****************************************************************************
+ * Camera API callbacks as defined by camera_device_ops structure.
+ *
+ * Callbacks here simply dispatch the calls to an appropriate method inside
+ * EmulatedCamera instance, defined by the 'dev' parameter.
+ ***************************************************************************/
+
+int EmulatedCamera::set_preview_window(struct camera_device* dev,
+                                       struct preview_stream_ops* window)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->setPreviewWindow(window);
+}
+
+void EmulatedCamera::set_callbacks(
+        struct camera_device* dev,
+        camera_notify_callback notify_cb,
+        camera_data_callback data_cb,
+        camera_data_timestamp_callback data_cb_timestamp,
+        camera_request_memory get_memory,
+        void* user)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return;
+    }
+    ec->setCallbacks(notify_cb, data_cb, data_cb_timestamp, get_memory, user);
+}
+
+void EmulatedCamera::enable_msg_type(struct camera_device* dev, int32_t msg_type)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return;
+    }
+    ec->enableMsgType(msg_type);
+}
+
+void EmulatedCamera::disable_msg_type(struct camera_device* dev, int32_t msg_type)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return;
+    }
+    ec->disableMsgType(msg_type);
+}
+
+int EmulatedCamera::msg_type_enabled(struct camera_device* dev, int32_t msg_type)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->isMsgTypeEnabled(msg_type);
+}
+
+int EmulatedCamera::start_preview(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->startPreview();
+}
+
+void EmulatedCamera::stop_preview(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return;
+    }
+    ec->stopPreview();
+}
+
+int EmulatedCamera::preview_enabled(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->isPreviewEnabled();
+}
+
+int EmulatedCamera::store_meta_data_in_buffers(struct camera_device* dev,
+                                               int enable)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->storeMetaDataInBuffers(enable);
+}
+
+int EmulatedCamera::start_recording(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->startRecording();
+}
+
+void EmulatedCamera::stop_recording(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return;
+    }
+    ec->stopRecording();
+}
+
+int EmulatedCamera::recording_enabled(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->isRecordingEnabled();
+}
+
+void EmulatedCamera::release_recording_frame(struct camera_device* dev,
+                                             const void* opaque)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return;
+    }
+    ec->releaseRecordingFrame(opaque);
+}
+
+int EmulatedCamera::auto_focus(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->setAutoFocus();
+}
+
+int EmulatedCamera::cancel_auto_focus(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->cancelAutoFocus();
+}
+
+int EmulatedCamera::take_picture(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->takePicture();
+}
+
+int EmulatedCamera::cancel_picture(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->cancelPicture();
+}
+
+int EmulatedCamera::set_parameters(struct camera_device* dev, const char* parms)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->setParameters(parms);
+}
+
+char* EmulatedCamera::get_parameters(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return NULL;
+    }
+    return ec->getParameters();
+}
+
+void EmulatedCamera::put_parameters(struct camera_device* dev, char* params)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return;
+    }
+    ec->putParameters(params);
+}
+
+int EmulatedCamera::send_command(struct camera_device* dev,
+                                 int32_t cmd,
+                                 int32_t arg1,
+                                 int32_t arg2)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->sendCommand(cmd, arg1, arg2);
+}
+
+void EmulatedCamera::release(struct camera_device* dev)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return;
+    }
+    ec->releaseCamera();
+}
+
+int EmulatedCamera::dump(struct camera_device* dev, int fd)
+{
+    EmulatedCamera* ec = reinterpret_cast<EmulatedCamera*>(dev->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->dumpCamera(fd);
+}
+
+int EmulatedCamera::close(struct hw_device_t* device)
+{
+    EmulatedCamera* ec =
+        reinterpret_cast<EmulatedCamera*>(reinterpret_cast<struct camera_device*>(device)->priv);
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->closeCamera();
+}
+
+/****************************************************************************
+ * Static initializer for the camera callback API
+ ****************************************************************************/
+
+camera_device_ops_t EmulatedCamera::mDeviceOps = {
+    EmulatedCamera::set_preview_window,
+    EmulatedCamera::set_callbacks,
+    EmulatedCamera::enable_msg_type,
+    EmulatedCamera::disable_msg_type,
+    EmulatedCamera::msg_type_enabled,
+    EmulatedCamera::start_preview,
+    EmulatedCamera::stop_preview,
+    EmulatedCamera::preview_enabled,
+    EmulatedCamera::store_meta_data_in_buffers,
+    EmulatedCamera::start_recording,
+    EmulatedCamera::stop_recording,
+    EmulatedCamera::recording_enabled,
+    EmulatedCamera::release_recording_frame,
+    EmulatedCamera::auto_focus,
+    EmulatedCamera::cancel_auto_focus,
+    EmulatedCamera::take_picture,
+    EmulatedCamera::cancel_picture,
+    EmulatedCamera::set_parameters,
+    EmulatedCamera::get_parameters,
+    EmulatedCamera::put_parameters,
+    EmulatedCamera::send_command,
+    EmulatedCamera::release,
+    EmulatedCamera::dump
+};
+
+/****************************************************************************
+ * Common keys
+ ***************************************************************************/
+
+const char EmulatedCamera::FACING_KEY[]         = "prop-facing";
+const char EmulatedCamera::ORIENTATION_KEY[]    = "prop-orientation";
+const char EmulatedCamera::RECORDING_HINT_KEY[] = "recording-hint";
+
+/****************************************************************************
+ * Common string values
+ ***************************************************************************/
+
+const char EmulatedCamera::FACING_BACK[]      = "back";
+const char EmulatedCamera::FACING_FRONT[]     = "front";
+
+/****************************************************************************
+ * Helper routines
+ ***************************************************************************/
+
+static char* AddValue(const char* param, const char* val)
+{
+    const size_t len1 = strlen(param);
+    const size_t len2 = strlen(val);
+    char* ret = reinterpret_cast<char*>(malloc(len1 + len2 + 2));
+    ALOGE_IF(ret == NULL, "%s: Memory failure", __FUNCTION__);
+    if (ret != NULL) {
+        memcpy(ret, param, len1);
+        ret[len1] = ',';
+        memcpy(ret + len1 + 1, val, len2);
+        ret[len1 + len2 + 1] = '\0';
+    }
+    return ret;
+}
+
+/****************************************************************************
+ * Parameter debugging helpers
+ ***************************************************************************/
+
+#if DEBUG_PARAM
+static void PrintParamDiff(const CameraParameters& current,
+                            const char* new_par)
+{
+    char tmp[2048];
+    const char* wrk = new_par;
+
+    /* Divided with ';' */
+    const char* next = strchr(wrk, ';');
+    while (next != NULL) {
+        snprintf(tmp, sizeof(tmp), "%.*s", (int)(intptr_t)(next-wrk), wrk);
+        /* in the form key=value */
+        char* val = strchr(tmp, '=');
+        if (val != NULL) {
+            *val = '\0'; val++;
+            const char* in_current = current.get(tmp);
+            if (in_current != NULL) {
+                if (strcmp(in_current, val)) {
+                    ALOGD("=== Value changed: %s: %s -> %s", tmp, in_current, val);
+                }
+            } else {
+                ALOGD("+++ New parameter: %s=%s", tmp, val);
+            }
+        } else {
+            ALOGW("No value separator in %s", tmp);
+        }
+        wrk = next + 1;
+        next = strchr(wrk, ';');
+    }
+}
+#endif  /* DEBUG_PARAM */
+
+}; /* namespace android */
diff --git a/guest/hals/camera/EmulatedCamera.h b/guest/hals/camera/EmulatedCamera.h
new file mode 100755
index 0000000..9976250
--- /dev/null
+++ b/guest/hals/camera/EmulatedCamera.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_EMULATED_CAMERA_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_H
+
+/*
+ * Contains declaration of a class EmulatedCamera that encapsulates
+ * functionality common to all version 1.0 emulated camera devices ("fake",
+ * "webcam", "video file", etc.).  Instances of this class (for each emulated
+ * camera) are created during the construction of the EmulatedCameraFactory
+ * instance.  This class serves as an entry point for all camera API calls that
+ * defined by camera_device_ops_t API.
+ */
+
+#include <camera/CameraParameters.h>
+#include "EmulatedBaseCamera.h"
+#include "EmulatedCameraDevice.h"
+#include "PreviewWindow.h"
+#include "CallbackNotifier.h"
+
+namespace android {
+
+/* Encapsulates functionality common to all version 1.0 emulated camera devices
+ * ("fake", "webcam", "file stream", etc.).
+ *
+ * Note that EmulatedCameraFactory instantiates object of this class just once,
+ * when EmulatedCameraFactory instance gets constructed. Connection to /
+ * disconnection from the actual camera device is handled by calls to
+ * connectDevice(), and closeCamera() methods of this class that are ivoked in
+ * response to hw_module_methods_t::open, and camera_device::close callbacks.
+ */
+class EmulatedCamera : public camera_device, public EmulatedBaseCamera {
+public:
+    /* Constructs EmulatedCamera instance.
+     * Param:
+     *  cameraId - Zero based camera identifier, which is an index of the camera
+     *      instance in camera factory's array.
+     *  module - Emulated camera HAL module descriptor.
+     */
+    EmulatedCamera(int cameraId,
+                   struct hw_module_t* module);
+
+    /* Destructs EmulatedCamera instance. */
+    virtual ~EmulatedCamera();
+
+    /****************************************************************************
+     * Abstract API
+     ***************************************************************************/
+
+public:
+    /* Gets emulated camera device used by this instance of the emulated camera.
+     */
+    virtual EmulatedCameraDevice* getCameraDevice() = 0;
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+public:
+    /** Override of base class method */
+    virtual status_t Initialize(const cvd::CameraDefinition& properties);
+
+    /* Next frame is available in the camera device.
+     * This is a notification callback that is invoked by the camera device when
+     * a new frame is available.
+     * Note that most likely this method is called in context of a worker thread
+     * that camera device has created for frame capturing.
+     * Param:
+     *  frame - Captured frame, or NULL if camera device didn't pull the frame
+     *      yet. If NULL is passed in this parameter use GetCurrentFrame method
+     *      of the camera device class to obtain the next frame. Also note that
+     *      the size of the frame that is passed here (as well as the frame
+     *      returned from the GetCurrentFrame method) is defined by the current
+     *      frame settings (width + height + pixel format) for the camera device.
+     * timestamp - Frame's timestamp.
+     * camera_dev - Camera device instance that delivered the frame.
+     */
+    virtual void onNextFrameAvailable(const void* frame,
+                                      nsecs_t timestamp,
+                                      EmulatedCameraDevice* camera_dev);
+
+    /* Entry point for notifications that occur in camera device.
+     * Param:
+     *  err - CAMERA_ERROR_XXX error code.
+     */
+    virtual void onCameraDeviceError(int err);
+
+    /* Device acquired focus.
+     * This is a notification callback that is invoked by the camera device
+     * when focusing operation (requested by client) completes.
+     */
+    virtual void onCameraFocusAcquired();
+
+    /****************************************************************************
+     * Camera API implementation
+     ***************************************************************************/
+
+public:
+    /** Override of base class method */
+    virtual status_t connectCamera(hw_device_t** device);
+
+    /** Override of base class method */
+    virtual status_t closeCamera();
+
+    /** Override of base class method */
+    virtual status_t getCameraInfo(struct camera_info* info);
+
+    /** Override of base class method */
+    virtual status_t getImageMetadata(struct ImageMetadata* meta);
+
+    /****************************************************************************
+     * Camera API implementation.
+     * These methods are called from the camera API callback routines.
+     ***************************************************************************/
+
+protected:
+    /* Actual handler for camera_device_ops_t::set_preview_window callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t setPreviewWindow(struct preview_stream_ops *window);
+
+    /* Actual handler for camera_device_ops_t::set_callbacks callback.
+     * NOTE: When this method is called the object is locked.
+     */
+    virtual void setCallbacks(camera_notify_callback notify_cb,
+                              camera_data_callback data_cb,
+                              camera_data_timestamp_callback data_cb_timestamp,
+                              camera_request_memory get_memory,
+                              void* user);
+
+    /* Actual handler for camera_device_ops_t::enable_msg_type callback.
+     * NOTE: When this method is called the object is locked.
+     */
+    virtual void enableMsgType(int32_t msg_type);
+
+    /* Actual handler for camera_device_ops_t::disable_msg_type callback.
+     * NOTE: When this method is called the object is locked.
+     */
+    virtual void disableMsgType(int32_t msg_type);
+
+    /* Actual handler for camera_device_ops_t::msg_type_enabled callback.
+     * NOTE: When this method is called the object is locked.
+     * Return:
+     *  0 if message(s) is (are) disabled, != 0 if enabled.
+     */
+    virtual int isMsgTypeEnabled(int32_t msg_type);
+
+    /* Actual handler for camera_device_ops_t::start_preview callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t startPreview();
+
+    /* Actual handler for camera_device_ops_t::stop_preview callback.
+     * NOTE: When this method is called the object is locked.
+     */
+    virtual void stopPreview();
+
+    /* Actual handler for camera_device_ops_t::preview_enabled callback.
+     * NOTE: When this method is called the object is locked.
+     * Return:
+     *  0 if preview is disabled, != 0 if enabled.
+     */
+    virtual int isPreviewEnabled();
+
+    /* Actual handler for camera_device_ops_t::store_meta_data_in_buffers callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t storeMetaDataInBuffers(int enable);
+
+    /* Actual handler for camera_device_ops_t::start_recording callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t startRecording();
+
+    /* Actual handler for camera_device_ops_t::stop_recording callback.
+     * NOTE: When this method is called the object is locked.
+     */
+    virtual void stopRecording();
+
+    /* Actual handler for camera_device_ops_t::recording_enabled callback.
+     * NOTE: When this method is called the object is locked.
+     * Return:
+     *  0 if recording is disabled, != 0 if enabled.
+     */
+    virtual int isRecordingEnabled();
+
+    /* Actual handler for camera_device_ops_t::release_recording_frame callback.
+     * NOTE: When this method is called the object is locked.
+     */
+    virtual void releaseRecordingFrame(const void* opaque);
+
+    /* Actual handler for camera_device_ops_t::auto_focus callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t setAutoFocus();
+
+    /* Actual handler for camera_device_ops_t::cancel_auto_focus callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t cancelAutoFocus();
+
+    /* Actual handler for camera_device_ops_t::take_picture callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t takePicture();
+
+    /* Actual handler for camera_device_ops_t::cancel_picture callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t cancelPicture();
+
+    /* Actual handler for camera_device_ops_t::set_parameters callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t setParameters(const char* parms);
+
+    /* Actual handler for camera_device_ops_t::get_parameters callback.
+     * NOTE: When this method is called the object is locked.
+     * Return:
+     *  Flattened parameters string. The caller will free the buffer allocated
+     *  for the string by calling camera_device_ops_t::put_parameters callback.
+     */
+    virtual char* getParameters();
+
+    /* Actual handler for camera_device_ops_t::put_parameters callback.
+     * Called to free the string returned from camera_device_ops_t::get_parameters
+     * callback. There is nothing more to it: the name of the callback is just
+     * misleading.
+     * NOTE: When this method is called the object is locked.
+     */
+    virtual void putParameters(char* params);
+
+    /* Actual handler for camera_device_ops_t::send_command callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
+
+    /* Actual handler for camera_device_ops_t::release callback.
+     * NOTE: When this method is called the object is locked.
+     */
+    virtual void releaseCamera();
+
+    /* Actual handler for camera_device_ops_t::dump callback.
+     * NOTE: When this method is called the object is locked.
+     * Note that failures in this method are reported as negave EXXX statuses.
+     */
+    virtual status_t dumpCamera(int fd);
+
+    /****************************************************************************
+     * Preview management.
+     ***************************************************************************/
+
+protected:
+    /* Starts preview.
+     * Note that when this method is called mPreviewWindow may be NULL,
+     * indicating that framework has an intention to start displaying video
+     * frames, but didn't create the preview window yet.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    virtual status_t doStartPreview();
+
+    /* Stops preview.
+     * This method reverts DoStartPreview.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    virtual status_t doStopPreview();
+
+    /****************************************************************************
+     * Private API.
+     ***************************************************************************/
+
+protected:
+    /* Cleans up camera when released. */
+    virtual status_t cleanupCamera();
+
+    /****************************************************************************
+     * Camera API callbacks as defined by camera_device_ops structure.
+     * See hardware/libhardware/include/hardware/camera.h for information on
+     * each of these callbacks. Implemented in this class, these callbacks simply
+     * dispatch the call into an instance of EmulatedCamera class defined by the
+     * 'camera_device' parameter.
+     ***************************************************************************/
+
+private:
+    static int set_preview_window(struct camera_device* dev,
+                                   struct preview_stream_ops* window);
+
+    static void set_callbacks(struct camera_device* dev,
+                              camera_notify_callback notify_cb,
+                              camera_data_callback data_cb,
+                              camera_data_timestamp_callback data_cb_timestamp,
+                              camera_request_memory get_memory,
+                              void* user);
+
+    static void enable_msg_type(struct camera_device* dev, int32_t msg_type);
+
+    static void disable_msg_type(struct camera_device* dev, int32_t msg_type);
+
+    static int msg_type_enabled(struct camera_device* dev, int32_t msg_type);
+
+    static int start_preview(struct camera_device* dev);
+
+    static void stop_preview(struct camera_device* dev);
+
+    static int preview_enabled(struct camera_device* dev);
+
+    static int store_meta_data_in_buffers(struct camera_device* dev, int enable);
+
+    static int start_recording(struct camera_device* dev);
+
+    static void stop_recording(struct camera_device* dev);
+
+    static int recording_enabled(struct camera_device* dev);
+
+    static void release_recording_frame(struct camera_device* dev,
+                                        const void* opaque);
+
+    static int auto_focus(struct camera_device* dev);
+
+    static int cancel_auto_focus(struct camera_device* dev);
+
+    static int take_picture(struct camera_device* dev);
+
+    static int cancel_picture(struct camera_device* dev);
+
+    static int set_parameters(struct camera_device* dev, const char* parms);
+
+    static char* get_parameters(struct camera_device* dev);
+
+    static void put_parameters(struct camera_device* dev, char* params);
+
+    static int send_command(struct camera_device* dev,
+                            int32_t cmd,
+                            int32_t arg1,
+                            int32_t arg2);
+
+    static void release(struct camera_device* dev);
+
+    static int dump(struct camera_device* dev, int fd);
+
+    static int close(struct hw_device_t* device);
+
+    /****************************************************************************
+     * Data members
+     ***************************************************************************/
+
+protected:
+    /* Locks this instance for parameters, state, etc. change. */
+    Mutex                           mObjectLock;
+
+    /* Camera parameters. */
+    CameraParameters                mParameters;
+
+    /* Preview window. */
+    PreviewWindow                   mPreviewWindow;
+
+    /* Callback notifier. */
+    CallbackNotifier                mCallbackNotifier;
+
+private:
+    /* Registered callbacks implementing camera API. */
+    static camera_device_ops_t      mDeviceOps;
+
+    /****************************************************************************
+     * Common keys
+     ***************************************************************************/
+
+public:
+    static const char FACING_KEY[];
+    static const char ORIENTATION_KEY[];
+    static const char RECORDING_HINT_KEY[];
+
+     /****************************************************************************
+     * Common string values
+     ***************************************************************************/
+
+    /* Possible values for FACING_KEY */
+    static const char FACING_BACK[];
+    static const char FACING_FRONT[];
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_CAMERA_H */
diff --git a/guest/hals/camera/EmulatedCamera2.cpp b/guest/hals/camera/EmulatedCamera2.cpp
new file mode 100644
index 0000000..bc5e391
--- /dev/null
+++ b/guest/hals/camera/EmulatedCamera2.cpp
@@ -0,0 +1,410 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedCamera that encapsulates
+ * functionality common to all version 2.0 emulated camera devices.  Instances
+ * of this class (for each emulated camera) are created during the construction
+ * of the EmulatedCameraFactory instance.  This class serves as an entry point
+ * for all camera API calls that defined by camera2_device_ops_t API.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera2_Camera"
+#include <cutils/log.h>
+
+#include "EmulatedCamera2.h"
+#include "system/camera_metadata.h"
+
+namespace android {
+
+/* Constructs EmulatedCamera2 instance.
+ * Param:
+ *  cameraId - Zero based camera identifier, which is an index of the camera
+ *      instance in camera factory's array.
+ *  module - Emulated camera HAL module descriptor.
+ */
+EmulatedCamera2::EmulatedCamera2(int cameraId,
+        struct hw_module_t* module):
+        EmulatedBaseCamera(cameraId,
+                CAMERA_DEVICE_API_VERSION_2_0,
+                &common,
+                module)
+{
+    common.close = EmulatedCamera2::close;
+    ops = &sDeviceOps;
+    priv = this;
+
+    mNotifyCb = NULL;
+
+    mRequestQueueSrc = NULL;
+    mFrameQueueDst = NULL;
+
+    mVendorTagOps.get_camera_vendor_section_name =
+            EmulatedCamera2::get_camera_vendor_section_name;
+    mVendorTagOps.get_camera_vendor_tag_name =
+            EmulatedCamera2::get_camera_vendor_tag_name;
+    mVendorTagOps.get_camera_vendor_tag_type =
+            EmulatedCamera2::get_camera_vendor_tag_type;
+    mVendorTagOps.parent = this;
+
+    mStatusPresent = true;
+}
+
+/* Destructs EmulatedCamera2 instance. */
+EmulatedCamera2::~EmulatedCamera2() {
+}
+
+/****************************************************************************
+ * Abstract API
+ ***************************************************************************/
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+status_t EmulatedCamera2::Initialize(const cvd::CameraDefinition& props) {
+    return NO_ERROR;
+}
+
+/****************************************************************************
+ * Camera API implementation
+ ***************************************************************************/
+
+status_t EmulatedCamera2::connectCamera(hw_device_t** device) {
+    *device = &common;
+    return NO_ERROR;
+}
+
+status_t EmulatedCamera2::closeCamera() {
+    return NO_ERROR;
+}
+
+status_t EmulatedCamera2::getCameraInfo(struct camera_info* info) {
+    return EmulatedBaseCamera::getCameraInfo(info);
+}
+
+/****************************************************************************
+ * Camera Device API implementation.
+ * These methods are called from the camera API callback routines.
+ ***************************************************************************/
+
+/** Request input queue */
+
+int EmulatedCamera2::requestQueueNotify() {
+    return INVALID_OPERATION;
+}
+
+/** Count of requests in flight */
+int EmulatedCamera2::getInProgressCount() {
+    return INVALID_OPERATION;
+}
+
+/** Cancel all captures in flight */
+int EmulatedCamera2::flushCapturesInProgress() {
+    return INVALID_OPERATION;
+}
+
+/** Construct a default request for a given use case */
+int EmulatedCamera2::constructDefaultRequest(
+        int request_template,
+        camera_metadata_t **request) {
+    return INVALID_OPERATION;
+}
+
+/** Output stream creation and management */
+
+int EmulatedCamera2::allocateStream(
+        uint32_t width,
+        uint32_t height,
+        int format,
+        const camera2_stream_ops_t *stream_ops,
+        uint32_t *stream_id,
+        uint32_t *format_actual,
+        uint32_t *usage,
+        uint32_t *max_buffers) {
+    return INVALID_OPERATION;
+}
+
+int EmulatedCamera2::registerStreamBuffers(
+        uint32_t stream_id,
+        int num_buffers,
+        buffer_handle_t *buffers) {
+    return INVALID_OPERATION;
+}
+
+
+int EmulatedCamera2::releaseStream(uint32_t stream_id) {
+    return INVALID_OPERATION;
+}
+
+/** Reprocessing input stream management */
+
+int EmulatedCamera2::allocateReprocessStream(
+        uint32_t width,
+        uint32_t height,
+        uint32_t format,
+        const camera2_stream_in_ops_t *reprocess_stream_ops,
+        uint32_t *stream_id,
+        uint32_t *consumer_usage,
+        uint32_t *max_buffers) {
+    return INVALID_OPERATION;
+}
+
+int EmulatedCamera2::allocateReprocessStreamFromStream(
+        uint32_t output_stream_id,
+        const camera2_stream_in_ops_t *reprocess_stream_ops,
+        uint32_t *stream_id) {
+    return INVALID_OPERATION;
+}
+
+int EmulatedCamera2::releaseReprocessStream(uint32_t stream_id) {
+    return INVALID_OPERATION;
+}
+
+/** 3A triggering */
+
+int EmulatedCamera2::triggerAction(uint32_t trigger_id,
+                                   int ext1, int ext2) {
+    return INVALID_OPERATION;
+}
+
+/** Custom tag query methods */
+
+const char* EmulatedCamera2::getVendorSectionName(uint32_t tag) {
+    return NULL;
+}
+
+const char* EmulatedCamera2::getVendorTagName(uint32_t tag) {
+    return NULL;
+}
+
+int EmulatedCamera2::getVendorTagType(uint32_t tag) {
+    return -1;
+}
+
+/** Debug methods */
+
+int EmulatedCamera2::dump(int fd) {
+    return INVALID_OPERATION;
+}
+
+/****************************************************************************
+ * Private API.
+ ***************************************************************************/
+
+/****************************************************************************
+ * Camera API callbacks as defined by camera2_device_ops structure.  See
+ * hardware/libhardware/include/hardware/camera2.h for information on each
+ * of these callbacks. Implemented in this class, these callbacks simply
+ * dispatch the call into an instance of EmulatedCamera2 class defined by the
+ * 'camera_device2' parameter, or set a member value in the same.
+ ***************************************************************************/
+
+EmulatedCamera2* getInstance(const camera2_device_t *d) {
+    const EmulatedCamera2* cec = static_cast<const EmulatedCamera2*>(d);
+    return const_cast<EmulatedCamera2*>(cec);
+}
+
+int EmulatedCamera2::set_request_queue_src_ops(const camera2_device_t *d,
+        const camera2_request_queue_src_ops *queue_src_ops) {
+    EmulatedCamera2* ec = getInstance(d);
+    ec->mRequestQueueSrc = queue_src_ops;
+    return NO_ERROR;
+}
+
+int EmulatedCamera2::notify_request_queue_not_empty(const camera2_device_t *d) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->requestQueueNotify();
+}
+
+int EmulatedCamera2::set_frame_queue_dst_ops(const camera2_device_t *d,
+        const camera2_frame_queue_dst_ops *queue_dst_ops) {
+    EmulatedCamera2* ec = getInstance(d);
+    ec->mFrameQueueDst = queue_dst_ops;
+    return NO_ERROR;
+}
+
+int EmulatedCamera2::get_in_progress_count(const camera2_device_t *d) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->getInProgressCount();
+}
+
+int EmulatedCamera2::flush_captures_in_progress(const camera2_device_t *d) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->flushCapturesInProgress();
+}
+
+int EmulatedCamera2::construct_default_request(const camera2_device_t *d,
+        int request_template,
+        camera_metadata_t **request) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->constructDefaultRequest(request_template, request);
+}
+
+int EmulatedCamera2::allocate_stream(const camera2_device_t *d,
+        uint32_t width,
+        uint32_t height,
+        int format,
+        const camera2_stream_ops_t *stream_ops,
+        uint32_t *stream_id,
+        uint32_t *format_actual,
+        uint32_t *usage,
+        uint32_t *max_buffers) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->allocateStream(width, height, format, stream_ops,
+            stream_id, format_actual, usage, max_buffers);
+}
+
+int EmulatedCamera2::register_stream_buffers(const camera2_device_t *d,
+        uint32_t stream_id,
+        int num_buffers,
+        buffer_handle_t *buffers) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->registerStreamBuffers(stream_id,
+            num_buffers,
+            buffers);
+}
+int EmulatedCamera2::release_stream(const camera2_device_t *d,
+        uint32_t stream_id) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->releaseStream(stream_id);
+}
+
+int EmulatedCamera2::allocate_reprocess_stream(const camera2_device_t *d,
+        uint32_t width,
+        uint32_t height,
+        uint32_t format,
+        const camera2_stream_in_ops_t *reprocess_stream_ops,
+        uint32_t *stream_id,
+        uint32_t *consumer_usage,
+        uint32_t *max_buffers) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->allocateReprocessStream(width, height, format,
+            reprocess_stream_ops, stream_id, consumer_usage, max_buffers);
+}
+
+int EmulatedCamera2::allocate_reprocess_stream_from_stream(
+            const camera2_device_t *d,
+            uint32_t output_stream_id,
+            const camera2_stream_in_ops_t *reprocess_stream_ops,
+            uint32_t *stream_id) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->allocateReprocessStreamFromStream(output_stream_id,
+            reprocess_stream_ops, stream_id);
+}
+
+
+int EmulatedCamera2::release_reprocess_stream(const camera2_device_t *d,
+        uint32_t stream_id) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->releaseReprocessStream(stream_id);
+}
+
+int EmulatedCamera2::trigger_action(const camera2_device_t *d,
+        uint32_t trigger_id,
+        int ext1,
+        int ext2) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->triggerAction(trigger_id, ext1, ext2);
+}
+
+int EmulatedCamera2::set_notify_callback(const camera2_device_t *d,
+        camera2_notify_callback notify_cb, void* user) {
+    EmulatedCamera2* ec = getInstance(d);
+    Mutex::Autolock l(ec->mMutex);
+    ec->mNotifyCb = notify_cb;
+    ec->mNotifyUserPtr = user;
+    return NO_ERROR;
+}
+
+int EmulatedCamera2::get_metadata_vendor_tag_ops(const camera2_device_t *d,
+        vendor_tag_query_ops_t **ops) {
+    EmulatedCamera2* ec = getInstance(d);
+    *ops = static_cast<vendor_tag_query_ops_t*>(
+            &ec->mVendorTagOps);
+    return NO_ERROR;
+}
+
+const char* EmulatedCamera2::get_camera_vendor_section_name(
+        const vendor_tag_query_ops_t *v,
+        uint32_t tag) {
+    EmulatedCamera2* ec = static_cast<const TagOps*>(v)->parent;
+    return ec->getVendorSectionName(tag);
+}
+
+const char* EmulatedCamera2::get_camera_vendor_tag_name(
+        const vendor_tag_query_ops_t *v,
+        uint32_t tag) {
+    EmulatedCamera2* ec = static_cast<const TagOps*>(v)->parent;
+    return ec->getVendorTagName(tag);
+}
+
+int EmulatedCamera2::get_camera_vendor_tag_type(
+        const vendor_tag_query_ops_t *v,
+        uint32_t tag)  {
+    EmulatedCamera2* ec = static_cast<const TagOps*>(v)->parent;
+    return ec->getVendorTagType(tag);
+}
+
+int EmulatedCamera2::dump(const camera2_device_t *d, int fd) {
+    EmulatedCamera2* ec = getInstance(d);
+    return ec->dump(fd);
+}
+
+int EmulatedCamera2::close(struct hw_device_t* device) {
+    EmulatedCamera2* ec =
+            static_cast<EmulatedCamera2*>(
+                reinterpret_cast<camera2_device_t*>(device) );
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera2 device", __FUNCTION__);
+        return -EINVAL;
+    }
+    return ec->closeCamera();
+}
+
+void EmulatedCamera2::sendNotification(int32_t msgType,
+        int32_t ext1, int32_t ext2, int32_t ext3) {
+    camera2_notify_callback notifyCb;
+    {
+        Mutex::Autolock l(mMutex);
+        notifyCb = mNotifyCb;
+    }
+    if (notifyCb != NULL) {
+        notifyCb(msgType, ext1, ext2, ext3, mNotifyUserPtr);
+    }
+}
+
+camera2_device_ops_t EmulatedCamera2::sDeviceOps = {
+    EmulatedCamera2::set_request_queue_src_ops,
+    EmulatedCamera2::notify_request_queue_not_empty,
+    EmulatedCamera2::set_frame_queue_dst_ops,
+    EmulatedCamera2::get_in_progress_count,
+    EmulatedCamera2::flush_captures_in_progress,
+    EmulatedCamera2::construct_default_request,
+    EmulatedCamera2::allocate_stream,
+    EmulatedCamera2::register_stream_buffers,
+    EmulatedCamera2::release_stream,
+    EmulatedCamera2::allocate_reprocess_stream,
+    EmulatedCamera2::allocate_reprocess_stream_from_stream,
+    EmulatedCamera2::release_reprocess_stream,
+    EmulatedCamera2::trigger_action,
+    EmulatedCamera2::set_notify_callback,
+    EmulatedCamera2::get_metadata_vendor_tag_ops,
+    EmulatedCamera2::dump
+};
+
+}; /* namespace android */
diff --git a/guest/hals/camera/EmulatedCamera2.h b/guest/hals/camera/EmulatedCamera2.h
new file mode 100644
index 0000000..baf5a18
--- /dev/null
+++ b/guest/hals/camera/EmulatedCamera2.h
@@ -0,0 +1,279 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_CAMERA2_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA2_H
+
+/*
+ * Contains declaration of a class EmulatedCamera that encapsulates
+ * functionality common to all version 2.0 emulated camera devices.  Instances
+ * of this class (for each emulated camera) are created during the construction
+ * of the EmulatedCameraFactory instance.  This class serves as an entry point
+ * for all camera API calls that defined by camera2_device_ops_t API.
+ */
+
+#include "hardware/camera2.h"
+#include "system/camera_metadata.h"
+#include "EmulatedBaseCamera.h"
+#include <utils/Thread.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+/* Encapsulates functionality common to all version 2.0 emulated camera devices
+ *
+ * Note that EmulatedCameraFactory instantiates object of this class just once,
+ * when EmulatedCameraFactory instance gets constructed. Connection to /
+ * disconnection from the actual camera device is handled by calls to
+ * connectDevice(), and closeCamera() methods of this class that are invoked in
+ * response to hw_module_methods_t::open, and camera_device::close callbacks.
+ */
+class EmulatedCamera2 : public camera2_device, public EmulatedBaseCamera {
+public:
+    /* Constructs EmulatedCamera2 instance.
+     * Param:
+     *  cameraId - Zero based camera identifier, which is an index of the camera
+     *      instance in camera factory's array.
+     *  module - Emulated camera HAL module descriptor.
+     */
+    EmulatedCamera2(int cameraId,
+            struct hw_module_t* module);
+
+    /* Destructs EmulatedCamera2 instance. */
+    virtual ~EmulatedCamera2();
+
+    /****************************************************************************
+     * Abstract API
+     ***************************************************************************/
+
+public:
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+public:
+    virtual status_t Initialize(const cvd::CameraDefinition& props);
+
+    /****************************************************************************
+     * Camera module API and generic hardware device API implementation
+     ***************************************************************************/
+
+public:
+    virtual status_t connectCamera(hw_device_t** device);
+
+    virtual status_t closeCamera();
+
+    virtual status_t getCameraInfo(struct camera_info* info) = 0;
+
+    virtual status_t getImageMetadata(struct ImageMetadata* meta) {
+        // TODO(ender): fill in Image metadata structure.
+        return ENOSYS;
+    }
+
+    /****************************************************************************
+     * Camera API implementation.
+     * These methods are called from the camera API callback routines.
+     ***************************************************************************/
+
+protected:
+    /** Request input queue notification */
+    virtual int requestQueueNotify();
+
+    /** Count of requests in flight */
+    virtual int getInProgressCount();
+
+    /** Cancel all captures in flight */
+    virtual int flushCapturesInProgress();
+
+    virtual int constructDefaultRequest(
+        int request_template,
+        camera_metadata_t **request);
+
+    /** Output stream creation and management */
+    virtual int allocateStream(
+            uint32_t width,
+            uint32_t height,
+            int format,
+            const camera2_stream_ops_t *stream_ops,
+            uint32_t *stream_id,
+            uint32_t *format_actual,
+            uint32_t *usage,
+            uint32_t *max_buffers);
+
+    virtual int registerStreamBuffers(
+            uint32_t stream_id,
+            int num_buffers,
+            buffer_handle_t *buffers);
+
+    virtual int releaseStream(uint32_t stream_id);
+
+    /** Input stream creation and management */
+    virtual int allocateReprocessStream(
+            uint32_t width,
+            uint32_t height,
+            uint32_t format,
+            const camera2_stream_in_ops_t *reprocess_stream_ops,
+            uint32_t *stream_id,
+            uint32_t *consumer_usage,
+            uint32_t *max_buffers);
+
+    virtual int allocateReprocessStreamFromStream(
+            uint32_t output_stream_id,
+            const camera2_stream_in_ops_t *reprocess_stream_ops,
+            uint32_t *stream_id);
+
+    virtual int releaseReprocessStream(uint32_t stream_id);
+
+    /** 3A action triggering */
+    virtual int triggerAction(uint32_t trigger_id,
+            int32_t ext1, int32_t ext2);
+
+    /** Custom tag definitions */
+    virtual const char* getVendorSectionName(uint32_t tag);
+    virtual const char* getVendorTagName(uint32_t tag);
+    virtual int         getVendorTagType(uint32_t tag);
+
+    /** Debug methods */
+
+    virtual int dump(int fd);
+
+    /****************************************************************************
+     * Camera API callbacks as defined by camera2_device_ops structure.  See
+     * hardware/libhardware/include/hardware/camera2.h for information on each
+     * of these callbacks. Implemented in this class, these callbacks simply
+     * dispatch the call into an instance of EmulatedCamera2 class defined in
+     * the 'camera_device2' parameter.
+     ***************************************************************************/
+
+private:
+    /** Input request queue */
+    static int set_request_queue_src_ops(const camera2_device_t *,
+            const camera2_request_queue_src_ops *queue_src_ops);
+    static int notify_request_queue_not_empty(const camera2_device_t *);
+
+    /** Output frame queue */
+    static int set_frame_queue_dst_ops(const camera2_device_t *,
+            const camera2_frame_queue_dst_ops *queue_dst_ops);
+
+    /** In-progress request management */
+    static int get_in_progress_count(const camera2_device_t *);
+
+    static int flush_captures_in_progress(const camera2_device_t *);
+
+    /** Request template creation */
+    static int construct_default_request(const camera2_device_t *,
+            int request_template,
+            camera_metadata_t **request);
+
+    /** Stream management */
+    static int allocate_stream(const camera2_device_t *,
+            uint32_t width,
+            uint32_t height,
+            int format,
+            const camera2_stream_ops_t *stream_ops,
+            uint32_t *stream_id,
+            uint32_t *format_actual,
+            uint32_t *usage,
+            uint32_t *max_buffers);
+
+    static int register_stream_buffers(const camera2_device_t *,
+            uint32_t stream_id,
+            int num_buffers,
+            buffer_handle_t *buffers);
+
+    static int release_stream(const camera2_device_t *,
+            uint32_t stream_id);
+
+    static int allocate_reprocess_stream(const camera2_device_t *,
+            uint32_t width,
+            uint32_t height,
+            uint32_t format,
+            const camera2_stream_in_ops_t *reprocess_stream_ops,
+            uint32_t *stream_id,
+            uint32_t *consumer_usage,
+            uint32_t *max_buffers);
+
+    static int allocate_reprocess_stream_from_stream(const camera2_device_t *,
+            uint32_t output_stream_id,
+            const camera2_stream_in_ops_t *reprocess_stream_ops,
+            uint32_t *stream_id);
+
+    static int release_reprocess_stream(const camera2_device_t *,
+            uint32_t stream_id);
+
+    /** 3A triggers*/
+    static int trigger_action(const camera2_device_t *,
+            uint32_t trigger_id,
+            int ext1,
+            int ext2);
+
+    /** Notifications to application */
+    static int set_notify_callback(const camera2_device_t *,
+            camera2_notify_callback notify_cb,
+            void *user);
+
+    /** Vendor metadata registration */
+    static int get_metadata_vendor_tag_ops(const camera2_device_t *,
+            vendor_tag_query_ops_t **ops);
+    // for get_metadata_vendor_tag_ops
+    static const char* get_camera_vendor_section_name(
+            const vendor_tag_query_ops_t *,
+            uint32_t tag);
+    static const char* get_camera_vendor_tag_name(
+            const vendor_tag_query_ops_t *,
+            uint32_t tag);
+    static int get_camera_vendor_tag_type(
+            const vendor_tag_query_ops_t *,
+            uint32_t tag);
+
+    static int dump(const camera2_device_t *, int fd);
+
+    /** For hw_device_t ops */
+    static int close(struct hw_device_t* device);
+
+    /****************************************************************************
+     * Data members shared with implementations
+     ***************************************************************************/
+  protected:
+    /** Mutex for calls through camera2 device interface */
+    Mutex mMutex;
+
+    bool mStatusPresent;
+
+    const camera2_request_queue_src_ops *mRequestQueueSrc;
+    const camera2_frame_queue_dst_ops *mFrameQueueDst;
+
+    struct TagOps : public vendor_tag_query_ops {
+        EmulatedCamera2 *parent;
+    };
+    TagOps      mVendorTagOps;
+
+    void sendNotification(int32_t msgType,
+            int32_t ext1, int32_t ext2, int32_t ext3);
+
+    /****************************************************************************
+     * Data members
+     ***************************************************************************/
+  private:
+    static camera2_device_ops_t sDeviceOps;
+    camera2_notify_callback mNotifyCb;
+    void* mNotifyUserPtr;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_CAMERA2_H */
diff --git a/guest/hals/camera/EmulatedCamera3.cpp b/guest/hals/camera/EmulatedCamera3.cpp
new file mode 100644
index 0000000..c9e48a5
--- /dev/null
+++ b/guest/hals/camera/EmulatedCamera3.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+/**
+ * Contains implementation of a class EmulatedCamera that encapsulates
+ * functionality common to all version 3.0 emulated camera devices.  Instances
+ * of this class (for each emulated camera) are created during the construction
+ * of the EmulatedCameraFactory instance.  This class serves as an entry point
+ * for all camera API calls that defined by camera3_device_ops_t API.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera3_Camera"
+#include <cutils/log.h>
+
+#include "EmulatedCamera3.h"
+#include "system/camera_metadata.h"
+
+namespace android {
+
+/**
+ * Constructs EmulatedCamera3 instance.
+ * Param:
+ *  cameraId - Zero based camera identifier, which is an index of the camera
+ *      instance in camera factory's array.
+ *  module - Emulated camera HAL module descriptor.
+ */
+EmulatedCamera3::EmulatedCamera3(int cameraId,
+        struct hw_module_t* module):
+        EmulatedBaseCamera(cameraId,
+                CAMERA_DEVICE_API_VERSION_3_3,
+                &common,
+                module),
+        mStatus(STATUS_ERROR)
+{
+    common.close = EmulatedCamera3::close;
+    ops = &sDeviceOps;
+
+    mCallbackOps = NULL;
+
+}
+
+/* Destructs EmulatedCamera3 instance. */
+EmulatedCamera3::~EmulatedCamera3() {
+}
+
+/****************************************************************************
+ * Abstract API
+ ***************************************************************************/
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+status_t EmulatedCamera3::Initialize(const cvd::CameraDefinition& params) {
+    ALOGV("%s", __FUNCTION__);
+
+    mStatus = STATUS_CLOSED;
+    return NO_ERROR;
+}
+
+/****************************************************************************
+ * Camera API implementation
+ ***************************************************************************/
+
+status_t EmulatedCamera3::connectCamera(hw_device_t** device) {
+    ALOGV("%s", __FUNCTION__);
+    if (device == NULL) return BAD_VALUE;
+
+    if (mStatus != STATUS_CLOSED) {
+        ALOGE("%s: Trying to open a camera in state %d!",
+                __FUNCTION__, mStatus);
+        return INVALID_OPERATION;
+    }
+
+    *device = &common;
+    mStatus = STATUS_OPEN;
+    return NO_ERROR;
+}
+
+status_t EmulatedCamera3::closeCamera() {
+    mStatus = STATUS_CLOSED;
+    return NO_ERROR;
+}
+
+status_t EmulatedCamera3::getCameraInfo(struct camera_info* info) {
+    return EmulatedBaseCamera::getCameraInfo(info);
+}
+
+/****************************************************************************
+ * Camera Device API implementation.
+ * These methods are called from the camera API callback routines.
+ ***************************************************************************/
+
+status_t EmulatedCamera3::initializeDevice(
+        const camera3_callback_ops *callbackOps) {
+    if (callbackOps == NULL) {
+        ALOGE("%s: NULL callback ops provided to HAL!",
+                __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (mStatus != STATUS_OPEN) {
+        ALOGE("%s: Trying to initialize a camera in state %d!",
+                __FUNCTION__, mStatus);
+        return INVALID_OPERATION;
+    }
+
+    mCallbackOps = callbackOps;
+    mStatus = STATUS_READY;
+
+    return NO_ERROR;
+}
+
+status_t EmulatedCamera3::configureStreams(
+        camera3_stream_configuration *streamList) {
+    ALOGE("%s: Not implemented", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+status_t EmulatedCamera3::registerStreamBuffers(
+        const camera3_stream_buffer_set *bufferSet) {
+    ALOGE("%s: Not implemented", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+const camera_metadata_t* EmulatedCamera3::constructDefaultRequestSettings(
+        int type) {
+    ALOGE("%s: Not implemented", __FUNCTION__);
+    return NULL;
+}
+
+status_t EmulatedCamera3::processCaptureRequest(
+        camera3_capture_request *request) {
+    ALOGE("%s: Not implemented", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+status_t EmulatedCamera3::flush() {
+    ALOGE("%s: Not implemented", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+/** Debug methods */
+
+void EmulatedCamera3::dump(int fd) {
+    ALOGE("%s: Not implemented", __FUNCTION__);
+    return;
+}
+
+/****************************************************************************
+ * Protected API. Callbacks to the framework.
+ ***************************************************************************/
+
+void EmulatedCamera3::sendCaptureResult(camera3_capture_result_t *result) {
+    mCallbackOps->process_capture_result(mCallbackOps, result);
+}
+
+void EmulatedCamera3::sendNotify(camera3_notify_msg_t *msg) {
+    mCallbackOps->notify(mCallbackOps, msg);
+}
+
+/****************************************************************************
+ * Private API.
+ ***************************************************************************/
+
+/****************************************************************************
+ * Camera API callbacks as defined by camera3_device_ops structure.  See
+ * hardware/libhardware/include/hardware/camera3.h for information on each
+ * of these callbacks. Implemented in this class, these callbacks simply
+ * dispatch the call into an instance of EmulatedCamera3 class defined by the
+ * 'camera_device3' parameter, or set a member value in the same.
+ ***************************************************************************/
+
+EmulatedCamera3* getInstance(const camera3_device_t *d) {
+    const EmulatedCamera3* cec = static_cast<const EmulatedCamera3*>(d);
+    return const_cast<EmulatedCamera3*>(cec);
+}
+
+int EmulatedCamera3::initialize(const struct camera3_device *d,
+        const camera3_callback_ops_t *callback_ops) {
+    EmulatedCamera3* ec = getInstance(d);
+    return ec->initializeDevice(callback_ops);
+}
+
+int EmulatedCamera3::configure_streams(const struct camera3_device *d,
+        camera3_stream_configuration_t *stream_list) {
+    EmulatedCamera3* ec = getInstance(d);
+    return ec->configureStreams(stream_list);
+}
+
+int EmulatedCamera3::register_stream_buffers(
+        const struct camera3_device *d,
+        const camera3_stream_buffer_set_t *buffer_set) {
+    EmulatedCamera3* ec = getInstance(d);
+    return ec->registerStreamBuffers(buffer_set);
+}
+
+int EmulatedCamera3::process_capture_request(
+        const struct camera3_device *d,
+        camera3_capture_request_t *request) {
+    EmulatedCamera3* ec = getInstance(d);
+    return ec->processCaptureRequest(request);
+}
+
+const camera_metadata_t* EmulatedCamera3::construct_default_request_settings(
+        const camera3_device_t *d, int type) {
+    EmulatedCamera3* ec = getInstance(d);
+    return ec->constructDefaultRequestSettings(type);
+}
+
+void EmulatedCamera3::dump(const camera3_device_t *d, int fd) {
+    EmulatedCamera3* ec = getInstance(d);
+    ec->dump(fd);
+}
+
+int EmulatedCamera3::flush(const camera3_device_t *d) {
+    EmulatedCamera3* ec = getInstance(d);
+    return ec->flush();
+}
+
+int EmulatedCamera3::close(struct hw_device_t* device) {
+    EmulatedCamera3* ec =
+            static_cast<EmulatedCamera3*>(
+                reinterpret_cast<camera3_device_t*>(device) );
+    if (ec == NULL) {
+        ALOGE("%s: Unexpected NULL camera3 device", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    return ec->closeCamera();
+}
+
+camera3_device_ops_t EmulatedCamera3::sDeviceOps = {
+    EmulatedCamera3::initialize,
+    EmulatedCamera3::configure_streams,
+    /* DEPRECATED: register_stream_buffers */ nullptr,
+    EmulatedCamera3::construct_default_request_settings,
+    EmulatedCamera3::process_capture_request,
+    /* DEPRECATED: get_metadata_vendor_tag_ops */ nullptr,
+    EmulatedCamera3::dump,
+    EmulatedCamera3::flush
+};
+
+const char* EmulatedCamera3::sAvailableCapabilitiesStrings[NUM_CAPABILITIES] = {
+    "BACKWARD_COMPATIBLE",
+    "MANUAL_SENSOR",
+    "MANUAL_POST_PROCESSING",
+    "RAW",
+    "PRIVATE_REPROCESSING",
+    "READ_SENSOR_SETTINGS",
+    "BURST_CAPTURE",
+    "YUV_REPROCESSING",
+    "DEPTH_OUTPUT",
+    "CONSTRAINED_HIGH_SPEED_VIDEO",
+    "FULL_LEVEL"
+};
+
+}; /* namespace android */
diff --git a/guest/hals/camera/EmulatedCamera3.h b/guest/hals/camera/EmulatedCamera3.h
new file mode 100644
index 0000000..e7a2ac1
--- /dev/null
+++ b/guest/hals/camera/EmulatedCamera3.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2013 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 HW_EMULATOR_CAMERA_EMULATED_CAMERA3_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA3_H
+
+/**
+ * Contains declaration of a class EmulatedCamera that encapsulates
+ * functionality common to all version 3.0 emulated camera devices.  Instances
+ * of this class (for each emulated camera) are created during the construction
+ * of the EmulatedCameraFactory instance.  This class serves as an entry point
+ * for all camera API calls that defined by camera3_device_ops_t API.
+ */
+
+#include "hardware/camera3.h"
+#include "system/camera_metadata.h"
+#include "EmulatedBaseCamera.h"
+
+namespace android {
+
+/**
+ * Encapsulates functionality common to all version 3.0 emulated camera devices
+ *
+ * Note that EmulatedCameraFactory instantiates an object of this class just
+ * once, when EmulatedCameraFactory instance gets constructed. Connection to /
+ * disconnection from the actual camera device is handled by calls to
+ * connectDevice(), and closeCamera() methods of this class that are invoked in
+ * response to hw_module_methods_t::open, and camera_device::close callbacks.
+ */
+class EmulatedCamera3 : public camera3_device, public EmulatedBaseCamera {
+public:
+    /* Constructs EmulatedCamera3 instance.
+     * Param:
+     *  cameraId - Zero based camera identifier, which is an index of the camera
+     *      instance in camera factory's array.
+     *  module - Emulated camera HAL module descriptor.
+     */
+    EmulatedCamera3(int cameraId,
+            struct hw_module_t* module);
+
+    /* Destructs EmulatedCamera2 instance. */
+    virtual ~EmulatedCamera3();
+
+    /* List of all defined capabilities plus useful HW levels */
+    enum AvailableCapabilities {
+        BACKWARD_COMPATIBLE,
+        MANUAL_SENSOR,
+        MANUAL_POST_PROCESSING,
+        RAW,
+        PRIVATE_REPROCESSING,
+        READ_SENSOR_SETTINGS,
+        BURST_CAPTURE,
+        YUV_REPROCESSING,
+        DEPTH_OUTPUT,
+        CONSTRAINED_HIGH_SPEED_VIDEO,
+        // Levels
+        FULL_LEVEL,
+
+        NUM_CAPABILITIES
+    };
+
+    // Char strings for above enum, with size NUM_CAPABILITIES
+    static const char *sAvailableCapabilitiesStrings[];
+
+    /****************************************************************************
+     * Abstract API
+     ***************************************************************************/
+
+public:
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+public:
+    virtual status_t Initialize(const cvd::CameraDefinition& params);
+
+    /****************************************************************************
+     * Camera module API and generic hardware device API implementation
+     ***************************************************************************/
+
+public:
+    virtual status_t connectCamera(hw_device_t** device);
+
+    virtual status_t closeCamera();
+
+    virtual status_t getCameraInfo(struct camera_info* info);
+
+    virtual status_t getImageMetadata(struct ImageMetadata* meta) {
+        // TODO(ender): fill in Image metadata structure.
+        return ENOSYS;
+    }
+
+    /****************************************************************************
+     * Camera API implementation.
+     * These methods are called from the camera API callback routines.
+     ***************************************************************************/
+
+protected:
+
+    virtual status_t initializeDevice(
+        const camera3_callback_ops *callbackOps);
+
+    virtual status_t configureStreams(
+        camera3_stream_configuration *streamList);
+
+    virtual status_t registerStreamBuffers(
+        const camera3_stream_buffer_set *bufferSet) ;
+
+    virtual const camera_metadata_t* constructDefaultRequestSettings(
+        int type);
+
+    virtual status_t processCaptureRequest(camera3_capture_request *request);
+
+    virtual status_t flush();
+
+    /** Debug methods */
+
+    virtual void dump(int fd);
+
+    /****************************************************************************
+     * Camera API callbacks as defined by camera3_device_ops structure.  See
+     * hardware/libhardware/include/hardware/camera3.h for information on each
+     * of these callbacks. Implemented in this class, these callbacks simply
+     * dispatch the call into an instance of EmulatedCamera3 class defined in
+     * the 'camera_device3' parameter.
+     ***************************************************************************/
+
+private:
+
+    /** Startup */
+    static int initialize(const struct camera3_device *,
+            const camera3_callback_ops_t *callback_ops);
+
+    /** Stream configuration and buffer registration */
+
+    static int configure_streams(const struct camera3_device *,
+            camera3_stream_configuration_t *stream_list);
+
+    static int register_stream_buffers(const struct camera3_device *,
+            const camera3_stream_buffer_set_t *buffer_set);
+
+    /** Template request settings provision */
+
+    static const camera_metadata_t* construct_default_request_settings(
+            const struct camera3_device *, int type);
+
+    /** Submission of capture requests to HAL */
+
+    static int process_capture_request(const struct camera3_device *,
+            camera3_capture_request_t *request);
+
+    static void dump(const camera3_device_t *, int fd);
+
+    static int flush(const camera3_device_t *);
+
+    /** For hw_device_t ops */
+    static int close(struct hw_device_t* device);
+
+    /****************************************************************************
+     * Data members shared with implementations
+     ***************************************************************************/
+  protected:
+
+    enum {
+        // State at construction time, and after a device operation error
+        STATUS_ERROR = 0,
+        // State after startup-time init and after device instance close
+        STATUS_CLOSED,
+        // State after being opened, before device instance init
+        STATUS_OPEN,
+        // State after device instance initialization
+        STATUS_READY,
+        // State while actively capturing data
+        STATUS_ACTIVE
+    } mStatus;
+
+    /**
+     * Callbacks back to the framework
+     */
+
+    void sendCaptureResult(camera3_capture_result_t *result);
+    void sendNotify(camera3_notify_msg_t *msg);
+
+    /****************************************************************************
+     * Data members
+     ***************************************************************************/
+  private:
+    static camera3_device_ops_t   sDeviceOps;
+    const camera3_callback_ops_t *mCallbackOps;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_CAMERA3_H */
diff --git a/guest/hals/camera/EmulatedCameraCommon.h b/guest/hals/camera/EmulatedCameraCommon.h
new file mode 100755
index 0000000..c1d575c
--- /dev/null
+++ b/guest/hals/camera/EmulatedCameraCommon.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_EMULATED_CAMERA_COMMON_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_COMMON_H
+
+/*
+ * Contains common declarations that are used across the camera emulation.
+ */
+
+#include <linux/videodev2.h>
+#include <hardware/camera.h>
+
+/* A helper class that tracks a routine execution.
+ * Basically, it dumps an enry message in its constructor, and an exit message
+ * in its destructor. Use LOGRE() macro (declared bellow) to create instances
+ * of this class at the beginning of the tracked routines / methods.
+ */
+class HWERoutineTracker {
+public:
+    /* Constructor that prints an "entry" trace message. */
+    explicit HWERoutineTracker(const char* name)
+            : mName(name) {
+        ALOGV("Entering %s", mName);
+    }
+
+    /* Destructor that prints a "leave" trace message. */
+    ~HWERoutineTracker() {
+        ALOGV("Leaving %s", mName);
+    }
+
+private:
+    /* Stores the routine name. */
+    const char* mName;
+};
+
+/* Logs an execution of a routine / method. */
+#define LOGRE() HWERoutineTracker hwertracker_##__LINE__(__FUNCTION__)
+
+/*
+ * min / max macros
+ */
+
+#define min(a,b)    (((a) < (b)) ? (a) : (b))
+#define max(a,b)    (((a) > (b)) ? (a) : (b))
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_CAMERA_COMMON_H */
diff --git a/guest/hals/camera/EmulatedCameraDevice.cpp b/guest/hals/camera/EmulatedCameraDevice.cpp
new file mode 100755
index 0000000..36de83a
--- /dev/null
+++ b/guest/hals/camera/EmulatedCameraDevice.cpp
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of an abstract class EmulatedCameraDevice that defines
+ * functionality expected from an emulated physical camera device:
+ *  - Obtaining and setting camera parameters
+ *  - Capturing frames
+ *  - Streaming video
+ *  - etc.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Device"
+#include <cutils/log.h>
+#include <sys/select.h>
+#include <cmath>
+#include "EmulatedCamera.h"
+#include "EmulatedCameraDevice.h"
+
+namespace android {
+
+const float GAMMA_CORRECTION = 2.2f;
+EmulatedCameraDevice::EmulatedCameraDevice(EmulatedCamera* camera_hal)
+    : mObjectLock(),
+      mCurFrameTimestamp(0),
+      mCameraHAL(camera_hal),
+      mCurrentFrame(NULL),
+      mExposureCompensation(1.0f),
+      mWhiteBalanceScale(NULL),
+      mIsFocusing(false),
+      mSupportedWhiteBalanceScale(),
+      mState(ECDS_CONSTRUCTED)
+{
+}
+
+EmulatedCameraDevice::~EmulatedCameraDevice()
+{
+    ALOGV("EmulatedCameraDevice destructor");
+    if (mCurrentFrame != NULL) {
+        delete[] mCurrentFrame;
+    }
+    for (size_t i = 0; i < mSupportedWhiteBalanceScale.size(); ++i) {
+        if (mSupportedWhiteBalanceScale.valueAt(i) != NULL) {
+            delete[] mSupportedWhiteBalanceScale.valueAt(i);
+        }
+    }
+}
+
+/****************************************************************************
+ * Emulated camera device public API
+ ***************************************************************************/
+
+status_t EmulatedCameraDevice::Initialize()
+{
+    if (isInitialized()) {
+        ALOGW("%s: Emulated camera device is already initialized: mState = %d",
+             __FUNCTION__, mState);
+        return NO_ERROR;
+    }
+
+    /* Instantiate worker thread object. */
+    mWorkerThread = new WorkerThread(this);
+    if (getWorkerThread() == NULL) {
+        ALOGE("%s: Unable to instantiate worker thread object", __FUNCTION__);
+        return ENOMEM;
+    }
+
+    mState = ECDS_INITIALIZED;
+
+    return NO_ERROR;
+}
+
+status_t EmulatedCameraDevice::startDeliveringFrames(bool one_burst)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    if (!isStarted()) {
+        ALOGE("%s: Device is not started", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* Frames will be delivered from the thread routine. */
+    const status_t res = startWorkerThread(one_burst);
+    ALOGE_IF(res != NO_ERROR, "%s: startWorkerThread failed", __FUNCTION__);
+    return res;
+}
+
+status_t EmulatedCameraDevice::stopDeliveringFrames()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    if (!isStarted()) {
+        ALOGW("%s: Device is not started", __FUNCTION__);
+        return NO_ERROR;
+    }
+
+    const status_t res = stopWorkerThread();
+    ALOGE_IF(res != NO_ERROR, "%s: startWorkerThread failed", __FUNCTION__);
+    return res;
+}
+
+void EmulatedCameraDevice::setExposureCompensation(const float ev) {
+    ALOGV("%s", __FUNCTION__);
+
+    if (!isStarted()) {
+        ALOGW("%s: Fake camera device is not started.", __FUNCTION__);
+    }
+
+    mExposureCompensation = std::pow(2.0f, ev / GAMMA_CORRECTION);
+    ALOGV("New exposure compensation is %f", mExposureCompensation);
+}
+
+void EmulatedCameraDevice::initializeWhiteBalanceModes(const char* mode,
+                                                       const float r_scale,
+                                                       const float b_scale) {
+    ALOGV("%s with %s, %f, %f", __FUNCTION__, mode, r_scale, b_scale);
+    float* value = new float[3];
+    value[0] = r_scale; value[1] = 1.0f; value[2] = b_scale;
+    mSupportedWhiteBalanceScale.add(String8(mode), value);
+}
+
+void EmulatedCameraDevice::setWhiteBalanceMode(const char* mode) {
+    ALOGV("%s with white balance %s", __FUNCTION__, mode);
+    mWhiteBalanceScale =
+            mSupportedWhiteBalanceScale.valueFor(String8(mode));
+}
+
+void EmulatedCameraDevice::startAutoFocus() {
+    mIsFocusing = true;
+}
+
+/* Computes the pixel value after adjusting the white balance to the current
+ * one. The input the y, u, v channel of the pixel and the adjusted value will
+ * be stored in place. The adjustment is done in RGB space.
+ */
+void EmulatedCameraDevice::changeWhiteBalance(uint8_t& y,
+                                              uint8_t& u,
+                                              uint8_t& v) const {
+    float r_scale = mWhiteBalanceScale[0];
+    float b_scale = mWhiteBalanceScale[2];
+    int r = static_cast<float>(YUV2R(y, u, v)) / r_scale;
+    int g = YUV2G(y, u, v);
+    int b = static_cast<float>(YUV2B(y, u, v)) / b_scale;
+
+    y = RGB2Y(r, g, b);
+    u = RGB2U(r, g, b);
+    v = RGB2V(r, g, b);
+}
+
+void EmulatedCameraDevice::simulateAutoFocus() {
+    if (mIsFocusing) {
+        ALOGV("%s: Simulating auto-focus", __FUNCTION__);
+        mCameraHAL->onCameraFocusAcquired();
+        mIsFocusing = false;
+    }
+}
+
+status_t EmulatedCameraDevice::getCurrentPreviewFrame(void* buffer)
+{
+    if (!isStarted()) {
+        ALOGE("%s: Device is not started", __FUNCTION__);
+        return EINVAL;
+    }
+    if (mCurrentFrame == NULL || buffer == NULL) {
+        ALOGE("%s: No framebuffer", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* In emulation the framebuffer is never RGB. */
+    switch (mPixelFormat) {
+        case V4L2_PIX_FMT_YVU420:
+            YV12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+            return NO_ERROR;
+        case V4L2_PIX_FMT_YUV420:
+            YU12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+            return NO_ERROR;
+        case V4L2_PIX_FMT_NV21:
+            NV21ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+            return NO_ERROR;
+        case V4L2_PIX_FMT_NV12:
+            NV12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+            return NO_ERROR;
+
+        default:
+            ALOGE("%s: Unknown pixel format %.4s",
+                 __FUNCTION__, reinterpret_cast<const char*>(&mPixelFormat));
+            return EINVAL;
+    }
+}
+
+/****************************************************************************
+ * Emulated camera device private API
+ ***************************************************************************/
+
+status_t EmulatedCameraDevice::commonStartDevice(int width,
+                                                 int height,
+                                                 uint32_t pix_fmt,
+                                                 int fps)
+{
+    /* Validate pixel format, and calculate framebuffer size at the same time. */
+    switch (pix_fmt) {
+        case V4L2_PIX_FMT_YVU420:
+        case V4L2_PIX_FMT_YUV420:
+        case V4L2_PIX_FMT_NV21:
+        case V4L2_PIX_FMT_NV12:
+            mFrameBufferSize = (width * height * 12) / 8;
+            break;
+
+        default:
+            ALOGE("%s: Unknown pixel format %.4s",
+                 __FUNCTION__, reinterpret_cast<const char*>(&pix_fmt));
+            return EINVAL;
+    }
+
+    /* Cache framebuffer info. */
+    mFrameWidth = width;
+    mFrameHeight = height;
+    mPixelFormat = pix_fmt;
+    mTotalPixels = width * height;
+    mTargetFps = fps;
+
+    /* Allocate framebuffer. */
+    mCurrentFrame = new uint8_t[mFrameBufferSize];
+    if (mCurrentFrame == NULL) {
+        ALOGE("%s: Unable to allocate framebuffer", __FUNCTION__);
+        return ENOMEM;
+    }
+    ALOGV("%s: Allocated %p %zu bytes for %d pixels in %.4s[%dx%d] frame",
+         __FUNCTION__, mCurrentFrame, mFrameBufferSize, mTotalPixels,
+         reinterpret_cast<const char*>(&mPixelFormat), mFrameWidth, mFrameHeight);
+    return NO_ERROR;
+}
+
+void EmulatedCameraDevice::commonStopDevice()
+{
+    mFrameWidth = mFrameHeight = mTotalPixels = 0;
+    mPixelFormat = 0;
+    mTargetFps = 0;
+
+    if (mCurrentFrame != NULL) {
+        delete[] mCurrentFrame;
+        mCurrentFrame = NULL;
+    }
+}
+
+status_t EmulatedCameraDevice::getImageMetadata(struct ImageMetadata* meta) {
+  meta->mWidth = mFrameWidth;
+  meta->mHeight = mFrameHeight;
+  return mCameraHAL->getImageMetadata(meta);
+}
+
+/****************************************************************************
+ * Worker thread management.
+ ***************************************************************************/
+
+status_t EmulatedCameraDevice::startWorkerThread(bool one_burst)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    if (!isInitialized()) {
+        ALOGE("%s: Emulated camera device is not initialized", __FUNCTION__);
+        return EINVAL;
+    }
+
+    const status_t res = getWorkerThread()->startThread(one_burst);
+    ALOGE_IF(res != NO_ERROR, "%s: Unable to start worker thread", __FUNCTION__);
+    return res;
+}
+
+status_t EmulatedCameraDevice::stopWorkerThread()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    if (!isInitialized()) {
+        ALOGE("%s: Emulated camera device is not initialized", __FUNCTION__);
+        return EINVAL;
+    }
+
+    const status_t res = getWorkerThread()->stopThread();
+    ALOGE_IF(res != NO_ERROR, "%s: Unable to stop worker thread", __FUNCTION__);
+    return res;
+}
+
+bool EmulatedCameraDevice::inWorkerThread()
+{
+    /* This will end the thread loop, and will terminate the thread. Derived
+     * classes must override this method. */
+    return false;
+}
+
+/****************************************************************************
+ * Worker thread implementation.
+ ***************************************************************************/
+
+status_t EmulatedCameraDevice::WorkerThread::readyToRun()
+{
+    ALOGV("Starting emulated camera device worker thread...");
+
+    ALOGW_IF(mThreadControl >= 0 || mControlFD >= 0,
+            "%s: Thread control FDs are opened", __FUNCTION__);
+    /* Create a pair of FDs that would be used to control the thread. */
+    int thread_fds[2];
+    status_t ret;
+    Mutex::Autolock lock(mCameraDevice->mObjectLock);
+    if (pipe(thread_fds) == 0) {
+        mThreadControl = thread_fds[1];
+        mControlFD = thread_fds[0];
+        ALOGV("Emulated device's worker thread has been started.");
+        ret = NO_ERROR;
+    } else {
+        ALOGE("%s: Unable to create thread control FDs: %d -> %s",
+             __FUNCTION__, errno, strerror(errno));
+        ret = errno;
+    }
+
+    mSetup.signal();
+    return ret;
+}
+
+status_t EmulatedCameraDevice::WorkerThread::stopThread()
+{
+    ALOGV("Stopping emulated camera device's worker thread...");
+
+    status_t res = EINVAL;
+
+    // Limit the scope of the Autolock
+    {
+      // If thread is running and readyToRun() has not finished running,
+      //    then wait until it is done.
+      Mutex::Autolock lock(mCameraDevice->mObjectLock);
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+      if (isRunning() && (mThreadControl < 0 || mControlFD < 0)) {
+#else
+      if (getTid() != -1 && (mThreadControl < 0 || mControlFD < 0)) {
+#endif
+          mSetup.wait(mCameraDevice->mObjectLock);
+      }
+    }
+
+    if (mThreadControl >= 0) {
+        /* Send "stop" message to the thread loop. */
+        const ControlMessage msg = THREAD_STOP;
+        const int wres =
+            TEMP_FAILURE_RETRY(write(mThreadControl, &msg, sizeof(msg)));
+        if (wres == sizeof(msg)) {
+            /* Stop the thread, and wait till it's terminated. */
+            res = requestExitAndWait();
+            if (res == NO_ERROR) {
+                /* Close control FDs. */
+                if (mThreadControl >= 0) {
+                    close(mThreadControl);
+                    mThreadControl = -1;
+                }
+                if (mControlFD >= 0) {
+                    close(mControlFD);
+                    mControlFD = -1;
+                }
+                ALOGV("Emulated camera device's worker thread has been stopped.");
+            } else {
+                ALOGE("%s: requestExitAndWait failed: %d -> %s",
+                     __FUNCTION__, res, strerror(-res));
+            }
+        } else {
+            ALOGE("%s: Unable to send THREAD_STOP message: %d -> %s",
+                 __FUNCTION__, errno, strerror(errno));
+            res = errno ? errno : EINVAL;
+        }
+    } else {
+        ALOGE("%s: Thread control FDs are not opened", __FUNCTION__);
+    }
+
+    return res;
+}
+
+EmulatedCameraDevice::WorkerThread::SelectRes
+EmulatedCameraDevice::WorkerThread::Select(int fd, int timeout)
+{
+    fd_set fds[1];
+    struct timeval tv, *tvp = NULL;
+
+    mCameraDevice->simulateAutoFocus();
+
+    const int fd_num = (fd >= 0) ? max(fd, mControlFD) + 1 :
+                                   mControlFD + 1;
+    FD_ZERO(fds);
+    FD_SET(mControlFD, fds);
+    if (fd >= 0) {
+        FD_SET(fd, fds);
+    }
+    if (timeout) {
+        tv.tv_sec = timeout / 1000000;
+        tv.tv_usec = timeout % 1000000;
+        tvp = &tv;
+    }
+    int res = TEMP_FAILURE_RETRY(select(fd_num, fds, NULL, NULL, tvp));
+    if (res < 0) {
+        ALOGE("%s: select returned %d and failed: %d -> %s",
+             __FUNCTION__, res, errno, strerror(errno));
+        return ERROR;
+    } else if (res == 0) {
+        /* Timeout. */
+        return TIMEOUT;
+    } else if (FD_ISSET(mControlFD, fds)) {
+        /* A control event. Lets read the message. */
+        ControlMessage msg;
+        res = TEMP_FAILURE_RETRY(read(mControlFD, &msg, sizeof(msg)));
+        if (res != sizeof(msg)) {
+            ALOGE("%s: Unexpected message size %d, or an error %d -> %s",
+                 __FUNCTION__, res, errno, strerror(errno));
+            return ERROR;
+        }
+        /* THREAD_STOP is the only message expected here. */
+        if (msg == THREAD_STOP) {
+            ALOGV("%s: THREAD_STOP message is received", __FUNCTION__);
+            return EXIT_THREAD;
+        } else {
+            ALOGE("Unknown worker thread message %d", msg);
+            return ERROR;
+        }
+    } else {
+        /* Must be an FD. */
+        ALOGW_IF(fd < 0 || !FD_ISSET(fd, fds), "%s: Undefined 'select' result",
+                __FUNCTION__);
+        return READY;
+    }
+}
+
+};  /* namespace android */
diff --git a/guest/hals/camera/EmulatedCameraDevice.h b/guest/hals/camera/EmulatedCameraDevice.h
new file mode 100755
index 0000000..7d0a749
--- /dev/null
+++ b/guest/hals/camera/EmulatedCameraDevice.h
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_EMULATED_CAMERA_DEVICE_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_DEVICE_H
+
+/*
+ * Contains declaration of an abstract class EmulatedCameraDevice that defines
+ * functionality expected from an emulated physical camera device:
+ *  - Obtaining and setting camera device parameters
+ *  - Capturing frames
+ *  - Streaming video
+ *  - etc.
+ */
+
+#include <utils/threads.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include "EmulatedCameraCommon.h"
+#include "Converters.h"
+#include "ImageMetadata.h"
+
+namespace android {
+
+class EmulatedCamera;
+
+/* Encapsulates an abstract class EmulatedCameraDevice that defines
+ * functionality expected from an emulated physical camera device:
+ *  - Obtaining and setting camera device parameters
+ *  - Capturing frames
+ *  - Streaming video
+ *  - etc.
+ */
+class EmulatedCameraDevice {
+public:
+    /* Constructs EmulatedCameraDevice instance.
+     * Param:
+     *  camera_hal - Emulated camera that implements the camera HAL API, and
+     *      manages (contains) this object.
+     */
+    explicit EmulatedCameraDevice(EmulatedCamera* camera_hal);
+
+    /* Destructs EmulatedCameraDevice instance. */
+    virtual ~EmulatedCameraDevice();
+
+    /***************************************************************************
+     * Emulated camera device abstract interface
+     **************************************************************************/
+
+public:
+    /* Connects to the camera device.
+     * This method must be called on an initialized instance of this class.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t connectDevice() = 0;
+
+    /* Disconnects from the camera device.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status. If this method is
+     *  called for already disconnected, or uninitialized instance of this class,
+     *  a successful status must be returned from this method. If this method is
+     *  called for an instance that is in the "started" state, this method must
+     *  return a failure.
+     */
+    virtual status_t disconnectDevice() = 0;
+
+    /* Starts the camera device.
+     * This method tells the camera device to start capturing frames of the given
+     * dimensions for the given pixel format. Note that this method doesn't start
+     * the delivery of the captured frames to the emulated camera. Call
+     * startDeliveringFrames method to start delivering frames. This method must
+     * be called on a connected instance of this class. If it is called on a
+     * disconnected instance, this method must return a failure.
+     * Param:
+     *  width, height - Frame dimensions to use when capturing video frames.
+     *  pix_fmt - Pixel format to use when capturing video frames.
+     *  fps - Target rate of frames per second.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t startDevice(int width,
+                                 int height,
+                                 uint32_t pix_fmt,
+                                 int fps) = 0;
+
+    /* Stops the camera device.
+     * This method tells the camera device to stop capturing frames. Note that
+     * this method doesn't stop delivering frames to the emulated camera. Always
+     * call stopDeliveringFrames prior to calling this method.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status. If this method is
+     *  called for an object that is not capturing frames, or is disconnected,
+     *  or is uninitialized, a successful status must be returned from this
+     *  method.
+     */
+    virtual status_t stopDevice() = 0;
+
+    /***************************************************************************
+     * Emulated camera device public API
+     **************************************************************************/
+
+public:
+    /* Initializes EmulatedCameraDevice instance.
+     * Derived classes should override this method in order to cache static
+     * properties of the physical device (list of supported pixel formats, frame
+     * sizes, etc.) If this method is called on an already initialized instance,
+     * it must return a successful status.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t Initialize();
+
+    /* Initializes the white balance modes parameters.
+     * The parameters are passed by each individual derived camera API to
+     * represent that different camera manufacturers may have different
+     * preferences on the white balance parameters. Green channel in the RGB
+     * color space is fixed to keep the luminance to be reasonably constant.
+     *
+     * Param:
+     * mode the text describing the current white balance mode
+     * r_scale the scale factor for the R channel in RGB space
+     * b_scale the scale factor for the B channel in RGB space.
+     */
+    void initializeWhiteBalanceModes(const char* mode,
+                                     const float r_scale,
+                                     const float b_scale);
+
+    /* Starts delivering frames captured from the camera device.
+     * This method will start the worker thread that would be pulling frames from
+     * the camera device, and will deliver the pulled frames back to the emulated
+     * camera via onNextFrameAvailable callback. This method must be called on a
+     * connected instance of this class with a started camera device. If it is
+     * called on a disconnected instance, or camera device has not been started,
+     * this method must return a failure.
+     * Param:
+     *  one_burst - Controls how many frames should be delivered. If this
+     *      parameter is 'true', only one captured frame will be delivered to the
+     *      emulated camera. If this parameter is 'false', frames will keep
+     *      coming until stopDeliveringFrames method is called. Typically, this
+     *      parameter is set to 'true' only in order to obtain a single frame
+     *      that will be used as a "picture" in takePicture method of the
+     *      emulated camera.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t startDeliveringFrames(bool one_burst);
+
+    /* Stops delivering frames captured from the camera device.
+     * This method will stop the worker thread started by startDeliveringFrames.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t stopDeliveringFrames();
+
+    /* Sets the exposure compensation for the camera device.
+     */
+    void setExposureCompensation(const float ev);
+
+    /* Sets the white balance mode for the device.
+     */
+    void setWhiteBalanceMode(const char* mode);
+
+    /* Initiates focus operation.
+     */
+    virtual void startAutoFocus();
+
+    /* Gets current framebuffer, converted into preview frame format.
+     * This method must be called on a connected instance of this class with a
+     * started camera device. If it is called on a disconnected instance, or
+     * camera device has not been started, this method must return a failure.
+     * Note that this method should be called only after at least one frame has
+     * been captured and delivered. Otherwise it will return garbage in the
+     * preview frame buffer. Typically, this method shuld be called from
+     * onNextFrameAvailable callback.
+     * Param:
+     *  buffer - Buffer, large enough to contain the entire preview frame.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t getCurrentPreviewFrame(void* buffer);
+
+    /* Gets width of the frame obtained from the physical device.
+     * Return:
+     *  Width of the frame obtained from the physical device. Note that value
+     *  returned from this method is valid only in case if camera device has been
+     *  started.
+     */
+    inline int getFrameWidth() const
+    {
+        ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+        return mFrameWidth;
+    }
+
+    /* Gets height of the frame obtained from the physical device.
+     * Return:
+     *  Height of the frame obtained from the physical device. Note that value
+     *  returned from this method is valid only in case if camera device has been
+     *  started.
+     */
+    inline int getFrameHeight() const
+    {
+        ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+        return mFrameHeight;
+    }
+
+    /* Gets byte size of the current frame buffer.
+     * Return:
+     *  Byte size of the frame buffer. Note that value returned from this method
+     *  is valid only in case if camera device has been started.
+     */
+    inline size_t getFrameBufferSize() const
+    {
+        ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+        return mFrameBufferSize;
+    }
+
+    /* Gets number of pixels in the current frame buffer.
+     * Return:
+     *  Number of pixels in the frame buffer. Note that value returned from this
+     *  method is valid only in case if camera device has been started.
+     */
+    inline int getPixelNum() const
+    {
+        ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+        return mTotalPixels;
+    }
+
+    /* Gets pixel format of the frame that camera device streams to this class.
+     * Throughout camera framework, there are three different forms of pixel
+     * format representation:
+     *  - Original format, as reported by the actual camera device. Values for
+     *    this format are declared in bionic/libc/kernel/common/linux/videodev2.h
+     *  - String representation as defined in CameraParameters::PIXEL_FORMAT_XXX
+     *    strings in frameworks/base/include/camera/CameraParameters.h
+     *  - HAL_PIXEL_FORMAT_XXX format, as defined in system/core/include/system/graphics.h
+     * Since emulated camera device gets its data from the actual device, it gets
+     * pixel format in the original form. And that's the pixel format
+     * representation that will be returned from this method. HAL components will
+     * need to translate value returned from this method to the appropriate form.
+     * This method must be called only on started instance of this class, since
+     * it's applicable only when camera device is ready to stream frames.
+     * Param:
+     *  pix_fmt - Upon success contains the original pixel format.
+     * Return:
+     *  Current framebuffer's pixel format. Note that value returned from this
+     *  method is valid only in case if camera device has been started.
+     */
+    inline uint32_t getOriginalPixelFormat() const
+    {
+        ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
+        return mPixelFormat;
+    }
+
+    /* Gets image metadata (from HAL).
+     * Return:
+     *  Filled in ImageMetadata structure (in/out parameter).
+     */
+    status_t getImageMetadata(struct ::ImageMetadata* meta);
+
+    /*
+     * State checkers.
+     */
+
+    inline bool isInitialized() const {
+        /* Instance is initialized when the worker thread has been successfuly
+         * created (but not necessarily started). */
+        return mWorkerThread.get() != NULL && mState != ECDS_CONSTRUCTED;
+    }
+    inline bool isConnected() const {
+        /* Instance is connected when its status is either"connected", or
+         * "started". */
+        return mState == ECDS_CONNECTED || mState == ECDS_STARTED;
+    }
+    inline bool isStarted() const {
+        return mState == ECDS_STARTED;
+    }
+
+    /****************************************************************************
+     * Emulated camera device private API
+     ***************************************************************************/
+protected:
+    /* Performs common validation and calculation of startDevice parameters.
+     * Param:
+     *  width, height, pix_fmt, fps - Parameters passed to startDevice method.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t commonStartDevice(int width,
+                                       int height,
+                                       uint32_t pix_fmt,
+                                       int fps);
+
+    /* Performs common cleanup on stopDevice.
+     * This method will undo what commonStartDevice had done.
+     */
+    virtual void commonStopDevice();
+
+    /** Computes a luminance value after taking the exposure compensation.
+     * value into account.
+     *
+     * Param:
+     * inputY - The input luminance value.
+     * Return:
+     * The luminance value after adjusting the exposure compensation.
+     */
+    inline uint8_t changeExposure(const uint8_t& inputY) const {
+        return static_cast<uint8_t>(clamp(static_cast<float>(inputY) *
+                                    mExposureCompensation));
+    }
+
+    /** Simulates focusing and reports completion to the client.
+     */
+    void simulateAutoFocus();
+
+    /** Computes the pixel value in YUV space after adjusting to the current
+     * white balance mode.
+     */
+    void changeWhiteBalance(uint8_t& y, uint8_t& u, uint8_t& v) const;
+
+    /****************************************************************************
+     * Worker thread management.
+     * Typicaly when emulated camera device starts capturing frames from the
+     * actual device, it does that in a worker thread created in StartCapturing,
+     * and terminated in StopCapturing. Since this is such a typical scenario,
+     * it makes sence to encapsulate worker thread management in the base class
+     * for all emulated camera devices.
+     ***************************************************************************/
+
+protected:
+    /* Starts the worker thread.
+     * Typically, worker thread is started from startDeliveringFrames method of
+     * this class.
+     * Param:
+     *  one_burst - Controls how many times thread loop should run. If this
+     *      parameter is 'true', thread routine will run only once If this
+     *      parameter is 'false', thread routine will run until stopWorkerThread
+     *      method is called. See startDeliveringFrames for more info.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t startWorkerThread(bool one_burst);
+
+    /* Stops the worker thread.
+     * Note that this method will always wait for the worker thread to terminate.
+     * Typically, worker thread is started from stopDeliveringFrames method of
+     * this class.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t stopWorkerThread();
+
+    /* Implementation of the worker thread routine.
+     * In the default implementation of the worker thread routine we simply
+     * return 'false' forcing the thread loop to exit, and the thread to
+     * terminate. Derived class should override that method to provide there the
+     * actual frame delivery.
+     * Return:
+     *  true To continue thread loop (this method will be called again), or false
+     *  to exit the thread loop and to terminate the thread.
+     */
+    virtual bool inWorkerThread();
+
+    /* Encapsulates a worker thread used by the emulated camera device.
+     */
+    friend class WorkerThread;
+    class WorkerThread : public Thread {
+
+        /****************************************************************************
+         * Public API
+         ***************************************************************************/
+
+        public:
+            inline explicit WorkerThread(EmulatedCameraDevice* camera_dev)
+                : Thread(true),   // Callbacks may involve Java calls.
+                  mCameraDevice(camera_dev),
+                  mThreadControl(-1),
+                  mControlFD(-1)
+            {
+            }
+
+            inline ~WorkerThread()
+            {
+                ALOGW_IF(mThreadControl >= 0 || mControlFD >= 0,
+                        "%s: Control FDs are opened in the destructor",
+                        __FUNCTION__);
+                if (mThreadControl >= 0) {
+                    close(mThreadControl);
+                }
+                if (mControlFD >= 0) {
+                    close(mControlFD);
+                }
+            }
+
+            /* Starts the thread
+             * Param:
+             *  one_burst - Controls how many times thread loop should run. If
+             *      this parameter is 'true', thread routine will run only once
+             *      If this parameter is 'false', thread routine will run until
+             *      stopThread method is called. See startWorkerThread for more
+             *      info.
+             * Return:
+             *  NO_ERROR on success, or an appropriate error status.
+             */
+            inline status_t startThread(bool one_burst)
+            {
+                mOneBurst = one_burst;
+                return run("Camera_startThread", ANDROID_PRIORITY_URGENT_DISPLAY, 0);
+            }
+
+            /* Overriden base class method.
+             * It is overriden in order to provide one-time initialization just
+             * prior to starting the thread routine.
+             */
+            status_t readyToRun();
+
+            /* Stops the thread. */
+            status_t stopThread();
+
+            /* Values returned from the Select method of this class. */
+            enum SelectRes {
+                /* A timeout has occurred. */
+                TIMEOUT,
+                /* Data are available for read on the provided FD. */
+                READY,
+                /* Thread exit request has been received. */
+                EXIT_THREAD,
+                /* An error has occurred. */
+                ERROR
+            };
+
+            /* Select on an FD event, keeping in mind thread exit message.
+             * Param:
+             *  fd - File descriptor on which to wait for an event. This
+             *      parameter may be negative. If it is negative this method will
+             *      only wait on a control message to the thread.
+             *  timeout - Timeout in microseconds. 0 indicates no timeout (wait
+             *      forever).
+             * Return:
+             *  See SelectRes enum comments.
+             */
+            SelectRes Select(int fd, int timeout);
+
+        /****************************************************************************
+         * Private API
+         ***************************************************************************/
+
+        private:
+            /* Implements abstract method of the base Thread class. */
+            bool threadLoop()
+            {
+                /* Simply dispatch the call to the containing camera device. */
+                if (mCameraDevice->inWorkerThread()) {
+                    /* Respect "one burst" parameter (see startThread). */
+                    return !mOneBurst;
+                } else {
+                    return false;
+                }
+            }
+
+            /* Containing camera device object. */
+            EmulatedCameraDevice*   mCameraDevice;
+
+            /* FD that is used to send control messages into the thread. */
+            int                     mThreadControl;
+
+            /* FD that thread uses to receive control messages. */
+            int                     mControlFD;
+
+            /* Controls number of times the thread loop runs.
+             * See startThread for more information. */
+            bool                    mOneBurst;
+
+            /* Enumerates control messages that can be sent into the thread. */
+            enum ControlMessage {
+                /* Stop the thread. */
+                THREAD_STOP
+            };
+
+            Condition mSetup;
+    };
+
+    /* Worker thread accessor. */
+    inline WorkerThread* getWorkerThread() const
+    {
+        return mWorkerThread.get();
+    }
+
+    /****************************************************************************
+     * Data members
+     ***************************************************************************/
+
+protected:
+    /* Locks this instance for parameters, state, etc. change. */
+    Mutex                       mObjectLock;
+
+    /* Worker thread that is used in frame capturing. */
+    sp<WorkerThread>            mWorkerThread;
+
+    /* Timestamp of the current frame. */
+    nsecs_t                     mCurFrameTimestamp;
+
+    /* Emulated camera object containing this instance. */
+    EmulatedCamera*             mCameraHAL;
+
+    /* Framebuffer containing the current frame. */
+    uint8_t*                    mCurrentFrame;
+
+    /*
+     * Framebuffer properties.
+     */
+
+    /* Byte size of the framebuffer. */
+    size_t                      mFrameBufferSize;
+
+    /* Original pixel format (one of the V4L2_PIX_FMT_XXX values, as defined in
+     * bionic/libc/kernel/common/linux/videodev2.h */
+    uint32_t                    mPixelFormat;
+
+    /* Frame width */
+    int                         mFrameWidth;
+
+    /* Frame height */
+    int                         mFrameHeight;
+
+    /* Total number of pixels */
+    int                         mTotalPixels;
+
+    /* Requested FPS rate */
+    int                         mTargetFps;
+
+    /* Exposure compensation value */
+    float                       mExposureCompensation;
+
+    float*                      mWhiteBalanceScale;
+
+    bool                        mIsFocusing;
+
+    DefaultKeyedVector<String8, float*>      mSupportedWhiteBalanceScale;
+
+    /* Defines possible states of the emulated camera device object.
+     */
+    enum EmulatedCameraDeviceState {
+        /* Object has been constructed. */
+        ECDS_CONSTRUCTED,
+        /* Object has been initialized. */
+        ECDS_INITIALIZED,
+        /* Object has been connected to the physical device. */
+        ECDS_CONNECTED,
+        /* Camera device has been started. */
+        ECDS_STARTED,
+    };
+
+    /* Object state. */
+    EmulatedCameraDeviceState   mState;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_CAMERA_DEVICE_H */
diff --git a/guest/hals/camera/EmulatedCameraFactory.cpp b/guest/hals/camera/EmulatedCameraFactory.cpp
new file mode 100755
index 0000000..00a3c99
--- /dev/null
+++ b/guest/hals/camera/EmulatedCameraFactory.cpp
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedCameraFactory that manages cameras
+ * available for emulation.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Factory"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include "guest/libs/platform_support/api_level_fixes.h"
+#include "EmulatedFakeCamera.h"
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+#include "EmulatedFakeCamera2.h"
+#include "EmulatedCameraHotplugThread.h"
+#endif
+
+#if VSOC_PLATFORM_SDK_AFTER(L_MR1)
+#include "EmulatedFakeCamera3.h"
+#endif
+
+#include "EmulatedCameraFactory.h"
+
+extern camera_module_t HAL_MODULE_INFO_SYM;
+
+namespace android {
+EmulatedCameraFactory& EmulatedCameraFactory::Instance() {
+    static EmulatedCameraFactory* factory = new EmulatedCameraFactory;
+    return *factory;
+}
+
+EmulatedCameraFactory::EmulatedCameraFactory()
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+        : mCallbacks(NULL)
+#endif
+{
+    mCameraConfiguration.Init();
+    const std::vector<cvd::CameraDefinition>& cameras =
+        mCameraConfiguration.cameras();
+    for (size_t camera_index = 0;
+         camera_index < cameras.size();
+         ++camera_index) {
+        mCameraDefinitions.push(cameras[camera_index]);
+        /* Reserve a spot for camera, but don't create just yet. */
+        mEmulatedCameras.push(NULL);
+    }
+
+    ALOGV("%d cameras are being emulated.", getEmulatedCameraNum());
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+    /* Create hotplug thread */
+    {
+        mHotplugThread = new EmulatedCameraHotplugThread(getEmulatedCameraNum());
+        mHotplugThread->run("EmulatedCameraHotplugThread");
+    }
+#endif
+}
+
+EmulatedBaseCamera* EmulatedCameraFactory::getOrCreateFakeCamera(size_t cameraId) {
+    ::cvd::LockGuard< ::cvd::Mutex > lock(mEmulatedCamerasMutex);
+
+    if (cameraId >= getEmulatedCameraNum()) {
+        ALOGE("%s: Invalid camera ID: %d", __FUNCTION__, cameraId);
+        return NULL;
+    }
+
+    if (mEmulatedCameras[cameraId] != NULL) {
+        return mEmulatedCameras[cameraId];
+    }
+
+    const cvd::CameraDefinition& definition = mCameraDefinitions[cameraId];
+    bool is_back_facing =
+            (definition.orientation == cvd::CameraDefinition::kBack);
+
+    EmulatedBaseCamera* camera;
+    /* Create, and initialize the fake camera */
+    switch (definition.hal_version) {
+        case cvd::CameraDefinition::kHalV1:
+            camera = new EmulatedFakeCamera(cameraId, is_back_facing,
+                                            &HAL_MODULE_INFO_SYM.common);
+            break;
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+        case cvd::CameraDefinition::kHalV2:
+            camera = new EmulatedFakeCamera2(cameraId, is_back_facing,
+                                             &HAL_MODULE_INFO_SYM.common);
+            break;
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(L_MR1)
+        case cvd::CameraDefinition::kHalV3:
+            camera = new EmulatedFakeCamera3(cameraId, is_back_facing,
+                                        &HAL_MODULE_INFO_SYM.common);
+            break;
+#endif
+        default:
+            ALOGE("%s: Unsupported camera hal version requested: %d",
+                  __FUNCTION__, definition.hal_version);
+            return NULL;
+    }
+
+    ALOGI("%s: Camera device %d hal version is %d", __FUNCTION__,
+          cameraId, definition.hal_version);
+    int res = camera->Initialize(definition);
+
+    if (res != NO_ERROR) {
+        ALOGE("%s: Unable to intialize camera %d: %s (%d)",
+              __FUNCTION__, cameraId, strerror(-res), res);
+        delete camera;
+        return NULL;
+    }
+
+    ALOGI("%s: Inserting camera", __FUNCTION__);
+    mEmulatedCameras.replaceAt(camera, cameraId);
+    ALOGI("%s: Done", __FUNCTION__);
+    return camera;
+}
+
+EmulatedCameraFactory::~EmulatedCameraFactory()
+{
+    for (size_t n = 0; n < mEmulatedCameras.size(); n++) {
+        if (mEmulatedCameras[n] != NULL) {
+            delete mEmulatedCameras[n];
+        }
+    }
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+    if (mHotplugThread != NULL) {
+        mHotplugThread->requestExit();
+        mHotplugThread->join();
+    }
+#endif
+}
+
+/****************************************************************************
+ * Camera HAL API handlers.
+ *
+ * Each handler simply verifies existence of an appropriate EmulatedBaseCamera
+ * instance, and dispatches the call to that instance.
+ *
+ ***************************************************************************/
+
+int EmulatedCameraFactory::cameraDeviceOpen(int camera_id, hw_device_t** device)
+{
+    ALOGV("%s: id = %d", __FUNCTION__, camera_id);
+
+    *device = NULL;
+
+    EmulatedBaseCamera* camera = getOrCreateFakeCamera(camera_id);
+    if (camera == NULL) return -EINVAL;
+
+    return camera->connectCamera(device);
+}
+
+int EmulatedCameraFactory::getCameraInfo(int camera_id, struct camera_info* info)
+{
+    ALOGV("%s: id = %d", __FUNCTION__, camera_id);
+
+    EmulatedBaseCamera* camera = getOrCreateFakeCamera(camera_id);
+    if (camera == NULL) return -EINVAL;
+
+    return camera->getCameraInfo(info);
+}
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+int EmulatedCameraFactory::setCallbacks(
+        const camera_module_callbacks_t *callbacks)
+{
+    ALOGV("%s: callbacks = %p", __FUNCTION__, callbacks);
+
+    mCallbacks = callbacks;
+
+    return OK;
+}
+
+void EmulatedCameraFactory::getVendorTagOps(vendor_tag_ops_t* ops) {
+    ALOGV("%s: ops = %p", __FUNCTION__, ops);
+
+    // No vendor tags defined for emulator yet, so not touching ops
+}
+#endif
+
+int EmulatedCameraFactory::setTorchMode(const char* camera_id, bool enabled) {
+    ALOGV("%s: camera_id = %s, enabled =%d", __FUNCTION__, camera_id, enabled);
+
+    EmulatedBaseCamera* camera = getOrCreateFakeCamera(atoi(camera_id));
+    if (camera == NULL) return -EINVAL;
+
+    return camera->setTorchMode(enabled);
+}
+
+/****************************************************************************
+ * Camera HAL API callbacks.
+ ***************************************************************************/
+
+int EmulatedCameraFactory::device_open(const hw_module_t* module,
+                                       const char* name,
+                                       hw_device_t** device)
+{
+    /*
+     * Simply verify the parameters, and dispatch the call inside the
+     * EmulatedCameraFactory instance.
+     */
+
+    if (module != &HAL_MODULE_INFO_SYM.common) {
+        ALOGE("%s: Invalid module %p expected %p",
+             __FUNCTION__, module, &HAL_MODULE_INFO_SYM.common);
+        return -EINVAL;
+    }
+    if (name == NULL) {
+        ALOGE("%s: NULL name is not expected here", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    return EmulatedCameraFactory::Instance().cameraDeviceOpen(atoi(name), device);
+}
+
+int EmulatedCameraFactory::get_number_of_cameras(void)
+{
+    return EmulatedCameraFactory::Instance().getEmulatedCameraNum();
+}
+
+int EmulatedCameraFactory::get_camera_info(int camera_id,
+                                           struct camera_info* info)
+{
+    return EmulatedCameraFactory::Instance().getCameraInfo(camera_id, info);
+}
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+int EmulatedCameraFactory::set_callbacks(
+        const camera_module_callbacks_t *callbacks)
+{
+    return EmulatedCameraFactory::Instance().setCallbacks(callbacks);
+}
+
+void EmulatedCameraFactory::get_vendor_tag_ops(vendor_tag_ops_t* ops)
+{
+    EmulatedCameraFactory::Instance().getVendorTagOps(ops);
+}
+#endif
+
+int EmulatedCameraFactory::open_legacy(const struct hw_module_t* module,
+        const char* id, uint32_t halVersion, struct hw_device_t** device) {
+    // Not supporting legacy open
+    return -ENOSYS;
+}
+
+int EmulatedCameraFactory::set_torch_mode(const char* camera_id, bool enabled) {
+    return EmulatedCameraFactory::Instance().setTorchMode(camera_id, enabled);
+}
+
+/********************************************************************************
+ * Internal API
+ *******************************************************************************/
+
+void EmulatedCameraFactory::onStatusChanged(int cameraId, int newStatus) {
+
+    EmulatedBaseCamera *cam = getOrCreateFakeCamera(cameraId);
+    if (!cam) {
+        ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
+        return;
+    }
+
+    /**
+     * (Order is important)
+     * Send the callback first to framework, THEN close the camera.
+     */
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+    if (newStatus == cam->getHotplugStatus()) {
+        ALOGW("%s: Ignoring transition to the same status", __FUNCTION__);
+        return;
+    }
+
+    const camera_module_callbacks_t* cb = mCallbacks;
+    if (cb != NULL && cb->camera_device_status_change != NULL) {
+        cb->camera_device_status_change(cb, cameraId, newStatus);
+    }
+
+    if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
+        cam->unplugCamera();
+    } else if (newStatus == CAMERA_DEVICE_STATUS_PRESENT) {
+        cam->plugCamera();
+    }
+#endif
+
+}
+
+void EmulatedCameraFactory::onTorchModeStatusChanged(int cameraId, int newStatus) {
+    EmulatedBaseCamera *cam = getOrCreateFakeCamera(cameraId);
+    if (!cam) {
+        ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
+        return;
+    }
+
+#if VSOC_PLATFORM_SDK_AFTER(L_MR1)
+    const camera_module_callbacks_t* cb = mCallbacks;
+    if (cb != NULL && cb->torch_mode_status_change != NULL) {
+        char id[10];
+        sprintf(id, "%d", cameraId);
+        cb->torch_mode_status_change(cb, id, newStatus);
+    }
+#endif
+
+}
+
+/********************************************************************************
+ * Initializer for the static member structure.
+ *******************************************************************************/
+
+/* Entry point for camera HAL API. */
+struct hw_module_methods_t EmulatedCameraFactory::mCameraModuleMethods = {
+    VSOC_STATIC_INITIALIZER(open) EmulatedCameraFactory::device_open
+};
+
+}; /* namespace android */
diff --git a/guest/hals/camera/EmulatedCameraFactory.h b/guest/hals/camera/EmulatedCameraFactory.h
new file mode 100755
index 0000000..64588fd
--- /dev/null
+++ b/guest/hals/camera/EmulatedCameraFactory.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_EMULATED_CAMERA_FACTORY_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_FACTORY_H
+
+#include <utils/RefBase.h>
+
+#include <utils/Vector.h>
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+#include "CameraConfiguration.h"
+#include "EmulatedBaseCamera.h"
+
+namespace android {
+
+class EmulatedCameraHotplugThread;
+
+/*
+ * Contains declaration of a class EmulatedCameraFactory that manages cameras
+ * available for the emulation. A global instance of this class is statically
+ * instantiated and initialized when camera emulation HAL is loaded.
+ */
+
+/* Class EmulatedCameraFactoryManages cameras available for the emulation.
+ *
+ * When the global static instance of this class is created on the module load,
+ * it enumerates cameras available for the emulation by connecting to the
+ * emulator's 'camera' service. For every camera found out there it creates an
+ * instance of an appropriate class, and stores it an in array of emulated
+ * cameras. In addition to the cameras reported by the emulator, a fake camera
+ * emulator is always created, so there is always at least one camera that is
+ * available.
+ *
+ * Instance of this class is also used as the entry point for the camera HAL API,
+ * including:
+ *  - hw_module_methods_t::open entry point
+ *  - camera_module_t::get_number_of_cameras entry point
+ *  - camera_module_t::get_camera_info entry point
+ *
+ */
+class EmulatedCameraFactory {
+public:
+    /* Constructs EmulatedCameraFactory instance.
+     * In this constructor the factory will create and initialize a list of
+     * emulated cameras. All errors that occur on this constructor are reported
+     * via mConstructedOK data member of this class.
+     */
+    EmulatedCameraFactory();
+
+    /* Destructs EmulatedCameraFactory instance. */
+    ~EmulatedCameraFactory();
+
+    /****************************************************************************
+     * Camera HAL API handlers.
+     ***************************************************************************/
+
+public:
+    /* Returns a (singleton) instance of the EmulatedCameraFactory.
+     */
+    static EmulatedCameraFactory& Instance();
+
+    /* Opens (connects to) a camera device.
+     * This method is called in response to hw_module_methods_t::open callback.
+     */
+    int cameraDeviceOpen(int camera_id, hw_device_t** device);
+
+    /* Gets emulated camera information.
+     * This method is called in response to camera_module_t::get_camera_info callback.
+     */
+    int getCameraInfo(int camera_id, struct camera_info *info);
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+    /* Sets emulated camera callbacks.
+     * This method is called in response to camera_module_t::set_callbacks callback.
+     */
+    int setCallbacks(const camera_module_callbacks_t *callbacks);
+
+    /* Fill in vendor tags for the module
+     * This method is called in response to camera_module_t::get_vendor_tag_ops callback.
+     */
+    void getVendorTagOps(vendor_tag_ops_t* ops);
+#endif
+
+    int setTorchMode(const char* camera_id, bool enabled);
+
+    /****************************************************************************
+     * Camera HAL API callbacks.
+     ***************************************************************************/
+
+public:
+    /* camera_module_t::get_number_of_cameras callback entry point. */
+    static int get_number_of_cameras(void);
+
+    /* camera_module_t::get_camera_info callback entry point. */
+    static int get_camera_info(int camera_id, struct camera_info *info);
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+    /* camera_module_t::set_callbacks callback entry point. */
+    static int set_callbacks(const camera_module_callbacks_t *callbacks);
+
+    /* camera_module_t::get_vendor_tag_ops callback entry point */
+    static void get_vendor_tag_ops(vendor_tag_ops_t* ops);
+#endif
+
+    /* camera_module_t::open_legacy callback entry point */
+    static int open_legacy(const struct hw_module_t* module, const char* id,
+            uint32_t halVersion, struct hw_device_t** device);
+
+    static int set_torch_mode(const char* camera_id, bool enabled);
+
+private:
+    /* hw_module_methods_t::open callback entry point. */
+    static int device_open(const hw_module_t* module,
+                           const char* name,
+                           hw_device_t** device);
+
+    /****************************************************************************
+     * Public API.
+     ***************************************************************************/
+
+public:
+
+    /* Gets fake camera orientation. */
+    int getFakeCameraOrientation() {
+        /* TODO: Have a boot property that controls that. */
+        return 90;
+    }
+
+    /* Gets number of emulated cameras.
+     */
+    inline size_t getEmulatedCameraNum() const {
+        return mCameraDefinitions.size();
+    }
+
+    void onStatusChanged(int cameraId, int newStatus);
+
+    void onTorchModeStatusChanged(int cameraId, int newStatus);
+
+    /****************************************************************************
+     * Private API
+     ***************************************************************************/
+
+private:
+    /* Create new or return existing fake camera based on camera definition
+     * found in mCameraDefinitions.
+     * Returns NULL if cameraId is not valid (= not a valid index of
+     * mCameraDefinitions)
+     */
+    EmulatedBaseCamera* getOrCreateFakeCamera(size_t cameraId);
+
+    /****************************************************************************
+     * Data members.
+     ***************************************************************************/
+
+private:
+    /* Array of cameras available for the emulation. */
+    Vector<EmulatedBaseCamera*>  mEmulatedCameras;
+
+    /* Guards access to mEmulatedCameras. */
+    cvd::Mutex mEmulatedCamerasMutex;
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+    /* Camera callbacks (for status changing) */
+    const camera_module_callbacks_t* mCallbacks;
+
+    /* Hotplug thread (to call onStatusChanged) */
+    sp<EmulatedCameraHotplugThread> mHotplugThread;
+#endif
+
+    /* Back- and front camera properties accessed from the vsoc device. */
+    cvd::CameraConfiguration mCameraConfiguration;
+    Vector<cvd::CameraDefinition> mCameraDefinitions;
+
+public:
+    /* Contains device open entry point, as required by HAL API. */
+    static struct hw_module_methods_t   mCameraModuleMethods;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_CAMERA_FACTORY_H */
diff --git a/guest/hals/camera/EmulatedCameraHal.cpp b/guest/hals/camera/EmulatedCameraHal.cpp
new file mode 100755
index 0000000..24c7c6d
--- /dev/null
+++ b/guest/hals/camera/EmulatedCameraHal.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of the camera HAL layer in the system running
+ * under the emulator.
+ *
+ * This file contains only required HAL header, which directs all the API calls
+ * to the EmulatedCameraFactory class implementation, wich is responsible for
+ * managing emulated cameras.
+ */
+
+#include "EmulatedCameraFactory.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+/*
+ * Required HAL header.
+ */
+camera_module_t HAL_MODULE_INFO_SYM = {
+  VSOC_STATIC_INITIALIZER(common) {
+         VSOC_STATIC_INITIALIZER(tag)                HARDWARE_MODULE_TAG,
+#if VSOC_PLATFORM_SDK_AFTER(L_MR1)
+         VSOC_STATIC_INITIALIZER(module_api_version) CAMERA_MODULE_API_VERSION_2_4,
+#elif VSOC_PLATFORM_SDK_AFTER(K)
+         VSOC_STATIC_INITIALIZER(module_api_version) CAMERA_MODULE_API_VERSION_2_3,
+#elif VSOC_PLATFORM_SDK_AFTER(J_MR2)
+         VSOC_STATIC_INITIALIZER(module_api_version) CAMERA_MODULE_API_VERSION_2_2,
+#else
+         VSOC_STATIC_INITIALIZER(module_api_version) CAMERA_MODULE_API_VERSION_2_0,
+#endif
+         VSOC_STATIC_INITIALIZER(hal_api_version)    HARDWARE_HAL_API_VERSION,
+         VSOC_STATIC_INITIALIZER(id)                 CAMERA_HARDWARE_MODULE_ID,
+         VSOC_STATIC_INITIALIZER(name)               "Emulated Camera Module",
+         VSOC_STATIC_INITIALIZER(author)             "The Android Open Source Project",
+         VSOC_STATIC_INITIALIZER(methods)            &android::EmulatedCameraFactory::mCameraModuleMethods,
+         VSOC_STATIC_INITIALIZER(dso)                NULL,
+         VSOC_STATIC_INITIALIZER(reserved)           {0},
+    },
+    VSOC_STATIC_INITIALIZER(get_number_of_cameras)  android::EmulatedCameraFactory::get_number_of_cameras,
+    VSOC_STATIC_INITIALIZER(get_camera_info)        android::EmulatedCameraFactory::get_camera_info,
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+    VSOC_STATIC_INITIALIZER(set_callbacks)          android::EmulatedCameraFactory::set_callbacks,
+    VSOC_STATIC_INITIALIZER(get_vendor_tag_ops)     android::EmulatedCameraFactory::get_vendor_tag_ops,
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(K)
+    VSOC_STATIC_INITIALIZER(open_legacy)            android::EmulatedCameraFactory::open_legacy,
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(L_MR1)
+    set_torch_mode:         android::EmulatedCameraFactory::set_torch_mode,
+#endif
+};
diff --git a/guest/hals/camera/EmulatedCameraHotplugThread.cpp b/guest/hals/camera/EmulatedCameraHotplugThread.cpp
new file mode 100644
index 0000000..459f037
--- /dev/null
+++ b/guest/hals/camera/EmulatedCameraHotplugThread.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2013 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_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_HotplugThread"
+#include <cutils/log.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/inotify.h>
+
+#include "EmulatedCameraHotplugThread.h"
+#include "EmulatedCameraFactory.h"
+
+#define FAKE_HOTPLUG_FILE "/data/misc/media/emulator.camera.hotplug"
+
+#define EVENT_SIZE (sizeof(struct inotify_event))
+#define EVENT_BUF_LEN (1024*(EVENT_SIZE+16))
+
+#define SubscriberInfo EmulatedCameraHotplugThread::SubscriberInfo
+
+namespace android {
+
+EmulatedCameraHotplugThread::EmulatedCameraHotplugThread(
+    size_t totalCameraCount) :
+        Thread(/*canCallJava*/false) {
+
+    mRunning = true;
+    mInotifyFd = 0;
+
+    for (size_t id = 0; id < totalCameraCount; ++id) {
+        if (createFileIfNotExists(id)) {
+            mSubscribedCameraIds.push_back(id);
+        }
+    }
+}
+
+EmulatedCameraHotplugThread::~EmulatedCameraHotplugThread() {
+}
+
+status_t EmulatedCameraHotplugThread::requestExitAndWait() {
+    ALOGE("%s: Not implemented. Use requestExit + join instead",
+          __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+void EmulatedCameraHotplugThread::requestExit() {
+    Mutex::Autolock al(mMutex);
+
+    ALOGV("%s: Requesting thread exit", __FUNCTION__);
+    mRunning = false;
+
+    bool rmWatchFailed = false;
+    Vector<SubscriberInfo>::iterator it;
+    for (it = mSubscribers.begin(); it != mSubscribers.end(); ++it) {
+
+        if (inotify_rm_watch(mInotifyFd, it->WatchID) == -1) {
+
+            ALOGE("%s: Could not remove watch for camID '%d',"
+                  " error: '%s' (%d)",
+                 __FUNCTION__, it->CameraID, strerror(errno),
+                 errno);
+
+            rmWatchFailed = true ;
+        } else {
+            ALOGV("%s: Removed watch for camID '%d'",
+                __FUNCTION__, it->CameraID);
+        }
+    }
+
+    if (rmWatchFailed) { // unlikely
+        // Give the thread a fighting chance to error out on the next
+        // read
+        if (close(mInotifyFd) == -1) {
+            ALOGE("%s: close failure error: '%s' (%d)",
+                 __FUNCTION__, strerror(errno), errno);
+        }
+    }
+
+    ALOGV("%s: Request exit complete.", __FUNCTION__);
+}
+
+status_t EmulatedCameraHotplugThread::readyToRun() {
+    Mutex::Autolock al(mMutex);
+
+    mInotifyFd = -1;
+
+    do {
+        ALOGV("%s: Initializing inotify", __FUNCTION__);
+
+        mInotifyFd = inotify_init();
+        if (mInotifyFd == -1) {
+            ALOGE("%s: inotify_init failure error: '%s' (%d)",
+                 __FUNCTION__, strerror(errno), errno);
+            mRunning = false;
+            break;
+        }
+
+        /**
+         * For each fake camera file, add a watch for when
+         * the file is closed (if it was written to)
+         */
+        Vector<int>::const_iterator it, end;
+        it = mSubscribedCameraIds.begin();
+        end = mSubscribedCameraIds.end();
+        for (; it != end; ++it) {
+            int cameraId = *it;
+            if (!addWatch(cameraId)) {
+                mRunning = false;
+                break;
+            }
+        }
+    } while(false);
+
+    if (!mRunning) {
+        status_t err = -errno;
+
+        if (mInotifyFd != -1) {
+            close(mInotifyFd);
+        }
+
+        return err;
+    }
+
+    return OK;
+}
+
+bool EmulatedCameraHotplugThread::threadLoop() {
+
+    // If requestExit was already called, mRunning will be false
+    while (mRunning) {
+        char buffer[EVENT_BUF_LEN];
+        int length = TEMP_FAILURE_RETRY(
+                        read(mInotifyFd, buffer, EVENT_BUF_LEN));
+
+        if (length < 0) {
+            ALOGE("%s: Error reading from inotify FD, error: '%s' (%d)",
+                 __FUNCTION__, strerror(errno),
+                 errno);
+            mRunning = false;
+            break;
+        }
+
+        ALOGV("%s: Read %d bytes from inotify FD", __FUNCTION__, length);
+
+        int i = 0;
+        while (i < length) {
+            inotify_event* event = (inotify_event*) &buffer[i];
+
+            if (event->mask & IN_IGNORED) {
+                Mutex::Autolock al(mMutex);
+                if (!mRunning) {
+                    ALOGV("%s: Shutting down thread", __FUNCTION__);
+                    break;
+                } else {
+                    ALOGE("%s: File was deleted, aborting",
+                          __FUNCTION__);
+                    mRunning = false;
+                    break;
+                }
+            } else if (event->mask & IN_CLOSE_WRITE) {
+                int cameraId = getCameraId(event->wd);
+
+                if (cameraId < 0) {
+                    ALOGE("%s: Got bad camera ID from WD '%d",
+                          __FUNCTION__, event->wd);
+                } else {
+                    // Check the file for the new hotplug event
+                    String8 filePath = getFilePath(cameraId);
+                    /**
+                     * NOTE: we carefully avoid getting an inotify
+                     * for the same exact file because it's opened for
+                     * read-only, but our inotify is for write-only
+                     */
+                    int newStatus = readFile(filePath);
+
+                    if (newStatus < 0) {
+                        mRunning = false;
+                        break;
+                    }
+
+                    int halStatus = newStatus ?
+                        CAMERA_DEVICE_STATUS_PRESENT :
+                        CAMERA_DEVICE_STATUS_NOT_PRESENT;
+                    EmulatedCameraFactory::Instance().onStatusChanged(cameraId,
+                                                                      halStatus);
+                }
+
+            } else {
+                ALOGW("%s: Unknown mask 0x%x",
+                      __FUNCTION__, event->mask);
+            }
+
+            i += EVENT_SIZE + event->len;
+        }
+    }
+
+    if (!mRunning) {
+        close(mInotifyFd);
+        return false;
+    }
+
+    return true;
+}
+
+String8 EmulatedCameraHotplugThread::getFilePath(int cameraId) const {
+    return String8::format(FAKE_HOTPLUG_FILE ".%d", cameraId);
+}
+
+bool EmulatedCameraHotplugThread::createFileIfNotExists(int cameraId) const
+{
+    String8 filePath = getFilePath(cameraId);
+    // make sure this file exists and we have access to it
+    int fd = TEMP_FAILURE_RETRY(
+                open(filePath.string(), O_WRONLY | O_CREAT | O_TRUNC,
+                     /* mode = ug+rwx */ S_IRWXU | S_IRWXG ));
+    if (fd == -1) {
+        ALOGE("%s: Could not create file '%s', error: '%s' (%d)",
+             __FUNCTION__, filePath.string(), strerror(errno), errno);
+        return false;
+    }
+
+    // File has '1' by default since we are plugged in by default
+    if (TEMP_FAILURE_RETRY(write(fd, "1\n", /*count*/2)) == -1) {
+        ALOGE("%s: Could not write '1' to file '%s', error: '%s' (%d)",
+             __FUNCTION__, filePath.string(), strerror(errno), errno);
+        return false;
+    }
+
+    close(fd);
+    return true;
+}
+
+int EmulatedCameraHotplugThread::getCameraId(String8 filePath) const {
+    Vector<int>::const_iterator it, end;
+    it = mSubscribedCameraIds.begin();
+    end = mSubscribedCameraIds.end();
+    for (; it != end; ++it) {
+        String8 camPath = getFilePath(*it);
+
+        if (camPath == filePath) {
+            return *it;
+        }
+    }
+
+    return NAME_NOT_FOUND;
+}
+
+int EmulatedCameraHotplugThread::getCameraId(int wd) const {
+    for (size_t i = 0; i < mSubscribers.size(); ++i) {
+        if (mSubscribers[i].WatchID == wd) {
+            return mSubscribers[i].CameraID;
+        }
+    }
+
+    return NAME_NOT_FOUND;
+}
+
+SubscriberInfo* EmulatedCameraHotplugThread::getSubscriberInfo(int cameraId)
+{
+    for (size_t i = 0; i < mSubscribers.size(); ++i) {
+        if (mSubscribers[i].CameraID == cameraId) {
+            return (SubscriberInfo*)&mSubscribers[i];
+        }
+    }
+
+    return NULL;
+}
+
+bool EmulatedCameraHotplugThread::addWatch(int cameraId) {
+    String8 camPath = getFilePath(cameraId);
+    int wd = inotify_add_watch(mInotifyFd,
+                               camPath.string(),
+                               IN_CLOSE_WRITE);
+
+    if (wd == -1) {
+        ALOGE("%s: Could not add watch for '%s', error: '%s' (%d)",
+             __FUNCTION__, camPath.string(), strerror(errno),
+             errno);
+
+        mRunning = false;
+        return false;
+    }
+
+    ALOGV("%s: Watch added for camID='%d', wd='%d'",
+          __FUNCTION__, cameraId, wd);
+
+    SubscriberInfo si = { cameraId, wd };
+    mSubscribers.push_back(si);
+
+    return true;
+}
+
+bool EmulatedCameraHotplugThread::removeWatch(int cameraId) {
+    SubscriberInfo* si = getSubscriberInfo(cameraId);
+
+    if (!si) return false;
+
+    if (inotify_rm_watch(mInotifyFd, si->WatchID) == -1) {
+
+        ALOGE("%s: Could not remove watch for camID '%d', error: '%s' (%d)",
+             __FUNCTION__, cameraId, strerror(errno),
+             errno);
+
+        return false;
+    }
+
+    Vector<SubscriberInfo>::iterator it;
+    for (it = mSubscribers.begin(); it != mSubscribers.end(); ++it) {
+        if (it->CameraID == cameraId) {
+            break;
+        }
+    }
+
+    if (it != mSubscribers.end()) {
+        mSubscribers.erase(it);
+    }
+
+    return true;
+}
+
+int EmulatedCameraHotplugThread::readFile(String8 filePath) const {
+
+    int fd = TEMP_FAILURE_RETRY(
+                open(filePath.string(), O_RDONLY, /*mode*/0));
+    if (fd == -1) {
+        ALOGE("%s: Could not open file '%s', error: '%s' (%d)",
+             __FUNCTION__, filePath.string(), strerror(errno), errno);
+        return -1;
+    }
+
+    char buffer[1];
+    int length;
+
+    length = TEMP_FAILURE_RETRY(
+                    read(fd, buffer, sizeof(buffer)));
+
+    int retval;
+
+    ALOGV("%s: Read file '%s', length='%d', buffer='%c'",
+         __FUNCTION__, filePath.string(), length, buffer[0]);
+
+    if (length == 0) { // EOF
+        retval = 0; // empty file is the same thing as 0
+    } else if (buffer[0] == '0') {
+        retval = 0;
+    } else { // anything non-empty that's not beginning with '0'
+        retval = 1;
+    }
+
+    close(fd);
+
+    return retval;
+}
+
+} //namespace android
diff --git a/guest/hals/camera/EmulatedCameraHotplugThread.h b/guest/hals/camera/EmulatedCameraHotplugThread.h
new file mode 100644
index 0000000..145322e
--- /dev/null
+++ b/guest/hals/camera/EmulatedCameraHotplugThread.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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 HW_EMULATOR_CAMERA_EMULATED_CAMERA_HOTPLUG_H
+#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_HOTPLUG_H
+
+/**
+ * This class emulates hotplug events by inotifying on a file, specific
+ * to a camera ID. When the file changes between 1/0 the hotplug
+ * status goes between PRESENT and NOT_PRESENT.
+ *
+ * Refer to FAKE_HOTPLUG_FILE in EmulatedCameraHotplugThread.cpp
+ */
+
+#include "EmulatedCamera2.h"
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace android {
+class EmulatedCameraHotplugThread : public Thread {
+  public:
+    EmulatedCameraHotplugThread(size_t totalCameraCount);
+    ~EmulatedCameraHotplugThread();
+
+    virtual void requestExit();
+    virtual status_t requestExitAndWait();
+
+  private:
+
+
+    virtual status_t readyToRun();
+    virtual bool threadLoop();
+
+    struct SubscriberInfo {
+        int CameraID;
+        int WatchID;
+    };
+
+    bool addWatch(int cameraId);
+    bool removeWatch(int cameraId);
+    SubscriberInfo* getSubscriberInfo(int cameraId);
+
+    int getCameraId(String8 filePath) const;
+    int getCameraId(int wd) const;
+
+    String8 getFilePath(int cameraId) const;
+    int readFile(String8 filePath) const;
+
+    bool createFileIfNotExists(int cameraId) const;
+
+    int mInotifyFd;
+    Vector<int> mSubscribedCameraIds;
+    Vector<SubscriberInfo> mSubscribers;
+
+    // variables above are unguarded:
+    // -- accessed in thread loop or in constructor only
+
+    Mutex mMutex;
+
+    bool mRunning;          // guarding only when it's important
+};
+} // namespace android
+
+#endif
diff --git a/guest/hals/camera/EmulatedFakeCamera.cpp b/guest/hals/camera/EmulatedFakeCamera.cpp
new file mode 100755
index 0000000..55dbc6c
--- /dev/null
+++ b/guest/hals/camera/EmulatedFakeCamera.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedFakeCamera that encapsulates
+ * functionality of a fake camera.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_FakeCamera"
+#include <sstream>
+#include <string>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include "EmulatedFakeCamera.h"
+#include "EmulatedCameraFactory.h"
+
+namespace android {
+
+EmulatedFakeCamera::EmulatedFakeCamera(int cameraId,
+                                       bool facingBack,
+                                       struct hw_module_t* module)
+        : EmulatedCamera(cameraId, module),
+          mFacingBack(facingBack),
+          mFakeCameraDevice(this)
+{
+}
+
+EmulatedFakeCamera::~EmulatedFakeCamera()
+{
+}
+
+/****************************************************************************
+ * Public API overrides
+ ***************************************************************************/
+
+status_t EmulatedFakeCamera::Initialize(const cvd::CameraDefinition& params)
+{
+    status_t res = mFakeCameraDevice.Initialize();
+    if (res != NO_ERROR) {
+        return res;
+    }
+
+    const char* facing = mFacingBack ? EmulatedCamera::FACING_BACK :
+                                       EmulatedCamera::FACING_FRONT;
+
+    mParameters.set(EmulatedCamera::FACING_KEY, facing);
+    ALOGD("%s: Fake camera is facing %s", __FUNCTION__, facing);
+
+    mParameters.set(EmulatedCamera::ORIENTATION_KEY,
+                    EmulatedCameraFactory::Instance().getFakeCameraOrientation());
+
+    res = EmulatedCamera::Initialize(params);
+    if (res != NO_ERROR) {
+        return res;
+    }
+
+    /*
+     * Parameters provided by the camera device.
+     */
+
+    /* 352x288 and 320x240 frame dimensions are required by the framework for
+     * video mode preview and video recording. */
+    std::ostringstream resolutions;
+    for (size_t index = 0; index < params.resolutions.size(); ++index) {
+      if (resolutions.str().size()) {
+        resolutions << ",";
+      }
+      resolutions << params.resolutions[index].width << "x"
+                  << params.resolutions[index].height;
+    }
+
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES,
+                    resolutions.str().c_str());
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES,
+                    resolutions.str().c_str());
+    mParameters.setPreviewSize(640, 480);
+    mParameters.setPictureSize(640, 480);
+
+    mParameters.set(CameraParameters::KEY_SUPPORTED_ANTIBANDING,
+                    CameraParameters::ANTIBANDING_AUTO);
+    mParameters.set(CameraParameters::KEY_ANTIBANDING,
+                    CameraParameters::ANTIBANDING_AUTO);
+    mParameters.set(CameraParameters::KEY_SUPPORTED_EFFECTS,
+                    CameraParameters::EFFECT_NONE);
+    mParameters.set(CameraParameters::KEY_EFFECT,
+                    CameraParameters::EFFECT_NONE);
+
+
+    return NO_ERROR;
+}
+
+EmulatedCameraDevice* EmulatedFakeCamera::getCameraDevice()
+{
+    return &mFakeCameraDevice;
+}
+
+};  /* namespace android */
diff --git a/guest/hals/camera/EmulatedFakeCamera.h b/guest/hals/camera/EmulatedFakeCamera.h
new file mode 100755
index 0000000..8657df9
--- /dev/null
+++ b/guest/hals/camera/EmulatedFakeCamera.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_H
+#define HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_H
+
+/*
+ * Contains declaration of a class EmulatedFakeCamera that encapsulates
+ * functionality of a fake camera. This class is nothing more than a placeholder
+ * for EmulatedFakeCameraDevice instance.
+ */
+
+#include "EmulatedCamera.h"
+#include "EmulatedFakeCameraDevice.h"
+
+namespace android {
+
+/* Encapsulates functionality of a fake camera.
+ * This class is nothing more than a placeholder for EmulatedFakeCameraDevice
+ * instance that emulates a fake camera device.
+ */
+class EmulatedFakeCamera : public EmulatedCamera {
+public:
+    /* Constructs EmulatedFakeCamera instance. */
+    EmulatedFakeCamera(int cameraId, bool facingBack, struct hw_module_t* module);
+
+    /* Destructs EmulatedFakeCamera instance. */
+    ~EmulatedFakeCamera();
+
+    /****************************************************************************
+     * EmulatedCamera virtual overrides.
+     ***************************************************************************/
+
+public:
+    /* Initializes EmulatedFakeCamera instance. */
+     status_t Initialize(const cvd::CameraDefinition& params);
+
+    /****************************************************************************
+     * EmulatedCamera abstract API implementation.
+     ***************************************************************************/
+
+protected:
+    /* Gets emulated camera device ised by this instance of the emulated camera.
+     */
+    EmulatedCameraDevice* getCameraDevice();
+
+    /****************************************************************************
+     * Data memebers.
+     ***************************************************************************/
+
+protected:
+    /* Facing back (true) or front (false) switch. */
+    bool                        mFacingBack;
+
+    /* Contained fake camera device object. */
+    EmulatedFakeCameraDevice    mFakeCameraDevice;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_H */
diff --git a/guest/hals/camera/EmulatedFakeCamera2.cpp b/guest/hals/camera/EmulatedFakeCamera2.cpp
new file mode 100644
index 0000000..e5f2387
--- /dev/null
+++ b/guest/hals/camera/EmulatedFakeCamera2.cpp
@@ -0,0 +1,2765 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedFakeCamera2 that encapsulates
+ * functionality of an advanced fake camera.
+ */
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_FakeCamera2"
+#include <utils/Log.h>
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+#include "EmulatedFakeCamera2.h"
+#include "EmulatedCameraFactory.h"
+#include "GrallocModule.h"
+
+#define ERROR_CAMERA_NOT_PRESENT -EPIPE
+
+#define CAMERA2_EXT_TRIGGER_TESTING_DISCONNECT 0xFFFFFFFF
+
+namespace android {
+
+const int64_t USEC = 1000LL;
+const int64_t MSEC = USEC * 1000LL;
+const int64_t SEC = MSEC * 1000LL;
+
+const uint32_t EmulatedFakeCamera2::kAvailableFormats[] = {
+#if VSOC_PLATFORM_SDK_AFTER(K)
+        HAL_PIXEL_FORMAT_RAW16,
+#endif
+        HAL_PIXEL_FORMAT_BLOB,
+        HAL_PIXEL_FORMAT_RGBA_8888,
+        //        HAL_PIXEL_FORMAT_YV12,
+        HAL_PIXEL_FORMAT_YCrCb_420_SP
+};
+
+const uint32_t EmulatedFakeCamera2::kAvailableRawSizes[2] = {
+    640, 480
+    //    mSensorWidth, mSensorHeight
+};
+
+const uint64_t EmulatedFakeCamera2::kAvailableRawMinDurations[1] = {
+    static_cast<uint64_t>(Sensor::kFrameDurationRange[0])
+};
+
+const uint32_t EmulatedFakeCamera2::kAvailableProcessedSizesBack[4] = {
+    640, 480, 320, 240
+    //    mSensorWidth, mSensorHeight
+};
+
+const uint32_t EmulatedFakeCamera2::kAvailableProcessedSizesFront[4] = {
+    320, 240, 160, 120
+    //    mSensorWidth, mSensorHeight
+};
+
+const uint64_t EmulatedFakeCamera2::kAvailableProcessedMinDurations[1] = {
+    static_cast<uint64_t>(Sensor::kFrameDurationRange[0])
+};
+
+const uint32_t EmulatedFakeCamera2::kAvailableJpegSizesBack[2] = {
+    640, 480
+    //    mSensorWidth, mSensorHeight
+};
+
+const uint32_t EmulatedFakeCamera2::kAvailableJpegSizesFront[2] = {
+    320, 240
+    //    mSensorWidth, mSensorHeight
+};
+
+
+const uint64_t EmulatedFakeCamera2::kAvailableJpegMinDurations[1] = {
+    static_cast<uint64_t>(Sensor::kFrameDurationRange[0])
+};
+
+
+EmulatedFakeCamera2::EmulatedFakeCamera2(int cameraId,
+        bool facingBack,
+        struct hw_module_t* module)
+        : EmulatedCamera2(cameraId,module),
+          mFacingBack(facingBack),
+          mIsConnected(false)
+{
+    ALOGD("Constructing emulated fake camera 2 facing %s",
+            facingBack ? "back" : "front");
+}
+
+EmulatedFakeCamera2::~EmulatedFakeCamera2() {
+    if (mCameraInfo != NULL) {
+        free_camera_metadata(mCameraInfo);
+    }
+}
+
+/****************************************************************************
+ * Public API overrides
+ ***************************************************************************/
+
+status_t EmulatedFakeCamera2::Initialize(const cvd::CameraDefinition& params) {
+    status_t res;
+
+    for (size_t index = 0; index < params.resolutions.size(); ++index) {
+        mAvailableRawSizes.push_back(params.resolutions[index].width);
+        mAvailableRawSizes.push_back(params.resolutions[index].height);
+        mAvailableProcessedSizes.push_back(params.resolutions[index].width);
+        mAvailableProcessedSizes.push_back(params.resolutions[index].height);
+        mAvailableJpegSizes.push_back(params.resolutions[index].width);
+        mAvailableJpegSizes.push_back(params.resolutions[index].height);
+    }
+
+    // Find max width/height
+    int32_t width = 0, height = 0;
+    for (size_t index = 0; index < params.resolutions.size(); ++index) {
+        if (width <= params.resolutions[index].width &&
+            height <= params.resolutions[index].height) {
+            width = params.resolutions[index].width;
+            height = params.resolutions[index].height;
+        }
+    }
+    if (width < 640 || height < 480) {
+        width = 640;
+        height = 480;
+    }
+    mSensorWidth = width;
+    mSensorHeight = height;
+
+    /* TODO(ender): probably should drop this. */
+    std::copy(kAvailableRawSizes,
+              kAvailableRawSizes + arraysize(kAvailableRawSizes),
+              std::back_inserter(mAvailableRawSizes));
+
+    if (params.orientation == cvd::CameraDefinition::kFront) {
+      std::copy(kAvailableProcessedSizesFront,
+                kAvailableProcessedSizesFront +
+                arraysize(kAvailableProcessedSizesFront),
+                std::back_inserter(mAvailableProcessedSizes));
+      std::copy(kAvailableJpegSizesFront,
+                kAvailableJpegSizesFront + arraysize(kAvailableJpegSizesFront),
+                std::back_inserter(mAvailableJpegSizes));
+    } else {
+      std::copy(kAvailableProcessedSizesBack,
+                kAvailableProcessedSizesBack +
+                arraysize(kAvailableProcessedSizesBack),
+                mAvailableProcessedSizes.begin());
+      std::copy(kAvailableJpegSizesBack,
+                kAvailableJpegSizesBack + arraysize(kAvailableJpegSizesBack),
+                mAvailableJpegSizes.begin());
+    }
+
+    res = constructStaticInfo(&mCameraInfo, true);
+    if (res != OK) {
+        ALOGE("%s: Unable to allocate static info: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        return res;
+    }
+    res = constructStaticInfo(&mCameraInfo, false);
+    if (res != OK) {
+        ALOGE("%s: Unable to fill in static info: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        return res;
+    }
+    if (res != OK) return res;
+
+    mNextStreamId = 1;
+    mNextReprocessStreamId = 1;
+    mRawStreamCount = 0;
+    mProcessedStreamCount = 0;
+    mJpegStreamCount = 0;
+    mReprocessStreamCount = 0;
+
+    return NO_ERROR;
+}
+
+/****************************************************************************
+ * Camera module API overrides
+ ***************************************************************************/
+
+status_t EmulatedFakeCamera2::connectCamera(hw_device_t** device) {
+    status_t res;
+    ALOGV("%s", __FUNCTION__);
+
+    {
+        Mutex::Autolock l(mMutex);
+        if (!mStatusPresent) {
+            ALOGE("%s: Camera ID %d is unplugged", __FUNCTION__,
+                  mCameraID);
+            return -ENODEV;
+        }
+    }
+
+    mConfigureThread = new ConfigureThread(this);
+    mReadoutThread = new ReadoutThread(this);
+    mControlThread = new ControlThread(this);
+    mSensor = new Sensor(mSensorWidth, mSensorHeight);
+    mJpegCompressor = new JpegCompressor();
+
+    mNextStreamId = 1;
+    mNextReprocessStreamId = 1;
+
+    res = mSensor->startUp();
+    if (res != NO_ERROR) return res;
+
+    res = mConfigureThread->run("EmulatedFakeCamera2::configureThread");
+    if (res != NO_ERROR) return res;
+
+    res = mReadoutThread->run("EmulatedFakeCamera2::readoutThread");
+    if (res != NO_ERROR) return res;
+
+    res = mControlThread->run("EmulatedFakeCamera2::controlThread");
+    if (res != NO_ERROR) return res;
+
+    status_t ret = EmulatedCamera2::connectCamera(device);
+
+    if (ret >= 0) {
+        mIsConnected = true;
+    }
+
+    return ret;
+}
+
+status_t EmulatedFakeCamera2::plugCamera() {
+    {
+        Mutex::Autolock l(mMutex);
+
+        if (!mStatusPresent) {
+            ALOGI("%s: Plugged back in", __FUNCTION__);
+            mStatusPresent = true;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t EmulatedFakeCamera2::unplugCamera() {
+    {
+        Mutex::Autolock l(mMutex);
+
+        if (mStatusPresent) {
+            ALOGI("%s: Unplugged camera", __FUNCTION__);
+            mStatusPresent = false;
+        }
+    }
+
+    return closeCamera();
+}
+
+camera_device_status_t EmulatedFakeCamera2::getHotplugStatus() {
+    Mutex::Autolock l(mMutex);
+    return mStatusPresent ?
+        CAMERA_DEVICE_STATUS_PRESENT :
+        CAMERA_DEVICE_STATUS_NOT_PRESENT;
+}
+
+
+
+status_t EmulatedFakeCamera2::closeCamera() {
+    {
+        Mutex::Autolock l(mMutex);
+
+        status_t res;
+        ALOGV("%s", __FUNCTION__);
+
+        if (!mIsConnected) {
+            return NO_ERROR;
+        }
+
+        res = mSensor->shutDown();
+        if (res != NO_ERROR) {
+            ALOGE("%s: Unable to shut down sensor: %d", __FUNCTION__, res);
+            return res;
+        }
+
+        mConfigureThread->requestExit();
+        mReadoutThread->requestExit();
+        mControlThread->requestExit();
+        mJpegCompressor->cancel();
+    }
+
+    // give up the lock since we will now block and the threads
+    // can call back into this object
+    mConfigureThread->join();
+    mReadoutThread->join();
+    mControlThread->join();
+
+    ALOGV("%s exit", __FUNCTION__);
+
+    {
+        Mutex::Autolock l(mMutex);
+        mIsConnected = false;
+    }
+
+    return NO_ERROR;
+}
+
+status_t EmulatedFakeCamera2::getCameraInfo(struct camera_info *info) {
+    info->facing = mFacingBack ? CAMERA_FACING_BACK : CAMERA_FACING_FRONT;
+    info->orientation = EmulatedCameraFactory::Instance().getFakeCameraOrientation();
+    return EmulatedCamera2::getCameraInfo(info);
+}
+
+/****************************************************************************
+ * Camera device API overrides
+ ***************************************************************************/
+
+/** Request input queue */
+
+int EmulatedFakeCamera2::requestQueueNotify() {
+    ALOGV("Request queue notification received");
+
+    ALOG_ASSERT(mRequestQueueSrc != NULL,
+            "%s: Request queue src not set, but received queue notification!",
+            __FUNCTION__);
+    ALOG_ASSERT(mFrameQueueDst != NULL,
+            "%s: Request queue src not set, but received queue notification!",
+            __FUNCTION__);
+    ALOG_ASSERT(mStreams.size() != 0,
+            "%s: No streams allocated, but received queue notification!",
+            __FUNCTION__);
+    return mConfigureThread->newRequestAvailable();
+}
+
+int EmulatedFakeCamera2::getInProgressCount() {
+    Mutex::Autolock l(mMutex);
+
+    if (!mStatusPresent) {
+        ALOGW("%s: Camera was physically disconnected", __FUNCTION__);
+        return ERROR_CAMERA_NOT_PRESENT;
+    }
+
+    int requestCount = 0;
+    requestCount += mConfigureThread->getInProgressCount();
+    requestCount += mReadoutThread->getInProgressCount();
+    requestCount += mJpegCompressor->isBusy() ? 1 : 0;
+
+    return requestCount;
+}
+
+int EmulatedFakeCamera2::constructDefaultRequest(
+        int request_template,
+        camera_metadata_t **request) {
+
+    if (request == NULL) return BAD_VALUE;
+    if (request_template < 0 || request_template >= CAMERA2_TEMPLATE_COUNT) {
+        return BAD_VALUE;
+    }
+
+    {
+        Mutex::Autolock l(mMutex);
+        if (!mStatusPresent) {
+            ALOGW("%s: Camera was physically disconnected", __FUNCTION__);
+            return ERROR_CAMERA_NOT_PRESENT;
+        }
+    }
+
+    status_t res;
+    // Pass 1, calculate size and allocate
+    res = constructDefaultRequest(request_template,
+            request,
+            true);
+    if (res != OK) {
+        return res;
+    }
+    // Pass 2, build request
+    res = constructDefaultRequest(request_template,
+            request,
+            false);
+    if (res != OK) {
+        ALOGE("Unable to populate new request for template %d",
+                request_template);
+    }
+
+    return res;
+}
+
+int EmulatedFakeCamera2::allocateStream(
+        uint32_t width,
+        uint32_t height,
+        int format,
+        const camera2_stream_ops_t *stream_ops,
+        uint32_t *stream_id,
+        uint32_t *format_actual,
+        uint32_t *usage,
+        uint32_t *max_buffers) {
+    Mutex::Autolock l(mMutex);
+
+    if (!mStatusPresent) {
+        ALOGW("%s: Camera was physically disconnected", __FUNCTION__);
+        return ERROR_CAMERA_NOT_PRESENT;
+    }
+
+    // Temporary shim until FORMAT_ZSL is removed
+    if (format == CAMERA2_HAL_PIXEL_FORMAT_ZSL) {
+        format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+    }
+
+    if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+        unsigned int numFormats = sizeof(kAvailableFormats) / sizeof(uint32_t);
+        unsigned int formatIdx = 0;
+        unsigned int sizeOffsetIdx = 0;
+        for (; formatIdx < numFormats; formatIdx++) {
+            if (format == (int)kAvailableFormats[formatIdx]) break;
+        }
+        if (formatIdx == numFormats) {
+            ALOGE("%s: Format 0x%x is not supported", __FUNCTION__, format);
+            return BAD_VALUE;
+        }
+    }
+
+    const uint32_t *availableSizes;
+    size_t availableSizeCount;
+    switch (format) {
+#if VSOC_PLATFORM_SDK_AFTER(K)
+        case HAL_PIXEL_FORMAT_RAW16:
+            availableSizes = &mAvailableRawSizes.front();
+            availableSizeCount = mAvailableRawSizes.size();
+            break;
+#endif
+        case HAL_PIXEL_FORMAT_BLOB:
+            availableSizes = &mAvailableJpegSizes.front();
+            availableSizeCount = mAvailableJpegSizes.size();
+            break;
+        case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+        case HAL_PIXEL_FORMAT_RGBA_8888:
+        case HAL_PIXEL_FORMAT_YV12:
+        case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+            availableSizes = &mAvailableProcessedSizes.front();
+            availableSizeCount = mAvailableProcessedSizes.size();
+            break;
+        default:
+            ALOGE("%s: Unknown format 0x%x", __FUNCTION__, format);
+            return BAD_VALUE;
+    }
+
+    unsigned int resIdx = 0;
+    for (; resIdx < availableSizeCount; resIdx++) {
+        if (availableSizes[resIdx * 2] == width &&
+                availableSizes[resIdx * 2 + 1] == height) break;
+    }
+    if (resIdx == availableSizeCount) {
+        ALOGE("%s: Format 0x%x does not support resolution %d, %d", __FUNCTION__,
+                format, width, height);
+        return BAD_VALUE;
+    }
+
+    switch (format) {
+#if VSOC_PLATFORM_SDK_AFTER(K)
+        case HAL_PIXEL_FORMAT_RAW16:
+            if (mRawStreamCount >= kMaxRawStreamCount) {
+                ALOGE("%s: Cannot allocate another raw stream (%d already allocated)",
+                        __FUNCTION__, mRawStreamCount);
+                return INVALID_OPERATION;
+            }
+            mRawStreamCount++;
+            break;
+#endif
+        case HAL_PIXEL_FORMAT_BLOB:
+            if (mJpegStreamCount >= kMaxJpegStreamCount) {
+                ALOGE("%s: Cannot allocate another JPEG stream (%d already allocated)",
+                        __FUNCTION__, mJpegStreamCount);
+                return INVALID_OPERATION;
+            }
+            mJpegStreamCount++;
+            break;
+        default:
+            if (mProcessedStreamCount >= kMaxProcessedStreamCount) {
+                ALOGE("%s: Cannot allocate another processed stream (%d already allocated)",
+                        __FUNCTION__, mProcessedStreamCount);
+                return INVALID_OPERATION;
+            }
+            mProcessedStreamCount++;
+    }
+
+    Stream newStream;
+    newStream.ops = stream_ops;
+    newStream.width = width;
+    newStream.height = height;
+    newStream.format = format;
+    // TODO: Query stride from gralloc
+    newStream.stride = width;
+
+    mStreams.add(mNextStreamId, newStream);
+
+    *stream_id = mNextStreamId;
+    if (format_actual) *format_actual = format;
+    *usage = GRALLOC_USAGE_HW_CAMERA_WRITE;
+    *max_buffers = kMaxBufferCount;
+
+    ALOGV("Stream allocated: %d, %d x %d, 0x%x. U: %x, B: %d",
+            *stream_id, width, height, format, *usage, *max_buffers);
+
+    mNextStreamId++;
+    return NO_ERROR;
+}
+
+int EmulatedFakeCamera2::registerStreamBuffers(
+            uint32_t stream_id,
+            int num_buffers,
+            buffer_handle_t *buffers) {
+    Mutex::Autolock l(mMutex);
+
+    if (!mStatusPresent) {
+        ALOGW("%s: Camera was physically disconnected", __FUNCTION__);
+        return ERROR_CAMERA_NOT_PRESENT;
+    }
+
+    ALOGV("%s: Stream %d registering %d buffers", __FUNCTION__,
+            stream_id, num_buffers);
+    // Need to find out what the final concrete pixel format for our stream is
+    // Assumes that all buffers have the same format.
+    if (num_buffers < 1) {
+        ALOGE("%s: Stream %d only has %d buffers!",
+                __FUNCTION__, stream_id, num_buffers);
+        return BAD_VALUE;
+    }
+
+    ssize_t streamIndex = mStreams.indexOfKey(stream_id);
+    if (streamIndex < 0) {
+        ALOGE("%s: Unknown stream id %d!", __FUNCTION__, stream_id);
+        return BAD_VALUE;
+    }
+
+    Stream &stream = mStreams.editValueAt(streamIndex);
+
+    int finalFormat = stream.format;
+
+    if (finalFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+        finalFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+    }
+
+    ALOGV("%s: Stream %d format set to %x, previously %x",
+            __FUNCTION__, stream_id, finalFormat, stream.format);
+
+    stream.format = finalFormat;
+
+    return NO_ERROR;
+}
+
+int EmulatedFakeCamera2::releaseStream(uint32_t stream_id) {
+    Mutex::Autolock l(mMutex);
+
+    ssize_t streamIndex = mStreams.indexOfKey(stream_id);
+    if (streamIndex < 0) {
+        ALOGE("%s: Unknown stream id %d!", __FUNCTION__, stream_id);
+        return BAD_VALUE;
+    }
+
+    if (isStreamInUse(stream_id)) {
+        ALOGE("%s: Cannot release stream %d; in use!", __FUNCTION__,
+                stream_id);
+        return BAD_VALUE;
+    }
+
+    switch(mStreams.valueAt(streamIndex).format) {
+#if VSOC_PLATFORM_SDK_AFTER(K)
+        case HAL_PIXEL_FORMAT_RAW16:
+            mRawStreamCount--;
+            break;
+#endif
+        case HAL_PIXEL_FORMAT_BLOB:
+            mJpegStreamCount--;
+            break;
+        default:
+            mProcessedStreamCount--;
+            break;
+    }
+
+    mStreams.removeItemsAt(streamIndex);
+
+    return NO_ERROR;
+}
+
+int EmulatedFakeCamera2::allocateReprocessStreamFromStream(
+        uint32_t output_stream_id,
+        const camera2_stream_in_ops_t *stream_ops,
+        uint32_t *stream_id) {
+    Mutex::Autolock l(mMutex);
+
+    if (!mStatusPresent) {
+        ALOGW("%s: Camera was physically disconnected", __FUNCTION__);
+        return ERROR_CAMERA_NOT_PRESENT;
+    }
+
+    ssize_t baseStreamIndex = mStreams.indexOfKey(output_stream_id);
+    if (baseStreamIndex < 0) {
+        ALOGE("%s: Unknown output stream id %d!", __FUNCTION__, output_stream_id);
+        return BAD_VALUE;
+    }
+
+    const Stream &baseStream = mStreams[baseStreamIndex];
+
+    // We'll reprocess anything we produced
+
+    if (mReprocessStreamCount >= kMaxReprocessStreamCount) {
+        ALOGE("%s: Cannot allocate another reprocess stream (%d already allocated)",
+                __FUNCTION__, mReprocessStreamCount);
+        return INVALID_OPERATION;
+    }
+    mReprocessStreamCount++;
+
+    ReprocessStream newStream;
+    newStream.ops = stream_ops;
+    newStream.width = baseStream.width;
+    newStream.height = baseStream.height;
+    newStream.format = baseStream.format;
+    newStream.stride = baseStream.stride;
+    newStream.sourceStreamId = output_stream_id;
+
+    *stream_id = mNextReprocessStreamId;
+    mReprocessStreams.add(mNextReprocessStreamId, newStream);
+
+    ALOGV("Reprocess stream allocated: %d: %d, %d, 0x%x. Parent stream: %d",
+            *stream_id, newStream.width, newStream.height, newStream.format,
+            output_stream_id);
+
+    mNextReprocessStreamId++;
+    return NO_ERROR;
+}
+
+int EmulatedFakeCamera2::releaseReprocessStream(uint32_t stream_id) {
+    Mutex::Autolock l(mMutex);
+
+    ssize_t streamIndex = mReprocessStreams.indexOfKey(stream_id);
+    if (streamIndex < 0) {
+        ALOGE("%s: Unknown reprocess stream id %d!", __FUNCTION__, stream_id);
+        return BAD_VALUE;
+    }
+
+    if (isReprocessStreamInUse(stream_id)) {
+        ALOGE("%s: Cannot release reprocessing stream %d; in use!", __FUNCTION__,
+                stream_id);
+        return BAD_VALUE;
+    }
+
+    mReprocessStreamCount--;
+    mReprocessStreams.removeItemsAt(streamIndex);
+
+    return NO_ERROR;
+}
+
+int EmulatedFakeCamera2::triggerAction(uint32_t trigger_id,
+        int32_t ext1,
+        int32_t ext2) {
+    Mutex::Autolock l(mMutex);
+
+    if (trigger_id == CAMERA2_EXT_TRIGGER_TESTING_DISCONNECT) {
+        ALOGI("%s: Disconnect trigger - camera must be closed", __FUNCTION__);
+        mStatusPresent = false;
+
+        EmulatedCameraFactory::Instance().onStatusChanged(
+                mCameraID,
+                CAMERA_DEVICE_STATUS_NOT_PRESENT);
+    }
+
+    if (!mStatusPresent) {
+        ALOGW("%s: Camera was physically disconnected", __FUNCTION__);
+        return ERROR_CAMERA_NOT_PRESENT;
+    }
+
+    return mControlThread->triggerAction(trigger_id,
+            ext1, ext2);
+}
+
+/** Shutdown and debug methods */
+
+int EmulatedFakeCamera2::dump(int fd) {
+    String8 result;
+
+    result.appendFormat("    Camera HAL device: EmulatedFakeCamera2\n");
+    result.appendFormat("      Streams:\n");
+    for (size_t i = 0; i < mStreams.size(); i++) {
+        int id = mStreams.keyAt(i);
+        const Stream& s = mStreams.valueAt(i);
+        result.appendFormat(
+            "         Stream %d: %d x %d, format 0x%x, stride %d\n",
+            id, s.width, s.height, s.format, s.stride);
+    }
+
+    write(fd, result.string(), result.size());
+
+    return NO_ERROR;
+}
+
+void EmulatedFakeCamera2::signalError() {
+    // TODO: Let parent know so we can shut down cleanly
+    ALOGE("Worker thread is signaling a serious error");
+}
+
+/** Pipeline control worker thread methods */
+
+EmulatedFakeCamera2::ConfigureThread::ConfigureThread(EmulatedFakeCamera2 *parent):
+        Thread(false),
+        mParent(parent),
+        mRequestCount(0),
+        mNextBuffers(NULL) {
+    mRunning = false;
+}
+
+EmulatedFakeCamera2::ConfigureThread::~ConfigureThread() {
+}
+
+status_t EmulatedFakeCamera2::ConfigureThread::readyToRun() {
+    Mutex::Autolock lock(mInputMutex);
+
+    ALOGV("Starting up ConfigureThread");
+    mRequest = NULL;
+    mActive  = false;
+    mRunning = true;
+
+    mInputSignal.signal();
+    return NO_ERROR;
+}
+
+status_t EmulatedFakeCamera2::ConfigureThread::waitUntilRunning() {
+    Mutex::Autolock lock(mInputMutex);
+    if (!mRunning) {
+        ALOGV("Waiting for configure thread to start");
+        mInputSignal.wait(mInputMutex);
+    }
+    return OK;
+}
+
+status_t EmulatedFakeCamera2::ConfigureThread::newRequestAvailable() {
+    waitUntilRunning();
+
+    Mutex::Autolock lock(mInputMutex);
+
+    mActive = true;
+    mInputSignal.signal();
+
+    return OK;
+}
+
+bool EmulatedFakeCamera2::ConfigureThread::isStreamInUse(uint32_t id) {
+    Mutex::Autolock lock(mInternalsMutex);
+
+    if (mNextBuffers == NULL) return false;
+    for (size_t i=0; i < mNextBuffers->size(); i++) {
+        if ((*mNextBuffers)[i].streamId == (int)id) return true;
+    }
+    return false;
+}
+
+int EmulatedFakeCamera2::ConfigureThread::getInProgressCount() {
+    Mutex::Autolock lock(mInputMutex);
+    return mRequestCount;
+}
+
+bool EmulatedFakeCamera2::ConfigureThread::threadLoop() {
+    status_t res;
+
+    // Check if we're currently processing or just waiting
+    {
+        Mutex::Autolock lock(mInputMutex);
+        if (!mActive) {
+            // Inactive, keep waiting until we've been signaled
+            status_t res;
+            res = mInputSignal.waitRelative(mInputMutex, kWaitPerLoop);
+            if (res != NO_ERROR && res != TIMED_OUT) {
+                ALOGE("%s: Error waiting for input requests: %d",
+                        __FUNCTION__, res);
+                return false;
+            }
+            if (!mActive) return true;
+            ALOGV("New request available");
+        }
+        // Active
+    }
+
+    if (mRequest == NULL) {
+        Mutex::Autolock il(mInternalsMutex);
+
+        ALOGV("Configure: Getting next request");
+        res = mParent->mRequestQueueSrc->dequeue_request(
+            mParent->mRequestQueueSrc,
+            &mRequest);
+        if (res != NO_ERROR) {
+            ALOGE("%s: Error dequeuing next request: %d", __FUNCTION__, res);
+            mParent->signalError();
+            return false;
+        }
+        if (mRequest == NULL) {
+            ALOGV("Configure: Request queue empty, going inactive");
+            // No requests available, go into inactive mode
+            Mutex::Autolock lock(mInputMutex);
+            mActive = false;
+            return true;
+        } else {
+            Mutex::Autolock lock(mInputMutex);
+            mRequestCount++;
+        }
+
+        camera_metadata_entry_t type;
+        res = find_camera_metadata_entry(mRequest,
+                ANDROID_REQUEST_TYPE,
+                &type);
+        if (res != NO_ERROR) {
+            ALOGE("%s: error reading request type", __FUNCTION__);
+            mParent->signalError();
+            return false;
+        }
+        bool success = false;;
+        switch (type.data.u8[0]) {
+            case ANDROID_REQUEST_TYPE_CAPTURE:
+                success = setupCapture();
+                break;
+            case ANDROID_REQUEST_TYPE_REPROCESS:
+                success = setupReprocess();
+                break;
+            default:
+                ALOGE("%s: Unexpected request type %d",
+                        __FUNCTION__, type.data.u8[0]);
+                mParent->signalError();
+                break;
+        }
+        if (!success) return false;
+
+    }
+
+    if (mWaitingForReadout) {
+        bool readoutDone;
+        readoutDone = mParent->mReadoutThread->waitForReady(kWaitPerLoop);
+        if (!readoutDone) return true;
+
+        if (mNextNeedsJpeg) {
+            ALOGV("Configure: Waiting for JPEG compressor");
+        } else {
+            ALOGV("Configure: Waiting for sensor");
+        }
+        mWaitingForReadout = false;
+    }
+
+    if (mNextNeedsJpeg) {
+        bool jpegDone;
+        jpegDone = mParent->mJpegCompressor->waitForDone(kWaitPerLoop);
+        if (!jpegDone) return true;
+
+        ALOGV("Configure: Waiting for sensor");
+        mNextNeedsJpeg = false;
+    }
+
+    if (mNextIsCapture) {
+        return configureNextCapture();
+    } else {
+        return configureNextReprocess();
+    }
+}
+
+bool EmulatedFakeCamera2::ConfigureThread::setupCapture() {
+    status_t res;
+
+    mNextIsCapture = true;
+    // Get necessary parameters for sensor config
+    mParent->mControlThread->processRequest(mRequest);
+
+    camera_metadata_entry_t streams;
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_REQUEST_OUTPUT_STREAMS,
+            &streams);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading output stream tag", __FUNCTION__);
+        mParent->signalError();
+        return false;
+    }
+
+    mNextBuffers = new Buffers;
+    mNextNeedsJpeg = false;
+    ALOGV("Configure: Setting up buffers for capture");
+    for (size_t i = 0; i < streams.count; i++) {
+        int streamId = streams.data.i32[i];
+        const Stream &s = mParent->getStreamInfo(streamId);
+        if (s.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+            ALOGE("%s: Stream %d does not have a concrete pixel format, but "
+                    "is included in a request!", __FUNCTION__, streamId);
+            mParent->signalError();
+            return false;
+        }
+        StreamBuffer b;
+        b.streamId = streamId; //streams.data.u8[i];
+        b.width  = s.width;
+        b.height = s.height;
+        b.format = s.format;
+        b.stride = s.stride;
+        mNextBuffers->push_back(b);
+        ALOGV("Configure:    Buffer %zu: Stream %d, %d x %d, format 0x%x, "
+                "stride %d",
+                i, b.streamId, b.width, b.height, b.format, b.stride);
+        if (b.format == HAL_PIXEL_FORMAT_BLOB) {
+            mNextNeedsJpeg = true;
+        }
+    }
+
+    camera_metadata_entry_t e;
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_REQUEST_FRAME_COUNT,
+            &e);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading frame count tag: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        mParent->signalError();
+        return false;
+    }
+    mNextFrameNumber = *e.data.i32;
+
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_SENSOR_EXPOSURE_TIME,
+            &e);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading exposure time tag: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        mParent->signalError();
+        return false;
+    }
+    mNextExposureTime = *e.data.i64;
+
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_SENSOR_FRAME_DURATION,
+            &e);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading frame duration tag", __FUNCTION__);
+        mParent->signalError();
+        return false;
+    }
+    mNextFrameDuration = *e.data.i64;
+
+    if (mNextFrameDuration <
+            mNextExposureTime + Sensor::kMinVerticalBlank) {
+        mNextFrameDuration = mNextExposureTime + Sensor::kMinVerticalBlank;
+    }
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_SENSOR_SENSITIVITY,
+            &e);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading sensitivity tag", __FUNCTION__);
+        mParent->signalError();
+        return false;
+    }
+    mNextSensitivity = *e.data.i32;
+
+    // Start waiting on readout thread
+    mWaitingForReadout = true;
+    ALOGV("Configure: Waiting for readout thread");
+
+    return true;
+}
+
+bool EmulatedFakeCamera2::ConfigureThread::configureNextCapture() {
+    bool vsync = mParent->mSensor->waitForVSync(kWaitPerLoop);
+    if (!vsync) return true;
+
+    Mutex::Autolock il(mInternalsMutex);
+    ALOGV("Configure: Configuring sensor for capture %d", mNextFrameNumber);
+    mParent->mSensor->setExposureTime(mNextExposureTime);
+    mParent->mSensor->setFrameDuration(mNextFrameDuration);
+    mParent->mSensor->setSensitivity(mNextSensitivity);
+
+    getBuffers();
+
+    ALOGV("Configure: Done configure for capture %d", mNextFrameNumber);
+    mParent->mReadoutThread->setNextOperation(true, mRequest, mNextBuffers);
+    mParent->mSensor->setDestinationBuffers(mNextBuffers);
+
+    mRequest = NULL;
+    mNextBuffers = NULL;
+
+    Mutex::Autolock lock(mInputMutex);
+    mRequestCount--;
+
+    return true;
+}
+
+bool EmulatedFakeCamera2::ConfigureThread::setupReprocess() {
+    status_t res;
+
+    mNextNeedsJpeg = true;
+    mNextIsCapture = false;
+
+    camera_metadata_entry_t reprocessStreams;
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_REQUEST_INPUT_STREAMS,
+            &reprocessStreams);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading output stream tag", __FUNCTION__);
+        mParent->signalError();
+        return false;
+    }
+
+    mNextBuffers = new Buffers;
+
+    ALOGV("Configure: Setting up input buffers for reprocess");
+    for (size_t i = 0; i < reprocessStreams.count; i++) {
+        int streamId = reprocessStreams.data.i32[i];
+        const ReprocessStream &s = mParent->getReprocessStreamInfo(streamId);
+        if (s.format != HAL_PIXEL_FORMAT_RGB_888) {
+            ALOGE("%s: Only ZSL reprocessing supported!",
+                    __FUNCTION__);
+            mParent->signalError();
+            return false;
+        }
+        StreamBuffer b;
+        b.streamId = -streamId;
+        b.width = s.width;
+        b.height = s.height;
+        b.format = s.format;
+        b.stride = s.stride;
+        mNextBuffers->push_back(b);
+    }
+
+    camera_metadata_entry_t streams;
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_REQUEST_OUTPUT_STREAMS,
+            &streams);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading output stream tag", __FUNCTION__);
+        mParent->signalError();
+        return false;
+    }
+
+    ALOGV("Configure: Setting up output buffers for reprocess");
+    for (size_t i = 0; i < streams.count; i++) {
+        int streamId = streams.data.i32[i];
+        const Stream &s = mParent->getStreamInfo(streamId);
+        if (s.format != HAL_PIXEL_FORMAT_BLOB) {
+            // TODO: Support reprocess to YUV
+            ALOGE("%s: Non-JPEG output stream %d for reprocess not supported",
+                    __FUNCTION__, streamId);
+            mParent->signalError();
+            return false;
+        }
+        StreamBuffer b;
+        b.streamId = streams.data.u8[i];
+        b.width  = s.width;
+        b.height = s.height;
+        b.format = s.format;
+        b.stride = s.stride;
+        mNextBuffers->push_back(b);
+        ALOGV("Configure:    Buffer %zu: Stream %d, %d x %d, format 0x%x, "
+                "stride %d",
+                i, b.streamId, b.width, b.height, b.format, b.stride);
+    }
+
+    camera_metadata_entry_t e;
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_REQUEST_FRAME_COUNT,
+            &e);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading frame count tag: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        mParent->signalError();
+        return false;
+    }
+    mNextFrameNumber = *e.data.i32;
+
+    return true;
+}
+
+bool EmulatedFakeCamera2::ConfigureThread::configureNextReprocess() {
+    Mutex::Autolock il(mInternalsMutex);
+
+    getBuffers();
+
+    ALOGV("Configure: Done configure for reprocess %d", mNextFrameNumber);
+    mParent->mReadoutThread->setNextOperation(false, mRequest, mNextBuffers);
+
+    mRequest = NULL;
+    mNextBuffers = NULL;
+
+    Mutex::Autolock lock(mInputMutex);
+    mRequestCount--;
+
+    return true;
+}
+
+bool EmulatedFakeCamera2::ConfigureThread::getBuffers() {
+    status_t res;
+    /** Get buffers to fill for this frame */
+    for (size_t i = 0; i < mNextBuffers->size(); i++) {
+        StreamBuffer &b = mNextBuffers->editItemAt(i);
+
+        if (b.streamId > 0) {
+            ALOGV("Configure: Dequeing buffer from stream %d", b.streamId);
+            Stream s = mParent->getStreamInfo(b.streamId);
+            res = s.ops->dequeue_buffer(s.ops, &(b.buffer) );
+            if (res != NO_ERROR || b.buffer == NULL) {
+                ALOGE("%s: Unable to dequeue buffer from stream %d: %s (%d)",
+                        __FUNCTION__, b.streamId, strerror(-res), res);
+                mParent->signalError();
+                return false;
+            }
+
+            /* Lock the buffer from the perspective of the graphics mapper */
+            res = GrallocModule::getInstance().lock(*(b.buffer),
+                    GRALLOC_USAGE_HW_CAMERA_WRITE,
+                    0, 0, s.width, s.height,
+                    (void**)&(b.img));
+
+
+            if (res != NO_ERROR) {
+                ALOGE("%s: grbuffer_mapper.lock failure: %s (%d)",
+                        __FUNCTION__, strerror(-res), res);
+                s.ops->cancel_buffer(s.ops,
+                        b.buffer);
+                mParent->signalError();
+                return false;
+            }
+        } else {
+            ALOGV("Configure: Acquiring buffer from reprocess stream %d",
+                    -b.streamId);
+            ReprocessStream s = mParent->getReprocessStreamInfo(-b.streamId);
+            res = s.ops->acquire_buffer(s.ops, &(b.buffer) );
+            if (res != NO_ERROR || b.buffer == NULL) {
+                ALOGE("%s: Unable to acquire buffer from reprocess stream %d: "
+                        "%s (%d)", __FUNCTION__, -b.streamId,
+                        strerror(-res), res);
+                mParent->signalError();
+                return false;
+            }
+
+            /* Lock the buffer from the perspective of the graphics mapper */
+            res = GrallocModule::getInstance().lock(*(b.buffer),
+                    GRALLOC_USAGE_HW_CAMERA_READ,
+                    0, 0, s.width, s.height,
+                    (void**)&(b.img) );
+            if (res != NO_ERROR) {
+                ALOGE("%s: grbuffer_mapper.lock failure: %s (%d)",
+                        __FUNCTION__, strerror(-res), res);
+                s.ops->release_buffer(s.ops,
+                        b.buffer);
+                mParent->signalError();
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+EmulatedFakeCamera2::ReadoutThread::ReadoutThread(EmulatedFakeCamera2 *parent):
+        Thread(false),
+        mParent(parent),
+        mRunning(false),
+        mActive(false),
+        mRequestCount(0),
+        mRequest(NULL),
+        mBuffers(NULL) {
+    mInFlightQueue = new InFlightQueue[kInFlightQueueSize];
+    mInFlightHead = 0;
+    mInFlightTail = 0;
+}
+
+EmulatedFakeCamera2::ReadoutThread::~ReadoutThread() {
+    delete[] mInFlightQueue;
+}
+
+status_t EmulatedFakeCamera2::ReadoutThread::readyToRun() {
+    Mutex::Autolock lock(mInputMutex);
+    ALOGV("Starting up ReadoutThread");
+    mRunning = true;
+    mInputSignal.signal();
+    return NO_ERROR;
+}
+
+status_t EmulatedFakeCamera2::ReadoutThread::waitUntilRunning() {
+    Mutex::Autolock lock(mInputMutex);
+    if (!mRunning) {
+        ALOGV("Waiting for readout thread to start");
+        mInputSignal.wait(mInputMutex);
+    }
+    return OK;
+}
+
+bool EmulatedFakeCamera2::ReadoutThread::waitForReady(nsecs_t timeout) {
+    status_t res;
+    Mutex::Autolock lock(mInputMutex);
+    while (!readyForNextCapture()) {
+        res = mReadySignal.waitRelative(mInputMutex, timeout);
+        if (res == TIMED_OUT) return false;
+        if (res != OK) {
+            ALOGE("%s: Error waiting for ready: %s (%d)", __FUNCTION__,
+                    strerror(-res), res);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool EmulatedFakeCamera2::ReadoutThread::readyForNextCapture() {
+    return (mInFlightTail + 1) % kInFlightQueueSize != mInFlightHead;
+}
+
+void EmulatedFakeCamera2::ReadoutThread::setNextOperation(
+        bool isCapture,
+        camera_metadata_t *request,
+        Buffers *buffers) {
+    Mutex::Autolock lock(mInputMutex);
+    if ( !readyForNextCapture() ) {
+        ALOGE("In flight queue full, dropping captures");
+        mParent->signalError();
+        return;
+    }
+    mInFlightQueue[mInFlightTail].isCapture = isCapture;
+    mInFlightQueue[mInFlightTail].request = request;
+    mInFlightQueue[mInFlightTail].buffers = buffers;
+    mInFlightTail = (mInFlightTail + 1) % kInFlightQueueSize;
+    mRequestCount++;
+
+    if (!mActive) {
+        mActive = true;
+        mInputSignal.signal();
+    }
+}
+
+bool EmulatedFakeCamera2::ReadoutThread::isStreamInUse(uint32_t id) {
+    // acquire in same order as threadLoop
+    Mutex::Autolock iLock(mInternalsMutex);
+    Mutex::Autolock lock(mInputMutex);
+
+    size_t i = mInFlightHead;
+    while (i != mInFlightTail) {
+        for (size_t j = 0; j < mInFlightQueue[i].buffers->size(); j++) {
+            if ( (*(mInFlightQueue[i].buffers))[j].streamId == (int)id )
+                return true;
+        }
+        i = (i + 1) % kInFlightQueueSize;
+    }
+
+
+    if (mBuffers != NULL) {
+        for (i = 0; i < mBuffers->size(); i++) {
+            if ( (*mBuffers)[i].streamId == (int)id) return true;
+        }
+    }
+
+    return false;
+}
+
+int EmulatedFakeCamera2::ReadoutThread::getInProgressCount() {
+    Mutex::Autolock lock(mInputMutex);
+
+    return mRequestCount;
+}
+
+bool EmulatedFakeCamera2::ReadoutThread::threadLoop() {
+    static const nsecs_t kWaitPerLoop = 10000000L; // 10 ms
+    status_t res;
+    int32_t frameNumber;
+
+    // Check if we're currently processing or just waiting
+    {
+        Mutex::Autolock lock(mInputMutex);
+        if (!mActive) {
+            // Inactive, keep waiting until we've been signaled
+            res = mInputSignal.waitRelative(mInputMutex, kWaitPerLoop);
+            if (res != NO_ERROR && res != TIMED_OUT) {
+                ALOGE("%s: Error waiting for capture requests: %d",
+                        __FUNCTION__, res);
+                mParent->signalError();
+                return false;
+            }
+            if (!mActive) return true;
+        }
+        // Active, see if we need a new request
+        if (mRequest == NULL) {
+            if (mInFlightHead == mInFlightTail) {
+                // Go inactive
+                ALOGV("Waiting for sensor data");
+                mActive = false;
+                return true;
+            } else {
+                Mutex::Autolock iLock(mInternalsMutex);
+                mReadySignal.signal();
+                mIsCapture = mInFlightQueue[mInFlightHead].isCapture;
+                mRequest = mInFlightQueue[mInFlightHead].request;
+                mBuffers  = mInFlightQueue[mInFlightHead].buffers;
+                mInFlightQueue[mInFlightHead].request = NULL;
+                mInFlightQueue[mInFlightHead].buffers = NULL;
+                mInFlightHead = (mInFlightHead + 1) % kInFlightQueueSize;
+                ALOGV("Ready to read out request %p, %zu buffers",
+                        mRequest, mBuffers->size());
+            }
+        }
+    }
+
+    // Active with request, wait on sensor to complete
+
+    nsecs_t captureTime;
+
+    if (mIsCapture) {
+        bool gotFrame;
+        gotFrame = mParent->mSensor->waitForNewFrame(kWaitPerLoop,
+                &captureTime);
+
+        if (!gotFrame) return true;
+    }
+
+    Mutex::Autolock iLock(mInternalsMutex);
+
+    camera_metadata_entry_t entry;
+    if (!mIsCapture) {
+        res = find_camera_metadata_entry(mRequest,
+                ANDROID_SENSOR_TIMESTAMP,
+            &entry);
+        if (res != NO_ERROR) {
+            ALOGE("%s: error reading reprocessing timestamp: %s (%d)",
+                    __FUNCTION__, strerror(-res), res);
+            mParent->signalError();
+            return false;
+        }
+        captureTime = entry.data.i64[0];
+    }
+
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_REQUEST_FRAME_COUNT,
+            &entry);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading frame count tag: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        mParent->signalError();
+        return false;
+    }
+    frameNumber = *entry.data.i32;
+
+    res = find_camera_metadata_entry(mRequest,
+            ANDROID_REQUEST_METADATA_MODE,
+            &entry);
+    if (res != NO_ERROR) {
+        ALOGE("%s: error reading metadata mode tag: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        mParent->signalError();
+        return false;
+    }
+
+    // Got sensor data and request, construct frame and send it out
+    ALOGV("Readout: Constructing metadata and frames for request %d",
+            frameNumber);
+
+    if (*entry.data.u8 == ANDROID_REQUEST_METADATA_MODE_FULL) {
+        ALOGV("Readout: Metadata requested, constructing");
+
+        camera_metadata_t *frame = NULL;
+
+        size_t frame_entries = get_camera_metadata_entry_count(mRequest);
+        size_t frame_data    = get_camera_metadata_data_count(mRequest);
+
+        // TODO: Dynamically calculate based on enabled statistics, etc
+        frame_entries += 10;
+        frame_data += 100;
+
+        res = mParent->mFrameQueueDst->dequeue_frame(mParent->mFrameQueueDst,
+                frame_entries, frame_data, &frame);
+
+        if (res != NO_ERROR || frame == NULL) {
+            ALOGE("%s: Unable to dequeue frame metadata buffer", __FUNCTION__);
+            mParent->signalError();
+            return false;
+        }
+
+        res = append_camera_metadata(frame, mRequest);
+        if (res != NO_ERROR) {
+            ALOGE("Unable to append request metadata");
+        }
+
+        if (mIsCapture) {
+            add_camera_metadata_entry(frame,
+                    ANDROID_SENSOR_TIMESTAMP,
+                    &captureTime,
+                    1);
+
+            collectStatisticsMetadata(frame);
+            // TODO: Collect all final values used from sensor in addition to timestamp
+        }
+
+        ALOGV("Readout: Enqueue frame %d", frameNumber);
+        mParent->mFrameQueueDst->enqueue_frame(mParent->mFrameQueueDst,
+                frame);
+    }
+    ALOGV("Readout: Free request");
+    res = mParent->mRequestQueueSrc->free_request(mParent->mRequestQueueSrc, mRequest);
+    if (res != NO_ERROR) {
+        ALOGE("%s: Unable to return request buffer to queue: %d",
+                __FUNCTION__, res);
+        mParent->signalError();
+        return false;
+    }
+    mRequest = NULL;
+
+    int compressedBufferIndex = -1;
+    ALOGV("Readout: Processing %zu buffers", mBuffers->size());
+    for (size_t i = 0; i < mBuffers->size(); i++) {
+        const StreamBuffer &b = (*mBuffers)[i];
+        ALOGV("Readout:    Buffer %zu: Stream %d, %d x %d, format 0x%x, stride %d",
+                i, b.streamId, b.width, b.height, b.format, b.stride);
+        if (b.streamId > 0) {
+            if (b.format == HAL_PIXEL_FORMAT_BLOB) {
+                // Assumes only one BLOB buffer type per capture
+                compressedBufferIndex = i;
+            } else {
+                ALOGV("Readout:    Sending image buffer %zu (%p) to output stream %d",
+                        i, (void*)*(b.buffer), b.streamId);
+                GrallocModule::getInstance().unlock(*(b.buffer));
+                const Stream &s = mParent->getStreamInfo(b.streamId);
+                res = s.ops->enqueue_buffer(s.ops, captureTime, b.buffer);
+                if (res != OK) {
+                    ALOGE("Error enqueuing image buffer %p: %s (%d)", b.buffer,
+                            strerror(-res), res);
+                    mParent->signalError();
+                }
+            }
+        }
+    }
+
+    if (compressedBufferIndex == -1) {
+        delete mBuffers;
+    } else {
+        ALOGV("Readout:  Starting JPEG compression for buffer %d, stream %d",
+                compressedBufferIndex,
+                (*mBuffers)[compressedBufferIndex].streamId);
+        mJpegTimestamp = captureTime;
+        // Takes ownership of mBuffers
+        mParent->mJpegCompressor->start(mBuffers, this);
+    }
+    mBuffers = NULL;
+
+    Mutex::Autolock l(mInputMutex);
+    mRequestCount--;
+    ALOGV("Readout: Done with request %d", frameNumber);
+    return true;
+}
+
+void EmulatedFakeCamera2::ReadoutThread::onJpegDone(
+        const StreamBuffer &jpegBuffer, bool success) {
+    status_t res;
+    if (!success) {
+        ALOGE("%s: Error queueing compressed image buffer %p",
+                __FUNCTION__, jpegBuffer.buffer);
+        mParent->signalError();
+        return;
+    }
+
+    // Write to JPEG output stream
+    ALOGV("%s: Compression complete, pushing to stream %d", __FUNCTION__,
+            jpegBuffer.streamId);
+
+    GrallocModule::getInstance().unlock(*(jpegBuffer.buffer));
+    const Stream &s = mParent->getStreamInfo(jpegBuffer.streamId);
+    res = s.ops->enqueue_buffer(s.ops, mJpegTimestamp, jpegBuffer.buffer);
+}
+
+void EmulatedFakeCamera2::ReadoutThread::onJpegInputDone(
+        const StreamBuffer &inputBuffer) {
+    status_t res;
+    GrallocModule::getInstance().unlock(*(inputBuffer.buffer));
+    const ReprocessStream &s =
+            mParent->getReprocessStreamInfo(-inputBuffer.streamId);
+    res = s.ops->release_buffer(s.ops, inputBuffer.buffer);
+    if (res != OK) {
+        ALOGE("Error releasing reprocess buffer %p: %s (%d)",
+                inputBuffer.buffer, strerror(-res), res);
+        mParent->signalError();
+    }
+}
+
+status_t EmulatedFakeCamera2::ReadoutThread::collectStatisticsMetadata(
+        camera_metadata_t *frame) {
+    // Completely fake face rectangles, don't correspond to real faces in scene
+    ALOGV("Readout:    Collecting statistics metadata");
+
+    status_t res;
+    camera_metadata_entry_t entry;
+    res = find_camera_metadata_entry(frame,
+                ANDROID_STATISTICS_FACE_DETECT_MODE,
+                &entry);
+    if (res != OK) {
+        ALOGE("%s: Unable to find face detect mode!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (entry.data.u8[0] == ANDROID_STATISTICS_FACE_DETECT_MODE_OFF) return OK;
+
+    // The coordinate system for the face regions is the raw sensor pixel
+    // coordinates. Here, we map from the scene coordinates (0-19 in both axis)
+    // to raw pixels, for the scene defined in fake-pipeline2/Scene.cpp. We
+    // approximately place two faces on top of the windows of the house. No
+    // actual faces exist there, but might one day. Note that this doesn't
+    // account for the offsets used to account for aspect ratio differences, so
+    // the rectangles don't line up quite right.
+    const size_t numFaces = 2;
+    int32_t rects[numFaces * 4] = {
+        static_cast<int32_t>(mParent->mSensorWidth * 10 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 15 / 20),
+        static_cast<int32_t>(mParent->mSensorWidth * 12 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 17 / 20),
+
+        static_cast<int32_t>(mParent->mSensorWidth * 16 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 15 / 20),
+        static_cast<int32_t>(mParent->mSensorWidth * 18 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 17 / 20)
+    };
+    // To simulate some kind of real detection going on, we jitter the rectangles on
+    // each frame by a few pixels in each dimension.
+    for (size_t i = 0; i < numFaces * 4; i++) {
+        rects[i] += (int32_t)(((float)rand() / RAND_MAX) * 6 - 3);
+    }
+    // The confidence scores (0-100) are similarly jittered.
+    uint8_t scores[numFaces] = { 85, 95 };
+    for (size_t i = 0; i < numFaces; i++) {
+        scores[i] += (int32_t)(((float)rand() / RAND_MAX) * 10 - 5);
+    }
+
+    res = add_camera_metadata_entry(frame, ANDROID_STATISTICS_FACE_RECTANGLES,
+            rects, numFaces * 4);
+    if (res != OK) {
+        ALOGE("%s: Unable to add face rectangles!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    res = add_camera_metadata_entry(frame, ANDROID_STATISTICS_FACE_SCORES,
+            scores, numFaces);
+    if (res != OK) {
+        ALOGE("%s: Unable to add face scores!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (entry.data.u8[0] == ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE) return OK;
+
+    // Advanced face detection options - add eye/mouth coordinates.  The
+    // coordinates in order are (leftEyeX, leftEyeY, rightEyeX, rightEyeY,
+    // mouthX, mouthY). The mapping is the same as the face rectangles.
+    int32_t features[numFaces * 6] = {
+        static_cast<int32_t>(mParent->mSensorWidth * 10.5 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 16 / 20),
+        static_cast<int32_t>(mParent->mSensorWidth * 11.5 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 16 / 20),
+        static_cast<int32_t>(mParent->mSensorWidth * 11 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 16.5 / 20),
+
+        static_cast<int32_t>(mParent->mSensorWidth * 16.5 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 16 / 20),
+        static_cast<int32_t>(mParent->mSensorWidth * 17.5 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 16 / 20),
+        static_cast<int32_t>(mParent->mSensorWidth * 17 / 20),
+        static_cast<int32_t>(mParent->mSensorHeight * 16.5 / 20),
+    };
+    // Jitter these a bit less than the rects
+    for (size_t i = 0; i < numFaces * 6; i++) {
+        features[i] += (int32_t)(((float)rand() / RAND_MAX) * 4 - 2);
+    }
+    // These are unique IDs that are used to identify each face while it's
+    // visible to the detector (if a face went away and came back, it'd get a
+    // new ID).
+    int32_t ids[numFaces] = {
+        100, 200
+    };
+
+    res = add_camera_metadata_entry(frame, ANDROID_STATISTICS_FACE_LANDMARKS,
+            features, numFaces * 6);
+    if (res != OK) {
+        ALOGE("%s: Unable to add face landmarks!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    res = add_camera_metadata_entry(frame, ANDROID_STATISTICS_FACE_IDS,
+            ids, numFaces);
+    if (res != OK) {
+        ALOGE("%s: Unable to add face scores!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    return OK;
+}
+
+EmulatedFakeCamera2::ControlThread::ControlThread(EmulatedFakeCamera2 *parent):
+        Thread(false),
+        mParent(parent) {
+    mRunning = false;
+}
+
+EmulatedFakeCamera2::ControlThread::~ControlThread() {
+}
+
+status_t EmulatedFakeCamera2::ControlThread::readyToRun() {
+    Mutex::Autolock lock(mInputMutex);
+
+    ALOGV("Starting up ControlThread");
+    mRunning = true;
+    mStartAf = false;
+    mCancelAf = false;
+    mStartPrecapture = false;
+
+    mControlMode = ANDROID_CONTROL_MODE_AUTO;
+
+    mEffectMode = ANDROID_CONTROL_EFFECT_MODE_OFF;
+    mSceneMode = ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY;
+
+    mAfMode = ANDROID_CONTROL_AF_MODE_AUTO;
+    mAfModeChange = false;
+
+    mAeMode = ANDROID_CONTROL_AE_MODE_ON;
+    mAwbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+
+    mAfTriggerId = 0;
+    mPrecaptureTriggerId = 0;
+
+    mAfState = ANDROID_CONTROL_AF_STATE_INACTIVE;
+    mAeState = ANDROID_CONTROL_AE_STATE_INACTIVE;
+    mAwbState = ANDROID_CONTROL_AWB_STATE_INACTIVE;
+
+    mExposureTime = kNormalExposureTime;
+
+    mInputSignal.signal();
+    return NO_ERROR;
+}
+
+status_t EmulatedFakeCamera2::ControlThread::waitUntilRunning() {
+    Mutex::Autolock lock(mInputMutex);
+    if (!mRunning) {
+        ALOGV("Waiting for control thread to start");
+        mInputSignal.wait(mInputMutex);
+    }
+    return OK;
+}
+
+// Override android.control.* fields with 3A values before sending request to sensor
+status_t EmulatedFakeCamera2::ControlThread::processRequest(camera_metadata_t *request) {
+    Mutex::Autolock lock(mInputMutex);
+    // TODO: Add handling for all android.control.* fields here
+    camera_metadata_entry_t mode;
+    status_t res;
+
+#define READ_IF_OK(res, what, def)                                             \
+    (((res) == OK) ? (what) : (uint8_t)(def))
+
+    res = find_camera_metadata_entry(request,
+            ANDROID_CONTROL_MODE,
+            &mode);
+    mControlMode = READ_IF_OK(res, mode.data.u8[0], ANDROID_CONTROL_MODE_OFF);
+
+    // disable all 3A
+    if (mControlMode == ANDROID_CONTROL_MODE_OFF) {
+        mEffectMode =   ANDROID_CONTROL_EFFECT_MODE_OFF;
+#if VSOC_PLATFORM_SDK_AFTER(K)
+        mSceneMode =    ANDROID_CONTROL_SCENE_MODE_DISABLED;
+#else
+        mSceneMode =    ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
+#endif
+        mAfMode =       ANDROID_CONTROL_AF_MODE_OFF;
+        mAeLock =       ANDROID_CONTROL_AE_LOCK_ON;
+        mAeMode =       ANDROID_CONTROL_AE_MODE_OFF;
+        mAfModeChange = true;
+        mStartAf =      false;
+        mCancelAf =     true;
+        mAeState =      ANDROID_CONTROL_AE_STATE_INACTIVE;
+        mAwbMode =      ANDROID_CONTROL_AWB_MODE_OFF;
+        return res;
+    }
+
+    res = find_camera_metadata_entry(request,
+            ANDROID_CONTROL_EFFECT_MODE,
+            &mode);
+    mEffectMode = READ_IF_OK(res, mode.data.u8[0],
+                             ANDROID_CONTROL_EFFECT_MODE_OFF);
+
+    res = find_camera_metadata_entry(request,
+            ANDROID_CONTROL_SCENE_MODE,
+            &mode);
+#if VSOC_PLATFORM_SDK_AFTER(K)
+    mSceneMode = READ_IF_OK(res, mode.data.u8[0],
+                             ANDROID_CONTROL_SCENE_MODE_DISABLED);
+#else
+    mSceneMode = READ_IF_OK(res, mode.data.u8[0],
+                             ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED);
+#endif
+
+    res = find_camera_metadata_entry(request,
+            ANDROID_CONTROL_AF_MODE,
+            &mode);
+    if (mAfMode != mode.data.u8[0]) {
+        ALOGV("AF new mode: %d, old mode %d", mode.data.u8[0], mAfMode);
+        mAfMode = mode.data.u8[0];
+        mAfModeChange = true;
+        mStartAf = false;
+        mCancelAf = false;
+    }
+
+    res = find_camera_metadata_entry(request,
+            ANDROID_CONTROL_AE_MODE,
+            &mode);
+    mAeMode = READ_IF_OK(res, mode.data.u8[0],
+                             ANDROID_CONTROL_AE_MODE_OFF);
+
+    res = find_camera_metadata_entry(request,
+            ANDROID_CONTROL_AE_LOCK,
+            &mode);
+    uint8_t aeLockVal = READ_IF_OK(res, mode.data.u8[0],
+                                   ANDROID_CONTROL_AE_LOCK_ON);
+    bool aeLock = (aeLockVal == ANDROID_CONTROL_AE_LOCK_ON);
+    if (mAeLock && !aeLock) {
+        mAeState = ANDROID_CONTROL_AE_STATE_INACTIVE;
+    }
+    mAeLock = aeLock;
+
+    res = find_camera_metadata_entry(request,
+            ANDROID_CONTROL_AWB_MODE,
+            &mode);
+    mAwbMode = READ_IF_OK(res, mode.data.u8[0],
+                          ANDROID_CONTROL_AWB_MODE_OFF);
+
+    // TODO: Override more control fields
+
+    if (mAeMode != ANDROID_CONTROL_AE_MODE_OFF) {
+        camera_metadata_entry_t exposureTime;
+        res = find_camera_metadata_entry(request,
+                ANDROID_SENSOR_EXPOSURE_TIME,
+                &exposureTime);
+        if (res == OK) {
+            exposureTime.data.i64[0] = mExposureTime;
+        }
+    }
+
+#undef READ_IF_OK
+
+    return OK;
+}
+
+status_t EmulatedFakeCamera2::ControlThread::triggerAction(uint32_t msgType,
+        int32_t ext1, int32_t ext2) {
+    ALOGV("%s: Triggering %d (%d, %d)", __FUNCTION__, msgType, ext1, ext2);
+    Mutex::Autolock lock(mInputMutex);
+    switch (msgType) {
+        case CAMERA2_TRIGGER_AUTOFOCUS:
+            mAfTriggerId = ext1;
+            mStartAf = true;
+            mCancelAf = false;
+            break;
+        case CAMERA2_TRIGGER_CANCEL_AUTOFOCUS:
+            mAfTriggerId = ext1;
+            mStartAf = false;
+            mCancelAf = true;
+            break;
+        case CAMERA2_TRIGGER_PRECAPTURE_METERING:
+            mPrecaptureTriggerId = ext1;
+            mStartPrecapture = true;
+            break;
+        default:
+            ALOGE("%s: Unknown action triggered: %d (arguments %d %d)",
+                    __FUNCTION__, msgType, ext1, ext2);
+            return BAD_VALUE;
+    }
+    return OK;
+}
+
+const nsecs_t EmulatedFakeCamera2::ControlThread::kControlCycleDelay = 100 * MSEC;
+const nsecs_t EmulatedFakeCamera2::ControlThread::kMinAfDuration = 500 * MSEC;
+const nsecs_t EmulatedFakeCamera2::ControlThread::kMaxAfDuration = 900 * MSEC;
+const float EmulatedFakeCamera2::ControlThread::kAfSuccessRate = 0.9;
+ // Once every 5 seconds
+const float EmulatedFakeCamera2::ControlThread::kContinuousAfStartRate =
+        kControlCycleDelay / 5.0 * SEC;
+const nsecs_t EmulatedFakeCamera2::ControlThread::kMinAeDuration = 500 * MSEC;
+const nsecs_t EmulatedFakeCamera2::ControlThread::kMaxAeDuration = 2 * SEC;
+const nsecs_t EmulatedFakeCamera2::ControlThread::kMinPrecaptureAeDuration = 100 * MSEC;
+const nsecs_t EmulatedFakeCamera2::ControlThread::kMaxPrecaptureAeDuration = 400 * MSEC;
+ // Once every 3 seconds
+const float EmulatedFakeCamera2::ControlThread::kAeScanStartRate =
+    kControlCycleDelay / 3000000000.0;
+
+const nsecs_t EmulatedFakeCamera2::ControlThread::kNormalExposureTime = 10 * MSEC;
+const nsecs_t EmulatedFakeCamera2::ControlThread::kExposureJump = 2 * MSEC;
+const nsecs_t EmulatedFakeCamera2::ControlThread::kMinExposureTime = 1 * MSEC;
+
+bool EmulatedFakeCamera2::ControlThread::threadLoop() {
+    bool afModeChange = false;
+    bool afTriggered = false;
+    bool afCancelled = false;
+    uint8_t afState;
+    uint8_t afMode;
+    int32_t afTriggerId;
+    bool precaptureTriggered = false;
+    uint8_t aeState;
+    uint8_t aeMode;
+    bool    aeLock;
+    int32_t precaptureTriggerId;
+    nsecs_t nextSleep = kControlCycleDelay;
+
+    {
+        Mutex::Autolock lock(mInputMutex);
+        if (mStartAf) {
+            ALOGD("Starting AF trigger processing");
+            afTriggered = true;
+            mStartAf = false;
+        } else if (mCancelAf) {
+            ALOGD("Starting cancel AF trigger processing");
+            afCancelled = true;
+            mCancelAf = false;
+        }
+        afState = mAfState;
+        afMode = mAfMode;
+        afModeChange = mAfModeChange;
+        mAfModeChange = false;
+
+        afTriggerId = mAfTriggerId;
+
+        if(mStartPrecapture) {
+            ALOGD("Starting precapture trigger processing");
+            precaptureTriggered = true;
+            mStartPrecapture = false;
+        }
+        aeState = mAeState;
+        aeMode = mAeMode;
+        aeLock = mAeLock;
+        precaptureTriggerId = mPrecaptureTriggerId;
+    }
+
+    if (afCancelled || afModeChange) {
+        ALOGV("Resetting AF state due to cancel/mode change");
+        afState = ANDROID_CONTROL_AF_STATE_INACTIVE;
+        updateAfState(afState, afTriggerId);
+        mAfScanDuration = 0;
+        mLockAfterPassiveScan = false;
+    }
+
+    uint8_t oldAfState = afState;
+
+    if (afTriggered) {
+        afState = processAfTrigger(afMode, afState);
+    }
+
+    afState = maybeStartAfScan(afMode, afState);
+    afState = updateAfScan(afMode, afState, &nextSleep);
+    updateAfState(afState, afTriggerId);
+
+    if (precaptureTriggered) {
+        aeState = processPrecaptureTrigger(aeMode, aeState);
+    }
+
+    aeState = maybeStartAeScan(aeMode, aeLock, aeState);
+    aeState = updateAeScan(aeMode, aeLock, aeState, &nextSleep);
+    updateAeState(aeState, precaptureTriggerId);
+
+    int ret;
+    timespec t;
+    t.tv_sec = 0;
+    t.tv_nsec = nextSleep;
+    do {
+        ret = nanosleep(&t, &t);
+    } while (ret != 0);
+
+    if (mAfScanDuration > 0) {
+        mAfScanDuration -= nextSleep;
+    }
+    if (mAeScanDuration > 0) {
+        mAeScanDuration -= nextSleep;
+    }
+
+    return true;
+}
+
+int EmulatedFakeCamera2::ControlThread::processAfTrigger(uint8_t afMode,
+        uint8_t afState) {
+    switch (afMode) {
+        case ANDROID_CONTROL_AF_MODE_OFF:
+        case ANDROID_CONTROL_AF_MODE_EDOF:
+            // Do nothing
+            break;
+        case ANDROID_CONTROL_AF_MODE_MACRO:
+        case ANDROID_CONTROL_AF_MODE_AUTO:
+            switch (afState) {
+                case ANDROID_CONTROL_AF_STATE_INACTIVE:
+                case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+                case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+                    // Start new focusing cycle
+                    mAfScanDuration =  ((double)rand() / RAND_MAX) *
+                        (kMaxAfDuration - kMinAfDuration) + kMinAfDuration;
+                    afState = ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN;
+                    ALOGV("%s: AF scan start, duration %" PRId64 " ms",
+                          __FUNCTION__, mAfScanDuration / 1000000);
+                    break;
+                case ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN:
+                    // Ignore new request, already scanning
+                    break;
+                default:
+                    ALOGE("Unexpected AF state in AUTO/MACRO AF mode: %d",
+                          afState);
+            }
+            break;
+        case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+            switch (afState) {
+                // Picture mode waits for passive scan to complete
+                case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+                    mLockAfterPassiveScan = true;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_INACTIVE:
+                    afState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+                    afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+                case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+                    // Must cancel to get out of these states
+                    break;
+                default:
+                    ALOGE("Unexpected AF state in CONTINUOUS_PICTURE AF mode: %d",
+                          afState);
+            }
+            break;
+        case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+            switch (afState) {
+                // Video mode does not wait for passive scan to complete
+                case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+                case ANDROID_CONTROL_AF_STATE_INACTIVE:
+                    afState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+                    afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+                case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+                    // Must cancel to get out of these states
+                    break;
+                default:
+                    ALOGE("Unexpected AF state in CONTINUOUS_VIDEO AF mode: %d",
+                          afState);
+            }
+            break;
+        default:
+            break;
+    }
+    return afState;
+}
+
+int EmulatedFakeCamera2::ControlThread::maybeStartAfScan(uint8_t afMode,
+        uint8_t afState) {
+    if ((afMode == ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO ||
+            afMode == ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE) &&
+        (afState == ANDROID_CONTROL_AF_STATE_INACTIVE ||
+            afState == ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED)) {
+
+        bool startScan = ((double)rand() / RAND_MAX) < kContinuousAfStartRate;
+        if (startScan) {
+            // Start new passive focusing cycle
+            mAfScanDuration =  ((double)rand() / RAND_MAX) *
+                (kMaxAfDuration - kMinAfDuration) + kMinAfDuration;
+            afState = ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN;
+            ALOGV("%s: AF passive scan start, duration %" PRId64 " ms",
+                __FUNCTION__, mAfScanDuration / 1000000);
+        }
+    }
+    return afState;
+}
+
+int EmulatedFakeCamera2::ControlThread::updateAfScan(uint8_t afMode,
+        uint8_t afState, nsecs_t *maxSleep) {
+    if (! (afState == ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN ||
+            afState == ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN ) ) {
+        return afState;
+    }
+
+    if (mAfScanDuration <= 0) {
+        ALOGV("%s: AF scan done", __FUNCTION__);
+        switch (afMode) {
+            case ANDROID_CONTROL_AF_MODE_MACRO:
+            case ANDROID_CONTROL_AF_MODE_AUTO: {
+                bool success = ((double)rand() / RAND_MAX) < kAfSuccessRate;
+                if (success) {
+                    afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+                } else {
+                    afState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
+                }
+                break;
+            }
+            case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+                if (mLockAfterPassiveScan) {
+                    afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+                    mLockAfterPassiveScan = false;
+                } else {
+                    afState = ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED;
+                }
+                break;
+            case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+                afState = ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED;
+                break;
+            default:
+                ALOGE("Unexpected AF mode in scan state");
+        }
+    } else {
+        if (mAfScanDuration <= *maxSleep) {
+            *maxSleep = mAfScanDuration;
+        }
+    }
+    return afState;
+}
+
+void EmulatedFakeCamera2::ControlThread::updateAfState(uint8_t newState,
+        int32_t triggerId) {
+    Mutex::Autolock lock(mInputMutex);
+    if (mAfState != newState) {
+        ALOGV("%s: Autofocus state now %d, id %d", __FUNCTION__,
+                newState, triggerId);
+        mAfState = newState;
+        mParent->sendNotification(CAMERA2_MSG_AUTOFOCUS,
+                newState, triggerId, 0);
+    }
+}
+
+int EmulatedFakeCamera2::ControlThread::processPrecaptureTrigger(uint8_t aeMode,
+        uint8_t aeState) {
+    switch (aeMode) {
+        case ANDROID_CONTROL_AE_MODE_OFF:
+            // Don't do anything for these
+            return aeState;
+        case ANDROID_CONTROL_AE_MODE_ON:
+        case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH:
+        case ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH:
+        case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
+            // Trigger a precapture cycle
+            aeState = ANDROID_CONTROL_AE_STATE_PRECAPTURE;
+            mAeScanDuration = ((double)rand() / RAND_MAX) *
+                    (kMaxPrecaptureAeDuration - kMinPrecaptureAeDuration) +
+                    kMinPrecaptureAeDuration;
+            ALOGD("%s: AE precapture scan start, duration %" PRId64 " ms",
+                    __FUNCTION__, mAeScanDuration / 1000000);
+
+    }
+    return aeState;
+}
+
+int EmulatedFakeCamera2::ControlThread::maybeStartAeScan(uint8_t aeMode,
+        bool aeLocked,
+        uint8_t aeState) {
+    if (aeLocked) return aeState;
+    switch (aeMode) {
+        case ANDROID_CONTROL_AE_MODE_OFF:
+            break;
+        case ANDROID_CONTROL_AE_MODE_ON:
+        case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH:
+        case ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH:
+        case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE: {
+            if (aeState != ANDROID_CONTROL_AE_STATE_INACTIVE &&
+                    aeState != ANDROID_CONTROL_AE_STATE_CONVERGED) break;
+
+            bool startScan = ((double)rand() / RAND_MAX) < kAeScanStartRate;
+            if (startScan) {
+                mAeScanDuration = ((double)rand() / RAND_MAX) *
+                (kMaxAeDuration - kMinAeDuration) + kMinAeDuration;
+                aeState = ANDROID_CONTROL_AE_STATE_SEARCHING;
+                ALOGV("%s: AE scan start, duration %" PRId64 " ms",
+                        __FUNCTION__, mAeScanDuration / 1000000);
+            }
+        }
+    }
+
+    return aeState;
+}
+
+int EmulatedFakeCamera2::ControlThread::updateAeScan(uint8_t aeMode,
+        bool aeLock, uint8_t aeState, nsecs_t *maxSleep) {
+    if (aeLock && aeState != ANDROID_CONTROL_AE_STATE_PRECAPTURE) {
+        mAeScanDuration = 0;
+        aeState = ANDROID_CONTROL_AE_STATE_LOCKED;
+    } else if ((aeState == ANDROID_CONTROL_AE_STATE_SEARCHING) ||
+            (aeState == ANDROID_CONTROL_AE_STATE_PRECAPTURE ) ) {
+        if (mAeScanDuration <= 0) {
+            ALOGV("%s: AE scan done", __FUNCTION__);
+            aeState = aeLock ?
+                    ANDROID_CONTROL_AE_STATE_LOCKED :ANDROID_CONTROL_AE_STATE_CONVERGED;
+
+            Mutex::Autolock lock(mInputMutex);
+            mExposureTime = kNormalExposureTime;
+        } else {
+            if (mAeScanDuration <= *maxSleep) {
+                *maxSleep = mAeScanDuration;
+            }
+
+            int64_t exposureDelta =
+                    ((double)rand() / RAND_MAX) * 2 * kExposureJump -
+                    kExposureJump;
+            Mutex::Autolock lock(mInputMutex);
+            mExposureTime = mExposureTime + exposureDelta;
+            if (mExposureTime < kMinExposureTime) mExposureTime = kMinExposureTime;
+        }
+    }
+
+    return aeState;
+}
+
+
+void EmulatedFakeCamera2::ControlThread::updateAeState(uint8_t newState,
+        int32_t triggerId) {
+    Mutex::Autolock lock(mInputMutex);
+    if (mAeState != newState) {
+        ALOGV("%s: Autoexposure state now %d, id %d", __FUNCTION__,
+                newState, triggerId);
+        mAeState = newState;
+        mParent->sendNotification(CAMERA2_MSG_AUTOEXPOSURE,
+                newState, triggerId, 0);
+    }
+}
+
+/** Private methods */
+
+status_t EmulatedFakeCamera2::constructStaticInfo(
+        camera_metadata_t **info,
+        bool sizeRequest) const {
+
+    size_t entryCount = 0;
+    size_t dataCount = 0;
+    status_t ret;
+
+#define ADD_OR_SIZE( tag, data, count ) \
+    if ( ( ret = addOrSize(*info, sizeRequest, &entryCount, &dataCount, \
+            tag, data, count) ) != OK ) return ret
+
+    // android.lens
+
+    // 5 cm min focus distance for back camera, infinity (fixed focus) for front
+    const float minFocusDistance = mFacingBack ? 1.0/0.05 : 0.0;
+    ADD_OR_SIZE(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+            &minFocusDistance, 1);
+    // 5 m hyperfocal distance for back camera, infinity (fixed focus) for front
+    const float hyperFocalDistance = mFacingBack ? 1.0/5.0 : 0.0;
+    ADD_OR_SIZE(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
+            &minFocusDistance, 1);
+
+    static const float focalLength = 3.30f; // mm
+    ADD_OR_SIZE(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
+            &focalLength, 1);
+    static const float aperture = 2.8f;
+    ADD_OR_SIZE(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
+            &aperture, 1);
+    static const float filterDensity = 0;
+    ADD_OR_SIZE(ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES,
+            &filterDensity, 1);
+    static const uint8_t availableOpticalStabilization =
+            ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+    ADD_OR_SIZE(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+            &availableOpticalStabilization, 1);
+
+    static const int32_t lensShadingMapSize[] = {1, 1};
+    ADD_OR_SIZE(ANDROID_LENS_INFO_SHADING_MAP_SIZE, lensShadingMapSize,
+            sizeof(lensShadingMapSize)/sizeof(int32_t));
+
+    int32_t lensFacing = mFacingBack ?
+            ANDROID_LENS_FACING_BACK : ANDROID_LENS_FACING_FRONT;
+    ADD_OR_SIZE(ANDROID_LENS_FACING, &lensFacing, 1);
+
+    // android.sensor
+
+    ADD_OR_SIZE(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
+            Sensor::kExposureTimeRange, 2);
+
+    ADD_OR_SIZE(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+            &Sensor::kFrameDurationRange[1], 1);
+
+    ADD_OR_SIZE(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
+            Sensor::kSensitivityRange,
+            sizeof(Sensor::kSensitivityRange)
+            /sizeof(int32_t));
+
+    ADD_OR_SIZE(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
+            &Sensor::kColorFilterArrangement, 1);
+
+    static const float sensorPhysicalSize[2] = {3.20f, 2.40f}; // mm
+    ADD_OR_SIZE(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
+            sensorPhysicalSize, 2);
+
+    const int32_t pixelArray[] = {mSensorWidth, mSensorHeight};
+    ADD_OR_SIZE(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+            pixelArray, 2);
+
+    ADD_OR_SIZE(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+            pixelArray, 2);
+
+    ADD_OR_SIZE(ANDROID_SENSOR_INFO_WHITE_LEVEL,
+            &Sensor::kMaxRawValue, 1);
+
+    static const int32_t blackLevelPattern[4] = {
+        static_cast<int32_t>(Sensor::kBlackLevel),
+        static_cast<int32_t>(Sensor::kBlackLevel),
+        static_cast<int32_t>(Sensor::kBlackLevel),
+        static_cast<int32_t>(Sensor::kBlackLevel)
+    };
+    ADD_OR_SIZE(ANDROID_SENSOR_BLACK_LEVEL_PATTERN,
+            blackLevelPattern, sizeof(blackLevelPattern)/sizeof(int32_t));
+
+    //TODO: sensor color calibration fields
+
+    // android.flash
+    static const uint8_t flashAvailable = 0;
+    ADD_OR_SIZE(ANDROID_FLASH_INFO_AVAILABLE, &flashAvailable, 1);
+
+    static const int64_t flashChargeDuration = 0;
+    ADD_OR_SIZE(ANDROID_FLASH_INFO_CHARGE_DURATION, &flashChargeDuration, 1);
+
+    // android.tonemap
+
+    static const int32_t tonemapCurvePoints = 128;
+    ADD_OR_SIZE(ANDROID_TONEMAP_MAX_CURVE_POINTS, &tonemapCurvePoints, 1);
+
+    // android.scaler
+
+    ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_FORMATS,
+            kAvailableFormats,
+            sizeof(kAvailableFormats)/sizeof(uint32_t));
+
+    ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_RAW_SIZES,
+            &mAvailableRawSizes.front(),
+            mAvailableRawSizes.size());
+
+    ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_RAW_MIN_DURATIONS,
+            kAvailableRawMinDurations,
+            sizeof(kAvailableRawMinDurations)/sizeof(uint64_t));
+
+    ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES,
+            &mAvailableProcessedSizes.front(),
+            mAvailableProcessedSizes.size());
+
+    ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS,
+            kAvailableProcessedMinDurations,
+            sizeof(kAvailableProcessedMinDurations)/sizeof(uint64_t));
+
+    ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_JPEG_SIZES,
+            &mAvailableJpegSizes.front(),
+            mAvailableJpegSizes.size());
+
+    ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_JPEG_MIN_DURATIONS,
+            kAvailableJpegMinDurations,
+            sizeof(kAvailableJpegMinDurations)/sizeof(uint64_t));
+
+    static const float maxZoom = 10;
+    ADD_OR_SIZE(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+            &maxZoom, 1);
+
+    // android.jpeg
+
+    static const int32_t jpegThumbnailSizes[] = {
+            0, 0,
+            160, 120,
+            320, 240
+     };
+    ADD_OR_SIZE(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+            jpegThumbnailSizes, sizeof(jpegThumbnailSizes)/sizeof(int32_t));
+
+    static const int32_t jpegMaxSize = JpegCompressor::kMaxJpegSize;
+    ADD_OR_SIZE(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1);
+
+    // android.stats
+
+    static const uint8_t availableFaceDetectModes[] = {
+        ANDROID_STATISTICS_FACE_DETECT_MODE_OFF,
+        ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE,
+        ANDROID_STATISTICS_FACE_DETECT_MODE_FULL
+    };
+
+    ADD_OR_SIZE(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+            availableFaceDetectModes,
+            sizeof(availableFaceDetectModes));
+
+    static const int32_t maxFaceCount = 8;
+    ADD_OR_SIZE(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+            &maxFaceCount, 1);
+
+    static const int32_t histogramSize = 64;
+    ADD_OR_SIZE(ANDROID_STATISTICS_INFO_HISTOGRAM_BUCKET_COUNT,
+            &histogramSize, 1);
+
+    static const int32_t maxHistogramCount = 1000;
+    ADD_OR_SIZE(ANDROID_STATISTICS_INFO_MAX_HISTOGRAM_COUNT,
+            &maxHistogramCount, 1);
+
+    static const int32_t sharpnessMapSize[2] = {64, 64};
+    ADD_OR_SIZE(ANDROID_STATISTICS_INFO_SHARPNESS_MAP_SIZE,
+            sharpnessMapSize, sizeof(sharpnessMapSize)/sizeof(int32_t));
+
+    static const int32_t maxSharpnessMapValue = 1000;
+    ADD_OR_SIZE(ANDROID_STATISTICS_INFO_MAX_SHARPNESS_MAP_VALUE,
+            &maxSharpnessMapValue, 1);
+
+    // android.control
+
+    static const uint8_t availableSceneModes[] = {
+#if VSOC_PLATFORM_SDK_AFTER(K)
+            ANDROID_CONTROL_SCENE_MODE_DISABLED
+#else
+            ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED
+#endif
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+            availableSceneModes, sizeof(availableSceneModes));
+
+    static const uint8_t availableEffects[] = {
+            ANDROID_CONTROL_EFFECT_MODE_OFF
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AVAILABLE_EFFECTS,
+            availableEffects, sizeof(availableEffects));
+
+    static const int32_t max3aRegions[] = {/*AE*/ 0,/*AWB*/ 0,/*AF*/ 0};
+    ADD_OR_SIZE(ANDROID_CONTROL_MAX_REGIONS,
+            max3aRegions, sizeof(max3aRegions)/sizeof(max3aRegions[0]));
+
+    static const uint8_t availableAeModes[] = {
+            ANDROID_CONTROL_AE_MODE_OFF,
+            ANDROID_CONTROL_AE_MODE_ON
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_AVAILABLE_MODES,
+            availableAeModes, sizeof(availableAeModes));
+
+    static const camera_metadata_rational exposureCompensationStep = {
+            1, 3
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_COMPENSATION_STEP,
+            &exposureCompensationStep, 1);
+
+    int32_t exposureCompensationRange[] = {-9, 9};
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+            exposureCompensationRange,
+            sizeof(exposureCompensationRange)/sizeof(int32_t));
+
+    static const int32_t availableTargetFpsRanges[] = {
+            5, 30, 15, 30
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+            availableTargetFpsRanges,
+            sizeof(availableTargetFpsRanges)/sizeof(int32_t));
+
+    static const uint8_t availableAntibandingModes[] = {
+            ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
+            ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+            availableAntibandingModes, sizeof(availableAntibandingModes));
+
+    static const uint8_t availableAwbModes[] = {
+            ANDROID_CONTROL_AWB_MODE_OFF,
+            ANDROID_CONTROL_AWB_MODE_AUTO,
+            ANDROID_CONTROL_AWB_MODE_INCANDESCENT,
+            ANDROID_CONTROL_AWB_MODE_FLUORESCENT,
+            ANDROID_CONTROL_AWB_MODE_DAYLIGHT,
+            ANDROID_CONTROL_AWB_MODE_SHADE
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+            availableAwbModes, sizeof(availableAwbModes));
+
+    static const uint8_t availableAfModesBack[] = {
+            ANDROID_CONTROL_AF_MODE_OFF,
+            ANDROID_CONTROL_AF_MODE_AUTO,
+            ANDROID_CONTROL_AF_MODE_MACRO,
+            ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO,
+            ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE
+    };
+
+    static const uint8_t availableAfModesFront[] = {
+            ANDROID_CONTROL_AF_MODE_OFF
+    };
+
+    if (mFacingBack) {
+        ADD_OR_SIZE(ANDROID_CONTROL_AF_AVAILABLE_MODES,
+                    availableAfModesBack, sizeof(availableAfModesBack));
+    } else {
+        ADD_OR_SIZE(ANDROID_CONTROL_AF_AVAILABLE_MODES,
+                    availableAfModesFront, sizeof(availableAfModesFront));
+    }
+
+    static const uint8_t availableVstabModes[] = {
+            ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+            availableVstabModes, sizeof(availableVstabModes));
+
+#undef ADD_OR_SIZE
+    /** Allocate metadata if sizing */
+    if (sizeRequest) {
+        ALOGV("Allocating %zu entries, %zu extra bytes for "
+                "static camera info",
+                entryCount, dataCount);
+        *info = allocate_camera_metadata(entryCount, dataCount);
+        if (*info == NULL) {
+            ALOGE("Unable to allocate camera static info"
+                    "(%zu entries, %zu bytes extra data)",
+                    entryCount, dataCount);
+            return NO_MEMORY;
+        }
+    }
+    return OK;
+}
+
+status_t EmulatedFakeCamera2::constructDefaultRequest(
+        int request_template,
+        camera_metadata_t **request,
+        bool sizeRequest) const {
+
+    size_t entryCount = 0;
+    size_t dataCount = 0;
+    status_t ret;
+
+#define ADD_OR_SIZE( tag, data, count ) \
+    if ( ( ret = addOrSize(*request, sizeRequest, &entryCount, &dataCount, \
+            tag, data, count) ) != OK ) return ret
+
+    /** android.request */
+
+    static const uint8_t requestType = ANDROID_REQUEST_TYPE_CAPTURE;
+    ADD_OR_SIZE(ANDROID_REQUEST_TYPE, &requestType, 1);
+
+    static const uint8_t metadataMode = ANDROID_REQUEST_METADATA_MODE_FULL;
+    ADD_OR_SIZE(ANDROID_REQUEST_METADATA_MODE, &metadataMode, 1);
+
+    static const int32_t id = 0;
+    ADD_OR_SIZE(ANDROID_REQUEST_ID, &id, 1);
+
+    static const int32_t frameCount = 0;
+    ADD_OR_SIZE(ANDROID_REQUEST_FRAME_COUNT, &frameCount, 1);
+
+    // OUTPUT_STREAMS set by user
+    entryCount += 1;
+    dataCount += 5; // TODO: Should be maximum stream number
+
+    /** android.lens */
+
+    static const float focusDistance = 0;
+    ADD_OR_SIZE(ANDROID_LENS_FOCUS_DISTANCE, &focusDistance, 1);
+
+    static const float aperture = 2.8f;
+    ADD_OR_SIZE(ANDROID_LENS_APERTURE, &aperture, 1);
+
+    static const float focalLength = 5.0f;
+    ADD_OR_SIZE(ANDROID_LENS_FOCAL_LENGTH, &focalLength, 1);
+
+    static const float filterDensity = 0;
+    ADD_OR_SIZE(ANDROID_LENS_FILTER_DENSITY, &filterDensity, 1);
+
+    static const uint8_t opticalStabilizationMode =
+            ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+    ADD_OR_SIZE(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+            &opticalStabilizationMode, 1);
+
+    // FOCUS_RANGE set only in frame
+
+    /** android.sensor */
+
+    static const int64_t exposureTime = 10 * MSEC;
+    ADD_OR_SIZE(ANDROID_SENSOR_EXPOSURE_TIME, &exposureTime, 1);
+
+    static const int64_t frameDuration = 33333333L; // 1/30 s
+    ADD_OR_SIZE(ANDROID_SENSOR_FRAME_DURATION, &frameDuration, 1);
+
+    static const int32_t sensitivity = 100;
+    ADD_OR_SIZE(ANDROID_SENSOR_SENSITIVITY, &sensitivity, 1);
+
+    // TIMESTAMP set only in frame
+
+    /** android.flash */
+
+    static const uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
+    ADD_OR_SIZE(ANDROID_FLASH_MODE, &flashMode, 1);
+
+    static const uint8_t flashPower = 10;
+    ADD_OR_SIZE(ANDROID_FLASH_FIRING_POWER, &flashPower, 1);
+
+    static const int64_t firingTime = 0;
+    ADD_OR_SIZE(ANDROID_FLASH_FIRING_TIME, &firingTime, 1);
+
+    /** Processing block modes */
+    uint8_t hotPixelMode = 0;
+    uint8_t demosaicMode = 0;
+    uint8_t noiseMode = 0;
+    uint8_t shadingMode = 0;
+    uint8_t colorMode = 0;
+    uint8_t tonemapMode = 0;
+    uint8_t edgeMode = 0;
+    switch (request_template) {
+      case CAMERA2_TEMPLATE_STILL_CAPTURE:
+        // fall-through
+      case CAMERA2_TEMPLATE_VIDEO_SNAPSHOT:
+        // fall-through
+      case CAMERA2_TEMPLATE_ZERO_SHUTTER_LAG:
+        hotPixelMode = ANDROID_HOT_PIXEL_MODE_HIGH_QUALITY;
+        demosaicMode = ANDROID_DEMOSAIC_MODE_HIGH_QUALITY;
+        noiseMode = ANDROID_NOISE_REDUCTION_MODE_HIGH_QUALITY;
+        shadingMode = ANDROID_SHADING_MODE_HIGH_QUALITY;
+        colorMode = ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY;
+        tonemapMode = ANDROID_TONEMAP_MODE_HIGH_QUALITY;
+        edgeMode = ANDROID_EDGE_MODE_HIGH_QUALITY;
+        break;
+      case CAMERA2_TEMPLATE_PREVIEW:
+        // fall-through
+      case CAMERA2_TEMPLATE_VIDEO_RECORD:
+        // fall-through
+      default:
+        hotPixelMode = ANDROID_HOT_PIXEL_MODE_FAST;
+        demosaicMode = ANDROID_DEMOSAIC_MODE_FAST;
+        noiseMode = ANDROID_NOISE_REDUCTION_MODE_FAST;
+        shadingMode = ANDROID_SHADING_MODE_FAST;
+        colorMode = ANDROID_COLOR_CORRECTION_MODE_FAST;
+        tonemapMode = ANDROID_TONEMAP_MODE_FAST;
+        edgeMode = ANDROID_EDGE_MODE_FAST;
+        break;
+    }
+    ADD_OR_SIZE(ANDROID_HOT_PIXEL_MODE, &hotPixelMode, 1);
+    ADD_OR_SIZE(ANDROID_DEMOSAIC_MODE, &demosaicMode, 1);
+    ADD_OR_SIZE(ANDROID_NOISE_REDUCTION_MODE, &noiseMode, 1);
+    ADD_OR_SIZE(ANDROID_SHADING_MODE, &shadingMode, 1);
+    ADD_OR_SIZE(ANDROID_COLOR_CORRECTION_MODE, &colorMode, 1);
+    ADD_OR_SIZE(ANDROID_TONEMAP_MODE, &tonemapMode, 1);
+    ADD_OR_SIZE(ANDROID_EDGE_MODE, &edgeMode, 1);
+
+    /** android.noise */
+    static const uint8_t noiseStrength = 5;
+    ADD_OR_SIZE(ANDROID_NOISE_REDUCTION_STRENGTH, &noiseStrength, 1);
+
+    /** android.color */
+    static const float colorTransform[9] = {
+        1.0f, 0.f, 0.f,
+        0.f, 1.f, 0.f,
+        0.f, 0.f, 1.f
+    };
+    ADD_OR_SIZE(ANDROID_COLOR_CORRECTION_TRANSFORM, colorTransform, 9);
+
+    /** android.tonemap */
+    static const float tonemapCurve[4] = {
+        0.f, 0.f,
+        1.f, 1.f
+    };
+    ADD_OR_SIZE(ANDROID_TONEMAP_CURVE_RED, tonemapCurve, 4);
+    ADD_OR_SIZE(ANDROID_TONEMAP_CURVE_GREEN, tonemapCurve, 4);
+    ADD_OR_SIZE(ANDROID_TONEMAP_CURVE_BLUE, tonemapCurve, 4);
+
+    /** android.edge */
+    static const uint8_t edgeStrength = 5;
+    ADD_OR_SIZE(ANDROID_EDGE_STRENGTH, &edgeStrength, 1);
+
+    /** android.scaler */
+    static const int32_t cropRegion[3] = {
+        0, 0, static_cast<int32_t>(mSensorWidth)
+    };
+    ADD_OR_SIZE(ANDROID_SCALER_CROP_REGION, cropRegion, 3);
+
+    /** android.jpeg */
+    static const int32_t jpegQuality = 80;
+    ADD_OR_SIZE(ANDROID_JPEG_QUALITY, &jpegQuality, 1);
+
+    static const int32_t thumbnailSize[2] = {
+        640, 480
+    };
+    ADD_OR_SIZE(ANDROID_JPEG_THUMBNAIL_SIZE, thumbnailSize, 2);
+
+    static const int32_t thumbnailQuality = 80;
+    ADD_OR_SIZE(ANDROID_JPEG_THUMBNAIL_QUALITY, &thumbnailQuality, 1);
+
+    static const double gpsCoordinates[2] = {
+        0, 0
+    };
+    ADD_OR_SIZE(ANDROID_JPEG_GPS_COORDINATES, gpsCoordinates, 2);
+
+    static const uint8_t gpsProcessingMethod[32] = "None";
+    ADD_OR_SIZE(ANDROID_JPEG_GPS_PROCESSING_METHOD, gpsProcessingMethod, 32);
+
+    static const int64_t gpsTimestamp = 0;
+    ADD_OR_SIZE(ANDROID_JPEG_GPS_TIMESTAMP, &gpsTimestamp, 1);
+
+    static const int32_t jpegOrientation = 0;
+    ADD_OR_SIZE(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1);
+
+    /** android.stats */
+
+    static const uint8_t faceDetectMode =
+        ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+    ADD_OR_SIZE(ANDROID_STATISTICS_FACE_DETECT_MODE, &faceDetectMode, 1);
+
+    static const uint8_t histogramMode = ANDROID_STATISTICS_HISTOGRAM_MODE_OFF;
+    ADD_OR_SIZE(ANDROID_STATISTICS_HISTOGRAM_MODE, &histogramMode, 1);
+
+    static const uint8_t sharpnessMapMode =
+        ANDROID_STATISTICS_SHARPNESS_MAP_MODE_OFF;
+    ADD_OR_SIZE(ANDROID_STATISTICS_SHARPNESS_MAP_MODE, &sharpnessMapMode, 1);
+
+    // faceRectangles, faceScores, faceLandmarks, faceIds, histogram,
+    // sharpnessMap only in frames
+
+    /** android.control */
+
+    uint8_t controlIntent = 0;
+    switch (request_template) {
+      case CAMERA2_TEMPLATE_PREVIEW:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
+        break;
+      case CAMERA2_TEMPLATE_STILL_CAPTURE:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
+        break;
+      case CAMERA2_TEMPLATE_VIDEO_RECORD:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
+        break;
+      case CAMERA2_TEMPLATE_VIDEO_SNAPSHOT:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
+        break;
+      case CAMERA2_TEMPLATE_ZERO_SHUTTER_LAG:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG;
+        break;
+      default:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_CUSTOM;
+        break;
+    }
+    ADD_OR_SIZE(ANDROID_CONTROL_CAPTURE_INTENT, &controlIntent, 1);
+
+    static const uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
+    ADD_OR_SIZE(ANDROID_CONTROL_MODE, &controlMode, 1);
+
+    static const uint8_t effectMode = ANDROID_CONTROL_EFFECT_MODE_OFF;
+    ADD_OR_SIZE(ANDROID_CONTROL_EFFECT_MODE, &effectMode, 1);
+
+    static const uint8_t sceneMode = ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY;
+    ADD_OR_SIZE(ANDROID_CONTROL_SCENE_MODE, &sceneMode, 1);
+
+    static const uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH;
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_MODE, &aeMode, 1);
+
+    static const uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_LOCK, &aeLock, 1);
+
+    static const int32_t controlRegions[5] = {
+        0, 0,
+        static_cast<int32_t>(mSensorWidth),
+        static_cast<int32_t>(mSensorHeight),
+        1000
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_REGIONS, controlRegions, 5);
+
+    static const int32_t aeExpCompensation = 0;
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, &aeExpCompensation, 1);
+
+    static const int32_t aeTargetFpsRange[2] = {
+        10, 30
+    };
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_TARGET_FPS_RANGE, aeTargetFpsRange, 2);
+
+    static const uint8_t aeAntibandingMode =
+            ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+    ADD_OR_SIZE(ANDROID_CONTROL_AE_ANTIBANDING_MODE, &aeAntibandingMode, 1);
+
+    static const uint8_t awbMode =
+            ANDROID_CONTROL_AWB_MODE_AUTO;
+    ADD_OR_SIZE(ANDROID_CONTROL_AWB_MODE, &awbMode, 1);
+
+    static const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+    ADD_OR_SIZE(ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
+
+    ADD_OR_SIZE(ANDROID_CONTROL_AWB_REGIONS, controlRegions, 5);
+
+    uint8_t afMode = 0;
+    switch (request_template) {
+      case CAMERA2_TEMPLATE_PREVIEW:
+        afMode = ANDROID_CONTROL_AF_MODE_AUTO;
+        break;
+      case CAMERA2_TEMPLATE_STILL_CAPTURE:
+        afMode = ANDROID_CONTROL_AF_MODE_AUTO;
+        break;
+      case CAMERA2_TEMPLATE_VIDEO_RECORD:
+        afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO;
+        break;
+      case CAMERA2_TEMPLATE_VIDEO_SNAPSHOT:
+        afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO;
+        break;
+      case CAMERA2_TEMPLATE_ZERO_SHUTTER_LAG:
+        afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE;
+        break;
+      default:
+        afMode = ANDROID_CONTROL_AF_MODE_AUTO;
+        break;
+    }
+    ADD_OR_SIZE(ANDROID_CONTROL_AF_MODE, &afMode, 1);
+
+    ADD_OR_SIZE(ANDROID_CONTROL_AF_REGIONS, controlRegions, 5);
+
+    static const uint8_t vstabMode =
+        ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+    ADD_OR_SIZE(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, &vstabMode, 1);
+
+    // aeState, awbState, afState only in frame
+
+    /** Allocate metadata if sizing */
+    if (sizeRequest) {
+        ALOGV("Allocating %zu entries, %zu extra bytes for "
+                "request template type %d",
+                entryCount, dataCount, request_template);
+        *request = allocate_camera_metadata(entryCount, dataCount);
+        if (*request == NULL) {
+            ALOGE("Unable to allocate new request template type %d "
+                    "(%zu entries, %zu bytes extra data)", request_template,
+                    entryCount, dataCount);
+            return NO_MEMORY;
+        }
+    }
+    return OK;
+#undef ADD_OR_SIZE
+}
+
+status_t EmulatedFakeCamera2::addOrSize(camera_metadata_t *request,
+        bool sizeRequest,
+        size_t *entryCount,
+        size_t *dataCount,
+        uint32_t tag,
+        const void *entryData,
+        size_t entryDataCount) {
+    status_t res;
+    if (!sizeRequest) {
+        return add_camera_metadata_entry(request, tag, entryData,
+                entryDataCount);
+    } else {
+        int type = get_camera_metadata_tag_type(tag);
+        if (type < 0 ) return BAD_VALUE;
+        (*entryCount)++;
+        (*dataCount) += calculate_camera_metadata_entry_data_size(type,
+                entryDataCount);
+        return OK;
+    }
+}
+
+bool EmulatedFakeCamera2::isStreamInUse(uint32_t id) {
+    // Assumes mMutex is locked; otherwise new requests could enter
+    // configureThread while readoutThread is being checked
+
+    // Order of isStreamInUse calls matters
+    if (mConfigureThread->isStreamInUse(id) ||
+            mReadoutThread->isStreamInUse(id) ||
+            mJpegCompressor->isStreamInUse(id) ) {
+        ALOGE("%s: Stream %d is in use in active requests!",
+                __FUNCTION__, id);
+        return true;
+    }
+    return false;
+}
+
+bool EmulatedFakeCamera2::isReprocessStreamInUse(uint32_t id) {
+    // TODO: implement
+    return false;
+}
+
+const Stream& EmulatedFakeCamera2::getStreamInfo(uint32_t streamId) {
+    Mutex::Autolock lock(mMutex);
+
+    return mStreams.valueFor(streamId);
+}
+
+const ReprocessStream& EmulatedFakeCamera2::getReprocessStreamInfo(uint32_t streamId) {
+    Mutex::Autolock lock(mMutex);
+
+    return mReprocessStreams.valueFor(streamId);
+}
+
+};  /* namespace android */
diff --git a/guest/hals/camera/EmulatedFakeCamera2.h b/guest/hals/camera/EmulatedFakeCamera2.h
new file mode 100644
index 0000000..c622d26
--- /dev/null
+++ b/guest/hals/camera/EmulatedFakeCamera2.h
@@ -0,0 +1,437 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA2_H
+#define HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA2_H
+
+/*
+ * Contains declaration of a class EmulatedFakeCamera2 that encapsulates
+ * functionality of a fake camera that implements version 2 of the camera device
+ * interface.
+ */
+
+#include <vector>
+
+#include "EmulatedCamera2.h"
+#include "fake-pipeline2/Base.h"
+#include "fake-pipeline2/Sensor.h"
+#include "fake-pipeline2/JpegCompressor.h"
+#include <utils/Condition.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+namespace android {
+
+/* Encapsulates functionality of an advanced fake camera.  This camera contains
+ * a simple simulation of a scene, sensor, and image processing pipeline.
+ */
+class EmulatedFakeCamera2 : public EmulatedCamera2 {
+public:
+    /* Constructs EmulatedFakeCamera instance. */
+    EmulatedFakeCamera2(int cameraId, bool facingBack, struct hw_module_t* module);
+
+    /* Destructs EmulatedFakeCamera instance. */
+    ~EmulatedFakeCamera2();
+
+    /****************************************************************************
+     * EmulatedCamera2 virtual overrides.
+     ***************************************************************************/
+
+public:
+    /* Initializes EmulatedFakeCamera2 instance. */
+    status_t Initialize(const cvd::CameraDefinition& props);
+
+    /****************************************************************************
+     * Camera Module API and generic hardware device API implementation
+     ***************************************************************************/
+public:
+
+    virtual status_t connectCamera(hw_device_t** device);
+
+    virtual status_t plugCamera();
+    virtual status_t unplugCamera();
+    virtual camera_device_status_t getHotplugStatus();
+
+    virtual status_t closeCamera();
+
+    virtual status_t getCameraInfo(struct camera_info *info);
+
+    /****************************************************************************
+     * EmulatedCamera2 abstract API implementation.
+     ***************************************************************************/
+protected:
+    /** Request input queue */
+
+    virtual int requestQueueNotify();
+
+    /** Count of requests in flight */
+    virtual int getInProgressCount();
+
+    /** Cancel all captures in flight */
+    //virtual int flushCapturesInProgress();
+
+    /** Construct default request */
+    virtual int constructDefaultRequest(
+            int request_template,
+            camera_metadata_t **request);
+
+    virtual int allocateStream(
+            uint32_t width,
+            uint32_t height,
+            int format,
+            const camera2_stream_ops_t *stream_ops,
+            uint32_t *stream_id,
+            uint32_t *format_actual,
+            uint32_t *usage,
+            uint32_t *max_buffers);
+
+    virtual int registerStreamBuffers(
+            uint32_t stream_id,
+            int num_buffers,
+            buffer_handle_t *buffers);
+
+    virtual int releaseStream(uint32_t stream_id);
+
+    // virtual int allocateReprocessStream(
+    //         uint32_t width,
+    //         uint32_t height,
+    //         uint32_t format,
+    //         const camera2_stream_ops_t *stream_ops,
+    //         uint32_t *stream_id,
+    //         uint32_t *format_actual,
+    //         uint32_t *usage,
+    //         uint32_t *max_buffers);
+
+    virtual int allocateReprocessStreamFromStream(
+            uint32_t output_stream_id,
+            const camera2_stream_in_ops_t *stream_ops,
+            uint32_t *stream_id);
+
+    virtual int releaseReprocessStream(uint32_t stream_id);
+
+    virtual int triggerAction(uint32_t trigger_id,
+            int32_t ext1,
+            int32_t ext2);
+
+    /** Debug methods */
+
+    virtual int dump(int fd);
+
+public:
+    /****************************************************************************
+     * Utility methods called by configure/readout threads and pipeline
+     ***************************************************************************/
+
+    // Get information about a given stream. Will lock mMutex
+    const Stream &getStreamInfo(uint32_t streamId);
+    const ReprocessStream &getReprocessStreamInfo(uint32_t streamId);
+
+    // Notifies rest of camera subsystem of serious error
+    void signalError();
+
+private:
+    /****************************************************************************
+     * Utility methods
+     ***************************************************************************/
+    /** Construct static camera metadata, two-pass */
+    status_t constructStaticInfo(
+            camera_metadata_t **info,
+            bool sizeRequest) const;
+
+    /** Two-pass implementation of constructDefaultRequest */
+    status_t constructDefaultRequest(
+            int request_template,
+            camera_metadata_t **request,
+            bool sizeRequest) const;
+    /** Helper function for constructDefaultRequest */
+    static status_t addOrSize( camera_metadata_t *request,
+            bool sizeRequest,
+            size_t *entryCount,
+            size_t *dataCount,
+            uint32_t tag,
+            const void *entry_data,
+            size_t entry_count);
+
+    /** Determine if the stream id is listed in any currently-in-flight
+     * requests. Assumes mMutex is locked */
+    bool isStreamInUse(uint32_t streamId);
+
+    /** Determine if the reprocess stream id is listed in any
+     * currently-in-flight requests. Assumes mMutex is locked */
+    bool isReprocessStreamInUse(uint32_t streamId);
+
+    /****************************************************************************
+     * Pipeline controller threads
+     ***************************************************************************/
+
+    class ConfigureThread: public Thread {
+      public:
+        ConfigureThread(EmulatedFakeCamera2 *parent);
+        ~ConfigureThread();
+
+        status_t waitUntilRunning();
+        status_t newRequestAvailable();
+        status_t readyToRun();
+
+        bool isStreamInUse(uint32_t id);
+        int getInProgressCount();
+      private:
+        EmulatedFakeCamera2 *mParent;
+        static const nsecs_t kWaitPerLoop = 10000000L; // 10 ms
+
+        bool mRunning;
+        bool threadLoop();
+
+        bool setupCapture();
+        bool setupReprocess();
+
+        bool configureNextCapture();
+        bool configureNextReprocess();
+
+        bool getBuffers();
+
+        Mutex mInputMutex; // Protects mActive, mRequestCount
+        Condition mInputSignal;
+        bool mActive; // Whether we're waiting for input requests or actively
+                      // working on them
+        size_t mRequestCount;
+
+        camera_metadata_t *mRequest;
+
+        Mutex mInternalsMutex; // Lock before accessing below members.
+        bool    mWaitingForReadout;
+        bool    mNextNeedsJpeg;
+        bool    mNextIsCapture;
+        int32_t mNextFrameNumber;
+        int64_t mNextExposureTime;
+        int64_t mNextFrameDuration;
+        int32_t mNextSensitivity;
+        Buffers *mNextBuffers;
+    };
+
+    class ReadoutThread: public Thread, private JpegCompressor::JpegListener {
+      public:
+        ReadoutThread(EmulatedFakeCamera2 *parent);
+        ~ReadoutThread();
+
+        status_t readyToRun();
+
+        // Input
+        status_t waitUntilRunning();
+        bool waitForReady(nsecs_t timeout);
+        void setNextOperation(bool isCapture,
+                camera_metadata_t *request,
+                Buffers *buffers);
+        bool isStreamInUse(uint32_t id);
+        int getInProgressCount();
+      private:
+        EmulatedFakeCamera2 *mParent;
+
+        bool mRunning;
+        bool threadLoop();
+
+        bool readyForNextCapture();
+        status_t collectStatisticsMetadata(camera_metadata_t *frame);
+
+        // Inputs
+        Mutex mInputMutex; // Protects mActive, mInFlightQueue, mRequestCount
+        Condition mInputSignal;
+        Condition mReadySignal;
+
+        bool mActive;
+
+        static const int kInFlightQueueSize = 4;
+        struct InFlightQueue {
+            bool isCapture;
+            camera_metadata_t *request;
+            Buffers *buffers;
+        } *mInFlightQueue;
+
+        size_t mInFlightHead;
+        size_t mInFlightTail;
+
+        size_t mRequestCount;
+
+        // Internals
+        Mutex mInternalsMutex;
+
+        bool mIsCapture;
+        camera_metadata_t *mRequest;
+        Buffers *mBuffers;
+
+        // Jpeg completion listeners
+        void onJpegDone(const StreamBuffer &jpegBuffer, bool success);
+        void onJpegInputDone(const StreamBuffer &inputBuffer);
+        nsecs_t mJpegTimestamp;
+    };
+
+    // 3A management thread (auto-exposure, focus, white balance)
+    class ControlThread: public Thread {
+      public:
+        ControlThread(EmulatedFakeCamera2 *parent);
+        ~ControlThread();
+
+        status_t readyToRun();
+
+        status_t waitUntilRunning();
+
+        // Interpret request's control parameters and override
+        // capture settings as needed
+        status_t processRequest(camera_metadata_t *request);
+
+        status_t triggerAction(uint32_t msgType,
+                int32_t ext1, int32_t ext2);
+      private:
+        ControlThread(const ControlThread &t);
+        ControlThread& operator=(const ControlThread &t);
+
+        // Constants controlling fake 3A behavior
+        static const nsecs_t kControlCycleDelay;
+        static const nsecs_t kMinAfDuration;
+        static const nsecs_t kMaxAfDuration;
+        static const float kAfSuccessRate;
+        static const float kContinuousAfStartRate;
+
+        static const float kAeScanStartRate;
+        static const nsecs_t kMinAeDuration;
+        static const nsecs_t kMaxAeDuration;
+        static const nsecs_t kMinPrecaptureAeDuration;
+        static const nsecs_t kMaxPrecaptureAeDuration;
+
+        static const nsecs_t kNormalExposureTime;
+        static const nsecs_t kExposureJump;
+        static const nsecs_t kMinExposureTime;
+
+        EmulatedFakeCamera2 *mParent;
+
+        bool mRunning;
+        bool threadLoop();
+
+        Mutex mInputMutex; // Protects input methods
+        Condition mInputSignal;
+
+        // Trigger notifications
+        bool mStartAf;
+        bool mCancelAf;
+        bool mStartPrecapture;
+
+        // Latest state for 3A request fields
+        uint8_t mControlMode;
+
+        uint8_t mEffectMode;
+        uint8_t mSceneMode;
+
+        uint8_t mAfMode;
+        bool mAfModeChange;
+
+        uint8_t mAwbMode;
+        uint8_t mAeMode;
+
+        // Latest trigger IDs
+        int32_t mAfTriggerId;
+        int32_t mPrecaptureTriggerId;
+
+        // Current state for 3A algorithms
+        uint8_t mAfState;
+        uint8_t mAeState;
+        uint8_t mAwbState;
+        bool    mAeLock;
+
+        // Current control parameters
+        nsecs_t mExposureTime;
+
+        // Private to threadLoop and its utility methods
+
+        nsecs_t mAfScanDuration;
+        nsecs_t mAeScanDuration;
+        bool mLockAfterPassiveScan;
+
+        // Utility methods for AF
+        int processAfTrigger(uint8_t afMode, uint8_t afState);
+        int maybeStartAfScan(uint8_t afMode, uint8_t afState);
+        int updateAfScan(uint8_t afMode, uint8_t afState, nsecs_t *maxSleep);
+        void updateAfState(uint8_t newState, int32_t triggerId);
+
+        // Utility methods for precapture trigger
+        int processPrecaptureTrigger(uint8_t aeMode, uint8_t aeState);
+        int maybeStartAeScan(uint8_t aeMode, bool aeLock, uint8_t aeState);
+        int updateAeScan(uint8_t aeMode, bool aeLock, uint8_t aeState,
+                nsecs_t *maxSleep);
+        void updateAeState(uint8_t newState, int32_t triggerId);
+    };
+
+    /****************************************************************************
+     * Static configuration information
+     ***************************************************************************/
+private:
+    static const uint32_t kMaxRawStreamCount = 1;
+    static const uint32_t kMaxProcessedStreamCount = 3;
+    static const uint32_t kMaxJpegStreamCount = 1;
+    static const uint32_t kMaxReprocessStreamCount = 2;
+    static const uint32_t kMaxBufferCount = 4;
+    static const uint32_t kAvailableFormats[];
+    static const uint32_t kAvailableRawSizes[];
+    static const uint64_t kAvailableRawMinDurations[];
+    static const uint32_t kAvailableProcessedSizesBack[];
+    static const uint32_t kAvailableProcessedSizesFront[];
+    static const uint64_t kAvailableProcessedMinDurations[];
+    static const uint32_t kAvailableJpegSizesBack[];
+    static const uint32_t kAvailableJpegSizesFront[];
+    static const uint64_t kAvailableJpegMinDurations[];
+
+    /****************************************************************************
+     * Data members.
+     ***************************************************************************/
+
+protected:
+    /* Facing back (true) or front (false) switch. */
+    bool mFacingBack;
+
+private:
+    bool mIsConnected;
+
+    int32_t mSensorWidth, mSensorHeight;
+
+    /** Stream manipulation */
+    uint32_t mNextStreamId;
+    uint32_t mRawStreamCount;
+    uint32_t mProcessedStreamCount;
+    uint32_t mJpegStreamCount;
+
+    std::vector<uint32_t> mAvailableRawSizes;
+    std::vector<uint32_t> mAvailableProcessedSizes;
+    std::vector<uint32_t> mAvailableJpegSizes;
+
+    uint32_t mNextReprocessStreamId;
+    uint32_t mReprocessStreamCount;
+
+    KeyedVector<uint32_t, Stream> mStreams;
+    KeyedVector<uint32_t, ReprocessStream> mReprocessStreams;
+
+    /** Simulated hardware interfaces */
+    sp<Sensor> mSensor;
+    sp<JpegCompressor> mJpegCompressor;
+
+    /** Pipeline control threads */
+    sp<ConfigureThread> mConfigureThread;
+    sp<ReadoutThread>   mReadoutThread;
+    sp<ControlThread>   mControlThread;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA2_H */
diff --git a/guest/hals/camera/EmulatedFakeCamera3.cpp b/guest/hals/camera/EmulatedFakeCamera3.cpp
new file mode 100644
index 0000000..dcfc34c
--- /dev/null
+++ b/guest/hals/camera/EmulatedFakeCamera3.cpp
@@ -0,0 +1,2625 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedFakeCamera3 that encapsulates
+ * functionality of an advanced fake camera.
+ */
+
+#include <cstdint>
+
+//#define LOG_NDEBUG 0
+//#define LOG_NNDEBUG 0
+#define LOG_TAG "EmulatedCamera_FakeCamera3"
+#include <cutils/properties.h>
+#include <utils/Log.h>
+
+#include "EmulatedFakeCamera3.h"
+#include "EmulatedCameraFactory.h"
+#include <ui/Fence.h>
+#include "GrallocModule.h"
+
+#include "fake-pipeline2/Sensor.h"
+#include "fake-pipeline2/JpegCompressor.h"
+#include <cmath>
+
+#include <vector>
+
+#if defined(LOG_NNDEBUG) && LOG_NNDEBUG == 0
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(...) ((void)0)
+#endif
+
+namespace android {
+
+/**
+ * Constants for camera capabilities
+ */
+
+const int64_t USEC = 1000LL;
+const int64_t MSEC = USEC * 1000LL;
+const int64_t SEC = MSEC * 1000LL;
+
+const int32_t EmulatedFakeCamera3::kAvailableFormats[] = {
+        HAL_PIXEL_FORMAT_RAW16,
+        HAL_PIXEL_FORMAT_BLOB,
+        HAL_PIXEL_FORMAT_RGBA_8888,
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+        // These are handled by YCbCr_420_888
+        //        HAL_PIXEL_FORMAT_YV12,
+        //        HAL_PIXEL_FORMAT_YCrCb_420_SP,
+        HAL_PIXEL_FORMAT_YCbCr_420_888,
+        HAL_PIXEL_FORMAT_Y16
+};
+
+/**
+ * 3A constants
+ */
+
+// Default exposure and gain targets for different scenarios
+const nsecs_t EmulatedFakeCamera3::kNormalExposureTime       = 10 * MSEC;
+const nsecs_t EmulatedFakeCamera3::kFacePriorityExposureTime = 30 * MSEC;
+const int     EmulatedFakeCamera3::kNormalSensitivity        = 100;
+const int     EmulatedFakeCamera3::kFacePrioritySensitivity  = 400;
+const float   EmulatedFakeCamera3::kExposureTrackRate        = 0.1;
+const int     EmulatedFakeCamera3::kPrecaptureMinFrames      = 10;
+const int     EmulatedFakeCamera3::kStableAeMaxFrames        = 100;
+const float   EmulatedFakeCamera3::kExposureWanderMin        = -2;
+const float   EmulatedFakeCamera3::kExposureWanderMax        = 1;
+
+/**
+ * Camera device lifecycle methods
+ */
+
+EmulatedFakeCamera3::EmulatedFakeCamera3(int cameraId, bool facingBack,
+        struct hw_module_t* module) :
+        EmulatedCamera3(cameraId, module),
+        mFacingBack(facingBack) {
+    ALOGI("Constructing emulated fake camera 3: ID %d, facing %s",
+            mCameraID, facingBack ? "back" : "front");
+
+    for (size_t i = 0; i < CAMERA3_TEMPLATE_COUNT; i++) {
+        mDefaultTemplates[i] = NULL;
+    }
+}
+
+EmulatedFakeCamera3::~EmulatedFakeCamera3() {
+    for (size_t i = 0; i < CAMERA3_TEMPLATE_COUNT; i++) {
+        if (mDefaultTemplates[i] != NULL) {
+            free_camera_metadata(mDefaultTemplates[i]);
+        }
+    }
+}
+
+status_t EmulatedFakeCamera3::Initialize(const cvd::CameraDefinition& params) {
+    ALOGV("%s: E", __FUNCTION__);
+    status_t res;
+
+    if (mStatus != STATUS_ERROR) {
+        ALOGE("%s: Already initialized!", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+
+    res = getCameraCapabilities();
+    if (res != OK) {
+        ALOGE("%s: Unable to get camera capabilities: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        return res;
+    }
+
+    res = constructStaticInfo(params);
+    if (res != OK) {
+        ALOGE("%s: Unable to allocate static info: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        return res;
+    }
+
+    return EmulatedCamera3::Initialize(params);
+}
+
+status_t EmulatedFakeCamera3::connectCamera(hw_device_t** device) {
+    ALOGV("%s: E", __FUNCTION__);
+    Mutex::Autolock l(mLock);
+    status_t res;
+
+    if (mStatus != STATUS_CLOSED) {
+        ALOGE("%s: Can't connect in state %d", __FUNCTION__, mStatus);
+        return INVALID_OPERATION;
+    }
+
+    mSensor = new Sensor(mSensorWidth, mSensorHeight);
+    mSensor->setSensorListener(this);
+
+    res = mSensor->startUp();
+    if (res != NO_ERROR) return res;
+
+    mReadoutThread = new ReadoutThread(this);
+    mJpegCompressor = new JpegCompressor();
+
+    res = mReadoutThread->run("EmuCam3::readoutThread");
+    if (res != NO_ERROR) return res;
+
+    // Initialize fake 3A
+
+    mControlMode  = ANDROID_CONTROL_MODE_AUTO;
+    mFacePriority = false;
+    mAeMode       = ANDROID_CONTROL_AE_MODE_ON;
+    mAfMode       = ANDROID_CONTROL_AF_MODE_AUTO;
+    mAwbMode      = ANDROID_CONTROL_AWB_MODE_AUTO;
+    mAeState      = ANDROID_CONTROL_AE_STATE_INACTIVE;
+    mAfState      = ANDROID_CONTROL_AF_STATE_INACTIVE;
+    mAwbState     = ANDROID_CONTROL_AWB_STATE_INACTIVE;
+    mAeCounter    = 0;
+    mAeTargetExposureTime = kNormalExposureTime;
+    mAeCurrentExposureTime = kNormalExposureTime;
+    mAeCurrentSensitivity  = kNormalSensitivity;
+
+    return EmulatedCamera3::connectCamera(device);
+}
+
+status_t EmulatedFakeCamera3::closeCamera() {
+    ALOGV("%s: E", __FUNCTION__);
+    status_t res;
+    {
+        Mutex::Autolock l(mLock);
+        if (mStatus == STATUS_CLOSED) return OK;
+
+        res = mSensor->shutDown();
+        if (res != NO_ERROR) {
+            ALOGE("%s: Unable to shut down sensor: %d", __FUNCTION__, res);
+            return res;
+        }
+        mSensor.clear();
+
+        mReadoutThread->requestExit();
+    }
+
+    mReadoutThread->join();
+
+    {
+        Mutex::Autolock l(mLock);
+        // Clear out private stream information
+        for (StreamIterator s = mStreams.begin(); s != mStreams.end(); s++) {
+            PrivateStreamInfo *privStream =
+                    static_cast<PrivateStreamInfo*>((*s)->priv);
+            delete privStream;
+            (*s)->priv = NULL;
+        }
+        mStreams.clear();
+        mReadoutThread.clear();
+    }
+
+    return EmulatedCamera3::closeCamera();
+}
+
+status_t EmulatedFakeCamera3::getCameraInfo(struct camera_info *info) {
+    info->facing = mFacingBack ? CAMERA_FACING_BACK : CAMERA_FACING_FRONT;
+    info->orientation = EmulatedCameraFactory::Instance().getFakeCameraOrientation();
+#if VSOC_PLATFORM_SDK_AFTER(L_MR1)
+    info->resource_cost = 100;
+    info->conflicting_devices = NULL;
+    info->conflicting_devices_length = 0;
+#endif
+    return EmulatedCamera3::getCameraInfo(info);
+}
+
+status_t EmulatedFakeCamera3::setTorchMode(bool enabled) {
+    if (!mFacingBack) {
+        ALOGE("%s: Front camera does not have flash unit", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+    EmulatedCameraFactory::Instance().onTorchModeStatusChanged(
+        mCameraID, enabled ?
+        TORCH_MODE_STATUS_AVAILABLE_ON :
+        TORCH_MODE_STATUS_AVAILABLE_OFF);
+    return NO_ERROR;
+}
+
+/**
+ * Camera3 interface methods
+ */
+
+status_t EmulatedFakeCamera3::configureStreams(
+        camera3_stream_configuration *streamList) {
+    Mutex::Autolock l(mLock);
+    ALOGV("%s: %d streams", __FUNCTION__, streamList->num_streams);
+
+    if (mStatus != STATUS_OPEN && mStatus != STATUS_READY) {
+        ALOGE("%s: Cannot configure streams in state %d",
+                __FUNCTION__, mStatus);
+        return NO_INIT;
+    }
+
+    /**
+     * Sanity-check input list.
+     */
+    if (streamList == NULL) {
+        ALOGE("%s: NULL stream configuration", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (streamList->streams == NULL) {
+        ALOGE("%s: NULL stream list", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (streamList->num_streams < 1) {
+        ALOGE("%s: Bad number of streams requested: %d", __FUNCTION__,
+                streamList->num_streams);
+        return BAD_VALUE;
+    }
+
+    camera3_stream_t *inputStream = NULL;
+    for (size_t i = 0; i < streamList->num_streams; i++) {
+        camera3_stream_t *newStream = streamList->streams[i];
+
+        if (newStream == NULL) {
+            ALOGE("%s: Stream index %zu was NULL",
+                  __FUNCTION__, i);
+            return BAD_VALUE;
+        }
+
+        ALOGV("%s: Stream %p (id %zu), type %d, usage 0x%x, format 0x%x",
+                __FUNCTION__, newStream, i, newStream->stream_type,
+                newStream->usage,
+                newStream->format);
+
+        if (newStream->stream_type == CAMERA3_STREAM_INPUT ||
+            newStream->stream_type == CAMERA3_STREAM_BIDIRECTIONAL) {
+            if (inputStream != NULL) {
+
+                ALOGE("%s: Multiple input streams requested!", __FUNCTION__);
+                return BAD_VALUE;
+            }
+            inputStream = newStream;
+        }
+
+        bool validFormat = false;
+        for (size_t f = 0;
+             f < sizeof(kAvailableFormats)/sizeof(kAvailableFormats[0]);
+             f++) {
+            if (newStream->format == kAvailableFormats[f]) {
+                validFormat = true;
+                break;
+            }
+        }
+        if (!validFormat) {
+            ALOGE("%s: Unsupported stream format 0x%x requested",
+                    __FUNCTION__, newStream->format);
+            return BAD_VALUE;
+        }
+    }
+    mInputStream = inputStream;
+
+    /**
+     * Initially mark all existing streams as not alive
+     */
+    for (StreamIterator s = mStreams.begin(); s != mStreams.end(); ++s) {
+        PrivateStreamInfo *privStream =
+                static_cast<PrivateStreamInfo*>((*s)->priv);
+        privStream->alive = false;
+    }
+
+    /**
+     * Find new streams and mark still-alive ones
+     */
+    for (size_t i = 0; i < streamList->num_streams; i++) {
+        camera3_stream_t *newStream = streamList->streams[i];
+        if (newStream->priv == NULL) {
+            // New stream, construct info
+            PrivateStreamInfo *privStream = new PrivateStreamInfo();
+            privStream->alive = true;
+
+            newStream->max_buffers = kMaxBufferCount;
+            newStream->priv = privStream;
+            mStreams.push_back(newStream);
+        } else {
+            // Existing stream, mark as still alive.
+            PrivateStreamInfo *privStream =
+                    static_cast<PrivateStreamInfo*>(newStream->priv);
+            privStream->alive = true;
+        }
+        // Always update usage and max buffers
+        newStream->max_buffers = kMaxBufferCount;
+        switch (newStream->stream_type) {
+            case CAMERA3_STREAM_OUTPUT:
+                newStream->usage = GRALLOC_USAGE_HW_CAMERA_WRITE;
+                break;
+            case CAMERA3_STREAM_INPUT:
+                newStream->usage = GRALLOC_USAGE_HW_CAMERA_READ;
+                break;
+            case CAMERA3_STREAM_BIDIRECTIONAL:
+                newStream->usage = GRALLOC_USAGE_HW_CAMERA_READ |
+                        GRALLOC_USAGE_HW_CAMERA_WRITE;
+                break;
+        }
+    }
+
+    /**
+     * Reap the dead streams
+     */
+    for (StreamIterator s = mStreams.begin(); s != mStreams.end();) {
+        PrivateStreamInfo *privStream =
+                static_cast<PrivateStreamInfo*>((*s)->priv);
+        if (!privStream->alive) {
+            (*s)->priv = NULL;
+            delete privStream;
+            s = mStreams.erase(s);
+        } else {
+            ++s;
+        }
+    }
+
+    /**
+     * Can't reuse settings across configure call
+     */
+    mPrevSettings.clear();
+
+    return OK;
+}
+
+status_t EmulatedFakeCamera3::registerStreamBuffers(
+        const camera3_stream_buffer_set *bufferSet) {
+    ALOGV("%s: E", __FUNCTION__);
+    Mutex::Autolock l(mLock);
+
+    // Should not be called in HAL versions >= 3.2
+
+    ALOGE("%s: Should not be invoked on new HALs!",
+            __FUNCTION__);
+    return NO_INIT;
+}
+
+const camera_metadata_t* EmulatedFakeCamera3::constructDefaultRequestSettings(
+        int type) {
+    ALOGV("%s: E", __FUNCTION__);
+    Mutex::Autolock l(mLock);
+
+    if (type < 0 || type >= CAMERA3_TEMPLATE_COUNT) {
+        ALOGE("%s: Unknown request settings template: %d",
+                __FUNCTION__, type);
+        return NULL;
+    }
+
+    if (!hasCapability(BACKWARD_COMPATIBLE) && type != CAMERA3_TEMPLATE_PREVIEW) {
+        ALOGE("%s: Template %d not supported w/o BACKWARD_COMPATIBLE capability",
+                __FUNCTION__, type);
+        return NULL;
+    }
+
+    /**
+     * Cache is not just an optimization - pointer returned has to live at
+     * least as long as the camera device instance does.
+     */
+    if (mDefaultTemplates[type] != NULL) {
+        return mDefaultTemplates[type];
+    }
+
+    CameraMetadata settings;
+
+    /** android.request */
+
+    static const uint8_t metadataMode = ANDROID_REQUEST_METADATA_MODE_FULL;
+    settings.update(ANDROID_REQUEST_METADATA_MODE, &metadataMode, 1);
+
+    static const int32_t id = 0;
+    settings.update(ANDROID_REQUEST_ID, &id, 1);
+
+    static const int32_t frameCount = 0;
+    settings.update(ANDROID_REQUEST_FRAME_COUNT, &frameCount, 1);
+
+    /** android.lens */
+
+    static const float focalLength = 5.0f;
+    settings.update(ANDROID_LENS_FOCAL_LENGTH, &focalLength, 1);
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const float focusDistance = 0;
+        settings.update(ANDROID_LENS_FOCUS_DISTANCE, &focusDistance, 1);
+
+        static const float aperture = 2.8f;
+        settings.update(ANDROID_LENS_APERTURE, &aperture, 1);
+
+        static const float filterDensity = 0;
+        settings.update(ANDROID_LENS_FILTER_DENSITY, &filterDensity, 1);
+
+        static const uint8_t opticalStabilizationMode =
+                ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+        settings.update(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+                &opticalStabilizationMode, 1);
+
+        // FOCUS_RANGE set only in frame
+    }
+
+    /** android.sensor */
+
+    if (hasCapability(MANUAL_SENSOR)) {
+        static const int64_t exposureTime = 10 * MSEC;
+        settings.update(ANDROID_SENSOR_EXPOSURE_TIME, &exposureTime, 1);
+
+        static const int64_t frameDuration = 33333333L; // 1/30 s
+        settings.update(ANDROID_SENSOR_FRAME_DURATION, &frameDuration, 1);
+
+        static const int32_t sensitivity = 100;
+        settings.update(ANDROID_SENSOR_SENSITIVITY, &sensitivity, 1);
+    }
+
+    // TIMESTAMP set only in frame
+
+    /** android.flash */
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
+        settings.update(ANDROID_FLASH_MODE, &flashMode, 1);
+
+        static const uint8_t flashPower = 10;
+        settings.update(ANDROID_FLASH_FIRING_POWER, &flashPower, 1);
+
+        static const int64_t firingTime = 0;
+        settings.update(ANDROID_FLASH_FIRING_TIME, &firingTime, 1);
+    }
+
+    /** Processing block modes */
+    if (hasCapability(MANUAL_POST_PROCESSING)) {
+        uint8_t hotPixelMode = 0;
+        uint8_t demosaicMode = 0;
+        uint8_t noiseMode = 0;
+        uint8_t shadingMode = 0;
+        uint8_t colorMode = 0;
+        uint8_t tonemapMode = 0;
+        uint8_t edgeMode = 0;
+        switch (type) {
+            case CAMERA3_TEMPLATE_STILL_CAPTURE:
+                // fall-through
+            case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+                // fall-through
+            case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+                hotPixelMode = ANDROID_HOT_PIXEL_MODE_HIGH_QUALITY;
+                demosaicMode = ANDROID_DEMOSAIC_MODE_HIGH_QUALITY;
+                noiseMode = ANDROID_NOISE_REDUCTION_MODE_HIGH_QUALITY;
+                shadingMode = ANDROID_SHADING_MODE_HIGH_QUALITY;
+                colorMode = ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY;
+                tonemapMode = ANDROID_TONEMAP_MODE_HIGH_QUALITY;
+                edgeMode = ANDROID_EDGE_MODE_HIGH_QUALITY;
+                break;
+            case CAMERA3_TEMPLATE_PREVIEW:
+                // fall-through
+            case CAMERA3_TEMPLATE_VIDEO_RECORD:
+                // fall-through
+            default:
+                hotPixelMode = ANDROID_HOT_PIXEL_MODE_FAST;
+                demosaicMode = ANDROID_DEMOSAIC_MODE_FAST;
+                noiseMode = ANDROID_NOISE_REDUCTION_MODE_FAST;
+                shadingMode = ANDROID_SHADING_MODE_FAST;
+                colorMode = ANDROID_COLOR_CORRECTION_MODE_FAST;
+                tonemapMode = ANDROID_TONEMAP_MODE_FAST;
+                edgeMode = ANDROID_EDGE_MODE_FAST;
+                break;
+        }
+        settings.update(ANDROID_HOT_PIXEL_MODE, &hotPixelMode, 1);
+        settings.update(ANDROID_DEMOSAIC_MODE, &demosaicMode, 1);
+        settings.update(ANDROID_NOISE_REDUCTION_MODE, &noiseMode, 1);
+        settings.update(ANDROID_SHADING_MODE, &shadingMode, 1);
+        settings.update(ANDROID_COLOR_CORRECTION_MODE, &colorMode, 1);
+        settings.update(ANDROID_TONEMAP_MODE, &tonemapMode, 1);
+        settings.update(ANDROID_EDGE_MODE, &edgeMode, 1);
+    }
+
+    /** android.colorCorrection */
+
+    if (hasCapability(MANUAL_POST_PROCESSING)) {
+        static const camera_metadata_rational colorTransform[9] = {
+            {1,1}, {0,1}, {0,1},
+            {0,1}, {1,1}, {0,1},
+            {0,1}, {0,1}, {1,1}
+        };
+        settings.update(ANDROID_COLOR_CORRECTION_TRANSFORM, colorTransform, 9);
+
+        static const float colorGains[4] = {
+            1.0f, 1.0f, 1.0f, 1.0f
+        };
+        settings.update(ANDROID_COLOR_CORRECTION_GAINS, colorGains, 4);
+    }
+
+    /** android.tonemap */
+
+    if (hasCapability(MANUAL_POST_PROCESSING)) {
+        static const float tonemapCurve[4] = {
+            0.f, 0.f,
+            1.f, 1.f
+        };
+        settings.update(ANDROID_TONEMAP_CURVE_RED, tonemapCurve, 4);
+        settings.update(ANDROID_TONEMAP_CURVE_GREEN, tonemapCurve, 4);
+        settings.update(ANDROID_TONEMAP_CURVE_BLUE, tonemapCurve, 4);
+    }
+
+    /** android.scaler */
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const int32_t cropRegion[4] = {
+            0, 0, mSensorWidth, mSensorHeight
+        };
+        settings.update(ANDROID_SCALER_CROP_REGION, cropRegion, 4);
+
+    }
+
+    /** android.jpeg */
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t jpegQuality = 80;
+        settings.update(ANDROID_JPEG_QUALITY, &jpegQuality, 1);
+
+        static const int32_t thumbnailSize[2] = {
+            640, 480
+        };
+        settings.update(ANDROID_JPEG_THUMBNAIL_SIZE, thumbnailSize, 2);
+
+        static const uint8_t thumbnailQuality = 80;
+        settings.update(ANDROID_JPEG_THUMBNAIL_QUALITY, &thumbnailQuality, 1);
+
+        static const double gpsCoordinates[2] = {
+            0, 0
+        };
+        settings.update(ANDROID_JPEG_GPS_COORDINATES, gpsCoordinates, 2);
+
+        static const uint8_t gpsProcessingMethod[32] = "None";
+        settings.update(ANDROID_JPEG_GPS_PROCESSING_METHOD, gpsProcessingMethod, 32);
+
+        static const int64_t gpsTimestamp = 0;
+        settings.update(ANDROID_JPEG_GPS_TIMESTAMP, &gpsTimestamp, 1);
+
+        static const int32_t jpegOrientation = 0;
+        settings.update(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1);
+    }
+
+    /** android.stats */
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t faceDetectMode =
+                ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+        settings.update(ANDROID_STATISTICS_FACE_DETECT_MODE, &faceDetectMode, 1);
+
+        static const uint8_t hotPixelMapMode =
+                ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF;
+        settings.update(ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE, &hotPixelMapMode, 1);
+    }
+
+    // faceRectangles, faceScores, faceLandmarks, faceIds, histogram,
+    // sharpnessMap only in frames
+
+    /** android.control */
+
+    uint8_t controlIntent = 0;
+    switch (type) {
+      case CAMERA3_TEMPLATE_PREVIEW:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
+        break;
+      case CAMERA3_TEMPLATE_STILL_CAPTURE:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
+        break;
+      case CAMERA3_TEMPLATE_VIDEO_RECORD:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
+        break;
+      case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
+        break;
+      case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG;
+        break;
+      case CAMERA3_TEMPLATE_MANUAL:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_MANUAL;
+        break;
+      default:
+        controlIntent = ANDROID_CONTROL_CAPTURE_INTENT_CUSTOM;
+        break;
+    }
+    settings.update(ANDROID_CONTROL_CAPTURE_INTENT, &controlIntent, 1);
+
+    const uint8_t controlMode = (type == CAMERA3_TEMPLATE_MANUAL) ?
+            ANDROID_CONTROL_MODE_OFF :
+            ANDROID_CONTROL_MODE_AUTO;
+    settings.update(ANDROID_CONTROL_MODE, &controlMode, 1);
+
+    int32_t aeTargetFpsRange[2] = {
+        5, 30
+    };
+    if (type == CAMERA3_TEMPLATE_VIDEO_RECORD || type == CAMERA3_TEMPLATE_VIDEO_SNAPSHOT) {
+        aeTargetFpsRange[0] = 30;
+    }
+    settings.update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE, aeTargetFpsRange, 2);
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+
+        static const uint8_t effectMode = ANDROID_CONTROL_EFFECT_MODE_OFF;
+        settings.update(ANDROID_CONTROL_EFFECT_MODE, &effectMode, 1);
+
+        static const uint8_t sceneMode = ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY;
+        settings.update(ANDROID_CONTROL_SCENE_MODE, &sceneMode, 1);
+
+        const uint8_t aeMode = (type == CAMERA3_TEMPLATE_MANUAL) ?
+                ANDROID_CONTROL_AE_MODE_OFF :
+                ANDROID_CONTROL_AE_MODE_ON;
+        settings.update(ANDROID_CONTROL_AE_MODE, &aeMode, 1);
+
+        static const uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
+        settings.update(ANDROID_CONTROL_AE_LOCK, &aeLock, 1);
+
+        static const int32_t controlRegions[5] = {
+            0, 0, 0, 0, 0
+        };
+        settings.update(ANDROID_CONTROL_AE_REGIONS, controlRegions, 5);
+
+        static const int32_t aeExpCompensation = 0;
+        settings.update(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, &aeExpCompensation, 1);
+
+
+        static const uint8_t aeAntibandingMode =
+                ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+        settings.update(ANDROID_CONTROL_AE_ANTIBANDING_MODE, &aeAntibandingMode, 1);
+
+        static const uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
+        settings.update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, &aePrecaptureTrigger, 1);
+
+        const uint8_t awbMode = (type == CAMERA3_TEMPLATE_MANUAL) ?
+                ANDROID_CONTROL_AWB_MODE_OFF :
+                ANDROID_CONTROL_AWB_MODE_AUTO;
+        settings.update(ANDROID_CONTROL_AWB_MODE, &awbMode, 1);
+
+        static const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+        settings.update(ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
+
+        uint8_t afMode = 0;
+
+        if (mFacingBack) {
+            switch (type) {
+                case CAMERA3_TEMPLATE_PREVIEW:
+                    afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE;
+                    break;
+                case CAMERA3_TEMPLATE_STILL_CAPTURE:
+                    afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE;
+                    break;
+                case CAMERA3_TEMPLATE_VIDEO_RECORD:
+                    afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO;
+                    break;
+                case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+                    afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO;
+                    break;
+                case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+                    afMode = ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE;
+                    break;
+                case CAMERA3_TEMPLATE_MANUAL:
+                    afMode = ANDROID_CONTROL_AF_MODE_OFF;
+                    break;
+                default:
+                    afMode = ANDROID_CONTROL_AF_MODE_AUTO;
+                    break;
+            }
+        } else {
+            afMode = ANDROID_CONTROL_AF_MODE_OFF;
+        }
+        settings.update(ANDROID_CONTROL_AF_MODE, &afMode, 1);
+
+        settings.update(ANDROID_CONTROL_AF_REGIONS, controlRegions, 5);
+
+        static const uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
+        settings.update(ANDROID_CONTROL_AF_TRIGGER, &afTrigger, 1);
+
+        static const uint8_t vstabMode =
+                ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+        settings.update(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, &vstabMode, 1);
+
+        static const uint8_t blackLevelLock = ANDROID_BLACK_LEVEL_LOCK_OFF;
+        settings.update(ANDROID_BLACK_LEVEL_LOCK, &blackLevelLock, 1);
+
+        static const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+        settings.update(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
+
+        static const uint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_FAST;
+        settings.update(ANDROID_COLOR_CORRECTION_ABERRATION_MODE, &aberrationMode, 1);
+
+        static const int32_t testPatternMode = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
+        settings.update(ANDROID_SENSOR_TEST_PATTERN_MODE, &testPatternMode, 1);
+    }
+
+    mDefaultTemplates[type] = settings.release();
+
+    return mDefaultTemplates[type];
+}
+
+status_t EmulatedFakeCamera3::processCaptureRequest(
+        camera3_capture_request *request) {
+
+    Mutex::Autolock l(mLock);
+    status_t res;
+
+    /** Validation */
+
+    if (mStatus < STATUS_READY) {
+        ALOGE("%s: Can't submit capture requests in state %d", __FUNCTION__,
+                mStatus);
+        return INVALID_OPERATION;
+    }
+
+    if (request == NULL) {
+        ALOGE("%s: NULL request!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    uint32_t frameNumber = request->frame_number;
+
+    if (request->settings == NULL && mPrevSettings.isEmpty()) {
+        ALOGE("%s: Request %d: NULL settings for first request after"
+                "configureStreams()", __FUNCTION__, frameNumber);
+        return BAD_VALUE;
+    }
+
+    if (request->input_buffer != NULL &&
+            request->input_buffer->stream != mInputStream) {
+        ALOGE("%s: Request %d: Input buffer not from input stream!",
+                __FUNCTION__, frameNumber);
+        ALOGV("%s: Bad stream %p, expected: %p",
+              __FUNCTION__, request->input_buffer->stream,
+              mInputStream);
+        ALOGV("%s: Bad stream type %d, expected stream type %d",
+              __FUNCTION__, request->input_buffer->stream->stream_type,
+              mInputStream ? mInputStream->stream_type : -1);
+
+        return BAD_VALUE;
+    }
+
+    if (request->num_output_buffers < 1 || request->output_buffers == NULL) {
+        ALOGE("%s: Request %d: No output buffers provided!",
+                __FUNCTION__, frameNumber);
+        return BAD_VALUE;
+    }
+
+    // Validate all buffers, starting with input buffer if it's given
+
+    ssize_t idx;
+    const camera3_stream_buffer_t *b;
+    if (request->input_buffer != NULL) {
+        idx = -1;
+        b = request->input_buffer;
+    } else {
+        idx = 0;
+        b = request->output_buffers;
+    }
+    do {
+        PrivateStreamInfo *priv =
+                static_cast<PrivateStreamInfo*>(b->stream->priv);
+        if (priv == NULL) {
+            ALOGE("%s: Request %d: Buffer %zu: Unconfigured stream!",
+                    __FUNCTION__, frameNumber, idx);
+            return BAD_VALUE;
+        }
+        if (!priv->alive) {
+            ALOGE("%s: Request %d: Buffer %zu: Dead stream!",
+                    __FUNCTION__, frameNumber, idx);
+            return BAD_VALUE;
+        }
+        if (b->status != CAMERA3_BUFFER_STATUS_OK) {
+            ALOGE("%s: Request %d: Buffer %zu: Status not OK!",
+                    __FUNCTION__, frameNumber, idx);
+            return BAD_VALUE;
+        }
+        if (b->release_fence != -1) {
+            ALOGE("%s: Request %d: Buffer %zu: Has a release fence!",
+                    __FUNCTION__, frameNumber, idx);
+            return BAD_VALUE;
+        }
+        if (b->buffer == NULL) {
+            ALOGE("%s: Request %d: Buffer %zu: NULL buffer handle!",
+                    __FUNCTION__, frameNumber, idx);
+            return BAD_VALUE;
+        }
+        idx++;
+        b = &(request->output_buffers[idx]);
+    } while (idx < (ssize_t)request->num_output_buffers);
+
+    // TODO: Validate settings parameters
+
+    /**
+     * Start processing this request
+     */
+
+    mStatus = STATUS_ACTIVE;
+
+    CameraMetadata settings;
+
+    if (request->settings == NULL) {
+        settings.acquire(mPrevSettings);
+    } else {
+        settings = request->settings;
+    }
+
+    res = process3A(settings);
+    if (res != OK) {
+        return res;
+    }
+
+    // TODO: Handle reprocessing
+
+    /**
+     * Get ready for sensor config
+     */
+
+    nsecs_t  exposureTime;
+    nsecs_t  frameDuration;
+    uint32_t sensitivity;
+    bool     needJpeg = false;
+    camera_metadata_entry_t entry;
+
+    entry = settings.find(ANDROID_SENSOR_EXPOSURE_TIME);
+    exposureTime = (entry.count > 0) ? entry.data.i64[0] : Sensor::kExposureTimeRange[0];
+    entry = settings.find(ANDROID_SENSOR_FRAME_DURATION);
+    frameDuration = (entry.count > 0)? entry.data.i64[0] : Sensor::kFrameDurationRange[0];
+    entry = settings.find(ANDROID_SENSOR_SENSITIVITY);
+    sensitivity = (entry.count > 0) ? entry.data.i32[0] : Sensor::kSensitivityRange[0];
+
+    if (exposureTime > frameDuration) {
+        frameDuration = exposureTime + Sensor::kMinVerticalBlank;
+        settings.update(ANDROID_SENSOR_FRAME_DURATION, &frameDuration, 1);
+    }
+
+    Buffers *sensorBuffers = new Buffers();
+    HalBufferVector *buffers = new HalBufferVector();
+
+    sensorBuffers->setCapacity(request->num_output_buffers);
+    buffers->setCapacity(request->num_output_buffers);
+
+    // Process all the buffers we got for output, constructing internal buffer
+    // structures for them, and lock them for writing.
+    for (size_t i = 0; i < request->num_output_buffers; i++) {
+        const camera3_stream_buffer &srcBuf = request->output_buffers[i];
+        StreamBuffer destBuf;
+        destBuf.streamId = kGenericStreamId;
+        destBuf.width    = srcBuf.stream->width;
+        destBuf.height   = srcBuf.stream->height;
+        // For GCE, IMPLEMENTATION_DEFINED is always RGBx_8888
+        destBuf.format = (srcBuf.stream->format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) ?
+                HAL_PIXEL_FORMAT_RGBA_8888 :
+                srcBuf.stream->format;
+        destBuf.stride   = srcBuf.stream->width;
+        destBuf.dataSpace = srcBuf.stream->data_space;
+        destBuf.buffer   = srcBuf.buffer;
+
+        if (destBuf.format == HAL_PIXEL_FORMAT_BLOB) {
+            needJpeg = true;
+        }
+
+        // Wait on fence
+        sp<Fence> bufferAcquireFence = new Fence(srcBuf.acquire_fence);
+        res = bufferAcquireFence->wait(kFenceTimeoutMs);
+        if (res == TIMED_OUT) {
+            ALOGE("%s: Request %d: Buffer %zu: Fence timed out after %d ms",
+                    __FUNCTION__, frameNumber, i, kFenceTimeoutMs);
+        }
+        if (res == OK) {
+            // Lock buffer for writing
+            if (srcBuf.stream->format == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+                if (destBuf.format == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+                    android_ycbcr ycbcr = android_ycbcr();
+                    res = GrallocModule::getInstance().lock_ycbcr(
+                        *(destBuf.buffer),
+                        GRALLOC_USAGE_HW_CAMERA_WRITE,
+                        0, 0, destBuf.width, destBuf.height,
+                        &ycbcr);
+                    // This is only valid because we know that emulator's
+                    // YCbCr_420_888 is really contiguous NV21 under the hood
+                    destBuf.img = static_cast<uint8_t*>(ycbcr.y);
+                } else {
+                    ALOGE("Unexpected private format for flexible YUV: 0x%x",
+                            destBuf.format);
+                    res = INVALID_OPERATION;
+                }
+            } else {
+                res = GrallocModule::getInstance().lock(
+                    *(destBuf.buffer),
+                    GRALLOC_USAGE_HW_CAMERA_WRITE,
+                    0, 0, destBuf.width, destBuf.height,
+                    (void**)&(destBuf.img));
+
+            }
+            if (res != OK) {
+                ALOGE("%s: Request %d: Buffer %zu: Unable to lock buffer",
+                        __FUNCTION__, frameNumber, i);
+            }
+        }
+
+        if (res != OK) {
+            // Either waiting or locking failed. Unlock locked buffers and bail
+            // out.
+            for (size_t j = 0; j < i; j++) {
+                GrallocModule::getInstance().unlock(
+                        *(request->output_buffers[i].buffer));
+            }
+            delete sensorBuffers;
+            delete buffers;
+            return NO_INIT;
+        }
+
+        sensorBuffers->push_back(destBuf);
+        buffers->push_back(srcBuf);
+    }
+
+    /**
+     * Wait for JPEG compressor to not be busy, if needed
+     */
+    if (needJpeg) {
+        bool ready = mJpegCompressor->waitForDone(kJpegTimeoutNs);
+        if (!ready) {
+            ALOGE("%s: Timeout waiting for JPEG compression to complete!",
+                    __FUNCTION__);
+            return NO_INIT;
+        }
+        res = mJpegCompressor->reserve();
+        if (res != OK) {
+            ALOGE("%s: Error managing JPEG compressor resources, can't reserve it!", __FUNCTION__);
+            return NO_INIT;
+        }
+    }
+
+    /**
+     * Wait until the in-flight queue has room
+     */
+    res = mReadoutThread->waitForReadout();
+    if (res != OK) {
+        ALOGE("%s: Timeout waiting for previous requests to complete!",
+                __FUNCTION__);
+        return NO_INIT;
+    }
+
+    /**
+     * Wait until sensor's ready. This waits for lengthy amounts of time with
+     * mLock held, but the interface spec is that no other calls may by done to
+     * the HAL by the framework while process_capture_request is happening.
+     */
+    int syncTimeoutCount = 0;
+    while(!mSensor->waitForVSync(kSyncWaitTimeout)) {
+        if (mStatus == STATUS_ERROR) {
+            return NO_INIT;
+        }
+        if (syncTimeoutCount == kMaxSyncTimeoutCount) {
+            ALOGE("%s: Request %d: Sensor sync timed out after %" PRId64 " ms",
+                    __FUNCTION__, frameNumber,
+                    kSyncWaitTimeout * kMaxSyncTimeoutCount / 1000000);
+            return NO_INIT;
+        }
+        syncTimeoutCount++;
+    }
+
+    /**
+     * Configure sensor and queue up the request to the readout thread
+     */
+    mSensor->setExposureTime(exposureTime);
+    mSensor->setFrameDuration(frameDuration);
+    mSensor->setSensitivity(sensitivity);
+    mSensor->setDestinationBuffers(sensorBuffers);
+    mSensor->setFrameNumber(request->frame_number);
+
+    ReadoutThread::Request r;
+    r.frameNumber = request->frame_number;
+    r.settings = settings;
+    r.sensorBuffers = sensorBuffers;
+    r.buffers = buffers;
+
+    mReadoutThread->queueCaptureRequest(r);
+    ALOGVV("%s: Queued frame %d", __FUNCTION__, request->frame_number);
+
+    // Cache the settings for next time
+    mPrevSettings.acquire(settings);
+
+    return OK;
+}
+
+status_t EmulatedFakeCamera3::flush() {
+    ALOGW("%s: Not implemented; ignored", __FUNCTION__);
+    return OK;
+}
+
+/** Debug methods */
+
+void EmulatedFakeCamera3::dump(int fd) {
+
+}
+
+/**
+ * Private methods
+ */
+
+status_t EmulatedFakeCamera3::getCameraCapabilities() {
+
+    const char *key = mFacingBack ? "qemu.sf.back_camera_caps" : "qemu.sf.front_camera_caps";
+
+    /* Defined by 'qemu.sf.*_camera_caps' boot property: if the
+     * property doesn't exist, it is assumed to list FULL. */
+    char prop[PROPERTY_VALUE_MAX];
+    if (property_get(key, prop, NULL) > 0) {
+        char *saveptr = nullptr;
+        char *cap = strtok_r(prop, " ,", &saveptr);
+        while (cap != NULL) {
+            for (int i = 0; i < NUM_CAPABILITIES; i++) {
+                if (!strcasecmp(cap, sAvailableCapabilitiesStrings[i])) {
+                    mCapabilities.add(static_cast<AvailableCapabilities>(i));
+                    break;
+                }
+            }
+            cap = strtok_r(NULL, " ,", &saveptr);
+        }
+        if (mCapabilities.size() == 0) {
+            ALOGE("qemu.sf.back_camera_caps had no valid capabilities: %s", prop);
+        }
+    }
+    // Default to FULL_LEVEL plus RAW if nothing is defined
+    if (mCapabilities.size() == 0) {
+        mCapabilities.add(FULL_LEVEL);
+        mCapabilities.add(RAW);
+    }
+
+    // Add level-based caps
+    if (hasCapability(FULL_LEVEL)) {
+        mCapabilities.add(BURST_CAPTURE);
+        mCapabilities.add(READ_SENSOR_SETTINGS);
+        mCapabilities.add(MANUAL_SENSOR);
+        mCapabilities.add(MANUAL_POST_PROCESSING);
+    };
+
+    // Backwards-compatible is required for most other caps
+    // Not required for DEPTH_OUTPUT, though.
+    if (hasCapability(BURST_CAPTURE) ||
+            hasCapability(READ_SENSOR_SETTINGS) ||
+            hasCapability(RAW) ||
+            hasCapability(MANUAL_SENSOR) ||
+            hasCapability(MANUAL_POST_PROCESSING) ||
+            hasCapability(PRIVATE_REPROCESSING) ||
+            hasCapability(YUV_REPROCESSING) ||
+            hasCapability(CONSTRAINED_HIGH_SPEED_VIDEO)) {
+        mCapabilities.add(BACKWARD_COMPATIBLE);
+    }
+
+    ALOGI("Camera %d capabilities:", mCameraID);
+    for (size_t i = 0; i < mCapabilities.size(); i++) {
+        ALOGI("  %s", sAvailableCapabilitiesStrings[mCapabilities[i]]);
+    }
+
+    return OK;
+}
+
+bool EmulatedFakeCamera3::hasCapability(AvailableCapabilities cap) {
+    ssize_t idx = mCapabilities.indexOf(cap);
+    return idx >= 0;
+}
+
+status_t EmulatedFakeCamera3::constructStaticInfo(
+    const cvd::CameraDefinition& params) {
+
+    CameraMetadata info;
+    Vector<int32_t> availableCharacteristicsKeys;
+    status_t res;
+
+    char* param_end;
+    int32_t width = 0, height = 0;
+
+    /* TODO(ender): this currently supports only maximum resolution. */
+    for (size_t index = 0; index < params.resolutions.size(); ++index) {
+        if (width <= params.resolutions[index].width &&
+            height <= params.resolutions[index].height) {
+            width = params.resolutions[index].width;
+            height = params.resolutions[index].height;
+        }
+    }
+
+    if (width < 640 || height < 480) {
+        width = 640;
+        height = 480;
+    }
+
+    mSensorWidth = width;
+    mSensorHeight = height;
+
+#define ADD_STATIC_ENTRY(name, varptr, count) \
+        availableCharacteristicsKeys.add(name);   \
+        res = info.update(name, varptr, count); \
+        if (res != OK) return res
+
+    // android.sensor
+
+    if (hasCapability(MANUAL_SENSOR)) {
+
+        ADD_STATIC_ENTRY(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
+                Sensor::kExposureTimeRange, 2);
+
+        ADD_STATIC_ENTRY(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+                &Sensor::kFrameDurationRange[1], 1);
+
+        ADD_STATIC_ENTRY(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
+                Sensor::kSensitivityRange,
+                sizeof(Sensor::kSensitivityRange)
+                /sizeof(int32_t));
+
+        ADD_STATIC_ENTRY(ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY,
+                &Sensor::kSensitivityRange[1], 1);
+    }
+
+    static const float sensorPhysicalSize[2] = {3.20f, 2.40f}; // mm
+    ADD_STATIC_ENTRY(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
+            sensorPhysicalSize, 2);
+
+    const int32_t pixelArray[] = {mSensorWidth, mSensorHeight};
+    ADD_STATIC_ENTRY(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+            pixelArray, 2);
+    const int32_t activeArray[] = {0, 0, mSensorWidth, mSensorHeight};
+    ADD_STATIC_ENTRY(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+            activeArray, 4);
+
+    static const int32_t orientation = 90; // Aligned with 'long edge'
+    ADD_STATIC_ENTRY(ANDROID_SENSOR_ORIENTATION, &orientation, 1);
+
+    static const uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME;
+    ADD_STATIC_ENTRY(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, &timestampSource, 1);
+
+    if (hasCapability(RAW)) {
+        ADD_STATIC_ENTRY(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
+                &Sensor::kColorFilterArrangement, 1);
+
+        ADD_STATIC_ENTRY(ANDROID_SENSOR_INFO_WHITE_LEVEL,
+                (int32_t*)&Sensor::kMaxRawValue, 1);
+
+        static const int32_t blackLevelPattern[4] = {
+            (int32_t)Sensor::kBlackLevel, (int32_t)Sensor::kBlackLevel,
+            (int32_t)Sensor::kBlackLevel, (int32_t)Sensor::kBlackLevel
+        };
+        ADD_STATIC_ENTRY(ANDROID_SENSOR_BLACK_LEVEL_PATTERN,
+                blackLevelPattern, sizeof(blackLevelPattern)/sizeof(int32_t));
+    }
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const int32_t availableTestPatternModes[] = {
+            ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
+        };
+        ADD_STATIC_ENTRY(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
+                availableTestPatternModes, sizeof(availableTestPatternModes)/sizeof(int32_t));
+    }
+
+    // android.lens
+
+    static const float focalLength = 3.30f; // mm
+    ADD_STATIC_ENTRY(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
+            &focalLength, 1);
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        // 5 cm min focus distance for back camera, infinity (fixed focus) for front
+        const float minFocusDistance = mFacingBack ? 1.0/0.05 : 0.0;
+        ADD_STATIC_ENTRY(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+                &minFocusDistance, 1);
+
+        // 5 m hyperfocal distance for back camera, infinity (fixed focus) for front
+        const float hyperFocalDistance = mFacingBack ? 1.0/5.0 : 0.0;
+        ADD_STATIC_ENTRY(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
+                &hyperFocalDistance, 1);
+
+        static const float aperture = 2.8f;
+        ADD_STATIC_ENTRY(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
+                &aperture, 1);
+        static const float filterDensity = 0;
+        ADD_STATIC_ENTRY(ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES,
+                &filterDensity, 1);
+        static const uint8_t availableOpticalStabilization =
+                ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+        ADD_STATIC_ENTRY(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+                &availableOpticalStabilization, 1);
+
+        static const int32_t lensShadingMapSize[] = {1, 1};
+        ADD_STATIC_ENTRY(ANDROID_LENS_INFO_SHADING_MAP_SIZE, lensShadingMapSize,
+                sizeof(lensShadingMapSize)/sizeof(int32_t));
+
+        static const uint8_t lensFocusCalibration =
+                ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE;
+        ADD_STATIC_ENTRY(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION, &lensFocusCalibration, 1);
+    }
+
+    if (hasCapability(DEPTH_OUTPUT)) {
+        // These could be included for non-DEPTH capability as well, but making this variable for
+        // testing coverage
+
+        // 90 degree rotation to align with long edge of a phone device that's by default portrait
+        static const float qO[] = { 0.707107f, 0.f, 0.f, 0.707107f};
+
+        // Either a 180-degree rotation for back-facing, or no rotation for front-facing
+        const float qF[] = {0, (mFacingBack ? 1.f : 0.f), 0, (mFacingBack ? 0.f : 1.f)};
+
+        // Quarternion product, orientation change then facing
+        const float lensPoseRotation[] = {qO[0]*qF[0] - qO[1]*qF[1] - qO[2]*qF[2] - qO[3]*qF[3],
+                                          qO[0]*qF[1] + qO[1]*qF[0] + qO[2]*qF[3] - qO[3]*qF[2],
+                                          qO[0]*qF[2] + qO[2]*qF[0] + qO[1]*qF[3] - qO[3]*qF[1],
+                                          qO[0]*qF[3] + qO[3]*qF[0] + qO[1]*qF[2] - qO[2]*qF[1]};
+
+        ADD_STATIC_ENTRY(ANDROID_LENS_POSE_ROTATION, lensPoseRotation,
+                sizeof(lensPoseRotation)/sizeof(float));
+
+        // Only one camera facing each way, so 0 translation needed to the center of the 'main'
+        // camera
+        static const float lensPoseTranslation[] = {0.f, 0.f, 0.f};
+
+        ADD_STATIC_ENTRY(ANDROID_LENS_POSE_TRANSLATION, lensPoseTranslation,
+                sizeof(lensPoseTranslation)/sizeof(float));
+
+        // Intrinsics are 'ideal' (f_x, f_y, c_x, c_y, s) match focal length and active array size
+        float f_x = focalLength * mSensorWidth / sensorPhysicalSize[0];
+        float f_y = focalLength * mSensorHeight / sensorPhysicalSize[1];
+        float c_x = mSensorWidth / 2.f;
+        float c_y = mSensorHeight / 2.f;
+        float s = 0.f;
+        const float lensIntrinsics[] = { f_x, f_y, c_x, c_y, s };
+
+        ADD_STATIC_ENTRY(ANDROID_LENS_INTRINSIC_CALIBRATION, lensIntrinsics,
+                sizeof(lensIntrinsics)/sizeof(float));
+
+        // No radial or tangential distortion
+
+        float lensRadialDistortion[] = {1.0f, 0.f, 0.f, 0.f, 0.f, 0.f};
+
+        ADD_STATIC_ENTRY(ANDROID_LENS_RADIAL_DISTORTION, lensRadialDistortion,
+                sizeof(lensRadialDistortion)/sizeof(float));
+
+    }
+
+
+    const uint8_t lensFacing = mFacingBack ?
+            ANDROID_LENS_FACING_BACK : ANDROID_LENS_FACING_FRONT;
+    ADD_STATIC_ENTRY(ANDROID_LENS_FACING, &lensFacing, 1);
+
+    // android.flash
+
+    const uint8_t flashAvailable = mFacingBack;
+    ADD_STATIC_ENTRY(ANDROID_FLASH_INFO_AVAILABLE, &flashAvailable, 1);
+
+    // android.tonemap
+
+    if (hasCapability(MANUAL_POST_PROCESSING)) {
+        static const int32_t tonemapCurvePoints = 128;
+        ADD_STATIC_ENTRY(ANDROID_TONEMAP_MAX_CURVE_POINTS, &tonemapCurvePoints, 1);
+
+        static const uint8_t availableToneMapModes[] = {
+            ANDROID_TONEMAP_MODE_CONTRAST_CURVE,  ANDROID_TONEMAP_MODE_FAST,
+            ANDROID_TONEMAP_MODE_HIGH_QUALITY
+        };
+        ADD_STATIC_ENTRY(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES, availableToneMapModes,
+                sizeof(availableToneMapModes));
+    }
+
+    // android.scaler
+
+    const std::vector<int32_t> availableStreamConfigurationsBasic = {
+        HAL_PIXEL_FORMAT_BLOB, width, height, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 320, 240, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+        HAL_PIXEL_FORMAT_YCbCr_420_888, 320, 240, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+        HAL_PIXEL_FORMAT_BLOB, 320, 240, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+    };
+
+    // Always need to include 640x480 in basic formats
+    const std::vector<int32_t> availableStreamConfigurationsBasic640 = {
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 640, 480, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+        HAL_PIXEL_FORMAT_YCbCr_420_888, 640, 480, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+        HAL_PIXEL_FORMAT_BLOB, 640, 480, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT
+    };
+
+    const std::vector<int32_t> availableStreamConfigurationsRaw = {
+        HAL_PIXEL_FORMAT_RAW16, width, height, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+    };
+
+    const std::vector<int32_t> availableStreamConfigurationsBurst = {
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, width, height, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+        HAL_PIXEL_FORMAT_YCbCr_420_888, width, height, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+        HAL_PIXEL_FORMAT_RGBA_8888, width, height, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+    };
+
+    std::vector<int32_t> availableStreamConfigurations;
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        availableStreamConfigurations.insert(availableStreamConfigurations.end(),
+                availableStreamConfigurationsBasic.begin(),
+                availableStreamConfigurationsBasic.end());
+        if (width > 640) {
+            availableStreamConfigurations.insert(availableStreamConfigurations.end(),
+                    availableStreamConfigurationsBasic640.begin(),
+                    availableStreamConfigurationsBasic640.end());
+        }
+    }
+    if (hasCapability(RAW)) {
+        availableStreamConfigurations.insert(availableStreamConfigurations.end(),
+                availableStreamConfigurationsRaw.begin(),
+                availableStreamConfigurationsRaw.end());
+    }
+    if (hasCapability(BURST_CAPTURE)) {
+        availableStreamConfigurations.insert(availableStreamConfigurations.end(),
+                availableStreamConfigurationsBurst.begin(),
+                availableStreamConfigurationsBurst.end());
+    }
+
+    if (availableStreamConfigurations.size() > 0) {
+        ADD_STATIC_ENTRY(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+                &availableStreamConfigurations[0],
+                availableStreamConfigurations.size());
+    }
+
+    const std::vector<int64_t> availableMinFrameDurationsBasic = {
+        HAL_PIXEL_FORMAT_BLOB, width, height, Sensor::kFrameDurationRange[0],
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 320, 240, Sensor::kFrameDurationRange[0],
+        HAL_PIXEL_FORMAT_YCbCr_420_888, 320, 240, Sensor::kFrameDurationRange[0],
+        HAL_PIXEL_FORMAT_BLOB, 320, 240, Sensor::kFrameDurationRange[0],
+    };
+
+    // Always need to include 640x480 in basic formats
+    const std::vector<int64_t> availableMinFrameDurationsBasic640 = {
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 640, 480, Sensor::kFrameDurationRange[0],
+        HAL_PIXEL_FORMAT_YCbCr_420_888, 640, 480, Sensor::kFrameDurationRange[0],
+        HAL_PIXEL_FORMAT_BLOB, 640, 480, Sensor::kFrameDurationRange[0]
+    };
+
+    const std::vector<int64_t> availableMinFrameDurationsRaw = {
+        HAL_PIXEL_FORMAT_RAW16, width, height, Sensor::kFrameDurationRange[0],
+    };
+
+    const std::vector<int64_t> availableMinFrameDurationsBurst = {
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, width, height, Sensor::kFrameDurationRange[0],
+        HAL_PIXEL_FORMAT_YCbCr_420_888, width, height, Sensor::kFrameDurationRange[0],
+        HAL_PIXEL_FORMAT_RGBA_8888, width, height, Sensor::kFrameDurationRange[0],
+    };
+
+    std::vector<int64_t> availableMinFrameDurations;
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        availableMinFrameDurations.insert(availableMinFrameDurations.end(),
+                availableMinFrameDurationsBasic.begin(),
+                availableMinFrameDurationsBasic.end());
+        if (width > 640) {
+            availableMinFrameDurations.insert(availableMinFrameDurations.end(),
+                    availableMinFrameDurationsBasic640.begin(),
+                    availableMinFrameDurationsBasic640.end());
+        }
+    }
+    if (hasCapability(RAW)) {
+        availableMinFrameDurations.insert(availableMinFrameDurations.end(),
+                availableMinFrameDurationsRaw.begin(),
+                availableMinFrameDurationsRaw.end());
+    }
+    if (hasCapability(BURST_CAPTURE)) {
+        availableMinFrameDurations.insert(availableMinFrameDurations.end(),
+                availableMinFrameDurationsBurst.begin(),
+                availableMinFrameDurationsBurst.end());
+    }
+
+    if (availableMinFrameDurations.size() > 0) {
+        ADD_STATIC_ENTRY(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+                &availableMinFrameDurations[0],
+                availableMinFrameDurations.size());
+    }
+
+    const std::vector<int64_t> availableStallDurationsBasic = {
+        HAL_PIXEL_FORMAT_BLOB, width, height, Sensor::kFrameDurationRange[0],
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 320, 240, 0,
+        HAL_PIXEL_FORMAT_YCbCr_420_888, 320, 240, 0,
+        HAL_PIXEL_FORMAT_RGBA_8888, 320, 240, 0,
+    };
+
+    // Always need to include 640x480 in basic formats
+    const std::vector<int64_t> availableStallDurationsBasic640 = {
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 640, 480, 0,
+        HAL_PIXEL_FORMAT_YCbCr_420_888, 640, 480, 0,
+        HAL_PIXEL_FORMAT_BLOB, 640, 480, Sensor::kFrameDurationRange[0]
+    };
+
+    const std::vector<int64_t> availableStallDurationsRaw = {
+        HAL_PIXEL_FORMAT_RAW16, width, height, Sensor::kFrameDurationRange[0]
+    };
+    const std::vector<int64_t> availableStallDurationsBurst = {
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, width, height, 0,
+        HAL_PIXEL_FORMAT_YCbCr_420_888, width, height, 0,
+        HAL_PIXEL_FORMAT_RGBA_8888, width, height, 0
+    };
+
+    std::vector<int64_t> availableStallDurations;
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        availableStallDurations.insert(availableStallDurations.end(),
+                availableStallDurationsBasic.begin(),
+                availableStallDurationsBasic.end());
+        if (width > 640) {
+            availableStallDurations.insert(availableStallDurations.end(),
+                    availableStallDurationsBasic640.begin(),
+                    availableStallDurationsBasic640.end());
+        }
+    }
+    if (hasCapability(RAW)) {
+        availableStallDurations.insert(availableStallDurations.end(),
+                availableStallDurationsRaw.begin(),
+                availableStallDurationsRaw.end());
+    }
+    if (hasCapability(BURST_CAPTURE)) {
+        availableStallDurations.insert(availableStallDurations.end(),
+                availableStallDurationsBurst.begin(),
+                availableStallDurationsBurst.end());
+    }
+
+    if (availableStallDurations.size() > 0) {
+        ADD_STATIC_ENTRY(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
+                &availableStallDurations[0],
+                availableStallDurations.size());
+    }
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_FREEFORM;
+        ADD_STATIC_ENTRY(ANDROID_SCALER_CROPPING_TYPE,
+                &croppingType, 1);
+
+        static const float maxZoom = 10;
+        ADD_STATIC_ENTRY(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+                &maxZoom, 1);
+    }
+
+    // android.jpeg
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const int32_t jpegThumbnailSizes[] = {
+            0, 0,
+            160, 120,
+            320, 240
+        };
+        ADD_STATIC_ENTRY(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+                jpegThumbnailSizes, sizeof(jpegThumbnailSizes)/sizeof(int32_t));
+
+        static const int32_t jpegMaxSize = JpegCompressor::kMaxJpegSize;
+        ADD_STATIC_ENTRY(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1);
+    }
+
+    // android.stats
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t availableFaceDetectModes[] = {
+            ANDROID_STATISTICS_FACE_DETECT_MODE_OFF,
+            ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE,
+            ANDROID_STATISTICS_FACE_DETECT_MODE_FULL
+        };
+        ADD_STATIC_ENTRY(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+                availableFaceDetectModes,
+                sizeof(availableFaceDetectModes));
+
+        static const int32_t maxFaceCount = 8;
+        ADD_STATIC_ENTRY(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+                &maxFaceCount, 1);
+
+
+        static const uint8_t availableShadingMapModes[] = {
+            ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF
+        };
+        ADD_STATIC_ENTRY(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+                availableShadingMapModes, sizeof(availableShadingMapModes));
+    }
+
+    // android.sync
+
+    static const int32_t maxLatency =
+            hasCapability(FULL_LEVEL) ? ANDROID_SYNC_MAX_LATENCY_PER_FRAME_CONTROL : 3;
+    ADD_STATIC_ENTRY(ANDROID_SYNC_MAX_LATENCY, &maxLatency, 1);
+
+    // android.control
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t availableControlModes[] = {
+            ANDROID_CONTROL_MODE_OFF, ANDROID_CONTROL_MODE_AUTO, ANDROID_CONTROL_MODE_USE_SCENE_MODE
+        };
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AVAILABLE_MODES,
+                availableControlModes, sizeof(availableControlModes));
+    } else {
+        static const uint8_t availableControlModes[] = {
+            ANDROID_CONTROL_MODE_AUTO
+        };
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AVAILABLE_MODES,
+                availableControlModes, sizeof(availableControlModes));
+    }
+
+    static const uint8_t availableSceneModes[] = {
+        hasCapability(BACKWARD_COMPATIBLE) ?
+            ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY :
+            ANDROID_CONTROL_SCENE_MODE_DISABLED
+    };
+    ADD_STATIC_ENTRY(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+            availableSceneModes, sizeof(availableSceneModes));
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t availableEffects[] = {
+            ANDROID_CONTROL_EFFECT_MODE_OFF
+        };
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AVAILABLE_EFFECTS,
+                availableEffects, sizeof(availableEffects));
+    }
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const int32_t max3aRegions[] = {/*AE*/ 1,/*AWB*/ 0,/*AF*/ 1};
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_MAX_REGIONS,
+                max3aRegions, sizeof(max3aRegions)/sizeof(max3aRegions[0]));
+
+        static const uint8_t availableAeModes[] = {
+            ANDROID_CONTROL_AE_MODE_OFF,
+            ANDROID_CONTROL_AE_MODE_ON
+        };
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AE_AVAILABLE_MODES,
+                availableAeModes, sizeof(availableAeModes));
+
+        static const camera_metadata_rational exposureCompensationStep = {
+            1, 3
+        };
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AE_COMPENSATION_STEP,
+                &exposureCompensationStep, 1);
+
+        int32_t exposureCompensationRange[] = {-9, 9};
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+                exposureCompensationRange,
+                sizeof(exposureCompensationRange)/sizeof(int32_t));
+    }
+
+    static const int32_t availableTargetFpsRanges[] = {
+            5, 30, 15, 30, 15, 15, 30, 30
+    };
+    ADD_STATIC_ENTRY(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+            availableTargetFpsRanges,
+            sizeof(availableTargetFpsRanges)/sizeof(int32_t));
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t availableAntibandingModes[] = {
+            ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
+            ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO
+        };
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+                availableAntibandingModes, sizeof(availableAntibandingModes));
+    }
+
+    static const uint8_t aeLockAvailable = hasCapability(BACKWARD_COMPATIBLE) ?
+            ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE : ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
+
+    ADD_STATIC_ENTRY(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+            &aeLockAvailable, 1);
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t availableAwbModes[] = {
+            ANDROID_CONTROL_AWB_MODE_OFF,
+            ANDROID_CONTROL_AWB_MODE_AUTO,
+            ANDROID_CONTROL_AWB_MODE_INCANDESCENT,
+            ANDROID_CONTROL_AWB_MODE_FLUORESCENT,
+            ANDROID_CONTROL_AWB_MODE_DAYLIGHT,
+            ANDROID_CONTROL_AWB_MODE_SHADE
+        };
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+                availableAwbModes, sizeof(availableAwbModes));
+    }
+
+    static const uint8_t awbLockAvailable = hasCapability(BACKWARD_COMPATIBLE) ?
+            ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE : ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
+
+    ADD_STATIC_ENTRY(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+            &awbLockAvailable, 1);
+
+    static const uint8_t availableAfModesBack[] = {
+            ANDROID_CONTROL_AF_MODE_OFF,
+            ANDROID_CONTROL_AF_MODE_AUTO,
+            ANDROID_CONTROL_AF_MODE_MACRO,
+            ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO,
+            ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE
+    };
+
+    static const uint8_t availableAfModesFront[] = {
+            ANDROID_CONTROL_AF_MODE_OFF
+    };
+
+    if (mFacingBack && hasCapability(BACKWARD_COMPATIBLE)) {
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AF_AVAILABLE_MODES,
+                availableAfModesBack, sizeof(availableAfModesBack));
+    } else {
+        ADD_STATIC_ENTRY(ANDROID_CONTROL_AF_AVAILABLE_MODES,
+                availableAfModesFront, sizeof(availableAfModesFront));
+    }
+
+    static const uint8_t availableVstabModes[] = {
+        ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF
+    };
+    ADD_STATIC_ENTRY(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+            availableVstabModes, sizeof(availableVstabModes));
+
+    // android.colorCorrection
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t availableAberrationModes[] = {
+            ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF,
+            ANDROID_COLOR_CORRECTION_ABERRATION_MODE_FAST,
+            ANDROID_COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY
+        };
+        ADD_STATIC_ENTRY(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+                availableAberrationModes, sizeof(availableAberrationModes));
+    } else {
+        static const uint8_t availableAberrationModes[] = {
+            ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF,
+        };
+        ADD_STATIC_ENTRY(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+                availableAberrationModes, sizeof(availableAberrationModes));
+    }
+    // android.edge
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t availableEdgeModes[] = {
+            ANDROID_EDGE_MODE_OFF, ANDROID_EDGE_MODE_FAST, ANDROID_EDGE_MODE_HIGH_QUALITY
+        };
+        ADD_STATIC_ENTRY(ANDROID_EDGE_AVAILABLE_EDGE_MODES,
+                availableEdgeModes, sizeof(availableEdgeModes));
+    } else {
+        static const uint8_t availableEdgeModes[] = {
+            ANDROID_EDGE_MODE_OFF
+        };
+        ADD_STATIC_ENTRY(ANDROID_EDGE_AVAILABLE_EDGE_MODES,
+                availableEdgeModes, sizeof(availableEdgeModes));
+    }
+
+    // android.info
+
+    static const uint8_t supportedHardwareLevel =
+            hasCapability(FULL_LEVEL) ? ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL :
+                    ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+    ADD_STATIC_ENTRY(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+                &supportedHardwareLevel,
+                /*count*/1);
+
+    // android.noiseReduction
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t availableNoiseReductionModes[] = {
+            ANDROID_NOISE_REDUCTION_MODE_OFF,
+            ANDROID_NOISE_REDUCTION_MODE_FAST,
+            ANDROID_NOISE_REDUCTION_MODE_HIGH_QUALITY
+        };
+        ADD_STATIC_ENTRY(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+                availableNoiseReductionModes, sizeof(availableNoiseReductionModes));
+    } else {
+        static const uint8_t availableNoiseReductionModes[] = {
+            ANDROID_NOISE_REDUCTION_MODE_OFF,
+        };
+        ADD_STATIC_ENTRY(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+                availableNoiseReductionModes, sizeof(availableNoiseReductionModes));
+    }
+
+    // android.depth
+
+    if (hasCapability(DEPTH_OUTPUT)) {
+
+        static const int32_t maxDepthSamples = 100;
+        ADD_STATIC_ENTRY(ANDROID_DEPTH_MAX_DEPTH_SAMPLES,
+                &maxDepthSamples, 1);
+
+        static const int32_t availableDepthStreamConfigurations[] = {
+            HAL_PIXEL_FORMAT_Y16, 160, 120, ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_OUTPUT,
+            HAL_PIXEL_FORMAT_BLOB, maxDepthSamples,1, ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_OUTPUT
+        };
+        ADD_STATIC_ENTRY(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS,
+                availableDepthStreamConfigurations,
+                sizeof(availableDepthStreamConfigurations)/sizeof(int32_t));
+
+        static const int64_t availableDepthMinFrameDurations[] = {
+            HAL_PIXEL_FORMAT_Y16, 160, 120, Sensor::kFrameDurationRange[0],
+            HAL_PIXEL_FORMAT_BLOB, maxDepthSamples,1, Sensor::kFrameDurationRange[0]
+        };
+        ADD_STATIC_ENTRY(ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS,
+                availableDepthMinFrameDurations,
+                sizeof(availableDepthMinFrameDurations)/sizeof(int64_t));
+
+        static const int64_t availableDepthStallDurations[] = {
+            HAL_PIXEL_FORMAT_Y16, 160, 120, Sensor::kFrameDurationRange[0],
+            HAL_PIXEL_FORMAT_BLOB, maxDepthSamples,1, Sensor::kFrameDurationRange[0]
+        };
+        ADD_STATIC_ENTRY(ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS,
+                availableDepthStallDurations,
+                sizeof(availableDepthStallDurations)/sizeof(int64_t));
+
+        uint8_t depthIsExclusive = ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE_FALSE;
+        ADD_STATIC_ENTRY(ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE,
+                &depthIsExclusive, 1);
+    }
+
+    // android.shading
+
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t availableShadingModes[] = {
+            ANDROID_SHADING_MODE_OFF, ANDROID_SHADING_MODE_FAST, ANDROID_SHADING_MODE_HIGH_QUALITY
+        };
+        ADD_STATIC_ENTRY(ANDROID_SHADING_AVAILABLE_MODES, availableShadingModes,
+                sizeof(availableShadingModes));
+    } else {
+        static const uint8_t availableShadingModes[] = {
+            ANDROID_SHADING_MODE_OFF
+        };
+        ADD_STATIC_ENTRY(ANDROID_SHADING_AVAILABLE_MODES, availableShadingModes,
+                sizeof(availableShadingModes));
+    }
+
+    // android.request
+
+    static const int32_t maxNumOutputStreams[] = {
+            kMaxRawStreamCount, kMaxProcessedStreamCount, kMaxJpegStreamCount
+    };
+    ADD_STATIC_ENTRY(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, maxNumOutputStreams, 3);
+
+    static const uint8_t maxPipelineDepth = kMaxBufferCount;
+    ADD_STATIC_ENTRY(ANDROID_REQUEST_PIPELINE_MAX_DEPTH, &maxPipelineDepth, 1);
+
+    static const int32_t partialResultCount = 1;
+    ADD_STATIC_ENTRY(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+            &partialResultCount, /*count*/1);
+
+    SortedVector<uint8_t> caps;
+    for (size_t i = 0; i < mCapabilities.size(); i++) {
+        switch(mCapabilities[i]) {
+            case BACKWARD_COMPATIBLE:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
+                break;
+            case MANUAL_SENSOR:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR);
+                break;
+            case MANUAL_POST_PROCESSING:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING);
+                break;
+            case RAW:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
+                break;
+            case PRIVATE_REPROCESSING:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
+                break;
+            case READ_SENSOR_SETTINGS:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS);
+                break;
+            case BURST_CAPTURE:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
+                break;
+            case YUV_REPROCESSING:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
+                break;
+            case DEPTH_OUTPUT:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
+                break;
+            case CONSTRAINED_HIGH_SPEED_VIDEO:
+                caps.add(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO);
+                break;
+            default:
+                // Ignore LEVELs
+                break;
+        }
+    }
+    ADD_STATIC_ENTRY(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, caps.array(), caps.size());
+
+    // Scan a default request template for included request keys
+    Vector<int32_t> availableRequestKeys;
+    const camera_metadata_t *previewRequest =
+        constructDefaultRequestSettings(CAMERA3_TEMPLATE_PREVIEW);
+    for (size_t i = 0; i < get_camera_metadata_entry_count(previewRequest); i++) {
+        camera_metadata_ro_entry_t entry;
+        get_camera_metadata_ro_entry(previewRequest, i, &entry);
+        availableRequestKeys.add(entry.tag);
+    }
+    ADD_STATIC_ENTRY(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, availableRequestKeys.array(),
+            availableRequestKeys.size());
+
+    // Add a few more result keys. Must be kept up to date with the various places that add these
+
+    Vector<int32_t> availableResultKeys(availableRequestKeys);
+    if (hasCapability(BACKWARD_COMPATIBLE)) {
+        availableResultKeys.add(ANDROID_CONTROL_AE_STATE);
+        availableResultKeys.add(ANDROID_CONTROL_AF_STATE);
+        availableResultKeys.add(ANDROID_CONTROL_AWB_STATE);
+        availableResultKeys.add(ANDROID_FLASH_STATE);
+        availableResultKeys.add(ANDROID_LENS_STATE);
+        availableResultKeys.add(ANDROID_LENS_FOCUS_RANGE);
+        availableResultKeys.add(ANDROID_SENSOR_ROLLING_SHUTTER_SKEW);
+        availableResultKeys.add(ANDROID_STATISTICS_SCENE_FLICKER);
+    }
+
+    if (hasCapability(DEPTH_OUTPUT)) {
+        availableResultKeys.add(ANDROID_LENS_POSE_ROTATION);
+        availableResultKeys.add(ANDROID_LENS_POSE_TRANSLATION);
+        availableResultKeys.add(ANDROID_LENS_INTRINSIC_CALIBRATION);
+        availableResultKeys.add(ANDROID_LENS_RADIAL_DISTORTION);
+    }
+
+    availableResultKeys.add(ANDROID_REQUEST_PIPELINE_DEPTH);
+    availableResultKeys.add(ANDROID_SENSOR_TIMESTAMP);
+
+    ADD_STATIC_ENTRY(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, availableResultKeys.array(),
+            availableResultKeys.size());
+
+    // Needs to be last, to collect all the keys set
+
+    availableCharacteristicsKeys.add(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+    info.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+            availableCharacteristicsKeys);
+
+    mCameraInfo = info.release();
+
+#undef ADD_STATIC_ENTRY
+    return OK;
+}
+
+status_t EmulatedFakeCamera3::process3A(CameraMetadata &settings) {
+    /**
+     * Extract top-level 3A controls
+     */
+    status_t res;
+
+    bool facePriority = false;
+
+    camera_metadata_entry e;
+
+    e = settings.find(ANDROID_CONTROL_MODE);
+    if (e.count == 0) {
+        ALOGE("%s: No control mode entry!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    uint8_t controlMode = e.data.u8[0];
+
+    if (controlMode == ANDROID_CONTROL_MODE_OFF) {
+        mAeMode   = ANDROID_CONTROL_AE_MODE_OFF;
+        mAfMode   = ANDROID_CONTROL_AF_MODE_OFF;
+        mAwbMode  = ANDROID_CONTROL_AWB_MODE_OFF;
+        mAeState  = ANDROID_CONTROL_AE_STATE_INACTIVE;
+        mAfState  = ANDROID_CONTROL_AF_STATE_INACTIVE;
+        mAwbState = ANDROID_CONTROL_AWB_STATE_INACTIVE;
+        update3A(settings);
+        return OK;
+    } else if (controlMode == ANDROID_CONTROL_MODE_USE_SCENE_MODE) {
+        if (!hasCapability(BACKWARD_COMPATIBLE)) {
+            ALOGE("%s: Can't use scene mode when BACKWARD_COMPATIBLE not supported!",
+                  __FUNCTION__);
+            return BAD_VALUE;
+        }
+
+        e = settings.find(ANDROID_CONTROL_SCENE_MODE);
+        if (e.count == 0) {
+            ALOGE("%s: No scene mode entry!", __FUNCTION__);
+            return BAD_VALUE;
+        }
+        uint8_t sceneMode = e.data.u8[0];
+
+        switch(sceneMode) {
+            case ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY:
+                mFacePriority = true;
+                break;
+            default:
+                ALOGE("%s: Emulator doesn't support scene mode %d",
+                        __FUNCTION__, sceneMode);
+                return BAD_VALUE;
+        }
+    } else {
+        mFacePriority = false;
+    }
+
+    // controlMode == AUTO or sceneMode = FACE_PRIORITY
+    // Process individual 3A controls
+
+    res = doFakeAE(settings);
+    if (res != OK) return res;
+
+    res = doFakeAF(settings);
+    if (res != OK) return res;
+
+    res = doFakeAWB(settings);
+    if (res != OK) return res;
+
+    update3A(settings);
+    return OK;
+}
+
+status_t EmulatedFakeCamera3::doFakeAE(CameraMetadata &settings) {
+    camera_metadata_entry e;
+
+    e = settings.find(ANDROID_CONTROL_AE_MODE);
+    if (e.count == 0 && hasCapability(BACKWARD_COMPATIBLE)) {
+        ALOGE("%s: No AE mode entry!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    uint8_t aeMode = (e.count > 0) ? e.data.u8[0] : (uint8_t)ANDROID_CONTROL_AE_MODE_ON;
+    mAeMode = aeMode;
+
+    switch (aeMode) {
+        case ANDROID_CONTROL_AE_MODE_OFF:
+            // AE is OFF
+            mAeState = ANDROID_CONTROL_AE_STATE_INACTIVE;
+            return OK;
+        case ANDROID_CONTROL_AE_MODE_ON:
+            // OK for AUTO modes
+            break;
+        default:
+            // Mostly silently ignore unsupported modes
+            ALOGV("%s: Emulator doesn't support AE mode %d, assuming ON",
+                    __FUNCTION__, aeMode);
+            break;
+    }
+
+    e = settings.find(ANDROID_CONTROL_AE_LOCK);
+    bool aeLocked = (e.count > 0) ? (e.data.u8[0] == ANDROID_CONTROL_AE_LOCK_ON) : false;
+
+    e = settings.find(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER);
+    bool precaptureTrigger = false;
+    if (e.count != 0) {
+        precaptureTrigger =
+                (e.data.u8[0] == ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_START);
+    }
+
+    if (precaptureTrigger) {
+        ALOGV("%s: Pre capture trigger = %d", __FUNCTION__, precaptureTrigger);
+    } else if (e.count > 0) {
+        ALOGV("%s: Pre capture trigger was present? %zu",
+              __FUNCTION__,
+              e.count);
+    }
+
+    if (precaptureTrigger || mAeState == ANDROID_CONTROL_AE_STATE_PRECAPTURE) {
+        // Run precapture sequence
+        if (mAeState != ANDROID_CONTROL_AE_STATE_PRECAPTURE) {
+            mAeCounter = 0;
+        }
+
+        if (mFacePriority) {
+            mAeTargetExposureTime = kFacePriorityExposureTime;
+        } else {
+            mAeTargetExposureTime = kNormalExposureTime;
+        }
+
+        if (mAeCounter > kPrecaptureMinFrames &&
+                (mAeTargetExposureTime - mAeCurrentExposureTime) <
+                mAeTargetExposureTime / 10) {
+            // Done with precapture
+            mAeCounter = 0;
+            mAeState = aeLocked ? ANDROID_CONTROL_AE_STATE_LOCKED :
+                    ANDROID_CONTROL_AE_STATE_CONVERGED;
+        } else {
+            // Converge some more
+            mAeCurrentExposureTime +=
+                    (mAeTargetExposureTime - mAeCurrentExposureTime) *
+                    kExposureTrackRate;
+            mAeCounter++;
+            mAeState = ANDROID_CONTROL_AE_STATE_PRECAPTURE;
+        }
+
+    } else if (!aeLocked) {
+        // Run standard occasional AE scan
+        switch (mAeState) {
+            case ANDROID_CONTROL_AE_STATE_CONVERGED:
+            case ANDROID_CONTROL_AE_STATE_INACTIVE:
+                mAeCounter++;
+                if (mAeCounter > kStableAeMaxFrames) {
+                    mAeTargetExposureTime =
+                            mFacePriority ? kFacePriorityExposureTime :
+                            kNormalExposureTime;
+                    float exposureStep = ((double)rand() / RAND_MAX) *
+                            (kExposureWanderMax - kExposureWanderMin) +
+                            kExposureWanderMin;
+                    mAeTargetExposureTime *= std::pow(2, exposureStep);
+                    mAeState = ANDROID_CONTROL_AE_STATE_SEARCHING;
+                }
+                break;
+            case ANDROID_CONTROL_AE_STATE_SEARCHING:
+                mAeCurrentExposureTime +=
+                        (mAeTargetExposureTime - mAeCurrentExposureTime) *
+                        kExposureTrackRate;
+                if (abs(mAeTargetExposureTime - mAeCurrentExposureTime) <
+                        mAeTargetExposureTime / 10) {
+                    // Close enough
+                    mAeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
+                    mAeCounter = 0;
+                }
+                break;
+            case ANDROID_CONTROL_AE_STATE_LOCKED:
+                mAeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
+                mAeCounter = 0;
+                break;
+            default:
+                ALOGE("%s: Emulator in unexpected AE state %d",
+                        __FUNCTION__, mAeState);
+                return INVALID_OPERATION;
+        }
+    } else {
+        // AE is locked
+        mAeState = ANDROID_CONTROL_AE_STATE_LOCKED;
+    }
+
+    return OK;
+}
+
+status_t EmulatedFakeCamera3::doFakeAF(CameraMetadata &settings) {
+    camera_metadata_entry e;
+
+    e = settings.find(ANDROID_CONTROL_AF_MODE);
+    if (e.count == 0 && hasCapability(BACKWARD_COMPATIBLE)) {
+        ALOGE("%s: No AF mode entry!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    uint8_t afMode = (e.count > 0) ? e.data.u8[0] : (uint8_t)ANDROID_CONTROL_AF_MODE_OFF;
+
+    e = settings.find(ANDROID_CONTROL_AF_TRIGGER);
+    typedef camera_metadata_enum_android_control_af_trigger af_trigger_t;
+    af_trigger_t afTrigger;
+    if (e.count != 0) {
+        afTrigger = static_cast<af_trigger_t>(e.data.u8[0]);
+
+        ALOGV("%s: AF trigger set to 0x%x", __FUNCTION__, afTrigger);
+        ALOGV("%s: AF mode is 0x%x", __FUNCTION__, afMode);
+    } else {
+        afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
+    }
+
+    switch (afMode) {
+        case ANDROID_CONTROL_AF_MODE_OFF:
+            mAfState = ANDROID_CONTROL_AF_STATE_INACTIVE;
+            return OK;
+        case ANDROID_CONTROL_AF_MODE_AUTO:
+        case ANDROID_CONTROL_AF_MODE_MACRO:
+        case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+        case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+            if (!mFacingBack) {
+                ALOGE("%s: Front camera doesn't support AF mode %d",
+                        __FUNCTION__, afMode);
+                return BAD_VALUE;
+            }
+            // OK, handle transitions lower on
+            break;
+        default:
+            ALOGE("%s: Emulator doesn't support AF mode %d",
+                    __FUNCTION__, afMode);
+            return BAD_VALUE;
+    }
+
+    bool afModeChanged = mAfMode != afMode;
+    mAfMode = afMode;
+
+    /**
+     * Simulate AF triggers. Transition at most 1 state per frame.
+     * - Focusing always succeeds (goes into locked, or PASSIVE_SCAN).
+     */
+
+    bool afTriggerStart = false;
+    bool afTriggerCancel = false;
+    switch (afTrigger) {
+        case ANDROID_CONTROL_AF_TRIGGER_IDLE:
+            break;
+        case ANDROID_CONTROL_AF_TRIGGER_START:
+            afTriggerStart = true;
+            break;
+        case ANDROID_CONTROL_AF_TRIGGER_CANCEL:
+            afTriggerCancel = true;
+            // Cancel trigger always transitions into INACTIVE
+            mAfState = ANDROID_CONTROL_AF_STATE_INACTIVE;
+
+            ALOGV("%s: AF State transition to STATE_INACTIVE", __FUNCTION__);
+
+            // Stay in 'inactive' until at least next frame
+            return OK;
+        default:
+            ALOGE("%s: Unknown af trigger value %d", __FUNCTION__, afTrigger);
+            return BAD_VALUE;
+    }
+
+    // If we get down here, we're either in an autofocus mode
+    //  or in a continuous focus mode (and no other modes)
+
+    int oldAfState = mAfState;
+    switch (mAfState) {
+        case ANDROID_CONTROL_AF_STATE_INACTIVE:
+            if (afTriggerStart) {
+                switch (afMode) {
+                    case ANDROID_CONTROL_AF_MODE_AUTO:
+                        // fall-through
+                    case ANDROID_CONTROL_AF_MODE_MACRO:
+                        mAfState = ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN;
+                        break;
+                    case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+                        // fall-through
+                    case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+                        mAfState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
+                        break;
+                }
+            } else {
+                // At least one frame stays in INACTIVE
+                if (!afModeChanged) {
+                    switch (afMode) {
+                        case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+                            // fall-through
+                        case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+                            mAfState = ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN;
+                            break;
+                    }
+                }
+            }
+            break;
+        case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+            /**
+             * When the AF trigger is activated, the algorithm should finish
+             * its PASSIVE_SCAN if active, and then transition into AF_FOCUSED
+             * or AF_NOT_FOCUSED as appropriate
+             */
+            if (afTriggerStart) {
+                // Randomly transition to focused or not focused
+                if (rand() % 3) {
+                    mAfState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+                } else {
+                    mAfState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
+                }
+            }
+            /**
+             * When the AF trigger is not involved, the AF algorithm should
+             * start in INACTIVE state, and then transition into PASSIVE_SCAN
+             * and PASSIVE_FOCUSED states
+             */
+            else if (!afTriggerCancel) {
+               // Randomly transition to passive focus
+                if (rand() % 3 == 0) {
+                    mAfState = ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED;
+                }
+            }
+
+            break;
+        case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+            if (afTriggerStart) {
+                // Randomly transition to focused or not focused
+                if (rand() % 3) {
+                    mAfState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+                } else {
+                    mAfState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
+                }
+            }
+            // TODO: initiate passive scan (PASSIVE_SCAN)
+            break;
+        case ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN:
+            // Simulate AF sweep completing instantaneously
+
+            // Randomly transition to focused or not focused
+            if (rand() % 3) {
+                mAfState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+            } else {
+                mAfState = ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
+            }
+            break;
+        case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+            if (afTriggerStart) {
+                switch (afMode) {
+                    case ANDROID_CONTROL_AF_MODE_AUTO:
+                        // fall-through
+                    case ANDROID_CONTROL_AF_MODE_MACRO:
+                        mAfState = ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN;
+                        break;
+                    case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+                        // fall-through
+                    case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+                        // continuous autofocus => trigger start has no effect
+                        break;
+                }
+            }
+            break;
+        case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+            if (afTriggerStart) {
+                switch (afMode) {
+                    case ANDROID_CONTROL_AF_MODE_AUTO:
+                        // fall-through
+                    case ANDROID_CONTROL_AF_MODE_MACRO:
+                        mAfState = ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN;
+                        break;
+                    case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+                        // fall-through
+                    case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+                        // continuous autofocus => trigger start has no effect
+                        break;
+                }
+            }
+            break;
+        default:
+            ALOGE("%s: Bad af state %d", __FUNCTION__, mAfState);
+    }
+
+    {
+        char afStateString[100] = {0,};
+        camera_metadata_enum_snprint(ANDROID_CONTROL_AF_STATE,
+                oldAfState,
+                afStateString,
+                sizeof(afStateString));
+
+        char afNewStateString[100] = {0,};
+        camera_metadata_enum_snprint(ANDROID_CONTROL_AF_STATE,
+                mAfState,
+                afNewStateString,
+                sizeof(afNewStateString));
+        ALOGVV("%s: AF state transitioned from %s to %s",
+              __FUNCTION__, afStateString, afNewStateString);
+    }
+
+
+    return OK;
+}
+
+status_t EmulatedFakeCamera3::doFakeAWB(CameraMetadata &settings) {
+    camera_metadata_entry e;
+
+    e = settings.find(ANDROID_CONTROL_AWB_MODE);
+    if (e.count == 0 && hasCapability(BACKWARD_COMPATIBLE)) {
+        ALOGE("%s: No AWB mode entry!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    uint8_t awbMode = (e.count > 0) ? e.data.u8[0] : (uint8_t)ANDROID_CONTROL_AWB_MODE_AUTO;
+
+    // TODO: Add white balance simulation
+
+    e = settings.find(ANDROID_CONTROL_AWB_LOCK);
+    bool awbLocked = (e.count > 0) ? (e.data.u8[0] == ANDROID_CONTROL_AWB_LOCK_ON) : false;
+
+    switch (awbMode) {
+        case ANDROID_CONTROL_AWB_MODE_OFF:
+            mAwbState = ANDROID_CONTROL_AWB_STATE_INACTIVE;
+            break;
+        case ANDROID_CONTROL_AWB_MODE_AUTO:
+        case ANDROID_CONTROL_AWB_MODE_INCANDESCENT:
+        case ANDROID_CONTROL_AWB_MODE_FLUORESCENT:
+        case ANDROID_CONTROL_AWB_MODE_DAYLIGHT:
+        case ANDROID_CONTROL_AWB_MODE_SHADE:
+            // Always magically right, or locked
+            mAwbState = awbLocked ? ANDROID_CONTROL_AWB_STATE_LOCKED :
+                    ANDROID_CONTROL_AWB_STATE_CONVERGED;
+            break;
+        default:
+            ALOGE("%s: Emulator doesn't support AWB mode %d",
+                    __FUNCTION__, awbMode);
+            return BAD_VALUE;
+    }
+
+    return OK;
+}
+
+
+void EmulatedFakeCamera3::update3A(CameraMetadata &settings) {
+    if (mAeMode != ANDROID_CONTROL_AE_MODE_OFF) {
+        settings.update(ANDROID_SENSOR_EXPOSURE_TIME,
+                &mAeCurrentExposureTime, 1);
+        settings.update(ANDROID_SENSOR_SENSITIVITY,
+                &mAeCurrentSensitivity, 1);
+    }
+
+    settings.update(ANDROID_CONTROL_AE_STATE,
+            &mAeState, 1);
+    settings.update(ANDROID_CONTROL_AF_STATE,
+            &mAfState, 1);
+    settings.update(ANDROID_CONTROL_AWB_STATE,
+            &mAwbState, 1);
+
+    uint8_t lensState;
+    switch (mAfState) {
+        case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+        case ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN:
+            lensState = ANDROID_LENS_STATE_MOVING;
+            break;
+        case ANDROID_CONTROL_AF_STATE_INACTIVE:
+        case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+        case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+        case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+        case ANDROID_CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
+        default:
+            lensState = ANDROID_LENS_STATE_STATIONARY;
+            break;
+    }
+    settings.update(ANDROID_LENS_STATE, &lensState, 1);
+
+}
+
+void EmulatedFakeCamera3::signalReadoutIdle() {
+    Mutex::Autolock l(mLock);
+    // Need to chek isIdle again because waiting on mLock may have allowed
+    // something to be placed in the in-flight queue.
+    if (mStatus == STATUS_ACTIVE && mReadoutThread->isIdle()) {
+        ALOGV("Now idle");
+        mStatus = STATUS_READY;
+    }
+}
+
+void EmulatedFakeCamera3::onSensorEvent(uint32_t frameNumber, Event e,
+        nsecs_t timestamp) {
+    switch(e) {
+        case Sensor::SensorListener::EXPOSURE_START: {
+            ALOGVV("%s: Frame %d: Sensor started exposure at %lld",
+                    __FUNCTION__, frameNumber, timestamp);
+            // Trigger shutter notify to framework
+            camera3_notify_msg_t msg;
+            msg.type = CAMERA3_MSG_SHUTTER;
+            msg.message.shutter.frame_number = frameNumber;
+            msg.message.shutter.timestamp = timestamp;
+            sendNotify(&msg);
+            break;
+        }
+        default:
+            ALOGW("%s: Unexpected sensor event %d at %" PRId64, __FUNCTION__,
+                    e, timestamp);
+            break;
+    }
+}
+
+EmulatedFakeCamera3::ReadoutThread::ReadoutThread(EmulatedFakeCamera3 *parent) :
+        mParent(parent), mJpegWaiting(false) {
+}
+
+EmulatedFakeCamera3::ReadoutThread::~ReadoutThread() {
+    for (List<Request>::iterator i = mInFlightQueue.begin();
+         i != mInFlightQueue.end(); i++) {
+        delete i->buffers;
+        delete i->sensorBuffers;
+    }
+}
+
+void EmulatedFakeCamera3::ReadoutThread::queueCaptureRequest(const Request &r) {
+    Mutex::Autolock l(mLock);
+
+    mInFlightQueue.push_back(r);
+    mInFlightSignal.signal();
+}
+
+bool EmulatedFakeCamera3::ReadoutThread::isIdle() {
+    Mutex::Autolock l(mLock);
+    return mInFlightQueue.empty() && !mThreadActive;
+}
+
+status_t EmulatedFakeCamera3::ReadoutThread::waitForReadout() {
+    status_t res;
+    Mutex::Autolock l(mLock);
+    int loopCount = 0;
+    while (mInFlightQueue.size() >= kMaxQueueSize) {
+        res = mInFlightSignal.waitRelative(mLock, kWaitPerLoop);
+        if (res != OK && res != TIMED_OUT) {
+            ALOGE("%s: Error waiting for in-flight queue to shrink",
+                    __FUNCTION__);
+            return INVALID_OPERATION;
+        }
+        if (loopCount == kMaxWaitLoops) {
+            ALOGE("%s: Timed out waiting for in-flight queue to shrink",
+                    __FUNCTION__);
+            return TIMED_OUT;
+        }
+        loopCount++;
+    }
+    return OK;
+}
+
+bool EmulatedFakeCamera3::ReadoutThread::threadLoop() {
+    status_t res;
+
+    ALOGVV("%s: ReadoutThread waiting for request", __FUNCTION__);
+
+    // First wait for a request from the in-flight queue
+
+    if (mCurrentRequest.settings.isEmpty()) {
+        Mutex::Autolock l(mLock);
+        if (mInFlightQueue.empty()) {
+            res = mInFlightSignal.waitRelative(mLock, kWaitPerLoop);
+            if (res == TIMED_OUT) {
+                ALOGVV("%s: ReadoutThread: Timed out waiting for request",
+                        __FUNCTION__);
+                return true;
+            } else if (res != NO_ERROR) {
+                ALOGE("%s: Error waiting for capture requests: %d",
+                        __FUNCTION__, res);
+                return false;
+            }
+        }
+        mCurrentRequest.frameNumber = mInFlightQueue.begin()->frameNumber;
+        mCurrentRequest.settings.acquire(mInFlightQueue.begin()->settings);
+        mCurrentRequest.buffers = mInFlightQueue.begin()->buffers;
+        mCurrentRequest.sensorBuffers = mInFlightQueue.begin()->sensorBuffers;
+        mInFlightQueue.erase(mInFlightQueue.begin());
+        mInFlightSignal.signal();
+        mThreadActive = true;
+        ALOGVV("%s: Beginning readout of frame %d", __FUNCTION__,
+                mCurrentRequest.frameNumber);
+    }
+
+    // Then wait for it to be delivered from the sensor
+    ALOGVV("%s: ReadoutThread: Wait for frame to be delivered from sensor",
+            __FUNCTION__);
+
+    nsecs_t captureTime;
+    bool gotFrame =
+            mParent->mSensor->waitForNewFrame(kWaitPerLoop, &captureTime);
+    if (!gotFrame) {
+        ALOGVV("%s: ReadoutThread: Timed out waiting for sensor frame",
+                __FUNCTION__);
+        return true;
+    }
+
+    ALOGVV("Sensor done with readout for frame %d, captured at %lld ",
+            mCurrentRequest.frameNumber, captureTime);
+
+    // Check if we need to JPEG encode a buffer, and send it for async
+    // compression if so. Otherwise prepare the buffer for return.
+    bool needJpeg = false;
+    HalBufferVector::iterator buf = mCurrentRequest.buffers->begin();
+    while(buf != mCurrentRequest.buffers->end()) {
+        bool goodBuffer = true;
+        if ( buf->stream->format ==
+                HAL_PIXEL_FORMAT_BLOB && buf->stream->data_space != HAL_DATASPACE_DEPTH) {
+            Mutex::Autolock jl(mJpegLock);
+            if (mJpegWaiting) {
+                // This shouldn't happen, because processCaptureRequest should
+                // be stalling until JPEG compressor is free.
+                ALOGE("%s: Already processing a JPEG!", __FUNCTION__);
+                goodBuffer = false;
+            }
+            if (goodBuffer) {
+                // Compressor takes ownership of sensorBuffers here
+                res = mParent->mJpegCompressor->start(mCurrentRequest.sensorBuffers,
+                        this);
+                goodBuffer = (res == OK);
+            }
+            if (goodBuffer) {
+                needJpeg = true;
+
+                mJpegHalBuffer = *buf;
+                mJpegFrameNumber = mCurrentRequest.frameNumber;
+                mJpegWaiting = true;
+
+                mCurrentRequest.sensorBuffers = NULL;
+                buf = mCurrentRequest.buffers->erase(buf);
+
+                continue;
+            }
+            ALOGE("%s: Error compressing output buffer: %s (%d)",
+                        __FUNCTION__, strerror(-res), res);
+            // fallthrough for cleanup
+        }
+        GrallocModule::getInstance().unlock(*(buf->buffer));
+
+        buf->status = goodBuffer ? CAMERA3_BUFFER_STATUS_OK :
+                CAMERA3_BUFFER_STATUS_ERROR;
+        buf->acquire_fence = -1;
+        buf->release_fence = -1;
+
+        ++buf;
+    } // end while
+
+    // Construct result for all completed buffers and results
+
+    camera3_capture_result result;
+
+    if (mParent->hasCapability(BACKWARD_COMPATIBLE)) {
+        static const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
+        mCurrentRequest.settings.update(ANDROID_STATISTICS_SCENE_FLICKER,
+                &sceneFlicker, 1);
+
+        static const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
+        mCurrentRequest.settings.update(ANDROID_FLASH_STATE,
+                &flashState, 1);
+
+        nsecs_t rollingShutterSkew = Sensor::kFrameDurationRange[0];
+        mCurrentRequest.settings.update(ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
+                &rollingShutterSkew, 1);
+
+        float focusRange[] = { 1.0f/5.0f, 0 }; // 5 m to infinity in focus
+        mCurrentRequest.settings.update(ANDROID_LENS_FOCUS_RANGE,
+                focusRange, sizeof(focusRange)/sizeof(float));
+    }
+
+    if (mParent->hasCapability(DEPTH_OUTPUT)) {
+        camera_metadata_entry_t entry;
+
+        find_camera_metadata_entry(mParent->mCameraInfo, ANDROID_LENS_POSE_TRANSLATION, &entry);
+        mCurrentRequest.settings.update(ANDROID_LENS_POSE_TRANSLATION,
+                entry.data.f, entry.count);
+
+        find_camera_metadata_entry(mParent->mCameraInfo, ANDROID_LENS_POSE_ROTATION, &entry);
+        mCurrentRequest.settings.update(ANDROID_LENS_POSE_ROTATION,
+                entry.data.f, entry.count);
+
+        find_camera_metadata_entry(mParent->mCameraInfo, ANDROID_LENS_INTRINSIC_CALIBRATION, &entry);
+        mCurrentRequest.settings.update(ANDROID_LENS_INTRINSIC_CALIBRATION,
+                entry.data.f, entry.count);
+
+        find_camera_metadata_entry(mParent->mCameraInfo, ANDROID_LENS_RADIAL_DISTORTION, &entry);
+        mCurrentRequest.settings.update(ANDROID_LENS_RADIAL_DISTORTION,
+                entry.data.f, entry.count);
+    }
+
+    mCurrentRequest.settings.update(ANDROID_SENSOR_TIMESTAMP,
+            &captureTime, 1);
+
+
+    // JPEGs take a stage longer
+    const uint8_t pipelineDepth = needJpeg ? kMaxBufferCount : kMaxBufferCount - 1;
+    mCurrentRequest.settings.update(ANDROID_REQUEST_PIPELINE_DEPTH,
+            &pipelineDepth, 1);
+
+    result.frame_number = mCurrentRequest.frameNumber;
+    result.result = mCurrentRequest.settings.getAndLock();
+    result.num_output_buffers = mCurrentRequest.buffers->size();
+    result.output_buffers = mCurrentRequest.buffers->array();
+    result.input_buffer = nullptr;
+    result.partial_result = 1;
+
+    // Go idle if queue is empty, before sending result
+    bool signalIdle = false;
+    {
+        Mutex::Autolock l(mLock);
+        if (mInFlightQueue.empty()) {
+            mThreadActive = false;
+            signalIdle = true;
+        }
+    }
+    if (signalIdle) mParent->signalReadoutIdle();
+
+    // Send it off to the framework
+    ALOGVV("%s: ReadoutThread: Send result to framework",
+            __FUNCTION__);
+    mParent->sendCaptureResult(&result);
+
+    // Clean up
+    mCurrentRequest.settings.unlock(result.result);
+
+    delete mCurrentRequest.buffers;
+    mCurrentRequest.buffers = NULL;
+    if (!needJpeg) {
+        delete mCurrentRequest.sensorBuffers;
+        mCurrentRequest.sensorBuffers = NULL;
+    }
+    mCurrentRequest.settings.clear();
+
+    return true;
+}
+
+void EmulatedFakeCamera3::ReadoutThread::onJpegDone(
+        const StreamBuffer &jpegBuffer, bool success) {
+    Mutex::Autolock jl(mJpegLock);
+
+    GrallocModule::getInstance().unlock(*(jpegBuffer.buffer));
+
+    mJpegHalBuffer.status = success ?
+            CAMERA3_BUFFER_STATUS_OK : CAMERA3_BUFFER_STATUS_ERROR;
+    mJpegHalBuffer.acquire_fence = -1;
+    mJpegHalBuffer.release_fence = -1;
+    mJpegWaiting = false;
+
+    camera3_capture_result result;
+
+    result.frame_number = mJpegFrameNumber;
+    result.result = NULL;
+    result.num_output_buffers = 1;
+    result.output_buffers = &mJpegHalBuffer;
+    result.input_buffer = nullptr;
+    result.partial_result = 0;
+
+    if (!success) {
+        ALOGE("%s: Compression failure, returning error state buffer to"
+                " framework", __FUNCTION__);
+    } else {
+        ALOGV("%s: Compression complete, returning buffer to framework",
+                __FUNCTION__);
+    }
+
+    mParent->sendCaptureResult(&result);
+}
+
+void EmulatedFakeCamera3::ReadoutThread::onJpegInputDone(
+        const StreamBuffer &inputBuffer) {
+    // Should never get here, since the input buffer has to be returned
+    // by end of processCaptureRequest
+    ALOGE("%s: Unexpected input buffer from JPEG compressor!", __FUNCTION__);
+}
+
+
+}; // namespace android
diff --git a/guest/hals/camera/EmulatedFakeCamera3.h b/guest/hals/camera/EmulatedFakeCamera3.h
new file mode 100644
index 0000000..f0cf68f
--- /dev/null
+++ b/guest/hals/camera/EmulatedFakeCamera3.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2013 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 HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA3_H
+#define HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA3_H
+
+/**
+ * Contains declaration of a class EmulatedCamera that encapsulates
+ * functionality of a fake camera that implements version 3 of the camera device
+ * interace.
+ */
+
+#include "EmulatedCamera3.h"
+#include "fake-pipeline2/Base.h"
+#include "fake-pipeline2/Sensor.h"
+#include "fake-pipeline2/JpegCompressor.h"
+#include <camera/CameraMetadata.h>
+#include <utils/SortedVector.h>
+#include <utils/List.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+/**
+ * Encapsulates functionality for a v3 HAL camera which produces synthetic data.
+ *
+ * Note that EmulatedCameraFactory instantiates an object of this class just
+ * once, when EmulatedCameraFactory instance gets constructed. Connection to /
+ * disconnection from the actual camera device is handled by calls to
+ * connectDevice(), and closeCamera() methods of this class that are invoked in
+ * response to hw_module_methods_t::open, and camera_device::close callbacks.
+ */
+class EmulatedFakeCamera3 : public EmulatedCamera3,
+        private Sensor::SensorListener {
+public:
+
+    EmulatedFakeCamera3(int cameraId, bool facingBack,
+            struct hw_module_t* module);
+
+    virtual ~EmulatedFakeCamera3();
+
+    /****************************************************************************
+     * EmulatedCamera3 virtual overrides
+     ***************************************************************************/
+
+public:
+
+    virtual status_t Initialize(const cvd::CameraDefinition& params);
+
+    /****************************************************************************
+     * Camera module API and generic hardware device API implementation
+     ***************************************************************************/
+
+public:
+    virtual status_t connectCamera(hw_device_t** device);
+
+    virtual status_t closeCamera();
+
+    virtual status_t getCameraInfo(struct camera_info *info);
+
+    virtual status_t setTorchMode(bool enabled);
+
+    /****************************************************************************
+     * EmulatedCamera3 abstract API implementation
+     ***************************************************************************/
+
+protected:
+
+    virtual status_t configureStreams(
+        camera3_stream_configuration *streamList);
+
+    virtual status_t registerStreamBuffers(
+        const camera3_stream_buffer_set *bufferSet) ;
+
+    virtual const camera_metadata_t* constructDefaultRequestSettings(
+        int type);
+
+    virtual status_t processCaptureRequest(camera3_capture_request *request);
+
+    virtual status_t flush();
+
+    /** Debug methods */
+
+    virtual void dump(int fd);
+
+private:
+
+    /**
+     * Get the requested capability set for this camera
+     */
+    status_t getCameraCapabilities();
+
+    bool hasCapability(AvailableCapabilities cap);
+
+    /**
+     * Build the static info metadata buffer for this device
+     */
+    status_t constructStaticInfo(const cvd::CameraDefinition& params);
+
+    /**
+     * Run the fake 3A algorithms as needed. May override/modify settings
+     * values.
+     */
+    status_t process3A(CameraMetadata &settings);
+
+    status_t doFakeAE(CameraMetadata &settings);
+    status_t doFakeAF(CameraMetadata &settings);
+    status_t doFakeAWB(CameraMetadata &settings);
+    void     update3A(CameraMetadata &settings);
+
+    /** Signal from readout thread that it doesn't have anything to do */
+    void     signalReadoutIdle();
+
+    /** Handle interrupt events from the sensor */
+    void     onSensorEvent(uint32_t frameNumber, Event e, nsecs_t timestamp);
+
+    /****************************************************************************
+     * Static configuration information
+     ***************************************************************************/
+private:
+    static const uint32_t kMaxRawStreamCount = 1;
+    static const uint32_t kMaxProcessedStreamCount = 3;
+    static const uint32_t kMaxJpegStreamCount = 1;
+    static const uint32_t kMaxReprocessStreamCount = 2;
+    static const uint32_t kMaxBufferCount = 4;
+    // We need a positive stream ID to distinguish external buffers from
+    // sensor-generated buffers which use a nonpositive ID. Otherwise, HAL3 has
+    // no concept of a stream id.
+    static const uint32_t kGenericStreamId = 1;
+    static const int32_t  kAvailableFormats[];
+
+    static const int64_t  kSyncWaitTimeout     = 10000000; // 10 ms
+    static const int32_t  kMaxSyncTimeoutCount = 1000; // 1000 kSyncWaitTimeouts
+    static const uint32_t kFenceTimeoutMs      = 2000; // 2 s
+    static const nsecs_t  kJpegTimeoutNs       = 5000000000l; // 5 s
+
+    /****************************************************************************
+     * Data members.
+     ***************************************************************************/
+
+    /* HAL interface serialization lock. */
+    Mutex              mLock;
+
+    /* Facing back (true) or front (false) switch. */
+    bool               mFacingBack;
+    int32_t            mSensorWidth;
+    int32_t            mSensorHeight;
+
+    SortedVector<AvailableCapabilities> mCapabilities;
+
+    /**
+     * Cache for default templates. Once one is requested, the pointer must be
+     * valid at least until close() is called on the device
+     */
+    camera_metadata_t *mDefaultTemplates[CAMERA3_TEMPLATE_COUNT];
+
+    /**
+     * Private stream information, stored in camera3_stream_t->priv.
+     */
+    struct PrivateStreamInfo {
+        bool alive;
+    };
+
+    // Shortcut to the input stream
+    camera3_stream_t*  mInputStream;
+
+    typedef List<camera3_stream_t*>           StreamList;
+    typedef List<camera3_stream_t*>::iterator StreamIterator;
+    typedef Vector<camera3_stream_buffer>     HalBufferVector;
+
+    // All streams, including input stream
+    StreamList         mStreams;
+
+    // Cached settings from latest submitted request
+    CameraMetadata     mPrevSettings;
+
+    /** Fake hardware interfaces */
+    sp<Sensor>         mSensor;
+    sp<JpegCompressor> mJpegCompressor;
+    friend class       JpegCompressor;
+
+    /** Processing thread for sending out results */
+
+    class ReadoutThread : public Thread, private JpegCompressor::JpegListener {
+      public:
+        ReadoutThread(EmulatedFakeCamera3 *parent);
+        ~ReadoutThread();
+
+        struct Request {
+            uint32_t         frameNumber;
+            CameraMetadata   settings;
+            HalBufferVector *buffers;
+            Buffers         *sensorBuffers;
+        };
+
+        /**
+         * Interface to parent class
+         */
+
+        // Place request in the in-flight queue to wait for sensor capture
+        void     queueCaptureRequest(const Request &r);
+
+        // Test if the readout thread is idle (no in-flight requests, not
+        // currently reading out anything
+        bool     isIdle();
+
+        // Wait until isIdle is true
+        status_t waitForReadout();
+
+      private:
+        static const nsecs_t kWaitPerLoop  = 10000000L; // 10 ms
+        static const nsecs_t kMaxWaitLoops = 1000;
+        static const size_t  kMaxQueueSize = 2;
+
+        EmulatedFakeCamera3 *mParent;
+        Mutex mLock;
+
+        List<Request> mInFlightQueue;
+        Condition     mInFlightSignal;
+        bool          mThreadActive;
+
+        virtual bool threadLoop();
+
+        // Only accessed by threadLoop
+
+        Request mCurrentRequest;
+
+        // Jpeg completion callbacks
+
+        Mutex                 mJpegLock;
+        bool                  mJpegWaiting;
+        camera3_stream_buffer mJpegHalBuffer;
+        uint32_t              mJpegFrameNumber;
+        virtual void onJpegDone(const StreamBuffer &jpegBuffer, bool success);
+        virtual void onJpegInputDone(const StreamBuffer &inputBuffer);
+    };
+
+    sp<ReadoutThread> mReadoutThread;
+
+    /** Fake 3A constants */
+
+    static const nsecs_t kNormalExposureTime;
+    static const nsecs_t kFacePriorityExposureTime;
+    static const int     kNormalSensitivity;
+    static const int     kFacePrioritySensitivity;
+    // Rate of converging AE to new target value, as fraction of difference between
+    // current and target value.
+    static const float   kExposureTrackRate;
+    // Minimum duration for precapture state. May be longer if slow to converge
+    // to target exposure
+    static const int     kPrecaptureMinFrames;
+    // How often to restart AE 'scanning'
+    static const int     kStableAeMaxFrames;
+    // Maximum stop below 'normal' exposure time that we'll wander to while
+    // pretending to converge AE. In powers of 2. (-2 == 1/4 as bright)
+    static const float   kExposureWanderMin;
+    // Maximum stop above 'normal' exposure time that we'll wander to while
+    // pretending to converge AE. In powers of 2. (2 == 4x as bright)
+    static const float   kExposureWanderMax;
+
+    /** Fake 3A state */
+
+    uint8_t mControlMode;
+    bool    mFacePriority;
+    uint8_t mAeState;
+    uint8_t mAfState;
+    uint8_t mAwbState;
+    uint8_t mAeMode;
+    uint8_t mAfMode;
+    uint8_t mAwbMode;
+
+    int     mAeCounter;
+    nsecs_t mAeCurrentExposureTime;
+    nsecs_t mAeTargetExposureTime;
+    int     mAeCurrentSensitivity;
+
+};
+
+} // namespace android
+
+#endif // HW_EMULATOR_CAMERA_EMULATED_CAMERA3_H
diff --git a/guest/hals/camera/EmulatedFakeCameraDevice.cpp b/guest/hals/camera/EmulatedFakeCameraDevice.cpp
new file mode 100755
index 0000000..8043add
--- /dev/null
+++ b/guest/hals/camera/EmulatedFakeCameraDevice.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedFakeCameraDevice that encapsulates
+ * fake camera device.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_FakeDevice"
+#include <cutils/log.h>
+#include "EmulatedFakeCamera.h"
+#include "EmulatedFakeCameraDevice.h"
+
+namespace android {
+
+EmulatedFakeCameraDevice::EmulatedFakeCameraDevice(EmulatedFakeCamera* camera_hal)
+    : EmulatedCameraDevice(camera_hal),
+      mBlackYUV(kBlack32),
+      mWhiteYUV(kWhite32),
+      mRedYUV(kRed8),
+      mGreenYUV(kGreen8),
+      mBlueYUV(kBlue8),
+      mLastRedrawn(0),
+      mCheckX(0),
+      mCheckY(0),
+      mCcounter(0)
+#if EFCD_ROTATE_FRAME
+      , mLastRotatedAt(0),
+        mCurrentFrameType(0),
+        mCurrentColor(&mWhiteYUV)
+#endif  // EFCD_ROTATE_FRAME
+{
+    // Makes the image with the original exposure compensation darker.
+    // So the effects of changing the exposure compensation can be seen.
+    mBlackYUV.Y = mBlackYUV.Y / 2;
+    mWhiteYUV.Y = mWhiteYUV.Y / 2;
+    mRedYUV.Y = mRedYUV.Y / 2;
+    mGreenYUV.Y = mGreenYUV.Y / 2;
+    mBlueYUV.Y = mBlueYUV.Y / 2;
+}
+
+EmulatedFakeCameraDevice::~EmulatedFakeCameraDevice()
+{
+}
+
+/****************************************************************************
+ * Emulated camera device abstract interface implementation.
+ ***************************************************************************/
+
+status_t EmulatedFakeCameraDevice::connectDevice()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isInitialized()) {
+        ALOGE("%s: Fake camera device is not initialized.", __FUNCTION__);
+        return EINVAL;
+    }
+    if (isConnected()) {
+        ALOGW("%s: Fake camera device is already connected.", __FUNCTION__);
+        return NO_ERROR;
+    }
+
+    /* There is no device to connect to. */
+    mState = ECDS_CONNECTED;
+
+    return NO_ERROR;
+}
+
+status_t EmulatedFakeCameraDevice::disconnectDevice()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isConnected()) {
+        ALOGW("%s: Fake camera device is already disconnected.", __FUNCTION__);
+        return NO_ERROR;
+    }
+    if (isStarted()) {
+        ALOGE("%s: Cannot disconnect from the started device.", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* There is no device to disconnect from. */
+    mState = ECDS_INITIALIZED;
+
+    return NO_ERROR;
+}
+
+status_t EmulatedFakeCameraDevice::startDevice(int width,
+                                               int height,
+                                               uint32_t pix_fmt,
+                                               int fps)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isConnected()) {
+        ALOGE("%s: Fake camera device is not connected.", __FUNCTION__);
+        return EINVAL;
+    }
+    if (isStarted()) {
+        ALOGE("%s: Fake camera device is already started.", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* Initialize the base class. */
+    const status_t res =
+        EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt, fps);
+    if (res == NO_ERROR) {
+        /* Calculate U/V panes inside the framebuffer. */
+        switch (mPixelFormat) {
+            case V4L2_PIX_FMT_YVU420:
+                mFrameV = mCurrentFrame + mTotalPixels;
+                mFrameU = mFrameU + mTotalPixels / 4;
+                mUVStep = 1;
+                mUVTotalNum = mTotalPixels / 4;
+                break;
+
+            case V4L2_PIX_FMT_YUV420:
+                mFrameU = mCurrentFrame + mTotalPixels;
+                mFrameV = mFrameU + mTotalPixels / 4;
+                mUVStep = 1;
+                mUVTotalNum = mTotalPixels / 4;
+                break;
+
+            case V4L2_PIX_FMT_NV21:
+                /* Interleaved UV pane, V first. */
+                mFrameV = mCurrentFrame + mTotalPixels;
+                mFrameU = mFrameV + 1;
+                mUVStep = 2;
+                mUVTotalNum = mTotalPixels / 4;
+                break;
+
+            case V4L2_PIX_FMT_NV12:
+                /* Interleaved UV pane, U first. */
+                mFrameU = mCurrentFrame + mTotalPixels;
+                mFrameV = mFrameU + 1;
+                mUVStep = 2;
+                mUVTotalNum = mTotalPixels / 4;
+                break;
+
+            default:
+                ALOGE("%s: Unknown pixel format %.4s", __FUNCTION__,
+                     reinterpret_cast<const char*>(&mPixelFormat));
+                return EINVAL;
+        }
+        /* Number of items in a single row inside U/V panes. */
+        mUVInRow = (width / 2) * mUVStep;
+        mState = ECDS_STARTED;
+        mCurFrameTimestamp = 0;
+    } else {
+        ALOGE("%s: commonStartDevice failed", __FUNCTION__);
+    }
+
+    return res;
+}
+
+status_t EmulatedFakeCameraDevice::stopDevice()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isStarted()) {
+        ALOGW("%s: Fake camera device is not started.", __FUNCTION__);
+        return NO_ERROR;
+    }
+
+    mFrameU = mFrameV = NULL;
+    EmulatedCameraDevice::commonStopDevice();
+    mState = ECDS_CONNECTED;
+
+    return NO_ERROR;
+}
+
+/****************************************************************************
+ * Worker thread management overrides.
+ ***************************************************************************/
+
+bool EmulatedFakeCameraDevice::inWorkerThread()
+{
+    /* Wait till FPS timeout expires, or thread exit message is received. */
+    WorkerThread::SelectRes res =
+        getWorkerThread()->Select(-1, 1000000 / (mTargetFps / 1000));
+    if (res == WorkerThread::EXIT_THREAD) {
+        ALOGV("%s: Worker thread has been terminated.", __FUNCTION__);
+        return false;
+    }
+
+    /* Lets see if we need to generate a new frame. */
+    if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRedrawn) >= mRedrawAfter) {
+        /*
+         * Time to generate a new frame.
+         */
+
+#if EFCD_ROTATE_FRAME
+        const int frame_type = rotateFrame();
+        switch (frame_type) {
+            case 0:
+                drawCheckerboard();
+                break;
+            case 1:
+                drawStripes();
+                break;
+            case 2:
+                drawSolid(mCurrentColor);
+                break;
+        }
+#else
+        /* Draw the checker board. */
+        drawCheckerboard();
+
+#endif  // EFCD_ROTATE_FRAME
+
+        // mCurFrameTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+        mCurFrameTimestamp += (1000000000 / (mTargetFps / 1000));
+        /* Timestamp the current frame, and notify the camera HAL about new frame. */
+        mLastRedrawn = systemTime(SYSTEM_TIME_MONOTONIC);
+        mCameraHAL->onNextFrameAvailable(mCurrentFrame, mCurFrameTimestamp, this);
+    }
+
+
+    return true;
+}
+
+/****************************************************************************
+ * Fake camera device private API
+ ***************************************************************************/
+
+void EmulatedFakeCameraDevice::drawCheckerboard()
+{
+    const int size = mFrameWidth / 10;
+    bool black = true;
+
+    if (size == 0) {
+        // When this happens, it happens at a very high rate,
+        //     so don't log any messages and just return.
+        return;
+    }
+
+
+    if((mCheckX / size) & 1)
+        black = false;
+    if((mCheckY / size) & 1)
+        black = !black;
+
+    int county = mCheckY % size;
+    int checkxremainder = mCheckX % size;
+    uint8_t* Y = mCurrentFrame;
+    uint8_t* U_pos = mFrameU;
+    uint8_t* V_pos = mFrameV;
+    uint8_t* U = U_pos;
+    uint8_t* V = V_pos;
+
+    YUVPixel adjustedWhite = YUVPixel(mWhiteYUV);
+    changeWhiteBalance(adjustedWhite.Y, adjustedWhite.U, adjustedWhite.V);
+
+    for(int y = 0; y < mFrameHeight; y++) {
+        int countx = checkxremainder;
+        bool current = black;
+        for(int x = 0; x < mFrameWidth; x += 2) {
+            if (current) {
+                mBlackYUV.get(Y, U, V);
+            } else {
+                adjustedWhite.get(Y, U, V);
+            }
+            *Y = changeExposure(*Y);
+            Y[1] = *Y;
+            Y += 2; U += mUVStep; V += mUVStep;
+            countx += 2;
+            if(countx >= size) {
+                countx = 0;
+                current = !current;
+            }
+        }
+        if (y & 0x1) {
+            U_pos = U;
+            V_pos = V;
+        } else {
+            U = U_pos;
+            V = V_pos;
+        }
+        if(county++ >= size) {
+            county = 0;
+            black = !black;
+        }
+    }
+    mCheckX += 3;
+    mCheckY++;
+
+    /* Run the square. */
+    int sqx = ((mCcounter * 3) & 255);
+    if(sqx > 128) sqx = 255 - sqx;
+    int sqy = ((mCcounter * 5) & 255);
+    if(sqy > 128) sqy = 255 - sqy;
+    const int sqsize = mFrameWidth / 10;
+    drawSquare(sqx * sqsize / 32, sqy * sqsize / 32, (sqsize * 5) >> 1,
+               (mCcounter & 0x100) ? &mRedYUV : &mGreenYUV);
+    mCcounter++;
+}
+
+void EmulatedFakeCameraDevice::drawSquare(int x,
+                                          int y,
+                                          int size,
+                                          const YUVPixel* color)
+{
+    const int square_xstop = min(mFrameWidth, x + size);
+    const int square_ystop = min(mFrameHeight, y + size);
+    uint8_t* Y_pos = mCurrentFrame + y * mFrameWidth + x;
+
+    YUVPixel adjustedColor = *color;
+    changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V);
+
+    // Draw the square.
+    for (; y < square_ystop; y++) {
+        const int iUV = (y / 2) * mUVInRow + (x / 2) * mUVStep;
+        uint8_t* sqU = mFrameU + iUV;
+        uint8_t* sqV = mFrameV + iUV;
+        uint8_t* sqY = Y_pos;
+        for (int i = x; i < square_xstop; i += 2) {
+            adjustedColor.get(sqY, sqU, sqV);
+            *sqY = changeExposure(*sqY);
+            sqY[1] = *sqY;
+            sqY += 2; sqU += mUVStep; sqV += mUVStep;
+        }
+        Y_pos += mFrameWidth;
+    }
+}
+
+#if EFCD_ROTATE_FRAME
+
+void EmulatedFakeCameraDevice::drawSolid(YUVPixel* color)
+{
+    YUVPixel adjustedColor = *color;
+    changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V);
+
+    /* All Ys are the same. */
+    memset(mCurrentFrame, changeExposure(adjustedColor.Y), mTotalPixels);
+
+    /* Fill U, and V panes. */
+    uint8_t* U = mFrameU;
+    uint8_t* V = mFrameV;
+    for (int k = 0; k < mUVTotalNum; k++, U += mUVStep, V += mUVStep) {
+        *U = color->U;
+        *V = color->V;
+    }
+}
+
+void EmulatedFakeCameraDevice::drawStripes()
+{
+    /* Divide frame into 4 stripes. */
+    const int change_color_at = mFrameHeight / 4;
+    const int each_in_row = mUVInRow / mUVStep;
+    uint8_t* pY = mCurrentFrame;
+    for (int y = 0; y < mFrameHeight; y++, pY += mFrameWidth) {
+        /* Select the color. */
+        YUVPixel* color;
+        const int color_index = y / change_color_at;
+        if (color_index == 0) {
+            /* White stripe on top. */
+            color = &mWhiteYUV;
+        } else if (color_index == 1) {
+            /* Then the red stripe. */
+            color = &mRedYUV;
+        } else if (color_index == 2) {
+            /* Then the green stripe. */
+            color = &mGreenYUV;
+        } else {
+            /* And the blue stripe at the bottom. */
+            color = &mBlueYUV;
+        }
+        changeWhiteBalance(color->Y, color->U, color->V);
+
+        /* All Ys at the row are the same. */
+        memset(pY, changeExposure(color->Y), mFrameWidth);
+
+        /* Offset of the current row inside U/V panes. */
+        const int uv_off = (y / 2) * mUVInRow;
+        /* Fill U, and V panes. */
+        uint8_t* U = mFrameU + uv_off;
+        uint8_t* V = mFrameV + uv_off;
+        for (int k = 0; k < each_in_row; k++, U += mUVStep, V += mUVStep) {
+            *U = color->U;
+            *V = color->V;
+        }
+    }
+}
+
+int EmulatedFakeCameraDevice::rotateFrame()
+{
+    if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRotatedAt) >= mRotateFreq) {
+        mLastRotatedAt = systemTime(SYSTEM_TIME_MONOTONIC);
+        mCurrentFrameType++;
+        if (mCurrentFrameType > 2) {
+            mCurrentFrameType = 0;
+        }
+        if (mCurrentFrameType == 2) {
+            ALOGD("********** Rotated to the SOLID COLOR frame **********");
+            /* Solid color: lets rotate color too. */
+            if (mCurrentColor == &mWhiteYUV) {
+                ALOGD("----- Painting a solid RED frame -----");
+                mCurrentColor = &mRedYUV;
+            } else if (mCurrentColor == &mRedYUV) {
+                ALOGD("----- Painting a solid GREEN frame -----");
+                mCurrentColor = &mGreenYUV;
+            } else if (mCurrentColor == &mGreenYUV) {
+                ALOGD("----- Painting a solid BLUE frame -----");
+                mCurrentColor = &mBlueYUV;
+            } else {
+                /* Back to white. */
+                ALOGD("----- Painting a solid WHITE frame -----");
+                mCurrentColor = &mWhiteYUV;
+            }
+        } else if (mCurrentFrameType == 0) {
+            ALOGD("********** Rotated to the CHECKERBOARD frame **********");
+        } else if (mCurrentFrameType == 1) {
+            ALOGD("********** Rotated to the STRIPED frame **********");
+        }
+    }
+
+    return mCurrentFrameType;
+}
+
+#endif  // EFCD_ROTATE_FRAME
+
+}; /* namespace android */
diff --git a/guest/hals/camera/EmulatedFakeCameraDevice.h b/guest/hals/camera/EmulatedFakeCameraDevice.h
new file mode 100755
index 0000000..a02d301
--- /dev/null
+++ b/guest/hals/camera/EmulatedFakeCameraDevice.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_DEVICE_H
+#define HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_DEVICE_H
+
+/*
+ * Contains declaration of a class EmulatedFakeCameraDevice that encapsulates
+ * a fake camera device.
+ */
+
+#include "Converters.h"
+#include "EmulatedCameraDevice.h"
+
+/* This is used for debugging format / conversion issues. If EFCD_ROTATE_FRAME is
+ * set to 0, the frame content will be always the "checkerboard". Otherwise, if
+ * EFCD_ROTATE_FRAME is set to a non-zero value, the frame content will "rotate"
+ * from a "checkerboard" frame to a "white/red/green/blue stripes" frame, to a
+ * "white/red/green/blue" frame. Frame content rotation helps finding bugs in
+ * format conversions.
+ */
+#define EFCD_ROTATE_FRAME   0
+
+namespace android {
+
+class EmulatedFakeCamera;
+
+/* Encapsulates a fake camera device.
+ * Fake camera device emulates a camera device by providing frames containing
+ * a black and white checker board, moving diagonally towards the 0,0 corner.
+ * There is also a green, or red square that bounces inside the frame, changing
+ * its color when bouncing off the 0,0 corner.
+ */
+class EmulatedFakeCameraDevice : public EmulatedCameraDevice {
+public:
+    /* Constructs EmulatedFakeCameraDevice instance. */
+    explicit EmulatedFakeCameraDevice(EmulatedFakeCamera* camera_hal);
+
+    /* Destructs EmulatedFakeCameraDevice instance. */
+    ~EmulatedFakeCameraDevice();
+
+    /***************************************************************************
+     * Emulated camera device abstract interface implementation.
+     * See declarations of these methods in EmulatedCameraDevice class for
+     * information on each of these methods.
+     **************************************************************************/
+
+public:
+    /* Connects to the camera device.
+     * Since there is no real device to connect to, this method does nothing,
+     * but changes the state.
+     */
+    status_t connectDevice();
+
+    /* Disconnects from the camera device.
+     * Since there is no real device to disconnect from, this method does
+     * nothing, but changes the state.
+     */
+    status_t disconnectDevice();
+
+    /* Starts the camera device. */
+    status_t startDevice(int width, int height, uint32_t pix_fmt, int fps);
+
+    /* Stops the camera device. */
+    status_t stopDevice();
+
+    /* Gets current preview fame into provided buffer. */
+    status_t getPreviewFrame(void* buffer) {
+      return OK;
+    }
+
+    /***************************************************************************
+     * Worker thread management overrides.
+     * See declarations of these methods in EmulatedCameraDevice class for
+     * information on each of these methods.
+     **************************************************************************/
+
+protected:
+    /* Implementation of the worker thread routine.
+     * This method simply sleeps for a period of time defined by the FPS property
+     * of the fake camera (simulating frame frequency), and then calls emulated
+     * camera's onNextFrameAvailable method.
+     */
+    bool inWorkerThread();
+
+    /****************************************************************************
+     * Fake camera device private API
+     ***************************************************************************/
+
+private:
+
+    /* Draws a black and white checker board in the current frame buffer. */
+    void drawCheckerboard();
+
+    /* Draws a square of the given color in the current frame buffer.
+     * Param:
+     *  x, y - Coordinates of the top left corner of the square in the buffer.
+     *  size - Size of the square's side.
+     *  color - Square's color.
+     */
+    void drawSquare(int x, int y, int size, const YUVPixel* color);
+
+#if EFCD_ROTATE_FRAME
+    void drawSolid(YUVPixel* color);
+    void drawStripes();
+    int rotateFrame();
+#endif  // EFCD_ROTATE_FRAME
+
+    /****************************************************************************
+     * Fake camera device data members
+     ***************************************************************************/
+
+private:
+    /*
+     * Pixel colors in YUV format used when drawing the checker board.
+     */
+
+    YUVPixel    mBlackYUV;
+    YUVPixel    mWhiteYUV;
+    YUVPixel    mRedYUV;
+    YUVPixel    mGreenYUV;
+    YUVPixel    mBlueYUV;
+
+    /* Last time the frame has been redrawn. */
+    nsecs_t     mLastRedrawn;
+
+    /*
+     * Precalculated values related to U/V panes.
+     */
+
+    /* U pane inside the framebuffer. */
+    uint8_t*    mFrameU;
+
+    /* V pane inside the framebuffer. */
+    uint8_t*    mFrameV;
+
+    /* Defines byte distance between adjacent U, and V values. */
+    int         mUVStep;
+
+    /* Defines number of Us and Vs in a row inside the U/V panes.
+     * Note that if U/V panes are interleaved, this value reflects the total
+     * number of both, Us and Vs in a single row in the interleaved UV pane. */
+    int         mUVInRow;
+
+    /* Total number of each, U, and V elements in the framebuffer. */
+    int         mUVTotalNum;
+
+    /*
+     * Checkerboard drawing related stuff
+     */
+
+    int         mCheckX;
+    int         mCheckY;
+    int         mCcounter;
+
+    /* Defines time (in nanoseconds) between redrawing the checker board.
+     * We will redraw the checker board every 15 milliseconds. */
+    static const nsecs_t    mRedrawAfter = 15000000LL;
+
+#if EFCD_ROTATE_FRAME
+    /* Frame rotation frequency in nanosec (currently - 3 sec) */
+    static const nsecs_t    mRotateFreq = 3000000000LL;
+
+    /* Last time the frame has rotated. */
+    nsecs_t     mLastRotatedAt;
+
+    /* Type of the frame to display in the current rotation:
+     *  0 - Checkerboard.
+     *  1 - White/Red/Green/Blue horisontal stripes
+     *  2 - Solid color. */
+    int         mCurrentFrameType;
+
+    /* Color to use to paint the solid color frame. Colors will rotate between
+     * white, red, gree, and blue each time rotation comes to the solid color
+     * frame. */
+    YUVPixel*   mCurrentColor;
+#endif  // EFCD_ROTATE_FRAME
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_FAKE_CAMERA_DEVICE_H */
diff --git a/guest/hals/camera/EmulatedQemuCamera.cpp b/guest/hals/camera/EmulatedQemuCamera.cpp
new file mode 100755
index 0000000..af1e324
--- /dev/null
+++ b/guest/hals/camera/EmulatedQemuCamera.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedQemuCamera that encapsulates
+ * functionality of an emulated camera connected to the host.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_QemuCamera"
+#include <cutils/log.h>
+#include "EmulatedQemuCamera.h"
+#include "EmulatedCameraFactory.h"
+
+namespace android {
+
+EmulatedQemuCamera::EmulatedQemuCamera(int cameraId, struct hw_module_t* module)
+        : EmulatedCamera(cameraId, module),
+          mQemuCameraDevice(this)
+{
+}
+
+EmulatedQemuCamera::~EmulatedQemuCamera()
+{
+}
+
+/****************************************************************************
+ * EmulatedCamera virtual overrides.
+ ***************************************************************************/
+
+status_t EmulatedQemuCamera::Initialize(const char* device_name,
+                                        const char* frame_dims,
+                                        const char* facing_dir)
+{
+    ALOGV("%s:\n   Name=%s\n   Facing '%s'\n   Dimensions=%s",
+         __FUNCTION__, device_name, facing_dir, frame_dims);
+    /* Save dimensions. */
+    mFrameDims = frame_dims;
+
+    /* Initialize camera device. */
+    status_t res = mQemuCameraDevice.Initialize(device_name);
+    if (res != NO_ERROR) {
+        return res;
+    }
+
+    /* Initialize base class. */
+    res = EmulatedCamera::Initialize();
+    if (res != NO_ERROR) {
+        return res;
+    }
+
+    /*
+     * Set customizable parameters.
+     */
+
+    mParameters.set(EmulatedCamera::FACING_KEY, facing_dir);
+    mParameters.set(EmulatedCamera::ORIENTATION_KEY,
+                    gEmulatedCameraFactory.getQemuCameraOrientation());
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, frame_dims);
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, frame_dims);
+
+    /*
+     * Use first dimension reported by the device to set current preview and
+     * picture sizes.
+     */
+
+    char first_dim[128];
+    /* Dimensions are separated with ',' */
+    const char* c = strchr(frame_dims, ',');
+    if (c == NULL) {
+        strncpy(first_dim, frame_dims, sizeof(first_dim));
+        first_dim[sizeof(first_dim)-1] = '\0';
+    } else if (static_cast<size_t>(c - frame_dims) < sizeof(first_dim)) {
+        memcpy(first_dim, frame_dims, c - frame_dims);
+        first_dim[c - frame_dims] = '\0';
+    } else {
+        memcpy(first_dim, frame_dims, sizeof(first_dim));
+        first_dim[sizeof(first_dim)-1] = '\0';
+    }
+
+    /* Width and height are separated with 'x' */
+    char* sep = strchr(first_dim, 'x');
+    if (sep == NULL) {
+        ALOGE("%s: Invalid first dimension format in %s",
+             __FUNCTION__, frame_dims);
+        return EINVAL;
+    }
+
+    *sep = '\0';
+    const int x = atoi(first_dim);
+    const int y = atoi(sep + 1);
+    mParameters.setPreviewSize(x, y);
+    mParameters.setPictureSize(x, y);
+
+    ALOGV("%s: Qemu camera %s is initialized. Current frame is %dx%d",
+         __FUNCTION__, device_name, x, y);
+
+    return NO_ERROR;
+}
+
+EmulatedCameraDevice* EmulatedQemuCamera::getCameraDevice()
+{
+    return &mQemuCameraDevice;
+}
+
+};  /* namespace android */
diff --git a/guest/hals/camera/EmulatedQemuCamera.h b/guest/hals/camera/EmulatedQemuCamera.h
new file mode 100755
index 0000000..1b826c7
--- /dev/null
+++ b/guest/hals/camera/EmulatedQemuCamera.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_H
+#define HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_H
+
+/*
+ * Contains declaration of a class EmulatedQemuCamera that encapsulates
+ * functionality of an emulated camera connected to the host.
+ */
+
+#include "EmulatedCamera.h"
+#include "EmulatedQemuCameraDevice.h"
+
+namespace android {
+
+/* Encapsulates functionality of an emulated camera connected to the host.
+ */
+class EmulatedQemuCamera : public EmulatedCamera {
+public:
+    /* Constructs EmulatedQemuCamera instance. */
+    EmulatedQemuCamera(int cameraId, struct hw_module_t* module);
+
+    /* Destructs EmulatedQemuCamera instance. */
+    ~EmulatedQemuCamera();
+
+    /***************************************************************************
+     * EmulatedCamera virtual overrides.
+     **************************************************************************/
+
+public:
+    /* Initializes EmulatedQemuCamera instance. */
+     status_t Initialize(const char* device_name,
+                         const char* frame_dims,
+                         const char* facing_dir);
+
+    /***************************************************************************
+     * EmulatedCamera abstract API implementation.
+     **************************************************************************/
+
+protected:
+    /* Gets emulated camera device ised by this instance of the emulated camera.
+     */
+    EmulatedCameraDevice* getCameraDevice();
+
+    /***************************************************************************
+     * Data memebers.
+     **************************************************************************/
+
+protected:
+    /* Contained qemu camera device object. */
+    EmulatedQemuCameraDevice    mQemuCameraDevice;
+
+    /* Supported frame dimensions reported by the camera device. */
+    String8                     mFrameDims;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_H */
diff --git a/guest/hals/camera/EmulatedQemuCamera2.cpp b/guest/hals/camera/EmulatedQemuCamera2.cpp
new file mode 100644
index 0000000..2c94f0e
--- /dev/null
+++ b/guest/hals/camera/EmulatedQemuCamera2.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedQemuCamera2 that encapsulates
+ * functionality of a host webcam with further processing to simulate the
+ * capabilities of a v2 camera device.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_QemuCamera2"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include "EmulatedQemuCamera2.h"
+#include "EmulatedCameraFactory.h"
+
+namespace android {
+
+EmulatedQemuCamera2::EmulatedQemuCamera2(int cameraId,
+        bool facingBack,
+        struct hw_module_t* module)
+        : EmulatedCamera2(cameraId,module),
+          mFacingBack(facingBack)
+{
+    ALOGD("Constructing emulated qemu camera 2 facing %s",
+            facingBack ? "back" : "front");
+}
+
+EmulatedQemuCamera2::~EmulatedQemuCamera2()
+{
+}
+
+/****************************************************************************
+ * Public API overrides
+ ***************************************************************************/
+
+status_t EmulatedQemuCamera2::Initialize()
+{
+    return NO_ERROR;
+}
+
+};  /* namespace android */
diff --git a/guest/hals/camera/EmulatedQemuCamera2.h b/guest/hals/camera/EmulatedQemuCamera2.h
new file mode 100644
index 0000000..520ccce
--- /dev/null
+++ b/guest/hals/camera/EmulatedQemuCamera2.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA2_H
+#define HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA2_H
+
+/*
+ * Contains declaration of a class EmulatedQemuCamera2 that encapsulates
+ * functionality of a host webcam with added processing to implement version 2
+ * of the camera device interface.
+ */
+
+#include "EmulatedCamera2.h"
+
+namespace android {
+
+/* Encapsulates functionality of an advanced fake camera based on real host camera data.
+ */
+class EmulatedQemuCamera2 : public EmulatedCamera2 {
+public:
+    /* Constructs EmulatedFakeCamera instance. */
+    EmulatedQemuCamera2(int cameraId, bool facingBack, struct hw_module_t* module);
+
+    /* Destructs EmulatedFakeCamera instance. */
+    ~EmulatedQemuCamera2();
+
+    /****************************************************************************
+     * EmulatedCamera2 virtual overrides.
+     ***************************************************************************/
+
+public:
+    /* Initializes EmulatedQemuCamera2 instance. */
+     status_t Initialize();
+
+    /****************************************************************************
+     * EmulatedCamera abstract API implementation.
+     ***************************************************************************/
+
+protected:
+
+    /****************************************************************************
+     * Data memebers.
+     ***************************************************************************/
+
+protected:
+    /* Facing back (true) or front (false) switch. */
+    bool                        mFacingBack;
+
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA2_H */
diff --git a/guest/hals/camera/EmulatedQemuCameraDevice.cpp b/guest/hals/camera/EmulatedQemuCameraDevice.cpp
new file mode 100755
index 0000000..ef5406b
--- /dev/null
+++ b/guest/hals/camera/EmulatedQemuCameraDevice.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of a class EmulatedQemuCameraDevice that encapsulates
+ * an emulated camera device connected to the host.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_QemuDevice"
+#include <cutils/log.h>
+#include "EmulatedQemuCamera.h"
+#include "EmulatedQemuCameraDevice.h"
+
+namespace android {
+
+EmulatedQemuCameraDevice::EmulatedQemuCameraDevice(EmulatedQemuCamera* camera_hal)
+    : EmulatedCameraDevice(camera_hal),
+      mQemuClient(),
+      mPreviewFrame(NULL)
+{
+}
+
+EmulatedQemuCameraDevice::~EmulatedQemuCameraDevice()
+{
+    if (mPreviewFrame != NULL) {
+        delete[] mPreviewFrame;
+    }
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+status_t EmulatedQemuCameraDevice::Initialize(const char* device_name)
+{
+    /* Connect to the service. */
+    char connect_str[256];
+    snprintf(connect_str, sizeof(connect_str), "name=%s", device_name);
+    status_t res = mQemuClient.connectClient(connect_str);
+    if (res != NO_ERROR) {
+        return res;
+    }
+
+    /* Initialize base class. */
+    res = EmulatedCameraDevice::Initialize();
+    if (res == NO_ERROR) {
+        ALOGV("%s: Connected to the emulated camera service '%s'",
+             __FUNCTION__, device_name);
+        mDeviceName = device_name;
+    } else {
+        mQemuClient.queryDisconnect();
+    }
+
+    return res;
+}
+
+/****************************************************************************
+ * Emulated camera device abstract interface implementation.
+ ***************************************************************************/
+
+status_t EmulatedQemuCameraDevice::connectDevice()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isInitialized()) {
+        ALOGE("%s: Qemu camera device is not initialized.", __FUNCTION__);
+        return EINVAL;
+    }
+    if (isConnected()) {
+        ALOGW("%s: Qemu camera device '%s' is already connected.",
+             __FUNCTION__, (const char*)mDeviceName);
+        return NO_ERROR;
+    }
+
+    /* Connect to the camera device via emulator. */
+    const status_t res = mQemuClient.queryConnect();
+    if (res == NO_ERROR) {
+        ALOGV("%s: Connected to device '%s'",
+             __FUNCTION__, (const char*)mDeviceName);
+        mState = ECDS_CONNECTED;
+    } else {
+        ALOGE("%s: Connection to device '%s' failed",
+             __FUNCTION__, (const char*)mDeviceName);
+    }
+
+    return res;
+}
+
+status_t EmulatedQemuCameraDevice::disconnectDevice()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isConnected()) {
+        ALOGW("%s: Qemu camera device '%s' is already disconnected.",
+             __FUNCTION__, (const char*)mDeviceName);
+        return NO_ERROR;
+    }
+    if (isStarted()) {
+        ALOGE("%s: Cannot disconnect from the started device '%s.",
+             __FUNCTION__, (const char*)mDeviceName);
+        return EINVAL;
+    }
+
+    /* Disconnect from the camera device via emulator. */
+    const status_t res = mQemuClient.queryDisconnect();
+    if (res == NO_ERROR) {
+        ALOGV("%s: Disonnected from device '%s'",
+             __FUNCTION__, (const char*)mDeviceName);
+        mState = ECDS_INITIALIZED;
+    } else {
+        ALOGE("%s: Disconnection from device '%s' failed",
+             __FUNCTION__, (const char*)mDeviceName);
+    }
+
+    return res;
+}
+
+status_t EmulatedQemuCameraDevice::startDevice(int width,
+                                               int height,
+                                               uint32_t pix_fmt,
+                                               int fps)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isConnected()) {
+        ALOGE("%s: Qemu camera device '%s' is not connected.",
+             __FUNCTION__, (const char*)mDeviceName);
+        return EINVAL;
+    }
+    if (isStarted()) {
+        ALOGW("%s: Qemu camera device '%s' is already started.",
+             __FUNCTION__, (const char*)mDeviceName);
+        return NO_ERROR;
+    }
+
+    status_t res = EmulatedCameraDevice::commonStartDevice(
+        width, height, pix_fmt, fps);
+    if (res != NO_ERROR) {
+        ALOGE("%s: commonStartDevice failed", __FUNCTION__);
+        return res;
+    }
+
+    /* Allocate preview frame buffer. */
+    /* TODO: Watch out for preview format changes! At this point we implement
+     * RGB32 only.*/
+    mPreviewFrame = new uint32_t[mTotalPixels];
+    if (mPreviewFrame == NULL) {
+        ALOGE("%s: Unable to allocate %d bytes for preview frame",
+             __FUNCTION__, mTotalPixels);
+        return ENOMEM;
+    }
+
+    /* Start the actual camera device. */
+    res = mQemuClient.queryStart(mPixelFormat, mFrameWidth, mFrameHeight);
+    if (res == NO_ERROR) {
+        ALOGV("%s: Qemu camera device '%s' is started for %.4s[%dx%d] frames",
+             __FUNCTION__, (const char*)mDeviceName,
+             reinterpret_cast<const char*>(&mPixelFormat),
+             mFrameWidth, mFrameHeight);
+        mState = ECDS_STARTED;
+    } else {
+        ALOGE("%s: Unable to start device '%s' for %.4s[%dx%d] frames",
+             __FUNCTION__, (const char*)mDeviceName,
+             reinterpret_cast<const char*>(&pix_fmt), width, height);
+    }
+
+    return res;
+}
+
+status_t EmulatedQemuCameraDevice::stopDevice()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isStarted()) {
+        ALOGW("%s: Qemu camera device '%s' is not started.",
+             __FUNCTION__, (const char*)mDeviceName);
+        return NO_ERROR;
+    }
+
+    /* Stop the actual camera device. */
+    status_t res = mQemuClient.queryStop();
+    if (res == NO_ERROR) {
+        if (mPreviewFrame == NULL) {
+            delete[] mPreviewFrame;
+            mPreviewFrame = NULL;
+        }
+        EmulatedCameraDevice::commonStopDevice();
+        mState = ECDS_CONNECTED;
+        ALOGV("%s: Qemu camera device '%s' is stopped",
+             __FUNCTION__, (const char*)mDeviceName);
+    } else {
+        ALOGE("%s: Unable to stop device '%s'",
+             __FUNCTION__, (const char*)mDeviceName);
+    }
+
+    return res;
+}
+
+/****************************************************************************
+ * EmulatedCameraDevice virtual overrides
+ ***************************************************************************/
+
+status_t EmulatedQemuCameraDevice::getCurrentPreviewFrame(void* buffer)
+{
+    ALOGW_IF(mPreviewFrame == NULL, "%s: No preview frame", __FUNCTION__);
+    if (mPreviewFrame != NULL) {
+        memcpy(buffer, mPreviewFrame, mTotalPixels * 4);
+        return 0;
+    } else {
+        return EmulatedCameraDevice::getCurrentPreviewFrame(buffer);
+    }
+}
+
+/****************************************************************************
+ * Worker thread management overrides.
+ ***************************************************************************/
+
+bool EmulatedQemuCameraDevice::inWorkerThread()
+{
+    /* Wait till FPS timeout expires, or thread exit message is received. */
+    WorkerThread::SelectRes res =
+        getWorkerThread()->Select(-1, 1000000 / mEmulatedFPS);
+    if (res == WorkerThread::EXIT_THREAD) {
+        ALOGV("%s: Worker thread has been terminated.", __FUNCTION__);
+        return false;
+    }
+
+    /* Query frames from the service. */
+    status_t query_res = mQemuClient.queryFrame(mCurrentFrame, mPreviewFrame,
+                                                 mFrameBufferSize,
+                                                 mTotalPixels * 4,
+                                                 mWhiteBalanceScale[0],
+                                                 mWhiteBalanceScale[1],
+                                                 mWhiteBalanceScale[2],
+                                                 mExposureCompensation);
+    if (query_res == NO_ERROR) {
+        /* Timestamp the current frame, and notify the camera HAL. */
+        mCurFrameTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+        mCameraHAL->onNextFrameAvailable(mCurrentFrame, mCurFrameTimestamp, this);
+        return true;
+    } else {
+        ALOGE("%s: Unable to get current video frame: %s",
+             __FUNCTION__, strerror(query_res));
+        mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED);
+        return false;
+    }
+}
+
+}; /* namespace android */
diff --git a/guest/hals/camera/EmulatedQemuCameraDevice.h b/guest/hals/camera/EmulatedQemuCameraDevice.h
new file mode 100755
index 0000000..6664f44
--- /dev/null
+++ b/guest/hals/camera/EmulatedQemuCameraDevice.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_DEVICE_H
+#define HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_DEVICE_H
+
+/*
+ * Contains declaration of a class EmulatedQemuCameraDevice that encapsulates
+ * an emulated camera device connected to the host.
+ */
+
+#include "EmulatedCameraDevice.h"
+#include "QemuClient.h"
+
+namespace android {
+
+class EmulatedQemuCamera;
+
+/* Encapsulates an emulated camera device connected to the host.
+ */
+class EmulatedQemuCameraDevice : public EmulatedCameraDevice {
+public:
+    /* Constructs EmulatedQemuCameraDevice instance. */
+    explicit EmulatedQemuCameraDevice(EmulatedQemuCamera* camera_hal);
+
+    /* Destructs EmulatedQemuCameraDevice instance. */
+    ~EmulatedQemuCameraDevice();
+
+    /***************************************************************************
+     * Public API
+     **************************************************************************/
+
+public:
+    /* Initializes EmulatedQemuCameraDevice instance.
+     * Param:
+     *  device_name - Name of the camera device connected to the host. The name
+     *      that is used here must have been reported by the 'factory' camera
+     *      service when it listed camera devices connected to the host.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    status_t Initialize(const char* device_name);
+
+    /***************************************************************************
+     * Emulated camera device abstract interface implementation.
+     * See declarations of these methods in EmulatedCameraDevice class for
+     * information on each of these methods.
+     **************************************************************************/
+
+public:
+    /* Connects to the camera device. */
+    status_t connectDevice();
+
+    /* Disconnects from the camera device. */
+    status_t disconnectDevice();
+
+    /* Starts capturing frames from the camera device. */
+    status_t startDevice(int width, int height, uint32_t pix_fmt, int fps);
+
+    /* Stops capturing frames from the camera device. */
+    status_t stopDevice();
+
+    /***************************************************************************
+     * EmulatedCameraDevice virtual overrides
+     * See declarations of these methods in EmulatedCameraDevice class for
+     * information on each of these methods.
+     **************************************************************************/
+
+public:
+    /* Gets current preview fame into provided buffer.
+     * We override this method in order to provide preview frames cached in this
+     * object.
+     */
+    status_t getCurrentPreviewFrame(void* buffer);
+
+    /***************************************************************************
+     * Worker thread management overrides.
+     * See declarations of these methods in EmulatedCameraDevice class for
+     * information on each of these methods.
+     **************************************************************************/
+
+protected:
+    /* Implementation of the worker thread routine. */
+    bool inWorkerThread();
+
+    /***************************************************************************
+     * Qemu camera device data members
+     **************************************************************************/
+
+private:
+    /* Qemu client that is used to communicate with the 'emulated camera'
+     * service, created for this instance in the emulator. */
+    CameraQemuClient    mQemuClient;
+
+    /* Name of the camera device connected to the host. */
+    String8             mDeviceName;
+
+    /* Current preview framebuffer. */
+    uint32_t*           mPreviewFrame;
+
+    /* Emulated FPS (frames per second).
+     * We will emulate 50 FPS. */
+    static const int    mEmulatedFPS = 50;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_DEVICE_H */
diff --git a/guest/hals/camera/ExifMetadataBuilder.cpp b/guest/hals/camera/ExifMetadataBuilder.cpp
new file mode 100644
index 0000000..71eaebc
--- /dev/null
+++ b/guest/hals/camera/ExifMetadataBuilder.cpp
@@ -0,0 +1,550 @@
+#include "ExifMetadataBuilder.h"
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "ExifMetadataBuilder"
+#include <cutils/log.h>
+
+#include <cmath>
+#include <stdlib.h>
+
+namespace android {
+// All supported EXIF data types.
+enum ExifDataType {
+  ExifUInt8     = 1,
+  ExifString    = 2,
+  ExifUInt16    = 3,
+  ExifUInt32    = 4,
+  ExifRational  = 5,
+  ExifUndefined = 7,
+  ExifSInt16    = 8,
+  ExifSInt32    = 9,
+  ExifFloat     = 11,
+  ExifDouble    = 12,
+};
+
+enum ExifTagId {
+  kExifTagGpsLatitudeRef = 0x1,
+  kExifTagGpsLatitude = 0x2,
+  kExifTagGpsLongitudeRef = 0x3,
+  kExifTagGpsLongitude = 0x4,
+  kExifTagGpsAltitudeRef = 0x5,
+  kExifTagGpsAltitude = 0x6,
+  kExifTagGpsTimestamp = 0x7,
+  kExifTagGpsProcessingMethod = 0x1b,
+  kExifTagGpsDatestamp = 0x1d,
+  kExifTagImageWidth = 0x100,
+  kExifTagImageHeight = 0x101,
+  kExifTagImageDateTime = 0x132,
+  kExifTagJpegData = 0x201,
+  kExifTagJpegLength = 0x202,
+  kExifTagCameraSubIFD = 0x8769,
+  kExifTagGpsSubIFD = 0x8825,
+  kExifTagCameraFocalLength = 0x920a,
+};
+
+const char kExifCharArrayAscii[8] = "ASCII";
+const char kExifCharArrayUnicode[8] = "UNICODE";
+
+// Structure of an individual EXIF tag.
+struct ExifTagInfo {
+  uint16_t tag;    // Describes unique tag type.
+  uint16_t type;   // Describes data type (see ExifDataType).
+  uint32_t count;  // Number of data elements.
+  uint32_t value;  // Value (for shorter items) or offset (for longer).
+};
+
+// Interface describing individual EXIF tag.
+class ExifTag {
+ public:
+  virtual ~ExifTag() {}
+  virtual size_t DataSize() { return 0; }
+  virtual void AppendTag(ExifTagInfo* info, size_t data_offset) = 0;
+  virtual void AppendData(uint8_t* target) {}
+};
+
+// EXIF structure.
+// This structure describes all types of tags:
+// - Main image,
+// - Thumbnail image,
+// - Sub-IFDs.
+class ExifStructure {
+ public:
+  typedef std::vector<ExifTag*> TagMap;
+
+  ExifStructure() {}
+
+  ~ExifStructure() {
+    for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
+      delete *it;
+    }
+  }
+
+  size_t TagSize() {
+    // Target tag structure:
+    // - uint16_t: mTags.size();
+    // - ExifTagInfo[mTags.size()]
+    // - uint32_t: next_structure_available ? self_offset + Size() : NULL
+    return sizeof(uint16_t)                       // mTags.size()
+           + mTags.size() * sizeof(ExifTagInfo)   // [tags]
+           + sizeof(uint32_t);                    // next offset
+  }
+
+  size_t DataSize() {
+    size_t data_size = 0;
+    for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
+      data_size += (*it)->DataSize();
+    }
+    return data_size;
+  }
+
+  size_t Size() {
+    return TagSize() + DataSize();
+  }
+
+  uint32_t Build(
+      uint8_t* buffer,
+      const uint32_t self_offset,
+      const bool next_structure_available) {
+    // Write number of items.
+    uint16_t num_elements = mTags.size();
+    memcpy(buffer, &num_elements, sizeof(num_elements));
+    buffer += sizeof(num_elements);
+
+    // Offset describes where tag data will be placed.
+    uint32_t offset = self_offset + TagSize();
+
+    // Combine EXIF for main image.
+    for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
+      // Each tag is exactly 12 bytes long, but data length can be anything.
+      // We supply the data offset to anyone who wants to use data.
+      (*it)->AppendTag(reinterpret_cast<ExifTagInfo*>(buffer), offset);
+      offset += (*it)->DataSize();
+      buffer += sizeof(ExifTagInfo);
+    }
+
+    // Append information about the second tag offset.
+    // |offset| holds exactly the right position:
+    // self_offset + TagSize() + DataSize().
+    uint32_t next_tag_offset = next_structure_available ? offset : 0;
+    memcpy(buffer, &next_tag_offset, sizeof(next_tag_offset));
+    buffer += sizeof(next_tag_offset);
+
+    // Combine EXIF data for main image.
+    for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
+      (*it)->AppendData(buffer);
+      buffer += (*it)->DataSize();
+    }
+
+    return offset;
+  }
+
+  void PushTag(ExifTag* tag) {
+    mTags.push_back(tag);
+  }
+
+ private:
+  TagMap mTags;
+};
+
+
+// EXIF tags.
+namespace {
+// Tag with 8-bit unsigned integer.
+class ExifUInt8Tag : public ExifTag {
+ public:
+  ExifUInt8Tag(uint16_t tag, size_t value) : mTag(tag), mValue(value) {}
+
+  void AppendTag(ExifTagInfo* info, size_t data_offset) {
+    info->tag = mTag;
+    info->type = ExifUInt8;
+    info->count = 1;
+    info->value = mValue << 24;
+  }
+
+ private:
+  uint16_t mTag;
+  uint32_t mValue;
+};
+
+// Tag with 32-bit unsigned integer.
+class ExifUInt32Tag : public ExifTag {
+ public:
+  ExifUInt32Tag(uint16_t tag, size_t value) : mTag(tag), mValue(value) {}
+
+  void AppendTag(ExifTagInfo* info, size_t data_offset) {
+    info->tag = mTag;
+    info->type = ExifUInt32;
+    info->count = 1;
+    info->value = mValue;
+  }
+
+ private:
+  uint16_t mTag;
+  uint32_t mValue;
+};
+
+// Char array tag.
+class ExifCharArrayTag : public ExifTag {
+ public:
+  ExifCharArrayTag(uint16_t tag, const char (&type)[8], const std::string& str)
+      : mTag(tag),
+        mType(type),
+        mString(str) {}
+
+  void AppendTag(ExifTagInfo* info, size_t data_offset) {
+    info->tag = mTag;
+    info->type = ExifUndefined;
+    info->count = DataSize();
+    info->value = data_offset;
+  }
+
+  size_t DataSize() {
+    return sizeof(mType) + mString.size();
+  }
+
+  void AppendData(uint8_t* data) {
+    memcpy(data, mType, sizeof(mType));
+    data += sizeof(mType);
+    memcpy(data, mString.data(), mString.size());
+  }
+
+ private:
+  uint16_t mTag;
+  const char (&mType)[8];
+  std::string mString;
+};
+
+// Data tag; writes LONG (pointer) and appends data.
+class ExifPointerTag : public ExifTag {
+ public:
+  ExifPointerTag(uint16_t tag, void* data, int size)
+      : mTag(tag),
+        mData(data),
+        mSize(size) {}
+
+  ~ExifPointerTag() {
+    free(mData);
+  }
+
+  void AppendTag(ExifTagInfo* info, size_t data_offset) {
+    info->tag = mTag;
+    info->type = ExifUInt32;
+    info->count = 1;
+    info->value = data_offset;
+  }
+
+  size_t DataSize() {
+    return mSize;
+  }
+
+  void AppendData(uint8_t* data) {
+    memcpy(data, mData, mSize);
+  }
+
+ private:
+  uint16_t mTag;
+  void* mData;
+  int mSize;
+};
+
+// String tag.
+class ExifStringTag : public ExifTag {
+ public:
+  ExifStringTag(uint16_t tag, const std::string& str)
+      : mTag(tag),
+        mString(str) {}
+
+  void AppendTag(ExifTagInfo* info, size_t data_offset) {
+    info->tag = mTag;
+    info->type = ExifString;
+    info->count = DataSize();
+    info->value = data_offset;
+  }
+
+  size_t DataSize() {
+    // Include padding \0.
+    return mString.size() + 1;
+  }
+
+  void AppendData(uint8_t* data) {
+    memcpy(data, mString.data(), DataSize());
+  }
+
+ private:
+  uint16_t mTag;
+  std::string mString;
+};
+
+// SubIFD: sub-tags.
+class ExifSubIfdTag : public ExifTag {
+ public:
+  ExifSubIfdTag(uint16_t tag)
+      : mTag(tag),
+        mSubStructure(new ExifStructure) {}
+
+  ~ExifSubIfdTag() {
+    delete mSubStructure;
+  }
+
+  void AppendTag(ExifTagInfo* info, size_t data_offset) {
+    info->tag = mTag;
+    info->type = ExifUInt32;
+    info->count = 1;
+    info->value = data_offset;
+    mDataOffset = data_offset;
+  }
+
+  size_t DataSize() {
+    return mSubStructure->Size();
+  }
+
+  void AppendData(uint8_t* data) {
+    mSubStructure->Build(data, mDataOffset, false);
+  }
+
+  ExifStructure* GetSubStructure() {
+    return mSubStructure;
+  }
+
+ private:
+  uint16_t mTag;
+  ExifStructure* mSubStructure;
+  size_t mDataOffset;
+};
+
+// Unsigned rational tag.
+class ExifURationalTag : public ExifTag {
+ public:
+  ExifURationalTag(uint16_t tag, double value)
+      : mTag(tag),
+        mCount(1) {
+    DoubleToRational(value,
+                     &mRationals[0].mNumerator, &mRationals[0].mDenominator);
+  }
+
+  ExifURationalTag(uint16_t tag, double value1, double value2, double value3)
+      : mTag(tag),
+        mCount(3) {
+    DoubleToRational(value1,
+                     &mRationals[0].mNumerator, &mRationals[0].mDenominator);
+    DoubleToRational(value2,
+                     &mRationals[1].mNumerator, &mRationals[1].mDenominator);
+    DoubleToRational(value3,
+                     &mRationals[2].mNumerator, &mRationals[2].mDenominator);
+  }
+
+  void DoubleToRational(double value,
+                        int32_t* numerator, int32_t* denominator) {
+    int sign = 1;
+    if (value < 0) {
+      sign = -sign;
+      value = -value;
+    }
+    // Take shortcuts. Plenty.
+    *numerator = value;
+    *denominator = 1;
+
+    // Set some (arbitrary) threshold beyond which we do not proceed.
+    while (*numerator < (1 << 24)) {
+      if (std::fabs((*numerator / *denominator) - value) <= 0.0001) break;
+      *denominator *= 10;
+      *numerator = value * (*denominator);
+    }
+    *numerator *= sign;
+  }
+
+  void AppendTag(ExifTagInfo* info, size_t data_offset) {
+    info->tag = mTag;
+    info->type = ExifRational;
+    info->count = mCount;
+    info->value = data_offset;
+  }
+
+  size_t DataSize() {
+    return sizeof(mRationals[0]) * mCount;
+  }
+
+  void AppendData(uint8_t* data) {
+    memcpy(data, &mRationals[0], DataSize());
+  }
+
+ private:
+  static const int kMaxSupportedRationals = 3;
+  uint16_t mTag;
+  uint16_t mCount;
+  struct {
+    int32_t mNumerator;
+    int32_t mDenominator;
+  } mRationals[kMaxSupportedRationals];
+};
+
+std::string ToAsciiDate(time_t time) {
+  struct tm loc;
+  char res[12]; // YYYY:MM:DD\0\0
+  localtime_r(&time, &loc);
+  strftime(res, sizeof(res), "%Y:%m:%d", &loc);
+  return res;
+}
+
+std::string ToAsciiTime(time_t time) {
+  struct tm loc;
+  char res[10]; // HH:MM:SS\0\0
+  localtime_r(&time, &loc);
+  strftime(res, sizeof(res), "%H:%M:%S", &loc);
+  return res;
+}
+
+}  // namespace
+
+ExifMetadataBuilder::ExifMetadataBuilder()
+    : mImageIfd(new ExifStructure),
+      mThumbnailIfd(new ExifStructure) {
+  // Mandatory tag: camera details.
+  ExifSubIfdTag* sub_ifd = new ExifSubIfdTag(kExifTagCameraSubIFD);
+  // Pass ownership to mImageIfd.
+  mImageIfd->PushTag(sub_ifd);
+  mCameraSubIfd = sub_ifd->GetSubStructure();
+
+  // Optional (yet, required) tag: GPS data.
+  sub_ifd = new ExifSubIfdTag(kExifTagGpsSubIFD);
+  // Pass ownership to mImageIfd.
+  mImageIfd->PushTag(sub_ifd);
+  mGpsSubIfd = sub_ifd->GetSubStructure();
+}
+
+ExifMetadataBuilder::~ExifMetadataBuilder() {
+  delete mImageIfd;
+  delete mThumbnailIfd;
+}
+
+void ExifMetadataBuilder::SetWidth(int width) {
+  mImageIfd->PushTag(new ExifUInt32Tag(kExifTagImageWidth, width));
+}
+
+void ExifMetadataBuilder::SetHeight(int height) {
+  mImageIfd->PushTag(new ExifUInt32Tag(kExifTagImageHeight, height));
+}
+
+void ExifMetadataBuilder::SetThumbnailWidth(int width) {
+  mThumbnailIfd->PushTag(new ExifUInt32Tag(kExifTagImageWidth, width));
+}
+
+void ExifMetadataBuilder::SetThumbnailHeight(int height) {
+  mThumbnailIfd->PushTag(new ExifUInt32Tag(kExifTagImageHeight, height));
+}
+
+void ExifMetadataBuilder::SetThumbnail(void* thumbnail, int size) {
+  mThumbnailIfd->PushTag(new ExifUInt32Tag(kExifTagJpegLength, size));
+  mThumbnailIfd->PushTag(new ExifPointerTag(kExifTagJpegData, thumbnail, size));
+}
+
+void ExifMetadataBuilder::SetDateTime(time_t date_time) {
+  std::string res = ToAsciiDate(date_time) + " " + ToAsciiTime(date_time);
+  mImageIfd->PushTag(new ExifStringTag(kExifTagImageDateTime, res));
+}
+
+void ExifMetadataBuilder::SetGpsLatitude(double latitude) {
+  if (latitude < 0) {
+    mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLatitudeRef, "S"));
+  } else {
+    mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLatitudeRef, "N"));
+  }
+  int degrees = latitude;
+  latitude = (latitude - degrees) * 60.;
+  int minutes = latitude;
+  latitude = (latitude - minutes) * 60.;
+  double seconds = latitude;
+  mGpsSubIfd->PushTag(new ExifURationalTag(kExifTagGpsLatitude,
+                                           degrees, minutes, seconds));
+}
+
+void ExifMetadataBuilder::SetGpsLongitude(double longitude) {
+  if (longitude < 0) {
+    mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLongitudeRef, "W"));
+  } else {
+    mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLongitudeRef, "E"));
+  }
+  int32_t degrees = longitude;
+  longitude = (longitude - degrees) * 60.;
+  int32_t minutes = longitude;
+  longitude = (longitude - minutes) * 60.;
+  double seconds = longitude;
+  mGpsSubIfd->PushTag(new ExifURationalTag(kExifTagGpsLongitude,
+                                           degrees, minutes, seconds));
+}
+
+void ExifMetadataBuilder::SetGpsAltitude(double altitude) {
+  mGpsSubIfd->PushTag(new ExifUInt8Tag(kExifTagGpsAltitudeRef, 0));
+  mGpsSubIfd->PushTag(new ExifURationalTag(kExifTagGpsAltitude, altitude));
+}
+
+void ExifMetadataBuilder::SetGpsProcessingMethod(const std::string& method) {
+  mGpsSubIfd->PushTag(new ExifCharArrayTag(
+      kExifTagGpsProcessingMethod, kExifCharArrayAscii, method));
+}
+
+void ExifMetadataBuilder::SetGpsDateTime(time_t timestamp) {
+  std::string date = ToAsciiDate(timestamp);
+  int32_t seconds = (timestamp % 60);
+  timestamp /= 60;
+  int32_t minutes = (timestamp % 60);
+  timestamp /= 60;
+  mGpsSubIfd->PushTag(new ExifURationalTag(
+      kExifTagGpsTimestamp, timestamp % 24, minutes, seconds));
+  mGpsSubIfd->PushTag(new ExifStringTag(
+      kExifTagGpsDatestamp, date));
+}
+
+void ExifMetadataBuilder::SetLensFocalLength(double length) {
+  mCameraSubIfd->PushTag(
+      new ExifURationalTag(kExifTagCameraFocalLength, length));
+}
+
+void ExifMetadataBuilder::Build() {
+  const uint8_t exif_header[] = {
+    'E', 'x', 'i', 'f', 0, 0,  // EXIF header.
+  };
+
+  const uint8_t tiff_header[] = {
+    'I', 'I', 0x2a, 0x00,      // TIFF Little endian header.
+  };
+
+  // EXIF data should be exactly this much.
+  size_t exif_size = sizeof(exif_header) + sizeof(tiff_header) +
+      sizeof(uint32_t) + // Offset of the following descriptors.
+      mImageIfd->Size() + mThumbnailIfd->Size();
+
+  const uint8_t marker[] = {
+    0xff, 0xd8, 0xff, 0xe1,          // EXIF marker.
+    uint8_t((exif_size + 2) >> 8),   // Data length (including the length field)
+    uint8_t((exif_size + 2) & 0xff),
+  };
+
+  // Reserve data for our exif info.
+  mData.Resize(sizeof(marker) + exif_size);
+  uint8_t* b = (uint8_t*)(mData.data());
+
+  // Initialize marker & headers.
+  memcpy(b, &marker, sizeof(marker));
+  b += sizeof(marker);
+  memcpy(b, &exif_header, sizeof(exif_header));
+  b += sizeof(exif_header);
+  memcpy(b, &tiff_header, sizeof(tiff_header));
+  uint32_t data_offset = sizeof(tiff_header);
+
+  // Write offset of the first IFD item.
+  uint32_t first_tag_offset = data_offset + sizeof(uint32_t);
+  memcpy(&b[data_offset], &first_tag_offset, sizeof(first_tag_offset));
+  data_offset += sizeof(first_tag_offset);
+
+  // At this moment |b| points to EXIF structure.
+  // TIFF header is part of the structure itself and explains endianness of the
+  // embedded data.
+  ALOGI("%s: Building Main image EXIF tags", __FUNCTION__);
+  data_offset = mImageIfd->Build(&b[data_offset], data_offset, true);
+  ALOGI("%s: Building Thumbnail image EXIF tags", __FUNCTION__);
+  data_offset = mThumbnailIfd->Build(&b[data_offset], data_offset, false);
+  ALOGI("%s: EXIF metadata constructed (%d bytes).", __FUNCTION__, data_offset);
+}
+
+}  // namespace android
diff --git a/guest/hals/camera/ExifMetadataBuilder.h b/guest/hals/camera/ExifMetadataBuilder.h
new file mode 100644
index 0000000..6180c45
--- /dev/null
+++ b/guest/hals/camera/ExifMetadataBuilder.h
@@ -0,0 +1,57 @@
+#ifndef EXIFMETADATAWRITER_H_
+#define EXIFMETADATAWRITER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <string>
+#include <vector>
+
+#include "common/libs/auto_resources/auto_resources.h"
+
+namespace android {
+class ExifStructure;
+
+// Simplistic EXIF metadata builder.
+// www.exif.org/Exif2-2.PDF
+//
+// TODO(ender): Revisit using libexif after we drop support for JB.
+class ExifMetadataBuilder {
+ public:
+  ExifMetadataBuilder();
+  ~ExifMetadataBuilder();
+
+  void SetWidth(int width);
+  void SetHeight(int height);
+  void SetThumbnailWidth(int width);
+  void SetThumbnailHeight(int height);
+  void SetThumbnail(void* thumbnail, int size);
+  void SetGpsLatitude(double degrees);
+  void SetGpsLongitude(double degrees);
+  void SetGpsAltitude(double altitude);
+  void SetGpsDateTime(time_t timestamp);
+  void SetGpsProcessingMethod(const std::string& method);
+  void SetDateTime(time_t t);
+  void SetLensFocalLength(double length);
+  void Build();
+
+  const AutoFreeBuffer& Buffer() {
+    return mData;
+  }
+
+ private:
+  ExifStructure* mImageIfd;
+  ExifStructure* mThumbnailIfd;
+  ExifStructure* mCameraSubIfd;
+  ExifStructure* mGpsSubIfd;
+
+  AutoFreeBuffer mData;
+
+  ExifMetadataBuilder(const ExifMetadataBuilder&);
+  ExifMetadataBuilder& operator= (const ExifMetadataBuilder&);
+};
+
+}  // namespace android
+
+#endif  // EXIFMETADATAWRITER_H_
diff --git a/guest/hals/camera/GrallocModule.h b/guest/hals/camera/GrallocModule.h
new file mode 100644
index 0000000..ad1eab4
--- /dev/null
+++ b/guest/hals/camera/GrallocModule.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef GUEST_HALS_CAMERA_GRALLOCMODULE_H_
+#define GUEST_HALS_CAMERA_GRALLOCMODULE_H_
+
+#include <hardware/gralloc.h>
+
+class GrallocModule
+{
+public:
+  static GrallocModule &getInstance() {
+    static GrallocModule instance;
+    return instance;
+  }
+
+  int lock(buffer_handle_t handle,
+      int usage, int l, int t, int w, int h, void **vaddr) {
+    return mModule->lock(mModule, handle, usage, l, t, w, h, vaddr);
+  }
+
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+  int lock_ycbcr(buffer_handle_t handle,
+      int usage, int l, int t, int w, int h,
+      struct android_ycbcr *ycbcr) {
+    return mModule->lock_ycbcr(mModule, handle, usage, l, t, w, h, ycbcr);
+  }
+#endif
+
+  int unlock(buffer_handle_t handle) {
+    return mModule->unlock(mModule, handle);
+  }
+
+private:
+  GrallocModule() {
+    const hw_module_t *module = NULL;
+    int ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+    if (ret) {
+      ALOGE("%s: Failed to get gralloc module: %d", __FUNCTION__, ret);
+    }
+    mModule = reinterpret_cast<const gralloc_module_t*>(module);
+  }
+  const gralloc_module_t *mModule;
+};
+
+#endif  // GUEST_HALS_CAMERA_GRALLOCMODULE_H_
diff --git a/guest/hals/camera/ImageMetadata.h b/guest/hals/camera/ImageMetadata.h
new file mode 100644
index 0000000..56020f2
--- /dev/null
+++ b/guest/hals/camera/ImageMetadata.h
@@ -0,0 +1,23 @@
+#ifndef IMAGEMETADATA_H_
+#define IMAGEMETADATA_H_
+
+#include <string>
+#include <stdint.h>
+
+extern "C" {
+/* Describes various attributes of the picture. */
+struct ImageMetadata {
+    int mWidth;
+    int mHeight;
+    int mThumbnailWidth;
+    int mThumbnailHeight;
+    double mLensFocalLength;
+    double mGpsLatitude;
+    double mGpsLongitude;
+    double mGpsAltitude;
+    time_t mGpsTimestamp;
+    std::string mGpsProcessingMethod;
+};
+}
+
+#endif  // IMAGEMETADATA_H_
diff --git a/guest/hals/camera/JpegCompressor.cpp b/guest/hals/camera/JpegCompressor.cpp
new file mode 100644
index 0000000..8ee069f
--- /dev/null
+++ b/guest/hals/camera/JpegCompressor.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of a class NV21JpegCompressor that encapsulates a
+ * converter between NV21, and JPEG formats.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_JPEG"
+#include <cutils/log.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include "JpegCompressor.h"
+
+namespace android {
+
+void* NV21JpegCompressor::mDl = NULL;
+
+static void* getSymbol(void* dl, const char* signature) {
+    void* res = dlsym(dl, signature);
+    assert (res != NULL);
+
+    return res;
+}
+
+typedef void (*InitFunc)(JpegStub* stub, int* strides);
+typedef void (*CleanupFunc)(JpegStub* stub);
+typedef int (*CompressFunc)(JpegStub* stub, const void* image,
+                            int quality, const ImageMetadata* meta);
+typedef void (*GetCompressedImageFunc)(JpegStub* stub, void* buff);
+typedef size_t (*GetCompressedSizeFunc)(JpegStub* stub);
+
+NV21JpegCompressor::NV21JpegCompressor()
+{
+    if (mDl == NULL) {
+        mDl = dlopen("/vendor/lib/hw/camera.gce_x86.jpeg.so", RTLD_NOW);
+    }
+    if (mDl == NULL) {
+        mDl = dlopen("/system/lib/hw/camera.gce_x86.jpeg.so", RTLD_NOW);
+    }
+    assert(mDl != NULL);
+
+    InitFunc f = (InitFunc)getSymbol(mDl, "JpegStub_init");
+    (*f)(&mStub, mStrides);
+}
+
+NV21JpegCompressor::~NV21JpegCompressor()
+{
+    CleanupFunc f = (CleanupFunc)getSymbol(mDl, "JpegStub_cleanup");
+    (*f)(&mStub);
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+status_t NV21JpegCompressor::compressRawImage(
+    const void* image, const ImageMetadata* meta, int quality)
+{
+    mStrides[0] = meta->mWidth;
+    mStrides[1] = meta->mWidth;
+    CompressFunc f = (CompressFunc)getSymbol(mDl, "JpegStub_compress");
+    return (status_t)(*f)(&mStub, image, quality, meta);
+}
+
+
+size_t NV21JpegCompressor::getCompressedSize()
+{
+    GetCompressedSizeFunc f = (GetCompressedSizeFunc)getSymbol(mDl,
+            "JpegStub_getCompressedSize");
+    return (*f)(&mStub);
+}
+
+void NV21JpegCompressor::getCompressedImage(void* buff)
+{
+    GetCompressedImageFunc f = (GetCompressedImageFunc)getSymbol(mDl,
+            "JpegStub_getCompressedImage");
+    (*f)(&mStub, buff);
+}
+
+}; /* namespace android */
diff --git a/guest/hals/camera/JpegCompressor.h b/guest/hals/camera/JpegCompressor.h
new file mode 100644
index 0000000..e27fd7d
--- /dev/null
+++ b/guest/hals/camera/JpegCompressor.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_JPEG_COMPRESSOR_H
+#define HW_EMULATOR_CAMERA_JPEG_COMPRESSOR_H
+
+/*
+ * Contains declaration of a class NV21JpegCompressor that encapsulates a
+ * converter between YV21, and JPEG formats.
+ */
+
+#include "JpegStub.h"
+#include <utils/threads.h>
+
+namespace android {
+
+/* Encapsulates a converter between YV12, and JPEG formats.
+ */
+class NV21JpegCompressor
+{
+public:
+    /* Constructs JpegCompressor instance. */
+    NV21JpegCompressor();
+    /* Destructs JpegCompressor instance. */
+    ~NV21JpegCompressor();
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+public:
+    /* Compresses raw NV21 image into a JPEG.
+     * The compressed image will be saved in mStream member of this class. Use
+     * getCompressedSize method to obtain buffer size of the compressed image,
+     * and getCompressedImage to copy out the compressed image.
+     * Param:
+     *  image - Raw NV21 image.
+     *  metadata - Image metadata (dimensions, location etc).
+     *  quality - JPEG quality.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     *
+     */
+    status_t compressRawImage(const void* image,
+                              const ImageMetadata* metadata,
+                              int quality);
+
+    /* Get size of the compressed JPEG buffer.
+     * This method must be called only after a successful completion of
+     * compressRawImage call.
+     * Return:
+     *  Size of the compressed JPEG buffer.
+     */
+    size_t getCompressedSize();
+
+    /* Copies out compressed JPEG buffer.
+     * This method must be called only after a successful completion of
+     * compressRawImage call.
+     * Param:
+     *  buff - Buffer where to copy the JPEG. Must be large enough to contain the
+     *      entire image.
+     */
+    void getCompressedImage(void* buff);
+
+    /****************************************************************************
+     * Class data
+     ***************************************************************************/
+
+protected:
+    /* Strides for Y (the first element), and UV (the second one) panes. */
+    int                     mStrides[2];
+
+private:
+    // library handle to dlopen
+    static void* mDl;
+    JpegStub mStub;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_JPEG_COMPRESSOR_H */
diff --git a/guest/hals/camera/JpegStub.cpp b/guest/hals/camera/JpegStub.cpp
new file mode 100644
index 0000000..ff23d4b
--- /dev/null
+++ b/guest/hals/camera/JpegStub.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2013 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_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_JPEGStub"
+#include <errno.h>
+#include <cutils/log.h>
+#include <libyuv.h>
+#include <YuvToJpegEncoder.h>
+
+#include "ExifMetadataBuilder.h"
+#include "JpegStub.h"
+
+namespace {
+bool GenerateThumbnail(
+    const uint8_t* source_nv21, int source_width, int source_height,
+    int thumbnail_width, int thumbnail_height, SkDynamicMemoryWStream* target) {
+  // We need to convert the image to Y'UV420SP to I420, which seems to be the
+  // only scalable format by the LibYUV.
+  // These formats are similar in their memory occupancy (both use about 3/2 of
+  // the total pixels).
+  int temp_y_size = source_width * source_height;
+  int temp_uv_size = temp_y_size / 4;
+  uint8_t* temp_y = (uint8_t*)malloc(temp_y_size + temp_uv_size + temp_uv_size);
+  uint8_t* temp_u = temp_y + temp_y_size;
+  uint8_t* temp_v = temp_u + temp_uv_size;
+
+  libyuv::NV12ToI420(
+      source_nv21, source_width,
+      source_nv21 + temp_y_size, source_width,
+      temp_y, source_width,
+      temp_u, source_width / 2,
+      temp_v, source_width / 2,
+      source_width, source_height);
+
+  // Compute and allocate memory for thumbnail I420.
+  int thumb_y_size = thumbnail_width * thumbnail_height;
+  int thumb_uv_size = thumb_y_size / 4;
+  uint8_t* thumb_y = (uint8_t*)malloc(thumb_y_size + thumb_uv_size + thumb_uv_size);
+  uint8_t* thumb_u = thumb_y + thumb_y_size;
+  uint8_t* thumb_v = thumb_u + thumb_uv_size;
+
+  libyuv::I420Scale(
+      temp_y, source_width,
+      temp_u, source_width / 2,
+      temp_v, source_width / 2,
+      source_width, source_height,
+      thumb_y, thumbnail_width,
+      thumb_u, thumbnail_width / 2,
+      thumb_v, thumbnail_width / 2,
+      thumbnail_width, thumbnail_height,
+      libyuv::kFilterBilinear);
+
+  // Combine U and V components back to NV21 format.
+  // We can re-use temp_y buffer for our needs at this point.
+  for (int pix = 0; pix < thumb_uv_size; ++pix) {
+    temp_y[2 * pix] = thumb_v[pix];
+    temp_y[2 * pix + 1] = thumb_u[pix];
+  }
+
+  // Put the memory back. After this, the thumb_y points to beginning of NV21
+  // image which we can compress.
+  memcpy(thumb_u, temp_y, thumb_uv_size * 2);
+
+  // Compress image.
+  int strides[2] = { thumbnail_width, thumbnail_width };
+  int offsets[2] = { 0, thumb_y_size };
+  Yuv420SpToJpegEncoder* encoder = new Yuv420SpToJpegEncoder(strides);
+
+  bool result = encoder->encode(
+      target, thumb_y, thumbnail_width, thumbnail_height, offsets, 90);
+
+  if (!result) {
+    ALOGE("%s: Thumbnail compression failed", __FUNCTION__);
+  }
+
+  delete(encoder);
+  free(thumb_y);
+  free(temp_y);
+
+  return result;
+}
+}  // namespace
+
+extern "C" void JpegStub_init(JpegStub* stub, int* strides) {
+    stub->mInternalEncoder = (void*) new Yuv420SpToJpegEncoder(strides);
+    stub->mInternalStream = (void*)new SkDynamicMemoryWStream();
+    stub->mExifBuilder = (void*)new android::ExifMetadataBuilder();
+}
+
+extern "C" void JpegStub_cleanup(JpegStub* stub) {
+    delete((Yuv420SpToJpegEncoder*)stub->mInternalEncoder);
+    delete((SkDynamicMemoryWStream*)stub->mInternalStream);
+    delete((android::ExifMetadataBuilder*)stub->mExifBuilder);
+}
+
+extern "C" int JpegStub_compress(JpegStub* stub, const void* image,
+                                 int quality, const ImageMetadata* meta)
+{
+    void* pY = const_cast<void*>(image);
+
+    int offsets[2];
+    offsets[0] = 0;
+    offsets[1] = meta->mWidth * meta->mHeight;
+
+    Yuv420SpToJpegEncoder* encoder =
+        (Yuv420SpToJpegEncoder*)stub->mInternalEncoder;
+    SkDynamicMemoryWStream* stream =
+        (SkDynamicMemoryWStream*)stub->mInternalStream;
+    android::ExifMetadataBuilder* exif =
+        (android::ExifMetadataBuilder*)stub->mExifBuilder;
+
+    exif->SetWidth(meta->mWidth);
+    exif->SetHeight(meta->mHeight);
+    exif->SetDateTime(time(NULL));
+    if (meta->mLensFocalLength != -1)
+      exif->SetLensFocalLength(meta->mLensFocalLength);
+    if (meta->mGpsTimestamp != -1) {
+      exif->SetGpsLatitude(meta->mGpsLatitude);
+      exif->SetGpsLongitude(meta->mGpsLongitude);
+      exif->SetGpsAltitude(meta->mGpsAltitude);
+      exif->SetGpsDateTime(meta->mGpsTimestamp);
+      exif->SetGpsProcessingMethod(meta->mGpsProcessingMethod);
+    }
+
+    ALOGV("%s: Requested thumbnail size: %dx%d",
+          __FUNCTION__, meta->mThumbnailWidth, meta->mThumbnailHeight);
+
+    // Thumbnail requested?
+    if (meta->mThumbnailWidth > 0 && meta->mThumbnailHeight > 0) {
+      exif->SetThumbnailWidth(meta->mThumbnailWidth);
+      exif->SetThumbnailHeight(meta->mThumbnailHeight);
+      SkDynamicMemoryWStream* thumbnail = new SkDynamicMemoryWStream();
+      GenerateThumbnail(
+          (uint8_t*)pY,
+          meta->mWidth, meta->mHeight,
+          meta->mThumbnailWidth, meta->mThumbnailHeight,
+          thumbnail);
+
+      int thumbnail_size = thumbnail->bytesWritten();
+      void* thumbnail_data = malloc(thumbnail_size);
+      thumbnail->read(thumbnail_data, 0, thumbnail_size);
+      // Pass ownership to EXIF builder.
+      exif->SetThumbnail(thumbnail_data, thumbnail_size);
+      delete thumbnail;
+    }
+
+    exif->Build();
+
+    if (encoder->encode(stream, pY, meta->mWidth, meta->mHeight,
+                        offsets, quality)) {
+        ALOGI("%s: Compressed JPEG: %d[%dx%d] -> %zu bytes",
+              __FUNCTION__, (meta->mWidth * meta->mHeight * 12) / 8,
+              meta->mWidth, meta->mHeight, stream->bytesWritten());
+        return 0;
+    } else {
+        ALOGE("%s: JPEG compression failed", __FUNCTION__);
+        return errno ? errno: EINVAL;
+    }
+}
+
+extern "C" void JpegStub_getCompressedImage(JpegStub* stub, void* buff) {
+    SkDynamicMemoryWStream* stream =
+        (SkDynamicMemoryWStream*)stub->mInternalStream;
+    android::ExifMetadataBuilder* exif =
+        (android::ExifMetadataBuilder*)stub->mExifBuilder;
+    char* target = (char*)buff;
+    memcpy(buff, exif->Buffer().data(), exif->Buffer().size());
+    target += exif->Buffer().size();
+
+    // Skip 0xFFD8 marker. This marker has already been included in Metadata.
+    stream->read(target, 2, stream->bytesWritten() - 2);
+}
+
+extern "C" size_t JpegStub_getCompressedSize(JpegStub* stub) {
+    SkDynamicMemoryWStream* stream =
+        (SkDynamicMemoryWStream*)stub->mInternalStream;
+    android::ExifMetadataBuilder* exif =
+        (android::ExifMetadataBuilder*)stub->mExifBuilder;
+    return stream->bytesWritten() + exif->Buffer().size() - 2;
+}
diff --git a/guest/hals/camera/JpegStub.h b/guest/hals/camera/JpegStub.h
new file mode 100644
index 0000000..10e5bf8
--- /dev/null
+++ b/guest/hals/camera/JpegStub.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 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 JPEGSTUB_H_
+#define JPEGSTUB_H_
+
+#include "ImageMetadata.h"
+
+extern "C" {
+
+struct JpegStub {
+    void* mInternalEncoder;
+    void* mInternalStream;
+    void* mExifBuilder;
+};
+
+void JpegStub_init(JpegStub* stub, int* strides);
+void JpegStub_cleanup(JpegStub* stub);
+int JpegStub_compress(JpegStub* stub, const void* image, int quality,
+                      const ImageMetadata* metadata);
+void JpegStub_getCompressedImage(JpegStub* stub, void* buff);
+size_t JpegStub_getCompressedSize(JpegStub* stub);
+
+};
+#endif // JPEGSTUB_H_
diff --git a/guest/hals/camera/PreviewWindow.cpp b/guest/hals/camera/PreviewWindow.cpp
new file mode 100755
index 0000000..281a3fa
--- /dev/null
+++ b/guest/hals/camera/PreviewWindow.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of a class PreviewWindow that encapsulates
+ * functionality of a preview window set via set_preview_window camera HAL API.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Preview"
+#include <cutils/log.h>
+#include "EmulatedCameraDevice.h"
+#include "PreviewWindow.h"
+#include "GrallocModule.h"
+
+namespace android {
+
+PreviewWindow::PreviewWindow()
+    : mPreviewWindow(NULL),
+      mLastPreviewed(0),
+      mPreviewFrameWidth(0),
+      mPreviewFrameHeight(0),
+      mPreviewEnabled(false)
+{
+}
+
+PreviewWindow::~PreviewWindow()
+{
+}
+
+/****************************************************************************
+ * Camera API
+ ***************************************************************************/
+
+status_t PreviewWindow::setPreviewWindow(struct preview_stream_ops* window,
+                                         int preview_fps)
+{
+    ALOGV("%s: current: %p -> new: %p", __FUNCTION__, mPreviewWindow, window);
+
+    status_t res = NO_ERROR;
+    Mutex::Autolock locker(&mObjectLock);
+
+    /* Reset preview info. */
+    mPreviewFrameWidth = mPreviewFrameHeight = 0;
+    mPreviewAfter = 0;
+    mLastPreviewed = 0;
+
+    if (window != NULL) {
+        /* The CPU will write each frame to the preview window buffer.
+         * Note that we delay setting preview window buffer geometry until
+         * frames start to come in. */
+        res = window->set_usage(window, GRALLOC_USAGE_SW_WRITE_OFTEN);
+        if (res == NO_ERROR) {
+            /* Set preview frequency. */
+            mPreviewAfter = 1000000 / preview_fps;
+        } else {
+            window = NULL;
+            res = -res; // set_usage returns a negative errno.
+            ALOGE("%s: Error setting preview window usage %d -> %s",
+                 __FUNCTION__, res, strerror(res));
+        }
+    }
+    mPreviewWindow = window;
+
+    return res;
+}
+
+status_t PreviewWindow::startPreview()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    mPreviewEnabled = true;
+
+    return NO_ERROR;
+}
+
+void PreviewWindow::stopPreview()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock locker(&mObjectLock);
+    mPreviewEnabled = false;
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+void PreviewWindow::onNextFrameAvailable(const void* frame,
+                                         nsecs_t timestamp,
+                                         EmulatedCameraDevice* camera_dev)
+{
+    int res;
+    Mutex::Autolock locker(&mObjectLock);
+
+    if (!isPreviewEnabled() || mPreviewWindow == NULL || !isPreviewTime()) {
+        return;
+    }
+
+    /* Make sure that preview window dimensions are OK with the camera device */
+    if (adjustPreviewDimensions(camera_dev)) {
+        /* Need to set / adjust buffer geometry for the preview window.
+         * Note that in the emulator preview window uses only RGB for pixel
+         * formats. */
+        ALOGV("%s: Adjusting preview windows %p geometry to %dx%d",
+             __FUNCTION__, mPreviewWindow, mPreviewFrameWidth,
+             mPreviewFrameHeight);
+        res = mPreviewWindow->set_buffers_geometry(mPreviewWindow,
+                                                   mPreviewFrameWidth,
+                                                   mPreviewFrameHeight,
+                                                   HAL_PIXEL_FORMAT_RGBA_8888);
+        if (res != NO_ERROR) {
+            ALOGE("%s: Error in set_buffers_geometry %d -> %s",
+                 __FUNCTION__, -res, strerror(-res));
+            return;
+        }
+    }
+
+    /*
+     * Push new frame to the preview window.
+     */
+
+    /* Dequeue preview window buffer for the frame. */
+    buffer_handle_t* buffer = NULL;
+    int stride = 0;
+    res = mPreviewWindow->dequeue_buffer(mPreviewWindow, &buffer, &stride);
+    if (res != NO_ERROR || buffer == NULL) {
+        ALOGE("%s: Unable to dequeue preview window buffer: %d -> %s",
+            __FUNCTION__, -res, strerror(-res));
+        return;
+    }
+
+    /* Let the preview window to lock the buffer. */
+    res = mPreviewWindow->lock_buffer(mPreviewWindow, buffer);
+    if (res != NO_ERROR) {
+        ALOGE("%s: Unable to lock preview window buffer: %d -> %s",
+             __FUNCTION__, -res, strerror(-res));
+        mPreviewWindow->cancel_buffer(mPreviewWindow, buffer);
+        return;
+    }
+
+    /* Now let the graphics framework to lock the buffer, and provide
+     * us with the framebuffer data address. */
+    void* img = NULL;
+    res = GrallocModule::getInstance().lock(
+        *buffer, GRALLOC_USAGE_SW_WRITE_OFTEN,
+        0, 0, mPreviewFrameWidth, mPreviewFrameHeight, &img);
+    if (res != NO_ERROR) {
+        ALOGE("%s: gralloc.lock failure: %d -> %s",
+             __FUNCTION__, res, strerror(res));
+        mPreviewWindow->cancel_buffer(mPreviewWindow, buffer);
+        return;
+    }
+
+    /* Frames come in in YV12/NV12/NV21 format. Since preview window doesn't
+     * supports those formats, we need to obtain the frame in RGB565. */
+    res = camera_dev->getCurrentPreviewFrame(img);
+    if (res == NO_ERROR) {
+        /* Show it. */
+        mPreviewWindow->set_timestamp(mPreviewWindow, timestamp);
+        mPreviewWindow->enqueue_buffer(mPreviewWindow, buffer);
+    } else {
+        ALOGE("%s: Unable to obtain preview frame: %d", __FUNCTION__, res);
+        mPreviewWindow->cancel_buffer(mPreviewWindow, buffer);
+    }
+    GrallocModule::getInstance().unlock(*buffer);
+}
+
+/***************************************************************************
+ * Private API
+ **************************************************************************/
+
+bool PreviewWindow::adjustPreviewDimensions(EmulatedCameraDevice* camera_dev)
+{
+    /* Match the cached frame dimensions against the actual ones. */
+    if (mPreviewFrameWidth == camera_dev->getFrameWidth() &&
+        mPreviewFrameHeight == camera_dev->getFrameHeight()) {
+        /* They match. */
+        return false;
+    }
+
+    /* They don't match: adjust the cache. */
+    mPreviewFrameWidth = camera_dev->getFrameWidth();
+    mPreviewFrameHeight = camera_dev->getFrameHeight();
+
+    return true;
+}
+
+bool PreviewWindow::isPreviewTime()
+{
+    timeval cur_time;
+    gettimeofday(&cur_time, NULL);
+    const uint64_t cur_mks = cur_time.tv_sec * 1000000LL + cur_time.tv_usec;
+    if ((cur_mks - mLastPreviewed) >= mPreviewAfter) {
+        mLastPreviewed = cur_mks;
+        return true;
+    }
+    return false;
+}
+
+}; /* namespace android */
diff --git a/guest/hals/camera/PreviewWindow.h b/guest/hals/camera/PreviewWindow.h
new file mode 100755
index 0000000..d037c95
--- /dev/null
+++ b/guest/hals/camera/PreviewWindow.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_PREVIEW_WINDOW_H
+#define HW_EMULATOR_CAMERA_PREVIEW_WINDOW_H
+
+/*
+ * Contains declaration of a class PreviewWindow that encapsulates functionality
+ * of a preview window set via set_preview_window camera HAL API.
+ */
+
+namespace android {
+
+class EmulatedCameraDevice;
+
+/* Encapsulates functionality of a preview window set via set_preview_window
+ * camera HAL API.
+ *
+ * Objects of this class are contained in EmulatedCamera objects, and handle
+ * relevant camera API callbacks.
+ */
+class PreviewWindow {
+public:
+    /* Constructs PreviewWindow instance. */
+    PreviewWindow();
+
+    /* Destructs PreviewWindow instance. */
+    ~PreviewWindow();
+
+    /***************************************************************************
+     * Camera API
+     **************************************************************************/
+
+public:
+    /* Actual handler for camera_device_ops_t::set_preview_window callback.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::set_preview_window callback.
+     * Param:
+     *  window - Preview window to set. This parameter might be NULL, which
+     *      indicates preview window reset.
+     *  preview_fps - Preview's frame frequency. This parameter determins when
+     *      a frame received via onNextFrameAvailable call will be pushed to
+     *      the preview window. If 'window' parameter passed to this method is
+     *      NULL, this parameter is ignored.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    status_t setPreviewWindow(struct preview_stream_ops* window,
+                              int preview_fps);
+
+    /* Starts the preview.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::start_preview callback.
+     */
+    status_t startPreview();
+
+    /* Stops the preview.
+     * This method is called by the containing emulated camera object when it is
+     * handing the camera_device_ops_t::start_preview callback.
+     */
+    void stopPreview();
+
+    /* Checks if preview is enabled. */
+    inline bool isPreviewEnabled()
+    {
+        return mPreviewEnabled;
+    }
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+public:
+    /* Next frame is available in the camera device.
+     * This is a notification callback that is invoked by the camera device when
+     * a new frame is available.
+     * Note that most likely this method is called in context of a worker thread
+     * that camera device has created for frame capturing.
+     * Param:
+     *  frame - Captured frame, or NULL if camera device didn't pull the frame
+     *      yet. If NULL is passed in this parameter use GetCurrentFrame method
+     *      of the camera device class to obtain the next frame. Also note that
+     *      the size of the frame that is passed here (as well as the frame
+     *      returned from the GetCurrentFrame method) is defined by the current
+     *      frame settings (width + height + pixel format) for the camera device.
+     * timestamp - Frame's timestamp.
+     * camera_dev - Camera device instance that delivered the frame.
+     */
+    void onNextFrameAvailable(const void* frame,
+                              nsecs_t timestamp,
+                              EmulatedCameraDevice* camera_dev);
+
+    /***************************************************************************
+     * Private API
+     **************************************************************************/
+
+protected:
+    /* Adjusts cached dimensions of the preview window frame according to the
+     * frame dimensions used by the camera device.
+     *
+     * When preview is started, it's not known (hard to define) what are going
+     * to be the dimensions of the frames that are going to be displayed. Plus,
+     * it might be possible, that such dimensions can be changed on the fly. So,
+     * in order to be always in sync with frame dimensions, this method is
+     * called for each frame passed to onNextFrameAvailable method, in order to
+     * properly adjust frame dimensions, used by the preview window.
+     * Note that this method must be called while object is locked.
+     * Param:
+     *  camera_dev - Camera device, prpviding frames displayed in the preview
+     *      window.
+     * Return:
+     *  true if cached dimensions have been adjusted, or false if cached
+     *  dimensions match device's frame dimensions.
+     */
+    bool adjustPreviewDimensions(EmulatedCameraDevice* camera_dev);
+
+    /* Checks if it's the time to push new frame to the preview window.
+     * Note that this method must be called while object is locked. */
+    bool isPreviewTime();
+
+    /***************************************************************************
+     * Data members
+     **************************************************************************/
+
+protected:
+    /* Locks this instance for data changes. */
+    Mutex                           mObjectLock;
+
+    /* Preview window instance. */
+    preview_stream_ops*             mPreviewWindow;
+
+    /* Timestamp (abs. microseconds) when last frame has been pushed to the
+     * preview window. */
+    uint64_t                        mLastPreviewed;
+
+    /* Preview frequency in microseconds. */
+    uint32_t                        mPreviewAfter;
+
+    /*
+     * Cached preview window frame dimensions.
+     */
+
+    int                             mPreviewFrameWidth;
+    int                             mPreviewFrameHeight;
+
+    /* Preview status. */
+    bool                            mPreviewEnabled;
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_PREVIEW_WINDOW_H */
diff --git a/guest/hals/camera/QemuClient.cpp b/guest/hals/camera/QemuClient.cpp
new file mode 100755
index 0000000..6c10dab
--- /dev/null
+++ b/guest/hals/camera/QemuClient.cpp
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Contains implementation of classes that encapsulate connection to camera
+ * services in the emulator via qemu pipe.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_QemuClient"
+#include <cutils/log.h>
+#include "EmulatedCamera.h"
+#include "QemuClient.h"
+
+#define LOG_QUERIES 0
+#if LOG_QUERIES
+#define LOGQ(...)   ALOGD(__VA_ARGS__)
+#else
+#define LOGQ(...)   (void(0))
+
+#endif  // LOG_QUERIES
+namespace android {
+
+/****************************************************************************
+ * Qemu query
+ ***************************************************************************/
+
+QemuQuery::QemuQuery()
+    : mQuery(mQueryPrealloc),
+      mQueryDeliveryStatus(NO_ERROR),
+      mReplyBuffer(NULL),
+      mReplyData(NULL),
+      mReplySize(0),
+      mReplyDataSize(0),
+      mReplyStatus(0)
+{
+    *mQuery = '\0';
+}
+
+QemuQuery::QemuQuery(const char* query_string)
+    : mQuery(mQueryPrealloc),
+      mQueryDeliveryStatus(NO_ERROR),
+      mReplyBuffer(NULL),
+      mReplyData(NULL),
+      mReplySize(0),
+      mReplyDataSize(0),
+      mReplyStatus(0)
+{
+    mQueryDeliveryStatus = QemuQuery::createQuery(query_string, NULL);
+}
+
+QemuQuery::QemuQuery(const char* query_name, const char* query_param)
+    : mQuery(mQueryPrealloc),
+      mQueryDeliveryStatus(NO_ERROR),
+      mReplyBuffer(NULL),
+      mReplyData(NULL),
+      mReplySize(0),
+      mReplyDataSize(0),
+      mReplyStatus(0)
+{
+    mQueryDeliveryStatus = QemuQuery::createQuery(query_name, query_param);
+}
+
+QemuQuery::~QemuQuery()
+{
+    QemuQuery::resetQuery();
+}
+
+status_t QemuQuery::createQuery(const char* name, const char* param)
+{
+    /* Reset from the previous use. */
+    resetQuery();
+
+    /* Query name cannot be NULL or an empty string. */
+    if (name == NULL || *name == '\0') {
+        ALOGE("%s: NULL or an empty string is passed as query name.",
+             __FUNCTION__);
+        mQueryDeliveryStatus = EINVAL;
+        return EINVAL;
+    }
+
+    const size_t name_len = strlen(name);
+    const size_t param_len = (param != NULL) ? strlen(param) : 0;
+    const size_t required = strlen(name) + (param_len ? (param_len + 2) : 1);
+
+    if (required > sizeof(mQueryPrealloc)) {
+        /* Preallocated buffer was too small. Allocate a bigger query buffer. */
+        mQuery = new char[required];
+        if (mQuery == NULL) {
+            ALOGE("%s: Unable to allocate %zu bytes for query buffer",
+                 __FUNCTION__, required);
+            mQueryDeliveryStatus = ENOMEM;
+            return ENOMEM;
+        }
+    }
+
+    /* At this point mQuery buffer is big enough for the query. */
+    if (param_len) {
+        sprintf(mQuery, "%s %s", name, param);
+    } else {
+        memcpy(mQuery, name, name_len + 1);
+    }
+
+    return NO_ERROR;
+}
+
+status_t QemuQuery::completeQuery(status_t status)
+{
+    /* Save query completion status. */
+    mQueryDeliveryStatus = status;
+    if (mQueryDeliveryStatus != NO_ERROR) {
+        return mQueryDeliveryStatus;
+    }
+
+    /* Make sure reply buffer contains at least 'ok', or 'ko'.
+     * Note that 'ok', or 'ko' prefixes are always 3 characters long: in case
+     * there are more data in the reply, that data will be separated from 'ok'/'ko'
+     * with a ':'. If there is no more data in the reply, the prefix will be
+     * zero-terminated, and the terminator will be inculded in the reply. */
+    if (mReplyBuffer == NULL || mReplySize < 3) {
+        ALOGE("%s: Invalid reply to the query", __FUNCTION__);
+        mQueryDeliveryStatus = EINVAL;
+        return EINVAL;
+    }
+
+    /* Lets see the reply status. */
+    if (!memcmp(mReplyBuffer, "ok", 2)) {
+        mReplyStatus = 1;
+    } else if (!memcmp(mReplyBuffer, "ko", 2)) {
+        mReplyStatus = 0;
+    } else {
+        ALOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
+        mQueryDeliveryStatus = EINVAL;
+        return EINVAL;
+    }
+
+    /* Lets see if there are reply data that follow. */
+    if (mReplySize > 3) {
+        /* There are extra data. Make sure they are separated from the status
+         * with a ':' */
+        if (mReplyBuffer[2] != ':') {
+            ALOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
+            mQueryDeliveryStatus = EINVAL;
+            return EINVAL;
+        }
+        mReplyData = mReplyBuffer + 3;
+        mReplyDataSize = mReplySize - 3;
+    } else {
+        /* Make sure reply buffer containing just 'ok'/'ko' ends with
+         * zero-terminator. */
+        if (mReplyBuffer[2] != '\0') {
+            ALOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
+            mQueryDeliveryStatus = EINVAL;
+            return EINVAL;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+void QemuQuery::resetQuery()
+{
+    if (mQuery != NULL && mQuery != mQueryPrealloc) {
+        delete[] mQuery;
+    }
+    mQuery = mQueryPrealloc;
+    mQueryDeliveryStatus = NO_ERROR;
+    if (mReplyBuffer != NULL) {
+        free(mReplyBuffer);
+        mReplyBuffer = NULL;
+    }
+    mReplyData = NULL;
+    mReplySize = mReplyDataSize = 0;
+    mReplyStatus = 0;
+}
+
+/****************************************************************************
+ * Qemu client base
+ ***************************************************************************/
+
+/* Camera service name. */
+const char QemuClient::mCameraServiceName[]   = "camera";
+
+QemuClient::QemuClient()
+    : mPipeFD(-1)
+{
+}
+
+QemuClient::~QemuClient()
+{
+    if (mPipeFD >= 0) {
+        close(mPipeFD);
+    }
+}
+
+/****************************************************************************
+ * Qemu client API
+ ***************************************************************************/
+
+status_t QemuClient::connectClient(const char* param)
+{
+    ALOGV("%s: '%s'", __FUNCTION__, param ? param : "");
+
+    /* Make sure that client is not connected already. */
+    if (mPipeFD >= 0) {
+        ALOGE("%s: Qemu client is already connected", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* Select one of the two: 'factory', or 'emulated camera' service */
+    if (param == NULL || *param == '\0') {
+        /* No parameters: connect to the factory service. */
+        char pipe_name[512];
+        snprintf(pipe_name, sizeof(pipe_name), "qemud:%s", mCameraServiceName);
+        mPipeFD = qemu_pipe_open(pipe_name);
+    } else {
+        /* One extra char ':' that separates service name and parameters + six
+         * characters for 'qemud:'. This is required by qemu pipe protocol. */
+        char* connection_str = new char[strlen(mCameraServiceName) +
+                                        strlen(param) + 8];
+        sprintf(connection_str, "qemud:%s:%s", mCameraServiceName, param);
+
+        mPipeFD = qemu_pipe_open(connection_str);
+        delete[] connection_str;
+    }
+    if (mPipeFD < 0) {
+        ALOGE("%s: Unable to connect to the camera service '%s': %s",
+             __FUNCTION__, param ? param : "Factory", strerror(errno));
+        return errno ? errno : EINVAL;
+    }
+
+    return NO_ERROR;
+}
+
+void QemuClient::disconnectClient()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    if (mPipeFD >= 0) {
+        close(mPipeFD);
+        mPipeFD = -1;
+    }
+}
+
+status_t QemuClient::sendMessage(const void* data, size_t data_size)
+{
+    if (mPipeFD < 0) {
+        ALOGE("%s: Qemu client is not connected", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* Note that we don't use here qemud_client_send, since with qemu pipes we
+     * don't need to provide payload size prior to payload when we're writing to
+     * the pipe. So, we can use simple write, and qemu pipe will take care of the
+     * rest, calling the receiving end with the number of bytes transferred. */
+    const size_t written = qemud_fd_write(mPipeFD, data, data_size);
+    if (written == data_size) {
+        return NO_ERROR;
+    } else {
+        ALOGE("%s: Error sending data via qemu pipe: '%s'",
+             __FUNCTION__, strerror(errno));
+        return errno ? errno : EIO;
+    }
+}
+
+status_t QemuClient::receiveMessage(void** data, size_t* data_size)
+{
+    *data = NULL;
+    *data_size = 0;
+
+    if (mPipeFD < 0) {
+        ALOGE("%s: Qemu client is not connected", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* The way the service replies to a query, it sends payload size first, and
+     * then it sends the payload itself. Note that payload size is sent as a
+     * string, containing 8 characters representing a hexadecimal payload size
+     * value. Note also, that the string doesn't contain zero-terminator. */
+    size_t payload_size;
+    char payload_size_str[9];
+    int rd_res = qemud_fd_read(mPipeFD, payload_size_str, 8);
+    if (rd_res != 8) {
+        ALOGE("%s: Unable to obtain payload size: %s",
+             __FUNCTION__, strerror(errno));
+        return errno ? errno : EIO;
+    }
+
+    /* Convert payload size. */
+    errno = 0;
+    payload_size_str[8] = '\0';
+    payload_size = strtol(payload_size_str, NULL, 16);
+    if (errno) {
+        ALOGE("%s: Invalid payload size '%s'", __FUNCTION__, payload_size_str);
+        return EIO;
+    }
+
+    /* Allocate payload data buffer, and read the payload there. */
+    *data = malloc(payload_size);
+    if (*data == NULL) {
+        ALOGE("%s: Unable to allocate %zu bytes payload buffer",
+             __FUNCTION__, payload_size);
+        return ENOMEM;
+    }
+    rd_res = qemud_fd_read(mPipeFD, *data, payload_size);
+    if (static_cast<size_t>(rd_res) == payload_size) {
+        *data_size = payload_size;
+        return NO_ERROR;
+    } else {
+        ALOGE("%s: Read size %d doesnt match expected payload size %zu: %s",
+             __FUNCTION__, rd_res, payload_size, strerror(errno));
+        free(*data);
+        *data = NULL;
+        return errno ? errno : EIO;
+    }
+}
+
+status_t QemuClient::doQuery(QemuQuery* query)
+{
+    /* Make sure that query has been successfuly constructed. */
+    if (query->mQueryDeliveryStatus != NO_ERROR) {
+        ALOGE("%s: Query is invalid", __FUNCTION__);
+        return query->mQueryDeliveryStatus;
+    }
+
+    LOGQ("Send query '%s'", query->mQuery);
+
+    /* Send the query. */
+    status_t res = sendMessage(query->mQuery, strlen(query->mQuery) + 1);
+    if (res == NO_ERROR) {
+        /* Read the response. */
+        res = receiveMessage(reinterpret_cast<void**>(&query->mReplyBuffer),
+                      &query->mReplySize);
+        if (res == NO_ERROR) {
+            LOGQ("Response to query '%s': Status = '%.2s', %d bytes in response",
+                 query->mQuery, query->mReplyBuffer, query->mReplySize);
+        } else {
+            ALOGE("%s Response to query '%s' has failed: %s",
+                 __FUNCTION__, query->mQuery, strerror(res));
+        }
+    } else {
+        ALOGE("%s: Send query '%s' failed: %s",
+             __FUNCTION__, query->mQuery, strerror(res));
+    }
+
+    /* Complete the query, and return its completion handling status. */
+    const status_t res1 = query->completeQuery(res);
+    ALOGE_IF(res1 != NO_ERROR && res1 != res,
+            "%s: Error %d in query '%s' completion",
+            __FUNCTION__, res1, query->mQuery);
+    return res1;
+}
+
+/****************************************************************************
+ * Qemu client for the 'factory' service.
+ ***************************************************************************/
+
+/*
+ * Factory service queries.
+ */
+
+/* Queries list of cameras connected to the host. */
+const char FactoryQemuClient::mQueryList[] = "list";
+
+FactoryQemuClient::FactoryQemuClient()
+    : QemuClient()
+{
+}
+
+FactoryQemuClient::~FactoryQemuClient()
+{
+}
+
+status_t FactoryQemuClient::listCameras(char** list)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    QemuQuery query(mQueryList);
+    if (doQuery(&query) || !query.isQuerySucceeded()) {
+        ALOGE("%s: List cameras query failed: %s", __FUNCTION__,
+             query.mReplyData ? query.mReplyData : "No error message");
+        return query.getCompletionStatus();
+    }
+
+    /* Make sure there is a list returned. */
+    if (query.mReplyDataSize == 0) {
+        ALOGE("%s: No camera list is returned.", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* Copy the list over. */
+    *list = (char*)malloc(query.mReplyDataSize);
+    if (*list != NULL) {
+        memcpy(*list, query.mReplyData, query.mReplyDataSize);
+        ALOGD("Emulated camera list: %s", *list);
+        return NO_ERROR;
+    } else {
+        ALOGE("%s: Unable to allocate %zu bytes",
+             __FUNCTION__, query.mReplyDataSize);
+        return ENOMEM;
+    }
+}
+
+/****************************************************************************
+ * Qemu client for an 'emulated camera' service.
+ ***************************************************************************/
+
+/*
+ * Emulated camera queries
+ */
+
+/* Connect to the camera device. */
+const char CameraQemuClient::mQueryConnect[]    = "connect";
+/* Disconect from the camera device. */
+const char CameraQemuClient::mQueryDisconnect[] = "disconnect";
+/* Start capturing video from the camera device. */
+const char CameraQemuClient::mQueryStart[]      = "start";
+/* Stop capturing video from the camera device. */
+const char CameraQemuClient::mQueryStop[]       = "stop";
+/* Get next video frame from the camera device. */
+const char CameraQemuClient::mQueryFrame[]      = "frame";
+
+CameraQemuClient::CameraQemuClient()
+    : QemuClient()
+{
+}
+
+CameraQemuClient::~CameraQemuClient()
+{
+
+}
+
+status_t CameraQemuClient::queryConnect()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    QemuQuery query(mQueryConnect);
+    doQuery(&query);
+    const status_t res = query.getCompletionStatus();
+    ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
+            __FUNCTION__, query.mReplyData ? query.mReplyData :
+                                             "No error message");
+    return res;
+}
+
+status_t CameraQemuClient::queryDisconnect()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    QemuQuery query(mQueryDisconnect);
+    doQuery(&query);
+    const status_t res = query.getCompletionStatus();
+    ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
+            __FUNCTION__, query.mReplyData ? query.mReplyData :
+                                             "No error message");
+    return res;
+}
+
+status_t CameraQemuClient::queryStart(uint32_t pixel_format,
+                                      int width,
+                                      int height)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    char query_str[256];
+    snprintf(query_str, sizeof(query_str), "%s dim=%dx%d pix=%d",
+             mQueryStart, width, height, pixel_format);
+    QemuQuery query(query_str);
+    doQuery(&query);
+    const status_t res = query.getCompletionStatus();
+    ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
+            __FUNCTION__, query.mReplyData ? query.mReplyData :
+                                             "No error message");
+    return res;
+}
+
+status_t CameraQemuClient::queryStop()
+{
+    ALOGV("%s", __FUNCTION__);
+
+    QemuQuery query(mQueryStop);
+    doQuery(&query);
+    const status_t res = query.getCompletionStatus();
+    ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
+            __FUNCTION__, query.mReplyData ? query.mReplyData :
+                                             "No error message");
+    return res;
+}
+
+status_t CameraQemuClient::queryFrame(void* vframe,
+                                      void* pframe,
+                                      size_t vframe_size,
+                                      size_t pframe_size,
+                                      float r_scale,
+                                      float g_scale,
+                                      float b_scale,
+                                      float exposure_comp)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    char query_str[256];
+    snprintf(query_str, sizeof(query_str), "%s video=%zu preview=%zu whiteb=%g,%g,%g expcomp=%g",
+             mQueryFrame, (vframe && vframe_size) ? vframe_size : 0,
+             (pframe && pframe_size) ? pframe_size : 0, r_scale, g_scale, b_scale,
+             exposure_comp);
+    QemuQuery query(query_str);
+    doQuery(&query);
+    const status_t res = query.getCompletionStatus();
+    if( res != NO_ERROR) {
+        ALOGE("%s: Query failed: %s",
+             __FUNCTION__, query.mReplyData ? query.mReplyData :
+                                              "No error message");
+        return res;
+    }
+
+    /* Copy requested frames. */
+    size_t cur_offset = 0;
+    const uint8_t* frame = reinterpret_cast<const uint8_t*>(query.mReplyData);
+    /* Video frame is always first. */
+    if (vframe != NULL && vframe_size != 0) {
+        /* Make sure that video frame is in. */
+        if ((query.mReplyDataSize - cur_offset) >= vframe_size) {
+            memcpy(vframe, frame, vframe_size);
+            cur_offset += vframe_size;
+        } else {
+            ALOGE("%s: Reply %zu bytes is to small to contain %zu bytes video frame",
+                 __FUNCTION__, query.mReplyDataSize - cur_offset, vframe_size);
+            return EINVAL;
+        }
+    }
+    if (pframe != NULL && pframe_size != 0) {
+        /* Make sure that preview frame is in. */
+        if ((query.mReplyDataSize - cur_offset) >= pframe_size) {
+            memcpy(pframe, frame + cur_offset, pframe_size);
+            cur_offset += pframe_size;
+        } else {
+            ALOGE("%s: Reply %zu bytes is to small to contain %zu bytes preview frame",
+                 __FUNCTION__, query.mReplyDataSize - cur_offset, pframe_size);
+            return EINVAL;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+}; /* namespace android */
diff --git a/guest/hals/camera/QemuClient.h b/guest/hals/camera/QemuClient.h
new file mode 100755
index 0000000..1644321
--- /dev/null
+++ b/guest/hals/camera/QemuClient.h
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2011 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 HW_EMULATOR_CAMERA_QEMU_CLIENT_H
+#define HW_EMULATOR_CAMERA_QEMU_CLIENT_H
+
+/*
+ * Contains declaration of classes that encapsulate connection to camera services
+ * in the emulator via qemu pipe.
+ */
+
+#include <hardware/qemud.h>
+
+namespace android {
+
+/****************************************************************************
+ * Qemu query
+ ***************************************************************************/
+
+/* Encapsulates a query to the emulator.
+ * Guest exchanges data with the emulator via queries sent over the qemu pipe.
+ * The queries as well as replies to the queries are all strings (except for the
+ * 'frame' query where reply is a framebuffer).
+ * Each query is formatted as such:
+ *
+ *      "<query name>[ <parameters>]",
+ *
+ * where <query name> is a string representing query name, and <parameters> are
+ * optional parameters for the query. If parameters are present, they must be
+ * separated from the query name with a single space, and they must be formatted
+ * as such:
+ *
+ *      "<name1>=<value1> <name2>=<value2> ... <nameN>=<valueN>"
+ *
+ * I.e.:
+ *  - Every parameter must have a name, and a value.
+ *  - Name and value must be separated with '='.
+ *  - No spaces are allowed around '=' separating name and value.
+ *  - Parameters must be separated with a single space character.
+ *  - No '=' character is allowed in name and in value.
+ *
+ * There are certain restrictions on strings used in the query:
+ *  - Spaces are allowed only as separators.
+ *  - '=' are allowed only to divide parameter names from parameter values.
+ *
+ * Emulator replies to each query in two chunks:
+ * - 8 bytes encoding the payload size as a string containing hexadecimal
+ *   representation of the payload size value. This is done in order to simplify
+ *   dealing with different endianness on the host, and on the guest.
+ * - Payload, whose size is defined by the first chunk.
+ *
+ * Every payload always begins with two characters, encoding the result of the
+ * query:
+ *  - 'ok' Encoding the success
+ *  - 'ko' Encoding a failure.
+ * After that payload may have optional data. If payload has more data following
+ * the query result, there is a ':' character separating them. If payload carries
+ * only the result, it always ends with a zero-terminator. So, payload 'ok'/'ko'
+ * prefix is always 3 bytes long: it either includes a zero-terminator, if there
+ * is no data, or a ':' separator.
+ */
+class QemuQuery {
+public:
+    /* Constructs an uninitialized QemuQuery instance. */
+    QemuQuery();
+
+    /* Constructs and initializes QemuQuery instance for a query.
+     * Param:
+     *  query_string - Query string. This constructor can also be used to
+     *      construct a query that doesn't have parameters. In this case query
+     *      name can be passed as a parameter here.
+     */
+    explicit QemuQuery(const char* query_string);
+
+    /* Constructs and initializes QemuQuery instance for a query with parameters.
+     * Param:
+     *  query_name - Query name.
+     *  query_param - Query parameters. Can be NULL.
+     */
+    QemuQuery(const char* query_name, const char* query_param);
+
+    /* Destructs QemuQuery instance. */
+    ~QemuQuery();
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+    /* Creates new query.
+     * Note: this method will reset this instance prior to creating a new query
+     * in order to discard possible "leftovers" from the previous query.
+     * Param:
+     *  query_name - Query name.
+     *  query_param - Query parameters. Can be NULL.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    status_t createQuery(const char* name, const char* param);
+
+    /* Completes the query after a reply from the emulator.
+     * This method will parse the reply buffer, and calculate the final query
+     * status, which depends not only on the transport success / failure, but
+     * also on 'ok' / 'ko' in the reply buffer.
+     * Param:
+     *  status - Query delivery status. This status doesn't necessarily reflects
+     *      the final query status (which is defined by 'ok'/'ko' prefix in the
+     *      reply buffer). This status simply states whether or not the query has
+     *      been sent, and a reply has been received successfuly. However, if
+     *      this status indicates a failure, it means that the entire query has
+     *      failed.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure. Note that
+     *  status returned here just signals whether or not the method has succeeded.
+     *  Use isQuerySucceeded() / getCompletionStatus() methods of this class to
+     *  check the final query status.
+     */
+    status_t completeQuery(status_t status);
+
+    /* Resets the query from a previous use. */
+    void resetQuery();
+
+    /* Checks if query has succeeded.
+     * Note that this method must be called after completeQuery() method of this
+     * class has been executed.
+     */
+    inline bool isQuerySucceeded() const {
+        return mQueryDeliveryStatus == NO_ERROR && mReplyStatus != 0;
+    }
+
+    /* Gets final completion status of the query.
+     * Note that this method must be called after completeQuery() method of this
+     * class has been executed.
+     * Return:
+     *  NO_ERROR if query has succeeded, or an appropriate error status on query
+     *  failure.
+     */
+    inline status_t getCompletionStatus() const {
+        if (mQueryDeliveryStatus == NO_ERROR) {
+            if (mReplyStatus) {
+                return NO_ERROR;
+            } else {
+                return EINVAL;
+            }
+        } else {
+            return mQueryDeliveryStatus;
+        }
+    }
+
+    /****************************************************************************
+     * Public data memebers
+     ***************************************************************************/
+
+public:
+    /* Query string. */
+    char*       mQuery;
+    /* Query delivery status. */
+    status_t    mQueryDeliveryStatus;
+    /* Reply buffer */
+    char*       mReplyBuffer;
+    /* Reply data (past 'ok'/'ko'). If NULL, there were no data in reply. */
+    char*       mReplyData;
+    /* Reply buffer size. */
+    size_t      mReplySize;
+    /* Reply data size. */
+    size_t      mReplyDataSize;
+    /* Reply status: 1 - ok, 0 - ko. */
+    int         mReplyStatus;
+
+    /****************************************************************************
+     * Private data memebers
+     ***************************************************************************/
+
+protected:
+    /* Preallocated buffer for small queries. */
+    char    mQueryPrealloc[256];
+};
+
+/****************************************************************************
+ * Qemu client base
+ ***************************************************************************/
+
+/* Encapsulates a connection to the 'camera' service in the emulator via qemu
+ * pipe.
+ */
+class QemuClient {
+public:
+    /* Constructs QemuClient instance. */
+    QemuClient();
+
+    /* Destructs QemuClient instance. */
+    virtual ~QemuClient();
+
+    /****************************************************************************
+     * Qemu client API
+     ***************************************************************************/
+
+public:
+    /* Connects to the 'camera' service in the emulator via qemu pipe.
+     * Param:
+     *  param - Parameters to pass to the camera service. There are two types of
+     *      camera services implemented by the emulator. The first one is a
+     *      'camera factory' type of service that provides list of cameras
+     *      connected to the host. Another one is an 'emulated camera' type of
+     *      service that provides interface to a camera connected to the host. At
+     *      the connection time emulator makes distinction between the two by
+     *      looking at connection parameters: no parameters means connection to
+     *      the 'factory' service, while connection with parameters means
+     *      connection to an 'emulated camera' service, where camera is identified
+     *      by one of the connection parameters. So, passing NULL, or an empty
+     *      string to this method will establish a connection with the 'factory'
+     *      service, while not empty string passed here will establish connection
+     *      with an 'emulated camera' service. Parameters defining the emulated
+     *      camera must be formatted as such:
+     *
+     *          "name=<device name> [inp_channel=<input channel #>]",
+     *
+     *      where 'device name' is a required parameter defining name of the
+     *      camera device, and 'input channel' is an optional parameter (positive
+     *      integer), defining the input channel to use on the camera device.
+     *      Note that device name passed here must have been previously obtained
+     *      from the factory service using 'list' query.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t connectClient(const char* param);
+
+    /* Disconnects from the service. */
+    virtual void disconnectClient();
+
+    /* Sends data to the service.
+     * Param:
+     *  data, data_size - Data to send.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    virtual status_t sendMessage(const void* data, size_t data_size);
+
+    /* Receives data from the service.
+     * This method assumes that data to receive will come in two chunks: 8
+     * characters encoding the payload size in hexadecimal string, followed by
+     * the paylod (if any).
+     * This method will allocate data buffer where to receive the response.
+     * Param:
+     *  data - Upon success contains address of the allocated data buffer with
+     *      the data received from the service. The caller is responsible for
+     *      freeing allocated data buffer.
+     *  data_size - Upon success contains size of the data received from the
+     *      service.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    virtual status_t receiveMessage(void** data, size_t* data_size);
+
+    /* Sends a query, and receives a response from the service.
+     * Param:
+     *  query - Query to send to the service. When this method returns, the query
+     *  is completed, and all its relevant data members are properly initialized.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure. Note that
+     *  status returned here is not the final query status. Use isQuerySucceeded(),
+     *  or getCompletionStatus() method on the query object to see if it has
+     *  succeeded. However, if this method returns a failure, it means that the
+     *  query has failed, and there is no guarantee that its data members are
+     *  properly initialized (except for the 'mQueryDeliveryStatus', which is
+     *  always in the proper state).
+     */
+    virtual status_t doQuery(QemuQuery* query);
+
+    /****************************************************************************
+     * Data members
+     ***************************************************************************/
+
+protected:
+    /* Qemu pipe handle. */
+    int     mPipeFD;
+
+private:
+    /* Camera service name. */
+    static const char mCameraServiceName[];
+};
+
+/****************************************************************************
+ * Qemu client for the 'factory' service.
+ ***************************************************************************/
+
+/* Encapsulates QemuClient for the 'factory' service. */
+class FactoryQemuClient : public QemuClient {
+public:
+    /* Constructs FactoryQemuClient instance. */
+    FactoryQemuClient();
+
+    /* Destructs FactoryQemuClient instance. */
+    ~FactoryQemuClient();
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+public:
+    /* Lists camera devices connected to the host.
+     * Param:
+     *  list - Upon success contains a list of cameras connected to the host. The
+     *      list returned here is represented as a string, containing multiple
+     *      lines separated with '\n', where each line represents a camera. Each
+     *      camera line is formatted as such:
+     *
+     *          "name=<device name> channel=<num> pix=<num> framedims=<dimensions>\n"
+     *
+     *      Where:
+     *      - 'name' is the name of the camera device attached to the host. This
+     *        name must be used for subsequent connection to the 'emulated camera'
+     *        service for that camera.
+     *      - 'channel' - input channel number (positive int) to use to communicate
+     *        with the camera.
+     *      - 'pix' - pixel format (a "fourcc" uint), chosen for the video frames
+     *        by the camera service.
+     *      - 'framedims' contains a list of frame dimensions supported by the
+     *        camera for the chosen pixel format. Each etry in the list is in form
+     *        '<width>x<height>', where 'width' and 'height' are numeric values
+     *        for width and height of a supported frame dimension. Entries in
+     *        this list are separated with ',' with no spaces between the entries.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    status_t listCameras(char** list);
+
+    /****************************************************************************
+     * Names of the queries available for the emulated camera factory.
+     ***************************************************************************/
+
+private:
+    /* List cameras connected to the host. */
+    static const char mQueryList[];
+};
+
+/****************************************************************************
+ * Qemu client for an 'emulated camera' service.
+ ***************************************************************************/
+
+/* Encapsulates QemuClient for an 'emulated camera' service.
+ */
+class CameraQemuClient : public QemuClient {
+public:
+    /* Constructs CameraQemuClient instance. */
+    CameraQemuClient();
+
+    /* Destructs CameraQemuClient instance. */
+    ~CameraQemuClient();
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+public:
+    /* Queries camera connection.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    status_t queryConnect();
+
+    /* Queries camera disconnection.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    status_t queryDisconnect();
+
+    /* Queries camera to start capturing video.
+     * Param:
+     *  pixel_format - Pixel format that is used by the client to push video
+     *      frames to the camera framework.
+     *  width, height - Frame dimensions, requested by the framework.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    status_t queryStart(uint32_t pixel_format, int width, int height);
+
+    /* Queries camera to stop capturing video.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    status_t queryStop();
+
+    /* Queries camera for the next video frame.
+     * Param:
+     *  vframe, vframe_size - Define buffer, allocated to receive a video frame.
+     *      Any of these parameters can be 0, indicating that the caller is
+     *      interested only in preview frame.
+     *  pframe, pframe_size - Define buffer, allocated to receive a preview frame.
+     *      Any of these parameters can be 0, indicating that the caller is
+     *      interested only in video frame.
+     *  r_scale, g_scale, b_scale - White balance scale.
+     *  exposure_comp - Expsoure compensation.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status on failure.
+     */
+    status_t queryFrame(void* vframe,
+                        void* pframe,
+                        size_t vframe_size,
+                        size_t pframe_size,
+                        float r_scale,
+                        float g_scale,
+                        float b_scale,
+                        float exposure_comp);
+
+    /****************************************************************************
+     * Names of the queries available for the emulated camera.
+     ***************************************************************************/
+
+private:
+    /* Connect to the camera. */
+    static const char mQueryConnect[];
+    /* Disconnect from the camera. */
+    static const char mQueryDisconnect[];
+    /* Start video capturing. */
+    static const char mQueryStart[];
+    /* Stop video capturing. */
+    static const char mQueryStop[];
+    /* Query frame(s). */
+    static const char mQueryFrame[];
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_QEMU_CLIENT_H */
diff --git a/guest/hals/camera/VSoCEmulatedCameraHotplugThread.cpp b/guest/hals/camera/VSoCEmulatedCameraHotplugThread.cpp
new file mode 100644
index 0000000..1f70fbf
--- /dev/null
+++ b/guest/hals/camera/VSoCEmulatedCameraHotplugThread.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_HotplugThread"
+#include <cutils/log.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "EmulatedCameraHotplugThread.h"
+#include "EmulatedCameraFactory.h"
+
+#define SubscriberInfo EmulatedCameraHotplugThread::SubscriberInfo
+
+namespace android {
+
+EmulatedCameraHotplugThread::EmulatedCameraHotplugThread(
+    size_t totalCameraCount) :
+        Thread(/*canCallJava*/false) {}
+
+EmulatedCameraHotplugThread::~EmulatedCameraHotplugThread() {}
+
+status_t EmulatedCameraHotplugThread::requestExitAndWait() {
+    ALOGE("%s: Not implemented. Use requestExit + join instead",
+          __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+void EmulatedCameraHotplugThread::requestExit() {
+    ALOGV("%s: Requesting thread exit", __FUNCTION__);
+    mRunning = false;
+}
+
+status_t EmulatedCameraHotplugThread::readyToRun() {
+    return OK;
+}
+
+bool EmulatedCameraHotplugThread::threadLoop() {
+    // Thread is irrelevant right now; hoplug is not supported.
+    return false;
+}
+
+String8 EmulatedCameraHotplugThread::getFilePath(int cameraId) const {
+    return String8();
+}
+
+bool EmulatedCameraHotplugThread::createFileIfNotExists(int cameraId) const
+{
+    return true;
+}
+
+int EmulatedCameraHotplugThread::getCameraId(String8 filePath) const {
+    // Not used anywhere.
+    return NAME_NOT_FOUND;
+}
+
+int EmulatedCameraHotplugThread::getCameraId(int wd) const {
+    // Not used anywhere.
+    return NAME_NOT_FOUND;
+}
+
+SubscriberInfo* EmulatedCameraHotplugThread::getSubscriberInfo(int cameraId)
+{
+    // Not used anywhere.
+    return NULL;
+}
+
+bool EmulatedCameraHotplugThread::addWatch(int cameraId) {
+    return true;
+}
+
+bool EmulatedCameraHotplugThread::removeWatch(int cameraId) {
+    return true;
+}
+
+int EmulatedCameraHotplugThread::readFile(String8 filePath) const {
+    return 1;
+}
+
+} //namespace android
diff --git a/guest/hals/camera/fake-pipeline2/Base.h b/guest/hals/camera/fake-pipeline2/Base.h
new file mode 100644
index 0000000..dfeeeca
--- /dev/null
+++ b/guest/hals/camera/fake-pipeline2/Base.h
@@ -0,0 +1,69 @@
+/*
+ * 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 file includes various basic structures that are needed by multiple parts
+ * of the fake camera 2 implementation.
+ */
+
+#ifndef HW_EMULATOR_CAMERA2_BASE_H
+#define HW_EMULATOR_CAMERA2_BASE_H
+
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+#if VSOC_PLATFORM_SDK_BEFORE(O_MR1)
+#include <system/window.h>
+#endif
+#include <hardware/camera2.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+
+/* Internal structure for passing buffers across threads */
+struct StreamBuffer {
+    // Positive numbers are output streams
+    // Negative numbers are input reprocess streams
+    // Zero is an auxillary buffer
+    int streamId;
+    uint32_t width, height;
+    uint32_t format;
+    uint32_t dataSpace;
+    uint32_t stride;
+    buffer_handle_t *buffer;
+    uint8_t *img;
+};
+typedef Vector<StreamBuffer> Buffers;
+
+struct Stream {
+    const camera2_stream_ops_t *ops;
+    uint32_t width, height;
+    int32_t format;
+    uint32_t stride;
+};
+
+struct ReprocessStream {
+    const camera2_stream_in_ops_t *ops;
+    uint32_t width, height;
+    int32_t format;
+    uint32_t stride;
+    // -1 if the reprocessing stream is independent
+    int32_t sourceStreamId;
+};
+
+} // namespace android;
+
+#endif
diff --git a/guest/hals/camera/fake-pipeline2/JpegCompressor.cpp b/guest/hals/camera/fake-pipeline2/JpegCompressor.cpp
new file mode 100644
index 0000000..f7ba234
--- /dev/null
+++ b/guest/hals/camera/fake-pipeline2/JpegCompressor.cpp
@@ -0,0 +1,295 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera2_JpegCompressor"
+
+#include <utils/Log.h>
+
+#include "JpegCompressor.h"
+#include "../EmulatedFakeCamera2.h"
+#include "../EmulatedFakeCamera3.h"
+
+namespace android {
+
+JpegCompressor::JpegCompressor():
+        Thread(false),
+        mIsBusy(false),
+        mSynchronous(false),
+        mBuffers(NULL),
+        mListener(NULL) {
+}
+
+JpegCompressor::~JpegCompressor() {
+    Mutex::Autolock lock(mMutex);
+}
+
+status_t JpegCompressor::reserve() {
+    Mutex::Autolock busyLock(mBusyMutex);
+    if (mIsBusy) {
+        ALOGE("%s: Already processing a buffer!", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+    mIsBusy = true;
+    return OK;
+}
+
+status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener) {
+    if (listener == NULL) {
+        ALOGE("%s: NULL listener not allowed!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    ALOGV("%s: Starting JPEG compression thread", __FUNCTION__);
+    Mutex::Autolock lock(mMutex);
+    {
+        Mutex::Autolock busyLock(mBusyMutex);
+
+        if (!mIsBusy) {
+            ALOGE("Called start without reserve() first!");
+            return INVALID_OPERATION;
+        }
+        mSynchronous = false;
+        mBuffers = buffers;
+        mListener = listener;
+    }
+
+    status_t res;
+    res = run("EmulatedFakeCamera2::JpegCompressor");
+    if (res != OK) {
+        ALOGE("%s: Unable to start up compression thread: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        delete mBuffers;
+    }
+    return res;
+}
+
+status_t JpegCompressor::compressSynchronous(Buffers *buffers) {
+    status_t res;
+
+    Mutex::Autolock lock(mMutex);
+    {
+        Mutex::Autolock busyLock(mBusyMutex);
+
+        if (mIsBusy) {
+            ALOGE("%s: Already processing a buffer!", __FUNCTION__);
+            return INVALID_OPERATION;
+        }
+
+        mIsBusy = true;
+        mSynchronous = true;
+        mBuffers = buffers;
+    }
+
+    res = compress();
+
+    cleanUp();
+
+    return res;
+}
+
+status_t JpegCompressor::cancel() {
+    requestExitAndWait();
+    return OK;
+}
+
+status_t JpegCompressor::readyToRun() {
+    return OK;
+}
+
+bool JpegCompressor::threadLoop() {
+    status_t res;
+    ALOGV("%s: Starting compression thread", __FUNCTION__);
+
+    res = compress();
+
+    mListener->onJpegDone(mJpegBuffer, res == OK);
+
+    cleanUp();
+
+    return false;
+}
+
+status_t JpegCompressor::compress() {
+    // Find source and target buffers. Assumes only one buffer matches
+    // each condition!
+    ALOGV("%s: Compressing start", __FUNCTION__);
+    bool foundJpeg = false, mFoundAux = false;
+    for (size_t i = 0; i < mBuffers->size(); i++) {
+        const StreamBuffer &b = (*mBuffers)[i];
+        if (b.format == HAL_PIXEL_FORMAT_BLOB) {
+            mJpegBuffer = b;
+            mFoundJpeg = true;
+        } else if (b.streamId <= 0) {
+            mAuxBuffer = b;
+            mFoundAux = true;
+        }
+        if (mFoundJpeg && mFoundAux) break;
+    }
+    if (!mFoundJpeg || !mFoundAux) {
+        ALOGE("%s: Unable to find buffers for JPEG source/destination",
+                __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    // Set up error management
+
+    mJpegErrorInfo = NULL;
+    JpegError error;
+    error.parent = this;
+
+    mCInfo.err = jpeg_std_error(&error);
+    mCInfo.err->error_exit = jpegErrorHandler;
+
+    jpeg_create_compress(&mCInfo);
+    if (checkError("Error initializing compression")) return NO_INIT;
+
+    // Route compressed data straight to output stream buffer
+
+    JpegDestination jpegDestMgr;
+    jpegDestMgr.parent = this;
+    jpegDestMgr.init_destination = jpegInitDestination;
+    jpegDestMgr.empty_output_buffer = jpegEmptyOutputBuffer;
+    jpegDestMgr.term_destination = jpegTermDestination;
+
+    mCInfo.dest = &jpegDestMgr;
+
+    // Set up compression parameters
+
+    mCInfo.image_width = mAuxBuffer.width;
+    mCInfo.image_height = mAuxBuffer.height;
+    mCInfo.input_components = 3;
+    mCInfo.in_color_space = JCS_RGB;
+
+    jpeg_set_defaults(&mCInfo);
+    if (checkError("Error configuring defaults")) return NO_INIT;
+
+    // Do compression
+
+    jpeg_start_compress(&mCInfo, TRUE);
+    if (checkError("Error starting compression")) return NO_INIT;
+
+    size_t rowStride = mAuxBuffer.stride * 3;
+    const size_t kChunkSize = 32;
+    while (mCInfo.next_scanline < mCInfo.image_height) {
+        JSAMPROW chunk[kChunkSize];
+        for (size_t i = 0 ; i < kChunkSize; i++) {
+            chunk[i] = (JSAMPROW)
+                    (mAuxBuffer.img + (i + mCInfo.next_scanline) * rowStride);
+        }
+        jpeg_write_scanlines(&mCInfo, chunk, kChunkSize);
+        if (checkError("Error while compressing")) return NO_INIT;
+        if (exitPending()) {
+            ALOGV("%s: Cancel called, exiting early", __FUNCTION__);
+            return TIMED_OUT;
+        }
+    }
+
+    jpeg_finish_compress(&mCInfo);
+    if (checkError("Error while finishing compression")) return NO_INIT;
+
+    // All done
+    ALOGV("%s: Compressing done", __FUNCTION__);
+
+    return OK;
+}
+
+bool JpegCompressor::isBusy() {
+    Mutex::Autolock busyLock(mBusyMutex);
+    return mIsBusy;
+}
+
+bool JpegCompressor::isStreamInUse(uint32_t id) {
+    Mutex::Autolock lock(mBusyMutex);
+
+    if (mBuffers && mIsBusy) {
+        for (size_t i = 0; i < mBuffers->size(); i++) {
+            if ( (*mBuffers)[i].streamId == (int)id ) return true;
+        }
+    }
+    return false;
+}
+
+bool JpegCompressor::waitForDone(nsecs_t timeout) {
+    Mutex::Autolock lock(mBusyMutex);
+    while (mIsBusy) {
+        status_t res = mDone.waitRelative(mBusyMutex, timeout);
+        if (res != OK) return false;
+    }
+    return true;
+}
+
+bool JpegCompressor::checkError(const char *msg) {
+    if (mJpegErrorInfo) {
+        char errBuffer[JMSG_LENGTH_MAX];
+        mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer);
+        ALOGE("%s: %s: %s",
+                __FUNCTION__, msg, errBuffer);
+        mJpegErrorInfo = NULL;
+        return true;
+    }
+    return false;
+}
+
+void JpegCompressor::cleanUp() {
+    status_t res;
+    jpeg_destroy_compress(&mCInfo);
+    Mutex::Autolock lock(mBusyMutex);
+
+    if (mFoundAux) {
+        if (mAuxBuffer.streamId == 0) {
+            delete[] mAuxBuffer.img;
+        } else if (!mSynchronous) {
+            mListener->onJpegInputDone(mAuxBuffer);
+        }
+    }
+    if (!mSynchronous) {
+        delete mBuffers;
+    }
+
+    mBuffers = NULL;
+
+    mIsBusy = false;
+    mDone.signal();
+}
+
+void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) {
+    JpegError *error = static_cast<JpegError*>(cinfo->err);
+    error->parent->mJpegErrorInfo = cinfo;
+}
+
+void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) {
+    JpegDestination *dest= static_cast<JpegDestination*>(cinfo->dest);
+    ALOGV("%s: Setting destination to %p, size %zu",
+            __FUNCTION__, dest->parent->mJpegBuffer.img, kMaxJpegSize);
+    dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer.img);
+    dest->free_in_buffer = kMaxJpegSize;
+}
+
+boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr cinfo) {
+    ALOGE("%s: JPEG destination buffer overflow!",
+            __FUNCTION__);
+    return true;
+}
+
+void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) {
+    ALOGV("%s: Done writing JPEG data. %zu bytes left in buffer",
+            __FUNCTION__, cinfo->dest->free_in_buffer);
+}
+
+JpegCompressor::JpegListener::~JpegListener() {
+}
+
+} // namespace android
diff --git a/guest/hals/camera/fake-pipeline2/JpegCompressor.h b/guest/hals/camera/fake-pipeline2/JpegCompressor.h
new file mode 100644
index 0000000..597cbdf
--- /dev/null
+++ b/guest/hals/camera/fake-pipeline2/JpegCompressor.h
@@ -0,0 +1,124 @@
+/*
+ * 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 class simulates a hardware JPEG compressor.  It receives image buffers
+ * in RGBA_8888 format, processes them in a worker thread, and then pushes them
+ * out to their destination stream.
+ */
+
+#ifndef HW_EMULATOR_CAMERA2_JPEG_H
+#define HW_EMULATOR_CAMERA2_JPEG_H
+
+#include "utils/Thread.h"
+#include "utils/Mutex.h"
+#include "utils/Timers.h"
+
+#include "Base.h"
+
+#include <stdio.h>
+
+extern "C" {
+#include <jpeglib.h>
+}
+
+namespace android {
+
+class JpegCompressor: private Thread, public virtual RefBase {
+  public:
+
+    JpegCompressor();
+    ~JpegCompressor();
+
+    struct JpegListener {
+        // Called when JPEG compression has finished, or encountered an error
+        virtual void onJpegDone(const StreamBuffer &jpegBuffer,
+                bool success) = 0;
+        // Called when the input buffer for JPEG is not needed any more,
+        // if the buffer came from the framework.
+        virtual void onJpegInputDone(const StreamBuffer &inputBuffer) = 0;
+        virtual ~JpegListener();
+    };
+
+    // Start compressing COMPRESSED format buffers; JpegCompressor takes
+    // ownership of the Buffers vector.
+    // Reserve() must be called first.
+    status_t start(Buffers *buffers, JpegListener *listener);
+
+    // Compress and block until buffer is complete.
+    status_t compressSynchronous(Buffers *buffers);
+
+    status_t cancel();
+
+    bool isBusy();
+    bool isStreamInUse(uint32_t id);
+
+    bool waitForDone(nsecs_t timeout);
+
+    // Reserve the compressor for a later start() call.
+    status_t reserve();
+
+    // TODO: Measure this
+    static const size_t kMaxJpegSize = 300000;
+
+  private:
+    Mutex mBusyMutex;
+    bool mIsBusy;
+    Condition mDone;
+    bool mSynchronous;
+
+    Mutex mMutex;
+
+    Buffers *mBuffers;
+    JpegListener *mListener;
+
+    StreamBuffer mJpegBuffer, mAuxBuffer;
+    bool mFoundJpeg, mFoundAux;
+
+    jpeg_compress_struct mCInfo;
+
+    struct JpegError : public jpeg_error_mgr {
+        JpegCompressor *parent;
+    };
+    j_common_ptr mJpegErrorInfo;
+
+    struct JpegDestination : public jpeg_destination_mgr {
+        JpegCompressor *parent;
+    };
+
+    static void jpegErrorHandler(j_common_ptr cinfo);
+
+    static void jpegInitDestination(j_compress_ptr cinfo);
+    static boolean jpegEmptyOutputBuffer(j_compress_ptr cinfo);
+    static void jpegTermDestination(j_compress_ptr cinfo);
+
+    bool checkError(const char *msg);
+    status_t compress();
+
+    void cleanUp();
+
+    /**
+     * Inherited Thread virtual overrides
+     */
+  private:
+    virtual status_t readyToRun();
+    virtual bool threadLoop();
+};
+
+} // namespace android
+
+#endif
diff --git a/guest/hals/camera/fake-pipeline2/Scene.cpp b/guest/hals/camera/fake-pipeline2/Scene.cpp
new file mode 100644
index 0000000..48296d2
--- /dev/null
+++ b/guest/hals/camera/fake-pipeline2/Scene.cpp
@@ -0,0 +1,478 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Scene"
+#include <utils/Log.h>
+#include <stdlib.h>
+#include <cmath>
+#include "Scene.h"
+
+// TODO: This should probably be done host-side in OpenGL for speed and better
+// quality
+
+namespace android {
+
+// Define single-letter shortcuts for scene definition, for directly indexing
+// mCurrentColors
+#define G (Scene::GRASS * Scene::NUM_CHANNELS)
+#define S (Scene::GRASS_SHADOW * Scene::NUM_CHANNELS)
+#define H (Scene::HILL * Scene::NUM_CHANNELS)
+#define W (Scene::WALL * Scene::NUM_CHANNELS)
+#define R (Scene::ROOF * Scene::NUM_CHANNELS)
+#define D (Scene::DOOR * Scene::NUM_CHANNELS)
+#define C (Scene::CHIMNEY * Scene::NUM_CHANNELS)
+#define I (Scene::WINDOW * Scene::NUM_CHANNELS)
+#define U (Scene::SUN * Scene::NUM_CHANNELS)
+#define K (Scene::SKY * Scene::NUM_CHANNELS)
+#define M (Scene::MOON * Scene::NUM_CHANNELS)
+
+const int Scene::kSceneWidth = 20;
+const int Scene::kSceneHeight = 20;
+
+const uint8_t Scene::kScene[Scene::kSceneWidth * Scene::kSceneHeight] = {
+    //      5         10        15        20
+    K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
+    K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
+    K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
+    K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
+    K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K, // 5
+    K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
+    K,K,K,K,K,K,K,K,H,H,H,H,H,H,H,H,H,H,H,H,
+    K,K,K,K,K,K,K,K,H,H,H,H,H,H,H,C,C,H,H,H,
+    K,K,K,K,K,K,H,H,H,H,H,H,H,H,H,C,C,H,H,H,
+    H,K,K,K,K,K,H,R,R,R,R,R,R,R,R,R,R,R,R,H, // 10
+    H,K,K,K,K,H,H,R,R,R,R,R,R,R,R,R,R,R,R,H,
+    H,H,H,K,K,H,H,R,R,R,R,R,R,R,R,R,R,R,R,H,
+    H,H,H,K,K,H,H,H,W,W,W,W,W,W,W,W,W,W,H,H,
+    S,S,S,G,G,S,S,S,W,W,W,W,W,W,W,W,W,W,S,S,
+    S,G,G,G,G,S,S,S,W,I,I,W,D,D,W,I,I,W,S,S, // 15
+    G,G,G,G,G,G,S,S,W,I,I,W,D,D,W,I,I,W,S,S,
+    G,G,G,G,G,G,G,G,W,W,W,W,D,D,W,W,W,W,G,G,
+    G,G,G,G,G,G,G,G,W,W,W,W,D,D,W,W,W,W,G,G,
+    G,G,G,G,G,G,G,G,S,S,S,S,S,S,S,S,S,S,G,G,
+    G,G,G,G,G,G,G,G,S,S,S,S,S,S,S,S,S,S,G,G, // 20
+    //      5         10        15        20
+};
+
+#undef G
+#undef S
+#undef H
+#undef W
+#undef R
+#undef D
+#undef C
+#undef I
+#undef U
+#undef K
+#undef M
+
+Scene::Scene(
+    int sensorWidthPx,
+    int sensorHeightPx,
+    float sensorSensitivity):
+        mSensorWidth(sensorWidthPx),
+        mSensorHeight(sensorHeightPx),
+        mHour(12),
+        mExposureDuration(0.033f),
+        mSensorSensitivity(sensorSensitivity)
+{
+    // Map scene to sensor pixels
+    if (mSensorWidth > mSensorHeight) {
+        mMapDiv = (mSensorWidth / (kSceneWidth + 1) ) + 1;
+    } else {
+        mMapDiv = (mSensorHeight / (kSceneHeight + 1) ) + 1;
+    }
+    mOffsetX = (kSceneWidth * mMapDiv - mSensorWidth) / 2;
+    mOffsetY = (kSceneHeight * mMapDiv - mSensorHeight) / 2;
+
+    // Assume that sensor filters are sRGB primaries to start
+    mFilterR[0]  =  3.2406f; mFilterR[1]  = -1.5372f; mFilterR[2]  = -0.4986f;
+    mFilterGr[0] = -0.9689f; mFilterGr[1] =  1.8758f; mFilterGr[2] =  0.0415f;
+    mFilterGb[0] = -0.9689f; mFilterGb[1] =  1.8758f; mFilterGb[2] =  0.0415f;
+    mFilterB[0]  =  0.0557f; mFilterB[1]  = -0.2040f; mFilterB[2]  =  1.0570f;
+
+
+}
+
+Scene::~Scene() {
+}
+
+void Scene::setColorFilterXYZ(
+        float rX, float rY, float rZ,
+        float grX, float grY, float grZ,
+        float gbX, float gbY, float gbZ,
+        float bX, float bY, float bZ) {
+    mFilterR[0]  = rX;  mFilterR[1]  = rY;  mFilterR[2]  = rZ;
+    mFilterGr[0] = grX; mFilterGr[1] = grY; mFilterGr[2] = grZ;
+    mFilterGb[0] = gbX; mFilterGb[1] = gbY; mFilterGb[2] = gbZ;
+    mFilterB[0]  = bX;  mFilterB[1]  = bY;  mFilterB[2]  = bZ;
+}
+
+void Scene::setHour(int hour) {
+    ALOGV("Hour set to: %d", hour);
+    mHour = hour % 24;
+}
+
+int Scene::getHour() {
+    return mHour;
+}
+
+void Scene::setExposureDuration(float seconds) {
+    mExposureDuration = seconds;
+}
+
+void Scene::calculateScene(nsecs_t time) {
+    // Calculate time fractions for interpolation
+    int timeIdx = mHour / kTimeStep;
+    int nextTimeIdx = (timeIdx + 1) % (24 / kTimeStep);
+    const nsecs_t kOneHourInNsec = 1e9 * 60 * 60;
+    nsecs_t timeSinceIdx = (mHour - timeIdx * kTimeStep) * kOneHourInNsec + time;
+    float timeFrac = timeSinceIdx / (float)(kOneHourInNsec * kTimeStep);
+
+    // Determine overall sunlight levels
+    float sunLux =
+            kSunlight[timeIdx] * (1 - timeFrac) +
+            kSunlight[nextTimeIdx] * timeFrac;
+    ALOGV("Sun lux: %f", sunLux);
+
+    float sunShadeLux = sunLux * (kDaylightShadeIllum / kDirectSunIllum);
+
+    // Determine sun/shade illumination chromaticity
+    float currentSunXY[2];
+    float currentShadeXY[2];
+
+    const float *prevSunXY, *nextSunXY;
+    const float *prevShadeXY, *nextShadeXY;
+    if (kSunlight[timeIdx] == kSunsetIllum ||
+            kSunlight[timeIdx] == kTwilightIllum) {
+        prevSunXY = kSunsetXY;
+        prevShadeXY = kSunsetXY;
+    } else {
+        prevSunXY = kDirectSunlightXY;
+        prevShadeXY = kDaylightXY;
+    }
+    if (kSunlight[nextTimeIdx] == kSunsetIllum ||
+            kSunlight[nextTimeIdx] == kTwilightIllum) {
+        nextSunXY = kSunsetXY;
+        nextShadeXY = kSunsetXY;
+    } else {
+        nextSunXY = kDirectSunlightXY;
+        nextShadeXY = kDaylightXY;
+    }
+    currentSunXY[0] = prevSunXY[0] * (1 - timeFrac) +
+            nextSunXY[0] * timeFrac;
+    currentSunXY[1] = prevSunXY[1] * (1 - timeFrac) +
+            nextSunXY[1] * timeFrac;
+
+    currentShadeXY[0] = prevShadeXY[0] * (1 - timeFrac) +
+            nextShadeXY[0] * timeFrac;
+    currentShadeXY[1] = prevShadeXY[1] * (1 - timeFrac) +
+            nextShadeXY[1] * timeFrac;
+
+    ALOGV("Sun XY: %f, %f, Shade XY: %f, %f",
+            currentSunXY[0], currentSunXY[1],
+            currentShadeXY[0], currentShadeXY[1]);
+
+    // Converting for xyY to XYZ:
+    // X = Y / y * x
+    // Y = Y
+    // Z = Y / y * (1 - x - y);
+    float sunXYZ[3] = {
+        sunLux / currentSunXY[1] * currentSunXY[0],
+        sunLux,
+        sunLux / currentSunXY[1] *
+        (1 - currentSunXY[0] - currentSunXY[1])
+    };
+    float sunShadeXYZ[3] = {
+        sunShadeLux / currentShadeXY[1] * currentShadeXY[0],
+        sunShadeLux,
+        sunShadeLux / currentShadeXY[1] *
+        (1 - currentShadeXY[0] - currentShadeXY[1])
+    };
+    ALOGV("Sun XYZ: %f, %f, %f",
+            sunXYZ[0], sunXYZ[1], sunXYZ[2]);
+    ALOGV("Sun shade XYZ: %f, %f, %f",
+            sunShadeXYZ[0], sunShadeXYZ[1], sunShadeXYZ[2]);
+
+    // Determine moonlight levels
+    float moonLux =
+            kMoonlight[timeIdx] * (1 - timeFrac) +
+            kMoonlight[nextTimeIdx] * timeFrac;
+    float moonShadeLux = moonLux * (kDaylightShadeIllum / kDirectSunIllum);
+
+    float moonXYZ[3] = {
+        moonLux / kMoonlightXY[1] * kMoonlightXY[0],
+        moonLux,
+        moonLux / kMoonlightXY[1] *
+        (1 - kMoonlightXY[0] - kMoonlightXY[1])
+    };
+    float moonShadeXYZ[3] = {
+        moonShadeLux / kMoonlightXY[1] * kMoonlightXY[0],
+        moonShadeLux,
+        moonShadeLux / kMoonlightXY[1] *
+        (1 - kMoonlightXY[0] - kMoonlightXY[1])
+    };
+
+    // Determine starlight level
+    const float kClearNightXYZ[3] = {
+        kClearNightIllum / kMoonlightXY[1] * kMoonlightXY[0],
+        kClearNightIllum,
+        kClearNightIllum / kMoonlightXY[1] *
+            (1 - kMoonlightXY[0] - kMoonlightXY[1])
+    };
+
+    // Calculate direct and shaded light
+    float directIllumXYZ[3] = {
+        sunXYZ[0] + moonXYZ[0] + kClearNightXYZ[0],
+        sunXYZ[1] + moonXYZ[1] + kClearNightXYZ[1],
+        sunXYZ[2] + moonXYZ[2] + kClearNightXYZ[2],
+    };
+
+    float shadeIllumXYZ[3] = {
+        kClearNightXYZ[0],
+        kClearNightXYZ[1],
+        kClearNightXYZ[2]
+    };
+
+    shadeIllumXYZ[0] += (mHour < kSunOverhead) ? sunXYZ[0] : sunShadeXYZ[0];
+    shadeIllumXYZ[1] += (mHour < kSunOverhead) ? sunXYZ[1] : sunShadeXYZ[1];
+    shadeIllumXYZ[2] += (mHour < kSunOverhead) ? sunXYZ[2] : sunShadeXYZ[2];
+
+    // Moon up period covers 23->0 transition, shift for simplicity
+    int adjHour = (mHour + 12) % 24;
+    int adjMoonOverhead = (kMoonOverhead + 12 ) % 24;
+    shadeIllumXYZ[0] += (adjHour < adjMoonOverhead) ?
+            moonXYZ[0] : moonShadeXYZ[0];
+    shadeIllumXYZ[1] += (adjHour < adjMoonOverhead) ?
+            moonXYZ[1] : moonShadeXYZ[1];
+    shadeIllumXYZ[2] += (adjHour < adjMoonOverhead) ?
+            moonXYZ[2] : moonShadeXYZ[2];
+
+    ALOGV("Direct XYZ: %f, %f, %f",
+            directIllumXYZ[0],directIllumXYZ[1],directIllumXYZ[2]);
+    ALOGV("Shade XYZ: %f, %f, %f",
+            shadeIllumXYZ[0], shadeIllumXYZ[1], shadeIllumXYZ[2]);
+
+    for (int i = 0; i < NUM_MATERIALS; i++) {
+        // Converting for xyY to XYZ:
+        // X = Y / y * x
+        // Y = Y
+        // Z = Y / y * (1 - x - y);
+        float matXYZ[3] = {
+            kMaterials_xyY[i][2] / kMaterials_xyY[i][1] *
+              kMaterials_xyY[i][0],
+            kMaterials_xyY[i][2],
+            kMaterials_xyY[i][2] / kMaterials_xyY[i][1] *
+              (1 - kMaterials_xyY[i][0] - kMaterials_xyY[i][1])
+        };
+
+        if (kMaterialsFlags[i] == 0 || kMaterialsFlags[i] & kSky) {
+            matXYZ[0] *= directIllumXYZ[0];
+            matXYZ[1] *= directIllumXYZ[1];
+            matXYZ[2] *= directIllumXYZ[2];
+        } else if (kMaterialsFlags[i] & kShadowed) {
+            matXYZ[0] *= shadeIllumXYZ[0];
+            matXYZ[1] *= shadeIllumXYZ[1];
+            matXYZ[2] *= shadeIllumXYZ[2];
+        } // else if (kMaterialsFlags[i] * kSelfLit), do nothing
+
+        ALOGV("Mat %d XYZ: %f, %f, %f", i, matXYZ[0], matXYZ[1], matXYZ[2]);
+        float luxToElectrons = mSensorSensitivity * mExposureDuration /
+                (kAperture * kAperture);
+        mCurrentColors[i*NUM_CHANNELS + 0] =
+                (mFilterR[0] * matXYZ[0] +
+                 mFilterR[1] * matXYZ[1] +
+                 mFilterR[2] * matXYZ[2])
+                * luxToElectrons;
+        mCurrentColors[i*NUM_CHANNELS + 1] =
+                (mFilterGr[0] * matXYZ[0] +
+                 mFilterGr[1] * matXYZ[1] +
+                 mFilterGr[2] * matXYZ[2])
+                * luxToElectrons;
+        mCurrentColors[i*NUM_CHANNELS + 2] =
+                (mFilterGb[0] * matXYZ[0] +
+                 mFilterGb[1] * matXYZ[1] +
+                 mFilterGb[2] * matXYZ[2])
+                * luxToElectrons;
+        mCurrentColors[i*NUM_CHANNELS + 3] =
+                (mFilterB[0] * matXYZ[0] +
+                 mFilterB[1] * matXYZ[1] +
+                 mFilterB[2] * matXYZ[2])
+                * luxToElectrons;
+
+        ALOGV("Color %d RGGB: %d, %d, %d, %d", i,
+                mCurrentColors[i*NUM_CHANNELS + 0],
+                mCurrentColors[i*NUM_CHANNELS + 1],
+                mCurrentColors[i*NUM_CHANNELS + 2],
+                mCurrentColors[i*NUM_CHANNELS + 3]);
+    }
+    // Shake viewpoint; horizontal and vertical sinusoids at roughly
+    // human handshake frequencies
+    mHandshakeX =
+            ( kFreq1Magnitude * std::sin(kHorizShakeFreq1 * timeSinceIdx) +
+              kFreq2Magnitude * std::sin(kHorizShakeFreq2 * timeSinceIdx) ) *
+            mMapDiv * kShakeFraction;
+
+    mHandshakeY =
+            ( kFreq1Magnitude * std::sin(kVertShakeFreq1 * timeSinceIdx) +
+              kFreq2Magnitude * std::sin(kVertShakeFreq2 * timeSinceIdx) ) *
+            mMapDiv * kShakeFraction;
+
+    // Set starting pixel
+    setReadoutPixel(0,0);
+}
+
+void Scene::setReadoutPixel(int x, int y) {
+    mCurrentX = x;
+    mCurrentY = y;
+    mSubX = (x + mOffsetX + mHandshakeX) % mMapDiv;
+    mSubY = (y + mOffsetY + mHandshakeY) % mMapDiv;
+    mSceneX = (x + mOffsetX + mHandshakeX) / mMapDiv;
+    mSceneY = (y + mOffsetY + mHandshakeY) / mMapDiv;
+    mSceneIdx = mSceneY * kSceneWidth + mSceneX;
+    mCurrentSceneMaterial = &(mCurrentColors[kScene[mSceneIdx]]);
+}
+
+const uint32_t* Scene::getPixelElectrons() {
+    const uint32_t *pixel = mCurrentSceneMaterial;
+    mCurrentX++;
+    mSubX++;
+    if (mCurrentX >= mSensorWidth) {
+        mCurrentX = 0;
+        mCurrentY++;
+        if (mCurrentY >= mSensorHeight) mCurrentY = 0;
+        setReadoutPixel(mCurrentX, mCurrentY);
+    } else if (mSubX > mMapDiv) {
+        mSceneIdx++;
+        mSceneX++;
+        mCurrentSceneMaterial = &(mCurrentColors[kScene[mSceneIdx]]);
+        mSubX = 0;
+    }
+    return pixel;
+}
+
+// Handshake model constants.
+// Frequencies measured in a nanosecond timebase
+const float Scene::kHorizShakeFreq1 = 2 * M_PI * 2  / 1e9; // 2 Hz
+const float Scene::kHorizShakeFreq2 = 2 * M_PI * 13 / 1e9; // 13 Hz
+const float Scene::kVertShakeFreq1  = 2 * M_PI * 3  / 1e9; // 3 Hz
+const float Scene::kVertShakeFreq2  = 2 * M_PI * 11 / 1e9; // 1 Hz
+const float Scene::kFreq1Magnitude  = 5;
+const float Scene::kFreq2Magnitude  = 1;
+const float Scene::kShakeFraction   = 0.03; // As a fraction of a scene tile
+
+// RGB->YUV, Jpeg standard
+const float Scene::kRgb2Yuv[12] = {
+       0.299f,    0.587f,    0.114f,    0.f,
+    -0.16874f, -0.33126f,      0.5f, -128.f,
+         0.5f, -0.41869f, -0.08131f, -128.f,
+};
+
+// Aperture of imaging lens
+const float Scene::kAperture = 2.8;
+
+// Sun illumination levels through the day
+const float Scene::kSunlight[24/kTimeStep] =
+{
+    0, // 00:00
+    0,
+    0,
+    kTwilightIllum, // 06:00
+    kDirectSunIllum,
+    kDirectSunIllum,
+    kDirectSunIllum, // 12:00
+    kDirectSunIllum,
+    kDirectSunIllum,
+    kSunsetIllum, // 18:00
+    kTwilightIllum,
+    0
+};
+
+// Moon illumination levels through the day
+const float Scene::kMoonlight[24/kTimeStep] =
+{
+    kFullMoonIllum, // 00:00
+    kFullMoonIllum,
+    0,
+    0, // 06:00
+    0,
+    0,
+    0, // 12:00
+    0,
+    0,
+    0, // 18:00
+    0,
+    kFullMoonIllum
+};
+
+const int Scene::kSunOverhead = 12;
+const int Scene::kMoonOverhead = 0;
+
+// Used for sun illumination levels
+const float Scene::kDirectSunIllum     = 100000;
+const float Scene::kSunsetIllum        = 400;
+const float Scene::kTwilightIllum      = 4;
+// Used for moon illumination levels
+const float Scene::kFullMoonIllum      = 1;
+// Other illumination levels
+const float Scene::kDaylightShadeIllum = 20000;
+const float Scene::kClearNightIllum    = 2e-3;
+const float Scene::kStarIllum          = 2e-6;
+const float Scene::kLivingRoomIllum    = 50;
+
+const float Scene::kIncandescentXY[2]   = { 0.44757f, 0.40745f};
+const float Scene::kDirectSunlightXY[2] = { 0.34842f, 0.35161f};
+const float Scene::kDaylightXY[2]       = { 0.31271f, 0.32902f};
+const float Scene::kNoonSkyXY[2]        = { 0.346f,   0.359f};
+const float Scene::kMoonlightXY[2]      = { 0.34842f, 0.35161f};
+const float Scene::kSunsetXY[2]         = { 0.527f,   0.413f};
+
+const uint8_t Scene::kSelfLit  = 0x01;
+const uint8_t Scene::kShadowed = 0x02;
+const uint8_t Scene::kSky      = 0x04;
+
+// For non-self-lit materials, the Y component is normalized with 1=full
+// reflectance; for self-lit materials, it's the constant illuminance in lux.
+const float Scene::kMaterials_xyY[Scene::NUM_MATERIALS][3] = {
+    { 0.3688f, 0.4501f, .1329f }, // GRASS
+    { 0.3688f, 0.4501f, .1329f }, // GRASS_SHADOW
+    { 0.3986f, 0.5002f, .4440f }, // HILL
+    { 0.3262f, 0.5040f, .2297f }, // WALL
+    { 0.4336f, 0.3787f, .1029f }, // ROOF
+    { 0.3316f, 0.2544f, .0639f }, // DOOR
+    { 0.3425f, 0.3577f, .0887f }, // CHIMNEY
+    { kIncandescentXY[0], kIncandescentXY[1], kLivingRoomIllum }, // WINDOW
+    { kDirectSunlightXY[0], kDirectSunlightXY[1], kDirectSunIllum }, // SUN
+    { kNoonSkyXY[0], kNoonSkyXY[1], kDaylightShadeIllum / kDirectSunIllum }, // SKY
+    { kMoonlightXY[0], kMoonlightXY[1], kFullMoonIllum } // MOON
+};
+
+const uint8_t Scene::kMaterialsFlags[Scene::NUM_MATERIALS] = {
+    0,
+    kShadowed,
+    kShadowed,
+    kShadowed,
+    kShadowed,
+    kShadowed,
+    kShadowed,
+    kSelfLit,
+    kSelfLit,
+    kSky,
+    kSelfLit,
+};
+
+} // namespace android
diff --git a/guest/hals/camera/fake-pipeline2/Scene.h b/guest/hals/camera/fake-pipeline2/Scene.h
new file mode 100644
index 0000000..66d1a69
--- /dev/null
+++ b/guest/hals/camera/fake-pipeline2/Scene.h
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+/**
+ * The Scene class implements a simple physical simulation of a scene, using the
+ * CIE 1931 colorspace to represent light in physical units (lux).
+ *
+ * It's fairly approximate, but does provide a scene with realistic widely
+ * variable illumination levels and colors over time.
+ *
+ */
+
+#ifndef HW_EMULATOR_CAMERA2_SCENE_H
+#define HW_EMULATOR_CAMERA2_SCENE_H
+
+#include "utils/Timers.h"
+
+namespace android {
+
+class Scene {
+  public:
+    Scene(int sensorWidthPx,
+            int sensorHeightPx,
+            float sensorSensitivity);
+    ~Scene();
+
+    // Set the filter coefficients for the red, green, and blue filters on the
+    // sensor. Used as an optimization to pre-calculate various illuminance
+    // values. Two different green filters can be provided, to account for
+    // possible cross-talk on a Bayer sensor. Must be called before
+    // calculateScene.
+    void setColorFilterXYZ(
+        float rX, float rY, float rZ,
+        float grX, float grY, float grZ,
+        float gbX, float gbY, float gbZ,
+        float bX, float bY, float bZ);
+
+    // Set time of day (24-hour clock). This controls the general light levels
+    // in the scene. Must be called before calculateScene
+    void setHour(int hour);
+    // Get current hour
+    int getHour();
+
+    // Set the duration of exposure for determining luminous exposure.
+    // Must be called before calculateScene
+    void setExposureDuration(float seconds);
+
+    // Calculate scene information for current hour and the time offset since
+    // the hour. Must be called at least once before calling getLuminousExposure.
+    // Resets pixel readout location to 0,0
+    void calculateScene(nsecs_t time);
+
+    // Set sensor pixel readout location.
+    void setReadoutPixel(int x, int y);
+
+    // Get sensor response in physical units (electrons) for light hitting the
+    // current readout pixel, after passing through color filters. The readout
+    // pixel will be auto-incremented. The returned array can be indexed with
+    // ColorChannels.
+    const uint32_t* getPixelElectrons();
+
+    enum ColorChannels {
+        R = 0,
+        Gr,
+        Gb,
+        B,
+        Y,
+        Cb,
+        Cr,
+        NUM_CHANNELS
+    };
+
+  private:
+    // Sensor color filtering coefficients in XYZ
+    float mFilterR[3];
+    float mFilterGr[3];
+    float mFilterGb[3];
+    float mFilterB[3];
+
+    int mOffsetX, mOffsetY;
+    int mMapDiv;
+
+    int mHandshakeX, mHandshakeY;
+
+    int mSensorWidth;
+    int mSensorHeight;
+    int mCurrentX;
+    int mCurrentY;
+    int mSubX;
+    int mSubY;
+    int mSceneX;
+    int mSceneY;
+    int mSceneIdx;
+    uint32_t *mCurrentSceneMaterial;
+
+    int mHour;
+    float mExposureDuration;
+    float mSensorSensitivity;
+
+    enum Materials {
+        GRASS = 0,
+        GRASS_SHADOW,
+        HILL,
+        WALL,
+        ROOF,
+        DOOR,
+        CHIMNEY,
+        WINDOW,
+        SUN,
+        SKY,
+        MOON,
+        NUM_MATERIALS
+    };
+
+    uint32_t mCurrentColors[NUM_MATERIALS*NUM_CHANNELS];
+
+    /**
+     * Constants for scene definition. These are various degrees of approximate.
+     */
+
+    // Fake handshake parameters. Two shake frequencies per axis, plus magnitude
+    // as a fraction of a scene tile, and relative magnitudes for the frequencies
+    static const float kHorizShakeFreq1;
+    static const float kHorizShakeFreq2;
+    static const float kVertShakeFreq1;
+    static const float kVertShakeFreq2;
+    static const float kFreq1Magnitude;
+    static const float kFreq2Magnitude;
+
+    static const float kShakeFraction;
+
+    // RGB->YUV conversion
+    static const float kRgb2Yuv[12];
+
+    // Aperture of imaging lens
+    static const float kAperture;
+
+    // Sun, moon illuminance levels in 2-hour increments. These don't match any
+    // real day anywhere.
+    static const uint32_t kTimeStep = 2;
+    static const float kSunlight[];
+    static const float kMoonlight[];
+    static const int kSunOverhead;
+    static const int kMoonOverhead;
+
+    // Illumination levels for various conditions, in lux
+    static const float kDirectSunIllum;
+    static const float kDaylightShadeIllum;
+    static const float kSunsetIllum;
+    static const float kTwilightIllum;
+    static const float kFullMoonIllum;
+    static const float kClearNightIllum;
+    static const float kStarIllum;
+    static const float kLivingRoomIllum;
+
+    // Chromaticity of various illumination sources
+    static const float kIncandescentXY[2];
+    static const float kDirectSunlightXY[2];
+    static const float kDaylightXY[2];
+    static const float kNoonSkyXY[2];
+    static const float kMoonlightXY[2];
+    static const float kSunsetXY[2];
+
+    static const uint8_t kSelfLit;
+    static const uint8_t kShadowed;
+    static const uint8_t kSky;
+
+    static const float kMaterials_xyY[NUM_MATERIALS][3];
+    static const uint8_t kMaterialsFlags[NUM_MATERIALS];
+
+    static const int kSceneWidth;
+    static const int kSceneHeight;
+    static const uint8_t kScene[];
+};
+
+}
+
+#endif // HW_EMULATOR_CAMERA2_SCENE_H
diff --git a/guest/hals/camera/fake-pipeline2/Sensor.cpp b/guest/hals/camera/fake-pipeline2/Sensor.cpp
new file mode 100644
index 0000000..108e336
--- /dev/null
+++ b/guest/hals/camera/fake-pipeline2/Sensor.cpp
@@ -0,0 +1,615 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+//#define LOG_NNDEBUG 0
+#define LOG_TAG "EmulatedCamera2_Sensor"
+
+#ifdef LOG_NNDEBUG
+#define ALOGVV(...) ALOGV(__VA_ARGS__)
+#else
+#define ALOGVV(...) ((void)0)
+#endif
+
+#include <utils/Log.h>
+
+#include "../EmulatedFakeCamera2.h"
+#include "Sensor.h"
+#include <cmath>
+#include <cstdlib>
+#include "guest/libs/platform_support/api_level_fixes.h"
+#include "system/camera_metadata.h"
+
+namespace android {
+
+//const nsecs_t Sensor::kExposureTimeRange[2] =
+//    {1000L, 30000000000L} ; // 1 us - 30 sec
+//const nsecs_t Sensor::kFrameDurationRange[2] =
+//    {33331760L, 30000000000L}; // ~1/30 s - 30 sec
+const nsecs_t Sensor::kExposureTimeRange[2] =
+    {1000L, 300000000L} ; // 1 us - 0.3 sec
+const nsecs_t Sensor::kFrameDurationRange[2] =
+    {33331760L, 300000000L}; // ~1/30 s - 0.3 sec
+
+const nsecs_t Sensor::kMinVerticalBlank = 10000L;
+
+const uint8_t Sensor::kColorFilterArrangement =
+    ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB;
+
+// Output image data characteristics
+const uint32_t Sensor::kMaxRawValue = 4000;
+const uint32_t Sensor::kBlackLevel  = 1000;
+
+// Sensor sensitivity
+const float Sensor::kSaturationVoltage      = 0.520f;
+const uint32_t Sensor::kSaturationElectrons = 2000;
+const float Sensor::kVoltsPerLuxSecond      = 0.100f;
+
+const float Sensor::kElectronsPerLuxSecond =
+        Sensor::kSaturationElectrons / Sensor::kSaturationVoltage
+        * Sensor::kVoltsPerLuxSecond;
+
+const float Sensor::kBaseGainFactor = (float)Sensor::kMaxRawValue /
+            Sensor::kSaturationElectrons;
+
+const float Sensor::kReadNoiseStddevBeforeGain = 1.177; // in electrons
+const float Sensor::kReadNoiseStddevAfterGain =  2.100; // in digital counts
+const float Sensor::kReadNoiseVarBeforeGain =
+            Sensor::kReadNoiseStddevBeforeGain *
+            Sensor::kReadNoiseStddevBeforeGain;
+const float Sensor::kReadNoiseVarAfterGain =
+            Sensor::kReadNoiseStddevAfterGain *
+            Sensor::kReadNoiseStddevAfterGain;
+
+const int32_t Sensor::kSensitivityRange[2] = {100, 1600};
+const uint32_t Sensor::kDefaultSensitivity = 100;
+
+/** A few utility functions for math, normal distributions */
+
+// Take advantage of IEEE floating-point format to calculate an approximate
+// square root. Accurate to within +-3.6%
+float sqrtf_approx(float r) {
+    // Modifier is based on IEEE floating-point representation; the
+    // manipulations boil down to finding approximate log2, dividing by two, and
+    // then inverting the log2. A bias is added to make the relative error
+    // symmetric about the real answer.
+    const int32_t modifier = 0x1FBB4000;
+
+    int32_t r_i = *(int32_t*)(&r);
+    r_i = (r_i >> 1) + modifier;
+
+    return *(float*)(&r_i);
+}
+
+
+
+Sensor::Sensor(uint32_t width, uint32_t height):
+        Thread(false),
+        mResolution{width, height},
+        mActiveArray{0, 0, width, height},
+        mRowReadoutTime(kFrameDurationRange[0] / height),
+        mGotVSync(false),
+        mExposureTime(kFrameDurationRange[0]-kMinVerticalBlank),
+        mFrameDuration(kFrameDurationRange[0]),
+        mGainFactor(kDefaultSensitivity),
+        mNextBuffers(NULL),
+        mFrameNumber(0),
+        mCapturedBuffers(NULL),
+        mListener(NULL),
+        mScene(width, height, kElectronsPerLuxSecond)
+{
+    ALOGV("Sensor created with pixel array %d x %d", width, height);
+}
+
+Sensor::~Sensor() {
+    shutDown();
+}
+
+status_t Sensor::startUp() {
+    ALOGV("%s: E", __FUNCTION__);
+
+    int res;
+    mCapturedBuffers = NULL;
+    res = run("EmulatedFakeCamera2::Sensor",
+            ANDROID_PRIORITY_URGENT_DISPLAY);
+
+    if (res != OK) {
+        ALOGE("Unable to start up sensor capture thread: %d", res);
+    }
+    return res;
+}
+
+status_t Sensor::shutDown() {
+    ALOGV("%s: E", __FUNCTION__);
+
+    int res;
+    res = requestExitAndWait();
+    if (res != OK) {
+        ALOGE("Unable to shut down sensor capture thread: %d", res);
+    }
+    return res;
+}
+
+Scene &Sensor::getScene() {
+    return mScene;
+}
+
+void Sensor::setExposureTime(uint64_t ns) {
+    Mutex::Autolock lock(mControlMutex);
+    ALOGVV("Exposure set to %f", ns/1000000.f);
+    mExposureTime = ns;
+}
+
+void Sensor::setFrameDuration(uint64_t ns) {
+    Mutex::Autolock lock(mControlMutex);
+    ALOGVV("Frame duration set to %f", ns/1000000.f);
+    mFrameDuration = ns;
+}
+
+void Sensor::setSensitivity(uint32_t gain) {
+    Mutex::Autolock lock(mControlMutex);
+    ALOGVV("Gain set to %d", gain);
+    mGainFactor = gain;
+}
+
+void Sensor::setDestinationBuffers(Buffers *buffers) {
+    Mutex::Autolock lock(mControlMutex);
+    mNextBuffers = buffers;
+}
+
+void Sensor::setFrameNumber(uint32_t frameNumber) {
+    Mutex::Autolock lock(mControlMutex);
+    mFrameNumber = frameNumber;
+}
+
+bool Sensor::waitForVSync(nsecs_t reltime) {
+    int res;
+    Mutex::Autolock lock(mControlMutex);
+
+    mGotVSync = false;
+    res = mVSync.waitRelative(mControlMutex, reltime);
+    if (res != OK && res != TIMED_OUT) {
+        ALOGE("%s: Error waiting for VSync signal: %d", __FUNCTION__, res);
+        return false;
+    }
+    return mGotVSync;
+}
+
+bool Sensor::waitForNewFrame(nsecs_t reltime,
+        nsecs_t *captureTime) {
+    Mutex::Autolock lock(mReadoutMutex);
+    uint8_t *ret;
+    if (mCapturedBuffers == NULL) {
+        int res;
+        res = mReadoutAvailable.waitRelative(mReadoutMutex, reltime);
+        if (res == TIMED_OUT) {
+            return false;
+        } else if (res != OK || mCapturedBuffers == NULL) {
+            ALOGE("Error waiting for sensor readout signal: %d", res);
+            return false;
+        }
+    }
+    mReadoutComplete.signal();
+
+    *captureTime = mCaptureTime;
+    mCapturedBuffers = NULL;
+    return true;
+}
+
+Sensor::SensorListener::~SensorListener() {
+}
+
+void Sensor::setSensorListener(SensorListener *listener) {
+    Mutex::Autolock lock(mControlMutex);
+    mListener = listener;
+}
+
+status_t Sensor::readyToRun() {
+    ALOGV("Starting up sensor thread");
+    mStartupTime = systemTime();
+    mNextCaptureTime = 0;
+    mNextCapturedBuffers = NULL;
+    return OK;
+}
+
+bool Sensor::threadLoop() {
+    /**
+     * Sensor capture operation main loop.
+     *
+     * Stages are out-of-order relative to a single frame's processing, but
+     * in-order in time.
+     */
+
+    /**
+     * Stage 1: Read in latest control parameters
+     */
+    uint64_t exposureDuration;
+    uint64_t frameDuration;
+    uint32_t gain;
+    Buffers *nextBuffers;
+    uint32_t frameNumber;
+    SensorListener *listener = NULL;
+    {
+        Mutex::Autolock lock(mControlMutex);
+        exposureDuration = mExposureTime;
+        frameDuration    = mFrameDuration;
+        gain             = mGainFactor;
+        nextBuffers      = mNextBuffers;
+        frameNumber      = mFrameNumber;
+        listener         = mListener;
+        // Don't reuse a buffer set
+        mNextBuffers = NULL;
+
+        // Signal VSync for start of readout
+        ALOGVV("Sensor VSync");
+        mGotVSync = true;
+        mVSync.signal();
+    }
+
+    /**
+     * Stage 3: Read out latest captured image
+     */
+
+    Buffers *capturedBuffers = NULL;
+    nsecs_t captureTime = 0;
+
+    nsecs_t startRealTime  = systemTime();
+    // Stagefright cares about system time for timestamps, so base simulated
+    // time on that.
+    nsecs_t simulatedTime    = startRealTime;
+    nsecs_t frameEndRealTime = startRealTime + frameDuration;
+    nsecs_t frameReadoutEndRealTime = startRealTime +
+            mRowReadoutTime * mResolution[1];
+
+    if (mNextCapturedBuffers != NULL) {
+        ALOGVV("Sensor starting readout");
+        // Pretend we're doing readout now; will signal once enough time has elapsed
+        capturedBuffers = mNextCapturedBuffers;
+        captureTime    = mNextCaptureTime;
+    }
+    simulatedTime += mRowReadoutTime + kMinVerticalBlank;
+
+    // TODO: Move this signal to another thread to simulate readout
+    // time properly
+    if (capturedBuffers != NULL) {
+        ALOGVV("Sensor readout complete");
+        Mutex::Autolock lock(mReadoutMutex);
+        if (mCapturedBuffers != NULL) {
+            ALOGV("Waiting for readout thread to catch up!");
+            mReadoutComplete.wait(mReadoutMutex);
+        }
+
+        mCapturedBuffers = capturedBuffers;
+        mCaptureTime = captureTime;
+        mReadoutAvailable.signal();
+        capturedBuffers = NULL;
+    }
+
+    /**
+     * Stage 2: Capture new image
+     */
+    mNextCaptureTime = simulatedTime;
+    mNextCapturedBuffers = nextBuffers;
+
+    if (mNextCapturedBuffers != NULL) {
+        if (listener != NULL) {
+            listener->onSensorEvent(frameNumber, SensorListener::EXPOSURE_START,
+                    mNextCaptureTime);
+        }
+        ALOGVV("Starting next capture: Exposure: %f ms, gain: %d",
+                (float)exposureDuration/1e6, gain);
+        mScene.setExposureDuration((float)exposureDuration/1e9);
+        mScene.calculateScene(mNextCaptureTime);
+
+        // Might be adding more buffers, so size isn't constant
+        for (size_t i = 0; i < mNextCapturedBuffers->size(); i++) {
+            const StreamBuffer &b = (*mNextCapturedBuffers)[i];
+            ALOGVV("Sensor capturing buffer %d: stream %d,"
+                    " %d x %d, format %x, stride %d, buf %p, img %p",
+                    i, b.streamId, b.width, b.height, b.format, b.stride,
+                    b.buffer, b.img);
+            switch(b.format) {
+#if VSOC_PLATFORM_SDK_AFTER(K)
+                case HAL_PIXEL_FORMAT_RAW16:
+                    captureRaw(b.img, gain, b.stride);
+                    break;
+#endif
+                case HAL_PIXEL_FORMAT_RGB_888:
+                    captureRGB(b.img, gain, b.stride);
+                    break;
+                case HAL_PIXEL_FORMAT_RGBA_8888:
+                    captureRGBA(b.img, gain, b.stride);
+                    break;
+                case HAL_PIXEL_FORMAT_BLOB:
+#if defined HAL_DATASPACE_DEPTH
+                    if (b.dataSpace != HAL_DATASPACE_DEPTH) {
+#endif
+                        // Add auxillary buffer of the right size
+                        // Assumes only one BLOB (JPEG) buffer in
+                        // mNextCapturedBuffers
+                        StreamBuffer bAux;
+                        bAux.streamId = 0;
+                        bAux.width = b.width;
+                        bAux.height = b.height;
+                        bAux.format = HAL_PIXEL_FORMAT_RGB_888;
+                        bAux.stride = b.width;
+                        bAux.buffer = NULL;
+                        // TODO: Reuse these
+                        bAux.img = new uint8_t[b.width * b.height * 3];
+                        mNextCapturedBuffers->push_back(bAux);
+#if defined HAL_DATASPACE_DEPTH
+                    } else {
+                        captureDepthCloud(b.img);
+                    }
+#endif
+                    break;
+                case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+                case HAL_PIXEL_FORMAT_YCbCr_420_888:
+                    captureNV21(b.img, gain, b.stride);
+                    break;
+                case HAL_PIXEL_FORMAT_YV12:
+                    // TODO:
+                    ALOGE("%s: Format %x is TODO", __FUNCTION__, b.format);
+                    break;
+                case HAL_PIXEL_FORMAT_Y16:
+                    captureDepth(b.img, gain, b.stride);
+                    break;
+                default:
+                    ALOGE("%s: Unknown format %x, no output", __FUNCTION__,
+                            b.format);
+                    break;
+            }
+        }
+    }
+
+    ALOGVV("Sensor vertical blanking interval");
+    nsecs_t workDoneRealTime = systemTime();
+    const nsecs_t timeAccuracy = 2e6; // 2 ms of imprecision is ok
+    if (workDoneRealTime < frameEndRealTime - timeAccuracy) {
+        timespec t;
+        t.tv_sec = (frameEndRealTime - workDoneRealTime)  / 1000000000L;
+        t.tv_nsec = (frameEndRealTime - workDoneRealTime) % 1000000000L;
+
+        int ret;
+        do {
+            ret = nanosleep(&t, &t);
+        } while (ret != 0);
+    }
+    nsecs_t endRealTime = systemTime();
+    ALOGVV("Frame cycle took %d ms, target %d ms",
+            (int)((endRealTime - startRealTime)/1000000),
+            (int)(frameDuration / 1000000));
+    return true;
+};
+
+void Sensor::captureRaw(uint8_t *img, uint32_t gain, uint32_t stride) {
+    float totalGain = gain/100.0 * kBaseGainFactor;
+    float noiseVarGain =  totalGain * totalGain;
+    float readNoiseVar = kReadNoiseVarBeforeGain * noiseVarGain
+            + kReadNoiseVarAfterGain;
+
+    int bayerSelect[4] = {Scene::R, Scene::Gr, Scene::Gb, Scene::B}; // RGGB
+    mScene.setReadoutPixel(0,0);
+    for (unsigned int y = 0; y < mResolution[1]; y++ ) {
+        int *bayerRow = bayerSelect + (y & 0x1) * 2;
+        uint16_t *px = (uint16_t*)img + y * stride;
+        for (unsigned int x = 0; x < mResolution[0]; x++) {
+            uint32_t electronCount;
+            electronCount = mScene.getPixelElectrons()[bayerRow[x & 0x1]];
+
+            // TODO: Better pixel saturation curve?
+            electronCount = (electronCount < kSaturationElectrons) ?
+                    electronCount : kSaturationElectrons;
+
+            // TODO: Better A/D saturation curve?
+            uint16_t rawCount = electronCount * totalGain;
+            rawCount = (rawCount < kMaxRawValue) ? rawCount : kMaxRawValue;
+
+            // Calculate noise value
+            // TODO: Use more-correct Gaussian instead of uniform noise
+            float photonNoiseVar = electronCount * noiseVarGain;
+            float noiseStddev = sqrtf_approx(readNoiseVar + photonNoiseVar);
+            // Scaled to roughly match gaussian/uniform noise stddev
+            float noiseSample = std::rand() * (2.5 / (1.0 + RAND_MAX)) - 1.25;
+
+            rawCount += kBlackLevel;
+            rawCount += noiseStddev * noiseSample;
+
+            *px++ = rawCount;
+        }
+        // TODO: Handle this better
+        //simulatedTime += mRowReadoutTime;
+    }
+    ALOGVV("Raw sensor image captured");
+}
+
+void Sensor::captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride) {
+    float totalGain = gain/100.0 * kBaseGainFactor;
+    // In fixed-point math, calculate total scaling from electrons to 8bpp
+    int scale64x = 64 * totalGain * 255 / kMaxRawValue;
+    uint32_t inc = ceil( (float) mResolution[0] / stride);
+
+    for (unsigned int y = 0, outY = 0; y < mResolution[1]; y+=inc, outY++ ) {
+        uint8_t *px = img + outY * stride * 4;
+        mScene.setReadoutPixel(0, y);
+        for (unsigned int x = 0; x < mResolution[0]; x+=inc) {
+            uint32_t rCount, gCount, bCount;
+            // TODO: Perfect demosaicing is a cheat
+            const uint32_t *pixel = mScene.getPixelElectrons();
+            rCount = pixel[Scene::R]  * scale64x;
+            gCount = pixel[Scene::Gr] * scale64x;
+            bCount = pixel[Scene::B]  * scale64x;
+
+            *px++ = rCount < 255*64 ? rCount / 64 : 255;
+            *px++ = gCount < 255*64 ? gCount / 64 : 255;
+            *px++ = bCount < 255*64 ? bCount / 64 : 255;
+            *px++ = 255;
+            for (unsigned int j = 1; j < inc; j++)
+                mScene.getPixelElectrons();
+        }
+        // TODO: Handle this better
+        //simulatedTime += mRowReadoutTime;
+    }
+    ALOGVV("RGBA sensor image captured");
+}
+
+void Sensor::captureRGB(uint8_t *img, uint32_t gain, uint32_t stride) {
+    float totalGain = gain/100.0 * kBaseGainFactor;
+    // In fixed-point math, calculate total scaling from electrons to 8bpp
+    int scale64x = 64 * totalGain * 255 / kMaxRawValue;
+    uint32_t inc = ceil( (float) mResolution[0] / stride);
+
+    for (unsigned int y = 0, outY = 0; y < mResolution[1]; y += inc, outY++ ) {
+        mScene.setReadoutPixel(0, y);
+        uint8_t *px = img + outY * stride * 3;
+        for (unsigned int x = 0; x < mResolution[0]; x += inc) {
+            uint32_t rCount, gCount, bCount;
+            // TODO: Perfect demosaicing is a cheat
+            const uint32_t *pixel = mScene.getPixelElectrons();
+            rCount = pixel[Scene::R]  * scale64x;
+            gCount = pixel[Scene::Gr] * scale64x;
+            bCount = pixel[Scene::B]  * scale64x;
+
+            *px++ = rCount < 255*64 ? rCount / 64 : 255;
+            *px++ = gCount < 255*64 ? gCount / 64 : 255;
+            *px++ = bCount < 255*64 ? bCount / 64 : 255;
+            for (unsigned int j = 1; j < inc; j++)
+                mScene.getPixelElectrons();
+        }
+        // TODO: Handle this better
+        //simulatedTime += mRowReadoutTime;
+    }
+    ALOGVV("RGB sensor image captured");
+}
+
+void Sensor::captureNV21(uint8_t *img, uint32_t gain, uint32_t stride) {
+    float totalGain = gain/100.0 * kBaseGainFactor;
+    // Using fixed-point math with 6 bits of fractional precision.
+    // In fixed-point math, calculate total scaling from electrons to 8bpp
+    const int scale64x = 64 * totalGain * 255 / kMaxRawValue;
+    // In fixed-point math, saturation point of sensor after gain
+    const int saturationPoint = 64 * 255;
+    // Fixed-point coefficients for RGB-YUV transform
+    // Based on JFIF RGB->YUV transform.
+    // Cb/Cr offset scaled by 64x twice since they're applied post-multiply
+    const int rgbToY[]  = {19, 37, 7};
+    const int rgbToCb[] = {-10,-21, 32, 524288};
+    const int rgbToCr[] = {32,-26, -5, 524288};
+    // Scale back to 8bpp non-fixed-point
+    const int scaleOut = 64;
+    const int scaleOutSq = scaleOut * scaleOut; // after multiplies
+
+    // inc = how many pixels to skip while reading every next pixel
+    // horizontally.
+    uint32_t inc = ceil( (float) mResolution[0] / stride);
+    // outH = projected vertical resolution based on stride.
+    uint32_t outH = mResolution[1] / inc;
+    for (unsigned int y = 0, outY = 0;
+         y < mResolution[1]; y+=inc, outY++) {
+        uint8_t *pxY = img + outY * stride;
+        uint8_t *pxVU = img + (outH + outY / 2) * stride;
+        mScene.setReadoutPixel(0,y);
+        for (unsigned int outX = 0; outX < stride; outX++) {
+            int32_t rCount, gCount, bCount;
+            // TODO: Perfect demosaicing is a cheat
+            const uint32_t *pixel = mScene.getPixelElectrons();
+            rCount = pixel[Scene::R]  * scale64x;
+            rCount = rCount < saturationPoint ? rCount : saturationPoint;
+            gCount = pixel[Scene::Gr] * scale64x;
+            gCount = gCount < saturationPoint ? gCount : saturationPoint;
+            bCount = pixel[Scene::B]  * scale64x;
+            bCount = bCount < saturationPoint ? bCount : saturationPoint;
+
+            *pxY++ = (rgbToY[0] * rCount +
+                    rgbToY[1] * gCount +
+                    rgbToY[2] * bCount) / scaleOutSq;
+            if (outY % 2 == 0 && outX % 2 == 0) {
+                *pxVU++ = (rgbToCb[0] * rCount +
+                        rgbToCb[1] * gCount +
+                        rgbToCb[2] * bCount +
+                        rgbToCb[3]) / scaleOutSq;
+                *pxVU++ = (rgbToCr[0] * rCount +
+                        rgbToCr[1] * gCount +
+                        rgbToCr[2] * bCount +
+                        rgbToCr[3]) / scaleOutSq;
+            }
+
+            // Skip unprocessed pixels from sensor.
+            for (unsigned int j = 1; j < inc; j++)
+                mScene.getPixelElectrons();
+        }
+    }
+    ALOGVV("NV21 sensor image captured");
+}
+
+void Sensor::captureDepth(uint8_t *img, uint32_t gain, uint32_t stride) {
+    float totalGain = gain/100.0 * kBaseGainFactor;
+    // In fixed-point math, calculate scaling factor to 13bpp millimeters
+    int scale64x = 64 * totalGain * 8191 / kMaxRawValue;
+    uint32_t inc = ceil( (float) mResolution[0] / stride);
+
+    for (unsigned int y = 0, outY = 0; y < mResolution[1]; y += inc, outY++ ) {
+        mScene.setReadoutPixel(0, y);
+        uint16_t *px = ((uint16_t*)img) + outY * stride;
+        for (unsigned int x = 0; x < mResolution[0]; x += inc) {
+            uint32_t depthCount;
+            // TODO: Make up real depth scene instead of using green channel
+            // as depth
+            const uint32_t *pixel = mScene.getPixelElectrons();
+            depthCount = pixel[Scene::Gr] * scale64x;
+
+            *px++ = depthCount < 8191*64 ? depthCount / 64 : 0;
+            for (unsigned int j = 1; j < inc; j++)
+                mScene.getPixelElectrons();
+        }
+        // TODO: Handle this better
+        //simulatedTime += mRowReadoutTime;
+    }
+    ALOGVV("Depth sensor image captured");
+}
+
+void Sensor::captureDepthCloud(uint8_t *img) {
+#if defined HAL_DATASPACE_DEPTH
+    android_depth_points *cloud = reinterpret_cast<android_depth_points*>(img);
+
+    cloud->num_points = 16;
+
+    // TODO: Create point cloud values that match RGB scene
+    const int FLOATS_PER_POINT = 4;
+    const float JITTER_STDDEV = 0.1f;
+    for (size_t y = 0, i = 0; y < 4; y++) {
+        for (size_t x = 0; x < 4; x++, i++) {
+            float randSampleX = std::rand() * (2.5f / (1.0f + RAND_MAX)) - 1.25f;
+            randSampleX *= JITTER_STDDEV;
+
+            float randSampleY = std::rand() * (2.5f / (1.0f + RAND_MAX)) - 1.25f;
+            randSampleY *= JITTER_STDDEV;
+
+            float randSampleZ = std::rand() * (2.5f / (1.0f + RAND_MAX)) - 1.25f;
+            randSampleZ *= JITTER_STDDEV;
+
+            cloud->xyzc_points[i * FLOATS_PER_POINT + 0] = x - 1.5f + randSampleX;
+            cloud->xyzc_points[i * FLOATS_PER_POINT + 1] = y - 1.5f + randSampleY;
+            cloud->xyzc_points[i * FLOATS_PER_POINT + 2] = 3.f + randSampleZ;
+            cloud->xyzc_points[i * FLOATS_PER_POINT + 3] = 0.8f;
+        }
+    }
+
+    ALOGVV("Depth point cloud captured");
+#endif
+}
+
+} // namespace android
diff --git a/guest/hals/camera/fake-pipeline2/Sensor.h b/guest/hals/camera/fake-pipeline2/Sensor.h
new file mode 100644
index 0000000..cdf1e97
--- /dev/null
+++ b/guest/hals/camera/fake-pipeline2/Sensor.h
@@ -0,0 +1,247 @@
+/*
+ * 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 class is a simple simulation of a typical CMOS cellphone imager chip,
+ * which outputs 12-bit Bayer-mosaic raw images.
+ *
+ * Unlike most real image sensors, this one's native color space is linear sRGB.
+ *
+ * The sensor is abstracted as operating as a pipeline 3 stages deep;
+ * conceptually, each frame to be captured goes through these three stages. The
+ * processing step for the sensor is marked off by vertical sync signals, which
+ * indicate the start of readout of the oldest frame. The interval between
+ * processing steps depends on the frame duration of the frame currently being
+ * captured. The stages are 1) configure, 2) capture, and 3) readout. During
+ * configuration, the sensor's registers for settings such as exposure time,
+ * frame duration, and gain are set for the next frame to be captured. In stage
+ * 2, the image data for the frame is actually captured by the sensor. Finally,
+ * in stage 3, the just-captured data is read out and sent to the rest of the
+ * system.
+ *
+ * The sensor is assumed to be rolling-shutter, so low-numbered rows of the
+ * sensor are exposed earlier in time than larger-numbered rows, with the time
+ * offset between each row being equal to the row readout time.
+ *
+ * The characteristics of this sensor don't correspond to any actual sensor,
+ * but are not far off typical sensors.
+ *
+ * Example timing diagram, with three frames:
+ *  Frame 0-1: Frame duration 50 ms, exposure time 20 ms.
+ *  Frame   2: Frame duration 75 ms, exposure time 65 ms.
+ * Legend:
+ *   C = update sensor registers for frame
+ *   v = row in reset (vertical blanking interval)
+ *   E = row capturing image data
+ *   R = row being read out
+ *   | = vertical sync signal
+ *time(ms)|   0          55        105       155            230     270
+ * Frame 0|   :configure : capture : readout :              :       :
+ *  Row # | ..|CCCC______|_________|_________|              :       :
+ *      0 |   :\          \vvvvvEEEER         \             :       :
+ *    500 |   : \          \vvvvvEEEER         \            :       :
+ *   1000 |   :  \          \vvvvvEEEER         \           :       :
+ *   1500 |   :   \          \vvvvvEEEER         \          :       :
+ *   2000 |   :    \__________\vvvvvEEEER_________\         :       :
+ * Frame 1|   :           configure  capture      readout   :       :
+ *  Row # |   :          |CCCC_____|_________|______________|       :
+ *      0 |   :          :\         \vvvvvEEEER              \      :
+ *    500 |   :          : \         \vvvvvEEEER              \     :
+ *   1000 |   :          :  \         \vvvvvEEEER              \    :
+ *   1500 |   :          :   \         \vvvvvEEEER              \   :
+ *   2000 |   :          :    \_________\vvvvvEEEER______________\  :
+ * Frame 2|   :          :          configure     capture    readout:
+ *  Row # |   :          :         |CCCC_____|______________|_______|...
+ *      0 |   :          :         :\         \vEEEEEEEEEEEEER       \
+ *    500 |   :          :         : \         \vEEEEEEEEEEEEER       \
+ *   1000 |   :          :         :  \         \vEEEEEEEEEEEEER       \
+ *   1500 |   :          :         :   \         \vEEEEEEEEEEEEER       \
+ *   2000 |   :          :         :    \_________\vEEEEEEEEEEEEER_______\
+ */
+
+#ifndef HW_EMULATOR_CAMERA2_SENSOR_H
+#define HW_EMULATOR_CAMERA2_SENSOR_H
+
+#include "utils/Thread.h"
+#include "utils/Mutex.h"
+#include "utils/Timers.h"
+
+#include "Scene.h"
+#include "Base.h"
+
+namespace android {
+
+class EmulatedFakeCamera2;
+
+class Sensor: private Thread, public virtual RefBase {
+  public:
+
+    // width: Width of pixel array
+    // height: Height of pixel array
+    Sensor(uint32_t width, uint32_t height);
+    ~Sensor();
+
+    /*
+     * Power control
+     */
+
+    status_t startUp();
+    status_t shutDown();
+
+    /*
+     * Access to scene
+     */
+    Scene &getScene();
+
+    /*
+     * Controls that can be updated every frame
+     */
+
+    void setExposureTime(uint64_t ns);
+    void setFrameDuration(uint64_t ns);
+    void setSensitivity(uint32_t gain);
+    // Buffer must be at least stride*height*2 bytes in size
+    void setDestinationBuffers(Buffers *buffers);
+    // To simplify tracking sensor's current frame
+    void setFrameNumber(uint32_t frameNumber);
+
+    /*
+     * Controls that cause reconfiguration delay
+     */
+
+    void setBinning(int horizontalFactor, int verticalFactor);
+
+    /*
+     * Synchronizing with sensor operation (vertical sync)
+     */
+
+    // Wait until the sensor outputs its next vertical sync signal, meaning it
+    // is starting readout of its latest frame of data. Returns true if vertical
+    // sync is signaled, false if the wait timed out.
+    bool waitForVSync(nsecs_t reltime);
+
+    // Wait until a new frame has been read out, and then return the time
+    // capture started.  May return immediately if a new frame has been pushed
+    // since the last wait for a new frame. Returns true if new frame is
+    // returned, false if timed out.
+    bool waitForNewFrame(nsecs_t reltime,
+            nsecs_t *captureTime);
+
+    /*
+     * Interrupt event servicing from the sensor. Only triggers for sensor
+     * cycles that have valid buffers to write to.
+     */
+    struct SensorListener {
+        enum Event {
+            EXPOSURE_START, // Start of exposure
+        };
+
+        virtual void onSensorEvent(uint32_t frameNumber, Event e,
+                nsecs_t timestamp) = 0;
+        virtual ~SensorListener();
+    };
+
+    void setSensorListener(SensorListener *listener);
+
+    /**
+     * Static sensor characteristics
+     */
+    const uint32_t mResolution[2];
+    const uint32_t mActiveArray[4];
+
+    static const nsecs_t kExposureTimeRange[2];
+    static const nsecs_t kFrameDurationRange[2];
+    static const nsecs_t kMinVerticalBlank;
+
+    static const uint8_t kColorFilterArrangement;
+
+    // Output image data characteristics
+    static const uint32_t kMaxRawValue;
+    static const uint32_t kBlackLevel;
+    // Sensor sensitivity, approximate
+
+    static const float kSaturationVoltage;
+    static const uint32_t kSaturationElectrons;
+    static const float kVoltsPerLuxSecond;
+    static const float kElectronsPerLuxSecond;
+
+    static const float kBaseGainFactor;
+
+    static const float kReadNoiseStddevBeforeGain; // In electrons
+    static const float kReadNoiseStddevAfterGain;  // In raw digital units
+    static const float kReadNoiseVarBeforeGain;
+    static const float kReadNoiseVarAfterGain;
+
+    // While each row has to read out, reset, and then expose, the (reset +
+    // expose) sequence can be overlapped by other row readouts, so the final
+    // minimum frame duration is purely a function of row readout time, at least
+    // if there's a reasonable number of rows.
+    const nsecs_t mRowReadoutTime;
+
+    static const int32_t kSensitivityRange[2];
+    static const uint32_t kDefaultSensitivity;
+
+  private:
+    Mutex mControlMutex; // Lock before accessing control parameters
+    // Start of control parameters
+    Condition mVSync;
+    bool      mGotVSync;
+    uint64_t  mExposureTime;
+    uint64_t  mFrameDuration;
+    uint32_t  mGainFactor;
+    Buffers  *mNextBuffers;
+    uint32_t  mFrameNumber;
+
+    // End of control parameters
+
+    Mutex mReadoutMutex; // Lock before accessing readout variables
+    // Start of readout variables
+    Condition mReadoutAvailable;
+    Condition mReadoutComplete;
+    Buffers  *mCapturedBuffers;
+    nsecs_t   mCaptureTime;
+    SensorListener *mListener;
+    // End of readout variables
+
+    // Time of sensor startup, used for simulation zero-time point
+    nsecs_t mStartupTime;
+
+    /**
+     * Inherited Thread virtual overrides, and members only used by the
+     * processing thread
+     */
+  private:
+    virtual status_t readyToRun();
+
+    virtual bool threadLoop();
+
+    nsecs_t mNextCaptureTime;
+    Buffers *mNextCapturedBuffers;
+
+    Scene mScene;
+
+    void captureRaw(uint8_t *img, uint32_t gain, uint32_t stride);
+    void captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride);
+    void captureRGB(uint8_t *img, uint32_t gain, uint32_t stride);
+    void captureNV21(uint8_t *img, uint32_t gain, uint32_t stride);
+    void captureDepth(uint8_t *img, uint32_t gain, uint32_t stride);
+    void captureDepthCloud(uint8_t *img);
+
+};
+
+}
+
+#endif // HW_EMULATOR_CAMERA2_SENSOR_H
diff --git a/guest/hals/camera/media_codecs.xml b/guest/hals/camera/media_codecs.xml
new file mode 100644
index 0000000..87d11f2
--- /dev/null
+++ b/guest/hals/camera/media_codecs.xml
@@ -0,0 +1,84 @@
+<?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
new file mode 100644
index 0000000..f3eeb12
--- /dev/null
+++ b/guest/hals/camera/media_codecs_performance.xml
@@ -0,0 +1,86 @@
+<?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
new file mode 100644
index 0000000..cd99857
--- /dev/null
+++ b/guest/hals/camera/media_profiles.xml
@@ -0,0 +1,368 @@
+<?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/gps/Android.mk b/guest/hals/gps/Android.mk
new file mode 100644
index 0000000..46c2d30
--- /dev/null
+++ b/guest/hals/gps/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# HAL module implemenation, not prelinked and stored in
+# hw/<LIGHTS_HARDWARE_MODULE_ID>.<ro.hardware>.so
+include $(CLEAR_VARS)
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MULTILIB := first
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_SRC_FILES := gps_vsoc.cpp gps_thread.cpp
+LOCAL_MODULE := gps.vsoc
+LOCAL_C_INCLUDES := device/google/cuttlefish_common
+
+LOCAL_CFLAGS := \
+    -Wall -Werror -Wno-missing-field-initializers \
+    -DLOG_TAG=\"VSoCGPS\" \
+    $(VSOC_VERSION_CFLAGS)
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/guest/hals/gps/gps_thread.cpp b/guest/hals/gps/gps_thread.cpp
new file mode 100644
index 0000000..c745d45
--- /dev/null
+++ b/guest/hals/gps/gps_thread.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/hals/gps/gps_thread.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <pthread.h>
+#include <sys/epoll.h>
+#include <time.h>
+
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+#include <hardware/gps.h>
+
+// Calls an callback function to pass received and parsed GPS data to Android.
+static void reader_call_callback(GpsDataReader* r) {
+  if (!r) {
+    ALOGW("%s: called with r=NULL", __FUNCTION__);
+    return;
+  }
+  if (!r->callback) {
+    ALOGW("%s: no callback registered; keeping the data to send later",
+          __FUNCTION__);
+    return;
+  }
+  if (!r->fix.flags) {
+    ALOGW("%s: no GPS fix", __FUNCTION__);
+    return;
+  }
+  // Always uses current time converted to UTC time in milliseconds.
+  time_t secs = time(NULL);  // seconds from 01/01/1970.
+  r->fix.timestamp = (long long)secs * 1000;
+
+#if GPS_DEBUG
+  D("* Parsed GPS Data");
+  if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
+    D(" - latitude = %g", r->fix.latitude);
+    D(" - longitude = %g", r->fix.longitude);
+  }
+  if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE)
+    D(" - altitude = %g", r->fix.altitude);
+  if (r->fix.flags & GPS_LOCATION_HAS_SPEED) D(" - speed = %g", r->fix.speed);
+  if (r->fix.flags & GPS_LOCATION_HAS_BEARING)
+    D(" - bearing = %g", r->fix.bearing);
+  if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY)
+    D(" - accuracy = %g", r->fix.accuracy);
+  long long utc_secs = r->fix.timestamp / 1000;
+  struct tm utc;
+  gmtime_r((time_t*)&utc_secs, &utc);
+  D(" - time = %s", asctime(&utc));
+#endif
+
+  D("Sending fix to callback %p", r->callback);
+  r->callback(&r->fix);
+}
+
+// Parses data received so far and calls reader_call_callback().
+static void reader_parse_message(GpsDataReader* r) {
+  D("Received: '%s'", r->buffer);
+
+  int num_read = sscanf(r->buffer, "%lf,%lf,%lf,%f,%f,%f", &r->fix.longitude,
+                        &r->fix.latitude, &r->fix.altitude, &r->fix.bearing,
+                        &r->fix.speed, &r->fix.accuracy);
+  if (num_read != 6) {
+    ALOGE("Couldn't find 6 values from the received message %s.", r->buffer);
+    return;
+  }
+  r->fix.flags = DEFAULT_GPS_LOCATION_FLAG;
+  reader_call_callback(r);
+}
+
+// Accepts a newly received string & calls reader_parse_message if '\n' is seen.
+static void reader_accept_string(GpsDataReader* r, char* const str,
+                                 const int len) {
+  int index;
+  for (index = 0; index < len; index++) {
+    if (r->index >= (int)sizeof(r->buffer) - 1) {
+      if (str[index] == '\n') {
+        ALOGW("Message longer than buffer; new byte (%d) skipped.", str[index]);
+        r->index = 0;
+      }
+    } else {
+      r->buffer[r->index++] = str[index];
+      if (str[index] == '\n') {
+        r->buffer[r->index] = '\0';
+        reader_parse_message(r);
+        r->index = 0;
+      }
+    }
+  }
+}
+
+// GPS state threads which communicates with control and data sockets.
+void gps_state_thread(void* arg) {
+  GpsState* state = (GpsState*)arg;
+  GpsDataReader reader;
+  int epoll_fd = epoll_create(2);
+  int started = -1;
+  int gps_fd = state->fd;
+  int control_fd = state->control[1];
+
+  memset(&reader, 0, sizeof(reader));
+  reader.fix.size = sizeof(reader.fix);
+
+  epoll_register(epoll_fd, control_fd);
+  epoll_register(epoll_fd, gps_fd);
+
+  while (1) {
+    struct epoll_event events[2];
+    int nevents, event_index;
+
+    nevents = epoll_wait(epoll_fd, events, 2, 500);
+    D("Thread received %d events", nevents);
+    if (nevents < 0) {
+      if (errno != EINTR)
+        ALOGE("epoll_wait() unexpected error: %s", strerror(errno));
+      continue;
+    } else if (nevents == 0) {
+      if (started == 1) {
+        reader_call_callback(&reader);
+      }
+      continue;
+    }
+
+    for (event_index = 0; event_index < nevents; event_index++) {
+      if ((events[event_index].events & (EPOLLERR | EPOLLHUP)) != 0) {
+        ALOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
+        goto Exit;
+      }
+
+      if ((events[event_index].events & EPOLLIN) != 0) {
+        int fd = events[event_index].data.fd;
+        if (fd == control_fd) {
+          unsigned char cmd = 255;
+          int ret;
+          do {
+            ret = read(fd, &cmd, 1);
+          } while (ret < 0 && errno == EINTR);
+
+          if (cmd == CMD_STOP || cmd == CMD_QUIT) {
+            if (started == 1) {
+              D("Thread stopping");
+              started = 0;
+              reader.callback = NULL;
+            }
+            if (cmd == CMD_QUIT) {
+              D("Thread quitting");
+              goto Exit;
+            }
+          } else if (cmd == CMD_START) {
+            if (started != 1) {
+              reader.callback = state->callbacks.location_cb;
+              D("Thread starting callback=%p", reader.callback);
+              reader_call_callback(&reader);
+              started = 1;
+            }
+          } else {
+            ALOGE("unknown control command %d", cmd);
+          }
+        } else if (fd == gps_fd) {
+          char buff[256];
+          int ret;
+          for (;;) {
+            ret = read(fd, buff, sizeof(buff));
+            if (ret < 0) {
+              if (errno == EINTR) continue;
+              if (errno != EWOULDBLOCK)
+                ALOGE("error while reading from gps daemon socket: %s:",
+                      strerror(errno));
+              break;
+            }
+            D("Thread received %d bytes: %.*s", ret, ret, buff);
+            reader_accept_string(&reader, buff, ret);
+          }
+        } else {
+          ALOGE("epoll_wait() returned unknown fd %d.", fd);
+        }
+      }
+    }
+  }
+
+Exit:
+  epoll_deregister(epoll_fd, control_fd);
+  epoll_deregister(epoll_fd, gps_fd);
+}
diff --git a/guest/hals/gps/gps_thread.h b/guest/hals/gps/gps_thread.h
new file mode 100644
index 0000000..7cd3c9e
--- /dev/null
+++ b/guest/hals/gps/gps_thread.h
@@ -0,0 +1,79 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+
+#include <hardware/gps.h>
+
+#define GPS_DEBUG 0
+#define GPS_DATA_BUFFER_MAX_SIZE 256
+
+#define DEFAULT_GPS_LOCATION_FLAG                          \
+  (GPS_LOCATION_HAS_LAT_LONG | GPS_LOCATION_HAS_ALTITUDE | \
+   GPS_LOCATION_HAS_BEARING | GPS_LOCATION_HAS_SPEED |     \
+   GPS_LOCATION_HAS_ACCURACY)
+
+#if GPS_DEBUG
+#define D(...) ALOGD(__VA_ARGS__)
+#else
+#define D(...) ((void)0)
+#endif
+
+// Control commands to GPS thread
+enum { CMD_QUIT = 0, CMD_START = 1, CMD_STOP = 2 };
+
+// GPS HAL's state
+typedef struct {
+  int init;
+  int fd;
+  int control[2];
+  pthread_t thread;
+  GpsCallbacks callbacks;
+} GpsState;
+
+typedef struct {
+  GpsLocation fix;
+  gps_location_callback callback;
+  char buffer[GPS_DATA_BUFFER_MAX_SIZE + 1];
+  int index;
+} GpsDataReader;
+
+void gps_state_thread(void* arg);
+
+static inline int epoll_register(int epoll_fd, int fd) {
+  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+  struct epoll_event ev;
+  ev.events = EPOLLIN;
+  ev.data.fd = fd;
+
+  int ret;
+  do {
+    ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+  } while (ret < 0 && errno == EINTR);
+  return ret;
+}
+
+static inline int epoll_deregister(int epoll_fd, int fd) {
+  int ret;
+  do {
+    ret = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
+  } while (ret < 0 && errno == EINTR);
+  return ret;
+}
diff --git a/guest/hals/gps/gps_vsoc.cpp b/guest/hals/gps/gps_vsoc.cpp
new file mode 100644
index 0000000..fde0e12
--- /dev/null
+++ b/guest/hals/gps/gps_vsoc.cpp
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+
+/* This implements a GPS hardware HAL library for cuttlefish.
+ * A produced shared library is placed in /system/lib/hw/gps.gce.so, and
+ * loaded by hardware/libhardware/hardware.c code which is called from
+ * android_location_GpsLocationProvider.cpp
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+#include <hardware/gps.h>
+
+#include "guest/hals/gps/gps_thread.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+static GpsState _gps_state;
+
+// Cleans up GpsState data structure.
+static void gps_state_cleanup(GpsState* s) {
+  char cmd = CMD_QUIT;
+
+  write(s->control[0], &cmd, 1);
+  if (s->thread > 0) {
+    pthread_join(s->thread, NULL);
+  }
+
+  close(s->control[0]);
+  close(s->control[1]);
+  close(s->fd);
+
+  s->thread = 0;
+  s->control[0] = -1;
+  s->control[1] = -1;
+  s->fd = -1;
+  s->init = 0;
+}
+
+static int gce_gps_init(GpsCallbacks* callbacks) {
+  D("%s: called", __FUNCTION__);
+  // Stop if the framework does not fulfill its interface contract.
+  // We don't want to return an error and continue to ensure that we
+  // catch framework breaks ASAP and to give a tombstone to track down the
+  // offending code.
+  LOG_ALWAYS_FATAL_IF(!callbacks->location_cb);
+  LOG_ALWAYS_FATAL_IF(!callbacks->status_cb);
+  LOG_ALWAYS_FATAL_IF(!callbacks->sv_status_cb);
+  LOG_ALWAYS_FATAL_IF(!callbacks->nmea_cb);
+  LOG_ALWAYS_FATAL_IF(!callbacks->set_capabilities_cb);
+  LOG_ALWAYS_FATAL_IF(!callbacks->acquire_wakelock_cb);
+  LOG_ALWAYS_FATAL_IF(!callbacks->release_wakelock_cb);
+  LOG_ALWAYS_FATAL_IF(!callbacks->create_thread_cb);
+  LOG_ALWAYS_FATAL_IF(!callbacks->request_utc_time_cb);
+  if (!_gps_state.init) {
+    _gps_state.init = 1;
+    _gps_state.control[0] = -1;
+    _gps_state.control[1] = -1;
+    _gps_state.thread = 0;
+
+    _gps_state.fd = socket_local_client(
+        "gps_broadcasts", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    if (_gps_state.fd < 0) {
+      ALOGE("no GPS emulation detected.");
+      goto Fail;
+    }
+    D("GPS HAL will receive data from remoter via gps_broadcasts channel.");
+
+    if (socketpair(AF_LOCAL, SOCK_STREAM, 0, _gps_state.control) < 0) {
+      ALOGE("could not create thread control socket pair: %s", strerror(errno));
+      goto Fail;
+    }
+
+    _gps_state.callbacks = *callbacks;
+    ALOGE("Starting thread callback=%p", callbacks->location_cb);
+    _gps_state.thread = callbacks->create_thread_cb(
+        "gps_state_thread", gps_state_thread, &_gps_state);
+    if (!_gps_state.thread) {
+      ALOGE("could not create GPS thread: %s", strerror(errno));
+      goto Fail;
+    }
+  }
+
+  if (_gps_state.fd < 0) return -1;
+  return 0;
+
+Fail:
+  gps_state_cleanup(&_gps_state);
+  return -1;
+}
+
+static void gce_gps_cleanup() {
+  D("%s: called", __FUNCTION__);
+  if (_gps_state.init) gps_state_cleanup(&_gps_state);
+}
+
+static int gce_gps_start() {
+  if (!_gps_state.init) {
+    ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
+    return -1;
+  }
+
+  char cmd = CMD_START;
+  int ret;
+  do {
+    ret = write(_gps_state.control[0], &cmd, 1);
+  } while (ret < 0 && errno == EINTR);
+
+  if (ret != 1) {
+    D("%s: could not send CMD_START command: ret=%d: %s", __FUNCTION__, ret,
+      strerror(errno));
+    return -1;
+  }
+
+  return 0;
+}
+
+static int gce_gps_stop() {
+  D("%s: called", __FUNCTION__);
+  if (!_gps_state.init) {
+    ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
+    return -1;
+  }
+
+  char cmd = CMD_STOP;
+  int ret;
+
+  do {
+    ret = write(_gps_state.control[0], &cmd, 1);
+  } while (ret < 0 && errno == EINTR);
+
+  if (ret != 1) {
+    ALOGE("%s: could not send CMD_STOP command: ret=%d: %s", __FUNCTION__, ret,
+          strerror(errno));
+    return -1;
+  }
+  return 0;
+}
+
+static int gce_gps_inject_time(GpsUtcTime /*time*/, int64_t /*time_ref*/,
+                               int /*uncertainty*/) {
+  D("%s: called", __FUNCTION__);
+  if (!_gps_state.init) {
+    ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
+    return -1;
+  }
+
+  return 0;
+}
+
+static int gce_gps_inject_location(double /*latitude*/, double /*longitude*/,
+                                   float /*accuracy*/) {
+  D("%s: called", __FUNCTION__);
+  if (!_gps_state.init) {
+    ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
+    return -1;
+  }
+
+  return 0;
+}
+
+static void gce_gps_delete_aiding_data(GpsAidingData /*flags*/) {
+  D("%s: called", __FUNCTION__);
+  if (!_gps_state.init) {
+    ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
+    return;
+  }
+}
+
+static int gce_gps_set_position_mode(GpsPositionMode mode,
+                                     GpsPositionRecurrence recurrence,
+                                     uint32_t min_interval,
+                                     uint32_t preferred_accuracy,
+                                     uint32_t preferred_time) {
+  D("%s: called", __FUNCTION__);
+  if (!_gps_state.init) {
+    ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
+    return -1;
+  }
+  ALOGE("%s(mode=%d, recurrence=%d, min_interval=%" PRIu32
+        ", "
+        "preferred_accuracy=%" PRIu32 ", preferred_time=%" PRIu32
+        ") unimplemented",
+        __FUNCTION__, mode, recurrence, min_interval, preferred_accuracy,
+        preferred_time);
+  return 0;
+}
+
+static const void* gce_gps_get_extension(const char* name) {
+  D("%s: called", __FUNCTION__);
+  // It is normal for this to be called before init.
+  ALOGE("%s(%s): called but not implemented.", __FUNCTION__,
+        name ? name : "NULL");
+  return NULL;
+}
+
+static const GpsInterface gceGpsInterface = {
+    sizeof(GpsInterface),
+    gce_gps_init,
+    gce_gps_start,
+    gce_gps_stop,
+    gce_gps_cleanup,
+    gce_gps_inject_time,
+    gce_gps_inject_location,
+    gce_gps_delete_aiding_data,
+    gce_gps_set_position_mode,
+    gce_gps_get_extension,
+};
+
+const GpsInterface* gps_get_gps_interface(struct gps_device_t* /*dev*/) {
+  return &gceGpsInterface;
+}
+
+static int open_gps(const struct hw_module_t* module, char const* /*name*/,
+                    struct hw_device_t** device) {
+  struct gps_device_t* dev =
+      (struct gps_device_t*)malloc(sizeof(struct gps_device_t));
+  LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+  memset(dev, 0, sizeof(*dev));
+
+  dev->common.tag = HARDWARE_DEVICE_TAG;
+  dev->common.version = 0;
+  dev->common.module = (struct hw_module_t*)module;
+  dev->get_gps_interface = gps_get_gps_interface;
+
+  *device = (struct hw_device_t*)dev;
+  return 0;
+}
+
+static struct hw_module_methods_t gps_module_methods = {
+    VSOC_STATIC_INITIALIZER(open) open_gps};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+    VSOC_STATIC_INITIALIZER(tag) HARDWARE_MODULE_TAG,
+    VSOC_STATIC_INITIALIZER(version_major) 1,
+    VSOC_STATIC_INITIALIZER(version_minor) 0,
+    VSOC_STATIC_INITIALIZER(id) GPS_HARDWARE_MODULE_ID,
+    VSOC_STATIC_INITIALIZER(name) "GCE GPS Module",
+    VSOC_STATIC_INITIALIZER(author) "The Android Open Source Project",
+    VSOC_STATIC_INITIALIZER(methods) & gps_module_methods,
+};
diff --git a/guest/hals/gralloc/Android.mk b/guest/hals/gralloc/Android.mk
new file mode 100644
index 0000000..5e4a648
--- /dev/null
+++ b/guest/hals/gralloc/Android.mk
@@ -0,0 +1,59 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := gralloc.vsoc-future
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+    gralloc.cpp \
+    mapper.cpp
+
+LOCAL_C_INCLUDES += \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_kernel \
+    hardware/libhardware/include \
+    system/core/base/include
+
+LOCAL_CFLAGS := \
+    -DLOG_TAG=\"gralloc_vsoc\" \
+    -Wno-missing-field-initializers \
+    -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libcutils \
+    cuttlefish_auto_resources \
+    libcuttlefish_fs \
+    liblog \
+    vsoc_lib
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+LOCAL_VENDOR_MODULE := true
+
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/hals/gralloc/gralloc.cpp b/guest/hals/gralloc/gralloc.cpp
new file mode 100644
index 0000000..32bcc07
--- /dev/null
+++ b/guest/hals/gralloc/gralloc.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gralloc_vsoc_priv.h"
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <log/log.h>
+#include <stdlib.h>
+
+namespace {
+
+static const int kSwiftShaderPadding = 4;
+
+inline void formatToYcbcr(
+    int format, int width, int height, void* base_v, android_ycbcr* ycbcr) {
+  uintptr_t it = reinterpret_cast<uintptr_t>(base_v);
+  // Clear reserved fields;
+  memset(ycbcr, 0, sizeof(*ycbcr));
+  switch (format) {
+    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+      ycbcr->ystride = align(width, 16);
+      ycbcr->cstride = align(ycbcr->ystride / 2, 16);
+      ycbcr->chroma_step = 1;
+      ycbcr->y = reinterpret_cast<void*>(it);
+      it += ycbcr->ystride * height;
+      ycbcr->cr = reinterpret_cast<void*>(it);
+      it += ycbcr->cstride * height / 2;
+      ycbcr->cb = reinterpret_cast<void*>(it);
+      break;
+    default:
+      ALOGE("%s: can't deal with format=0x%x", __FUNCTION__, format);
+  }
+}
+
+inline int formatToBytesPerPixel(int format) {
+  switch (format) {
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+    // The camera 3.0 implementation assumes that IMPLEMENTATION_DEFINED
+    // means HAL_PIXEL_FORMAT_RGBA_8888
+    case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+      return 4;
+    case HAL_PIXEL_FORMAT_RGB_888:
+      return 3;
+    case HAL_PIXEL_FORMAT_RGB_565:
+    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+      return 2;
+    case HAL_PIXEL_FORMAT_BLOB:
+      return 1;
+    default:
+      ALOGE("%s: unknown format=%d", __FUNCTION__, format);
+      return 4;
+  }
+}
+
+inline int formatToBytesPerFrame(int format, int w, int h) {
+  int bytes_per_pixel = formatToBytesPerPixel(format);
+  int w16, h16;
+  int y_size, c_size;
+
+  switch (format) {
+    // BLOB is used to allocate buffers for JPEG formatted data. Bytes per pixel
+    // is 1, the desired buffer size is in w, and h should be 1. We refrain from
+    // adding additional padding, although the caller is likely to round
+    // up to a page size.
+    case HAL_PIXEL_FORMAT_BLOB:
+      return bytes_per_pixel * w * h;
+    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+      android_ycbcr strides;
+      formatToYcbcr(format, w, h, NULL, &strides);
+      y_size = strides.ystride * h;
+      c_size = strides.cstride * h / 2;
+      return (y_size + 2 * c_size + kSwiftShaderPadding);
+    /*case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+    case HAL_PIXEL_FORMAT_RGB_888:
+    case HAL_PIXEL_FORMAT_RGB_565:*/
+    default:
+      w16 = align(w, 16);
+      h16 = align(h, 16);
+      return bytes_per_pixel * w16 * h16 + kSwiftShaderPadding;
+  }
+}
+
+}
+
+/******************************************************************************/
+
+void dump(struct alloc_device_t */*dev*/, char */*buff*/, int /*buff_len*/) {}
+
+/******************************************************************************/
+
+int lock(struct gralloc_module_t const* /*module*/,
+         buffer_handle_t handle,
+         int /*usage*/,
+         int /*l*/,
+         int /*t*/,
+         int /*w*/,
+         int /*h*/,
+         void** vaddr) {
+  if (!vaddr || vsoc_buffer_handle_t::validate(handle)) {
+    return -EINVAL;
+  }
+  // TODO(jemoreira): Check allocation usage flags against requested usage.
+  const vsoc_buffer_handle_t* hnd =
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle);
+  void* mapped = reference_buffer(hnd);
+  if (mapped == NULL) {
+    ALOGE("Unable to reference buffer, %s", __FUNCTION__);
+    return -1;
+  }
+  *vaddr = mapped;
+  return 0;
+}
+
+int unlock(struct gralloc_module_t const* /*module*/, buffer_handle_t handle) {
+  if (vsoc_buffer_handle_t::validate(handle)) {
+    return -EINVAL;
+  }
+  return unreference_buffer(
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+}
+
+int lock_ycbcr(struct gralloc_module_t const* module,
+               buffer_handle_t handle,
+               int usage,
+               int l,
+               int t,
+               int w,
+               int h,
+               struct android_ycbcr* ycbcr) {
+  void* mapped;
+  int retval = lock(module, handle, usage, l, t, w, h, &mapped);
+  if (retval) {
+    return retval;
+  }
+  const vsoc_buffer_handle_t* hnd =
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle);
+  formatToYcbcr(hnd->format, w, h, mapped, ycbcr);
+  return 0;
+}
+
+/******************************************************************************/
+
+static int gralloc_alloc(
+    alloc_device_t* dev, int w, int h, int format, int /*usage*/,
+    buffer_handle_t* pHandle, int* pStrideInPixels) {
+  int fd = -1;
+
+  int bytes_per_pixel = formatToBytesPerPixel(format);
+  int bytes_per_line;
+  int stride_in_pixels;
+  int size = 0;
+  uint32_t offset = 0;
+  // SwiftShader can't handle RGB_888, so fail fast and hard if we try to create
+  // a gralloc buffer in this format.
+  ALOG_ASSERT(format != HAL_PIXEL_FORMAT_RGB_888);
+  if (format == HAL_PIXEL_FORMAT_YV12) {
+    bytes_per_line = align(bytes_per_pixel * w, 16);
+  } else {
+    bytes_per_line = align(bytes_per_pixel * w, 8);
+  }
+  size = align(size + formatToBytesPerFrame(format, w, h), PAGE_SIZE);
+  size += PAGE_SIZE;
+  fd = reinterpret_cast<vsoc_alloc_device_t*>(dev)
+           ->gralloc_region->AllocateBuffer(size, &offset);
+  if (fd < 0) {
+    ALOGE("Unable to allocate buffer (%s)", strerror(-fd));
+    return fd;
+  }
+
+  stride_in_pixels = bytes_per_line / bytes_per_pixel;
+  vsoc_buffer_handle_t* hnd = new vsoc_buffer_handle_t(fd,
+                                                       offset,
+                                                       size,
+                                                       format,
+                                                       w, h,
+                                                       stride_in_pixels);
+  void* addr =
+      reference_buffer(reinterpret_cast<const vsoc_buffer_handle_t*>(hnd));
+  if (!addr) {
+    ALOGE("Unable to reference buffer, %s", __FUNCTION__);
+    return -EIO;
+  }
+
+  *pHandle = hnd;
+  *pStrideInPixels = stride_in_pixels;
+
+  return 0;
+}
+
+static int gralloc_free(alloc_device_t* /*dev*/, buffer_handle_t handle) {
+  // No need to do anything else, the buffer will be atomatically deallocated
+  // when the handle is closed.
+  return unreference_buffer(
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+}
+
+static int register_buffer(struct gralloc_module_t const* /*module*/,
+                          buffer_handle_t handle) {
+  if (vsoc_buffer_handle_t::validate(handle)) {
+    return -EINVAL;
+  }
+  void* addr =
+      reference_buffer(reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+  if (!addr) {
+    ALOGE("Unable to reference buffer, %s", __FUNCTION__);
+    return -EIO;
+  }
+  return 0;
+}
+
+int unregister_buffer(struct gralloc_module_t const* /*module*/,
+                     buffer_handle_t handle) {
+  if (vsoc_buffer_handle_t::validate(handle)) {
+    return -EINVAL;
+  }
+  return unreference_buffer(
+      reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+}
+
+/******************************************************************************/
+
+static int gralloc_device_close(struct hw_device_t *dev) {
+  vsoc_alloc_device_t* pdev = reinterpret_cast<vsoc_alloc_device_t*>(dev);
+  if (pdev) {
+    free(pdev);
+  }
+  return 0;
+}
+
+static int gralloc_device_open(
+    const hw_module_t* module, const char* name, hw_device_t** device) {
+    int status = -EINVAL;
+  if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
+    vsoc_alloc_device_t *dev;
+    dev = (vsoc_alloc_device_t*) malloc(sizeof(*dev));
+    LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+
+    /* initialize our state here */
+    memset(dev, 0, sizeof(*dev));
+
+    /* initialize the procs */
+    dev->device.common.tag = HARDWARE_DEVICE_TAG;
+    dev->device.common.version = 0; // TODO(jemoreira): Bump to 0_2 when stable
+    dev->device.common.module = const_cast<hw_module_t*>(module);
+    dev->device.common.close = gralloc_device_close;
+
+    dev->device.alloc   = gralloc_alloc;
+    dev->device.free    = gralloc_free;
+
+    dev->gralloc_region = vsoc::gralloc::GrallocRegionView::GetInstance();
+    if (!dev->gralloc_region) {
+      LOG_FATAL("Unable to instantiate the gralloc region");
+      free(dev);
+      return -EIO;
+    }
+
+    *device = &dev->device.common;
+    status = 0;
+  }
+  // TODO(jemoreira): Consider opening other type of devices (framebuffer)
+  return status;
+}
+
+/******************************************************************************/
+
+static struct hw_module_methods_t gralloc_module_methods = {
+  .open = gralloc_device_open
+};
+
+struct vsoc_gralloc_module_t HAL_MODULE_INFO_SYM = {
+  .base = {
+    .common = {
+      .tag = HARDWARE_MODULE_TAG,
+      .version_major = GRALLOC_MODULE_API_VERSION_0_2,
+      .version_minor = 0,
+      .id = GRALLOC_HARDWARE_MODULE_ID,
+      .name = "VSoC X86 Graphics Memory Allocator Module",
+      .author = "The Android Open Source Project",
+      .methods = &gralloc_module_methods,
+      .dso = NULL,
+      .reserved = {0},
+    },
+    .registerBuffer = register_buffer,
+    .unregisterBuffer = unregister_buffer,
+    .lock = lock,
+    .unlock = unlock,
+    .lock_ycbcr = lock_ycbcr,
+    .perform = NULL,
+  },
+};
diff --git a/guest/hals/gralloc/gralloc_vsoc_priv.h b/guest/hals/gralloc/gralloc_vsoc_priv.h
new file mode 100644
index 0000000..2f4f287
--- /dev/null
+++ b/guest/hals/gralloc/gralloc_vsoc_priv.h
@@ -0,0 +1,102 @@
+#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 <cutils/native_handle.h>
+#include <hardware/gralloc.h>
+#include <log/log.h>
+
+#include "guest/vsoc/lib/gralloc_region_view.h"
+
+struct vsoc_alloc_device_t {
+  alloc_device_t device;
+  vsoc::gralloc::GrallocRegionView* gralloc_region;
+};
+
+struct vsoc_gralloc_module_t {
+  gralloc_module_t base;
+};
+
+static_assert(sizeof(int) >= 4, "At least 4 bytes are needed for offsets");
+
+struct vsoc_buffer_handle_t : public native_handle {
+  // File descriptors
+  int fd;
+  // ints
+  int magic;
+  int format;
+  int x_res;
+  int y_res;
+  int stride_in_pixels;
+  int size;
+  // buffer offset in bytes divided by PAGE_SIZE
+  int offset;
+
+  static inline int sNumInts() {
+    return ((sizeof(vsoc_buffer_handle_t) - sizeof(native_handle_t)) /
+                sizeof(int) -
+            sNumFds);
+  }
+  static const int sNumFds = 1;
+  static const int sMagic = 0xc63752f4;
+
+  vsoc_buffer_handle_t(int fd,
+                       int offset,
+                       int size,
+                       int format,
+                       int x_res,
+                       int y_res,
+                       int stride_in_pixels)
+      : fd(fd),
+        magic(sMagic),
+        format(format),
+        x_res(x_res),
+        y_res(y_res),
+        stride_in_pixels(stride_in_pixels),
+        size(size),
+        offset(offset) {
+    version = sizeof(native_handle);
+    numInts = sNumInts();
+    numFds = sNumFds;
+  }
+
+  ~vsoc_buffer_handle_t() {
+    magic = 0;
+  }
+
+  static int validate(const native_handle* handle) {
+    const vsoc_buffer_handle_t* hnd =
+        reinterpret_cast<const vsoc_buffer_handle_t*>(handle);
+    if (!hnd || hnd->version != sizeof(native_handle) ||
+        hnd->numInts != sNumInts() || hnd->numFds != sNumFds ||
+        hnd->magic != sMagic) {
+      ALOGE("Invalid gralloc handle (at %p)", handle);
+      return -EINVAL;
+    }
+    return 0;
+  }
+};
+
+// These functions are to be used to map/unmap gralloc buffers. They are thread
+// safe and ensure that the same buffer never gets mapped twice.
+void* reference_buffer(const vsoc_buffer_handle_t* hnd);
+int unreference_buffer(const vsoc_buffer_handle_t* hnd);
+
+// TODO(jemoreira): Move this to a place where it can be used by the gralloc
+// region as well.
+inline int align(int input, int alignment) {
+  return (input + alignment - 1) & -alignment;
+}
diff --git a/guest/hals/gralloc/legacy/Android.mk b/guest/hals/gralloc/legacy/Android.mk
new file mode 100644
index 0000000..96ac256
--- /dev/null
+++ b/guest/hals/gralloc/legacy/Android.mk
@@ -0,0 +1,52 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Temporary, should be removed once vsoc hals are in usable state
+
+LOCAL_PATH := $(call my-dir)
+
+VSOC_GRALLOC_COMMON_SRC_FILES := \
+    gralloc.cpp \
+    framebuffer.cpp \
+    mapper.cpp
+
+VSOC_GRALLOC_COMMON_CFLAGS:= \
+    -DLOG_TAG=\"gralloc_vsoc_legacy\" \
+    -Wno-missing-field-initializers \
+    -Wall -Werror \
+    $(VSOC_VERSION_CFLAGS)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := gralloc.vsoc
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(VSOC_GRALLOC_COMMON_SRC_FILES)
+
+LOCAL_CFLAGS := $(VSOC_GRALLOC_COMMON_CFLAGS)
+LOCAL_C_INCLUDES := device/google/cuttlefish_common
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    libutils \
+    libcutils \
+    libvsocframebuffer
+
+LOCAL_VENDOR_MODULE := true
+
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/gralloc/legacy/framebuffer.cpp b/guest/hals/gralloc/legacy/framebuffer.cpp
new file mode 100644
index 0000000..4251433
--- /dev/null
+++ b/guest/hals/gralloc/legacy/framebuffer.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <sys/mman.h>
+
+#include <dlfcn.h>
+
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include <sys/system_properties.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/atomic.h>
+#include <private/android_filesystem_config.h>
+
+#if defined(__ANDROID__)
+#include <linux/fb.h>
+#endif
+
+#include "gralloc_vsoc_priv.h"
+
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer.h>
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h>
+#include <guest/libs/legacy_framebuffer/RegionRegistry.h>
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+
+/*****************************************************************************/
+
+struct fb_context_t {
+  framebuffer_device_t  device;
+};
+
+/*****************************************************************************/
+
+static int fb_setSwapInterval(struct framebuffer_device_t* dev, int interval) {
+  if (interval < dev->minSwapInterval || interval > dev->maxSwapInterval) {
+    return -EINVAL;
+  }
+  // FIXME: implement fb_setSwapInterval
+  return 0;
+}
+
+/*
+ * These functions (and probably the entire framebuffer device) are most likely
+ * not used when the hardware composer device is present, however is hard to be
+ * 100% sure.
+ */
+static int fb_setUpdateRect(
+    struct framebuffer_device_t* dev __unused, int l, int t, int w, int h) {
+  if (((w|h) <= 0) || ((l|t)<0)) {
+    return -EINVAL;
+  }
+  // TODO(jemoreira): Find a way to broadcast this with the framebuffer control.
+  return 0;
+}
+
+static int fb_post(struct framebuffer_device_t* dev __unused, buffer_handle_t buffer) {
+  const int yoffset = YOffsetFromHandle(buffer);
+  if (yoffset >= 0) {
+    int retval =
+        VSoCFrameBufferControl::getInstance().BroadcastFrameBufferChanged(
+            yoffset);
+    if (retval) ALOGI("Failed to post framebuffer");
+
+    return retval;
+  }
+  return -1;
+}
+
+/*****************************************************************************/
+
+int initUserspaceFrameBuffer(struct private_module_t* module) {
+  cvd::LockGuard<pthread_mutex_t> guard(module->lock);
+  if (module->framebuffer) {
+    return 0;
+  }
+
+  int fd;
+  if (!VSoCFrameBuffer::OpenFrameBuffer(&fd)) {
+    return -errno;
+  }
+
+  const VSoCFrameBuffer& config = VSoCFrameBuffer::getInstance();
+
+  /*
+   * map the framebuffer
+   */
+  module->framebuffer =
+      new private_handle_t(fd,
+                           config.total_buffer_size(),
+                           config.hal_format(),
+                           config.x_res(),
+                           config.y_res(),
+                           config.line_length() / (config.bits_per_pixel() / 8),
+                           private_handle_t::PRIV_FLAGS_FRAMEBUFFER);
+  reference_region("framebuffer_init", module->framebuffer);
+
+  return 0;
+}
+
+
+/*****************************************************************************/
+
+static int fb_close(struct hw_device_t *dev) {
+  fb_context_t* ctx = (fb_context_t*)dev;
+  if (ctx) {
+    free(ctx);
+  }
+  return 0;
+}
+
+int fb_device_open(
+    hw_module_t const* module, const char* name, hw_device_t** device) {
+  int status = -EINVAL;
+  if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {
+    /* initialize our state here */
+    fb_context_t* dev = (fb_context_t*) malloc(sizeof(*dev));
+    LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+    memset(dev, 0, sizeof(*dev));
+
+    /* initialize the procs */
+    dev->device.common.tag = HARDWARE_DEVICE_TAG;
+    dev->device.common.version = 0;
+    dev->device.common.module = const_cast<hw_module_t*>(module);
+    dev->device.common.close = fb_close;
+    dev->device.setSwapInterval = fb_setSwapInterval;
+    dev->device.post            = fb_post;
+    dev->device.setUpdateRect   = fb_setUpdateRect;
+
+    private_module_t* m = (private_module_t*)module;
+
+    status = initUserspaceFrameBuffer(m);
+
+    const VSoCFrameBuffer& config = VSoCFrameBuffer::getInstance();
+
+    if (status >= 0) {
+      int stride = config.line_length() / (config.bits_per_pixel() / 8);
+      int format = config.hal_format();
+      const_cast<uint32_t&>(dev->device.flags) = 0;
+      const_cast<uint32_t&>(dev->device.width) = config.x_res();
+      const_cast<uint32_t&>(dev->device.height) = config.y_res();
+      const_cast<int&>(dev->device.stride) = stride;
+      const_cast<int&>(dev->device.format) = format;
+      const_cast<float&>(dev->device.xdpi) = config.dpi();
+      const_cast<float&>(dev->device.ydpi) = config.dpi();
+      // TODO (jemoreira): DRY!! Managed by the vsync thread in the hwcomposer
+      const_cast<float&>(dev->device.fps) = (60 * 1000) / 1000.0f;
+      const_cast<int&>(dev->device.minSwapInterval) = 1;
+      const_cast<int&>(dev->device.maxSwapInterval) = 1;
+      *device = &dev->device.common;
+    }
+  }
+  return status;
+}
diff --git a/guest/hals/gralloc/legacy/gralloc.cpp b/guest/hals/gralloc/legacy/gralloc.cpp
new file mode 100644
index 0000000..e2c4104
--- /dev/null
+++ b/guest/hals/gralloc/legacy/gralloc.cpp
@@ -0,0 +1,304 @@
+/*
+ * 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 <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+#include <utils/String8.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+
+#include <guest/libs/platform_support/api_level_fixes.h>
+
+#include "guest/libs/legacy_framebuffer/vsoc_framebuffer.h"
+#include "guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h"
+#include "guest/libs/legacy_framebuffer/RegionRegistry.h"
+#include "gralloc_vsoc_priv.h"
+#include "common/libs/auto_resources/auto_resources.h"
+
+/*****************************************************************************/
+
+static int gralloc_alloc_buffer(
+    alloc_device_t* /*dev*/, int format, int w, int h,
+    buffer_handle_t* pHandle, int* pStrideInPixels) {
+  int err = 0;
+  int fd = -1;
+  static int sequence = 0;
+
+  int bytes_per_pixel = formatToBytesPerPixel(format);
+  int bytes_per_line;
+  int stride_in_pixels;
+  int size = 0;
+  // SwiftShader can't handle RGB_888, so fail fast and hard if we try to create
+  // a gralloc buffer in this format.
+  ALOG_ASSERT(format != HAL_PIXEL_FORMAT_RGB_888);
+  if (format == HAL_PIXEL_FORMAT_YV12) {
+    bytes_per_line = VSoCFrameBuffer::align(bytes_per_pixel * w, 16);
+  } else {
+    bytes_per_line = VSoCFrameBuffer::align(bytes_per_pixel * w);
+  }
+  size = roundUpToPageSize(size + formatToBytesPerFrame(format, w, h));
+  size += PAGE_SIZE;
+  fd = ashmem_create_region(
+      android::String8::format(
+          "gralloc-%d.%d", getpid(), sequence++).string(),
+      size);
+  if (fd < 0) {
+    ALOGE("couldn't create ashmem (%s)", strerror(-errno));
+    err = -errno;
+  }
+
+  if (err == 0) {
+    stride_in_pixels = bytes_per_line / bytes_per_pixel;
+    private_handle_t* hnd =
+        new private_handle_t(fd, size, format, w, h, stride_in_pixels, 0);
+    void* base = reference_region(__FUNCTION__, hnd);
+    if (base) {
+      *pHandle = hnd;
+      *pStrideInPixels = stride_in_pixels;
+    } else {
+      err = -EIO;
+    }
+  }
+
+  ALOGE_IF(err, "gralloc failed err=%s", strerror(-err));
+
+  return err;
+}
+
+/*****************************************************************************/
+
+static int ensure_framebuffer_allocated(private_module_t* m) {
+  // allocate the framebuffer
+  if (m->framebuffer == NULL) {
+    // The framebuffer is mapped once and forever.
+    int err = initUserspaceFrameBuffer(m);
+    if (err < 0) {
+      ALOGE("Failed to map framebuffer (%d)", errno);
+      return err;
+    }
+  }
+  return 0;
+}
+
+static int gralloc_free_framebuffer(private_handle_t const * hnd) {
+    static const VSoCFrameBuffer& config = VSoCFrameBuffer::getInstance();
+    static VSoCFrameBufferControl& fb_control =
+        VSoCFrameBufferControl::getInstance();
+
+    // free this buffer
+    const size_t bufferSize = config.line_length() * config.y_res();
+    int index = hnd->frame_offset / bufferSize;
+    return fb_control.UnsetBufferBits(1LU << index);
+}
+
+// Allocates a framebuffer taken from the set of buffers given by buffer_mask.
+static int gralloc_alloc_framebuffer(alloc_device_t* dev,
+                                     buffer_handle_t* pHandle,
+                                     int* pStrideInPixels,
+                                     uint32_t buffer_mask) {
+  static const VSoCFrameBuffer& config = VSoCFrameBuffer::getInstance();
+  static VSoCFrameBufferControl& fb_control =
+      VSoCFrameBufferControl::getInstance();
+
+  private_module_t* m = reinterpret_cast<private_module_t*>(
+      dev->common.module);
+
+  ensure_framebuffer_allocated(m);
+
+  const uint32_t numBuffers = VSoCFrameBuffer::kNumBuffers;
+  const size_t bufferSize = config.bufferSize();
+
+  // Paranoia: Force the mask to be valid
+  buffer_mask &= (1LU << numBuffers) - 1;
+
+  uint32_t bit = fb_control.GetAndSetNextAvailableBufferBit(buffer_mask);
+  if (!bit) {
+    // All buffers in the mask have been allocated already or there was another
+    // error
+    return -ENOMEM;
+  }
+
+  int frame_offset = 0;
+  while (bit != 1LU) {
+    bit >>= 1;
+    frame_offset += bufferSize;
+  }
+
+  int stride_in_pixels = config.line_length() / (config.bits_per_pixel() / 8);
+
+  // create a "fake" handle for it
+  private_handle_t* hnd = new private_handle_t(
+      dup(m->framebuffer->fd), config.total_buffer_size(),
+      config.hal_format(), config.x_res(), config.y_res(),
+      stride_in_pixels, private_handle_t::PRIV_FLAGS_FRAMEBUFFER, frame_offset);
+
+  *pHandle = hnd;
+  if (pStrideInPixels) {
+    *pStrideInPixels = stride_in_pixels;
+  }
+
+  return 0;
+}
+
+static int gralloc_alloc_sf_framebuffer(alloc_device_t* dev,
+                                        buffer_handle_t* pHandle,
+                                        int* pStrideInPixels) {
+  uint32_t mask = (1LU << VSoCFrameBuffer::kNumSfBuffers) - 1LU;
+  // Skip the first buffers since those are for HWC usage
+  mask <<= VSoCFrameBuffer::kNumHwcBuffers;
+  return gralloc_alloc_framebuffer(dev, pHandle, pStrideInPixels, mask);
+}
+
+static int gralloc_alloc_hwc_framebuffer(alloc_device_t* dev,
+                                         buffer_handle_t* pHandle) {
+  // Use the first kNumHwcBuffers for hwcomposer
+  uint32_t mask = (1LU << VSoCFrameBuffer::kNumHwcBuffers) - 1LU;
+  return gralloc_alloc_framebuffer(dev, pHandle, NULL, mask);
+}
+
+/*****************************************************************************/
+
+static int gralloc_alloc(
+    alloc_device_t* dev, int w, int h, int format, int usage,
+    buffer_handle_t* pHandle, int* pStrideInPixels) {
+  if (!pHandle || !pStrideInPixels)
+    return -EINVAL;
+
+  int err;
+  if (usage & GRALLOC_USAGE_HW_FB) {
+    err = gralloc_alloc_sf_framebuffer(dev, pHandle, pStrideInPixels);
+  } else {
+    err = gralloc_alloc_buffer(dev, format, w, h, pHandle, pStrideInPixels);
+  }
+
+  if (err < 0) {
+    return err;
+  }
+  return 0;
+}
+
+static int gralloc_free(alloc_device_t* /*dev*/, buffer_handle_t handle) {
+  if (private_handle_t::validate(handle) < 0) {
+    return -EINVAL;
+  }
+
+  int retval = 0;
+
+  private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(
+    handle);
+  if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
+    retval = gralloc_free_framebuffer(hnd);
+  } else {
+    retval = unreference_region(__FUNCTION__, hnd);
+  }
+
+  close(hnd->fd);
+  delete hnd;
+  return retval;
+}
+
+/*****************************************************************************/
+
+static int gralloc_close(struct hw_device_t *dev) {
+  priv_alloc_device_t* ctx = reinterpret_cast<priv_alloc_device_t*>(dev);
+  if (ctx) {
+    /* TODO: keep a list of all buffer_handle_t created, and free them
+     * all here.
+     */
+    free(ctx);
+  }
+  return 0;
+}
+
+static int gralloc_device_open(
+    const hw_module_t* module, const char* name, hw_device_t** device) {
+  int status = -EINVAL;
+  if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
+    priv_alloc_device_t *dev;
+    dev = (priv_alloc_device_t*) malloc(sizeof(*dev));
+    LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+
+    /* initialize our state here */
+    memset(dev, 0, sizeof(*dev));
+
+    /* initialize the procs */
+    dev->device.common.tag = HARDWARE_DEVICE_TAG;
+    dev->device.common.version = 0;
+    dev->device.common.module = const_cast<hw_module_t*>(module);
+    dev->device.common.close = gralloc_close;
+
+    dev->device.alloc   = gralloc_alloc;
+    dev->device.free    = gralloc_free;
+    dev->alloc_hwc_framebuffer = gralloc_alloc_hwc_framebuffer;
+
+
+    *device = &dev->device.common;
+    status = 0;
+  } else {
+    status = fb_device_open(module, name, device);
+  }
+  return status;
+}
+
+/*****************************************************************************/
+
+static struct hw_module_methods_t gralloc_module_methods = {
+  VSOC_STATIC_INITIALIZER(open) gralloc_device_open
+};
+
+struct private_module_t HAL_MODULE_INFO_SYM = {
+  VSOC_STATIC_INITIALIZER(base) {
+    VSOC_STATIC_INITIALIZER(common) {
+      VSOC_STATIC_INITIALIZER(tag) HARDWARE_MODULE_TAG,
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+      VSOC_STATIC_INITIALIZER(version_major) GRALLOC_MODULE_API_VERSION_0_2,
+#else
+      VSOC_STATIC_INITIALIZER(version_major) 1,
+#endif
+      VSOC_STATIC_INITIALIZER(version_minor) 0,
+      VSOC_STATIC_INITIALIZER(id) GRALLOC_HARDWARE_MODULE_ID,
+      VSOC_STATIC_INITIALIZER(name) "VSOC X86 Graphics Memory Allocator Module",
+      VSOC_STATIC_INITIALIZER(author) "The Android Open Source Project",
+      VSOC_STATIC_INITIALIZER(methods) &gralloc_module_methods,
+      VSOC_STATIC_INITIALIZER(dso) NULL,
+      VSOC_STATIC_INITIALIZER(reserved) {0},
+    },
+    VSOC_STATIC_INITIALIZER(registerBuffer) gralloc_register_buffer,
+    VSOC_STATIC_INITIALIZER(unregisterBuffer) gralloc_unregister_buffer,
+    VSOC_STATIC_INITIALIZER(lock) gralloc_lock,
+    VSOC_STATIC_INITIALIZER(unlock) gralloc_unlock,
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+    VSOC_STATIC_INITIALIZER(perform) NULL,
+    VSOC_STATIC_INITIALIZER(lock_ycbcr) gralloc_lock_ycbcr,
+#endif
+  },
+  VSOC_STATIC_INITIALIZER(framebuffer) 0,
+  VSOC_STATIC_INITIALIZER(lock) PTHREAD_MUTEX_INITIALIZER,
+};
diff --git a/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h b/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h
new file mode 100644
index 0000000..0fc2fd8
--- /dev/null
+++ b/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h
@@ -0,0 +1,264 @@
+#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 <stdint.h>
+#include <limits.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#include <hardware/gralloc.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <cutils/native_handle.h>
+#include <cutils/log.h>
+
+#include <linux/fb.h>
+
+#include "guest/libs/legacy_framebuffer/vsoc_framebuffer.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+#ifndef GRALLOC_MODULE_API_VERSION_0_2
+// This structure will be defined in later releases of Android. Declare it
+// here to allow us to structure the code well.
+struct android_ycbcr {
+  void* y;
+  void* cb;
+  void* cr;
+  size_t ystride;
+  size_t cstride;
+  size_t chroma_step;
+  uint32_t reserved[8];
+};
+#endif
+
+/*****************************************************************************/
+
+struct private_handle_t;
+
+struct private_module_t {
+  gralloc_module_t base;
+
+  private_handle_t* framebuffer;
+  pthread_mutex_t lock;
+};
+
+int initUserspaceFrameBuffer(struct private_module_t* module);
+
+/*****************************************************************************/
+
+struct priv_alloc_device_t {
+  alloc_device_t  device;
+  // Creates handles for the hwcomposer-specific framebuffers
+  int (*alloc_hwc_framebuffer)(alloc_device_t* m,
+                                buffer_handle_t* handle);
+};
+
+/*****************************************************************************/
+
+struct private_handle_t : public native_handle {
+  enum {
+    PRIV_FLAGS_FRAMEBUFFER = 0x00000001
+  };
+
+  // file-descriptors
+  int     fd;
+  // ints
+  int     magic;
+  int     flags;
+  int     format;
+  int     x_res;
+  int     y_res;
+  int     stride_in_pixels;
+  // Use to indicate which frame we're using.
+  int     frame_offset;
+  int     total_size;
+  int     lock_level;
+
+  static inline int sNumInts() {
+    return (((sizeof(private_handle_t) - sizeof(native_handle_t))/sizeof(int)) - sNumFds);
+  }
+  static const int sNumFds = 1;
+  static const int sMagic = 0x3141592;
+
+  private_handle_t(int fd, int size, int format, int x_res, int y_res,
+                   int stride_in_pixels, int flags, int frame_offset = 0)
+      : fd(fd),
+        magic(sMagic),
+        flags(flags),
+        format(format),
+        x_res(x_res),
+        y_res(y_res),
+        stride_in_pixels(stride_in_pixels),
+        frame_offset(frame_offset),
+        total_size(size),
+        lock_level(0) {
+    version = sizeof(native_handle);
+    numInts = sNumInts();
+    numFds = sNumFds;
+  }
+
+  ~private_handle_t() {
+    magic = 0;
+  }
+
+  static int validate(const native_handle* h) {
+    const private_handle_t* hnd = (const private_handle_t*)h;
+    if (!h || h->version != sizeof(native_handle) ||
+        h->numInts != sNumInts() || h->numFds != sNumFds ||
+        hnd->magic != sMagic) {
+      ALOGE("invalid gralloc handle (at %p)", h);
+      return -EINVAL;
+    }
+    return 0;
+  }
+};
+
+
+static inline int formatToBytesPerPixel(int format) {
+  switch (format) {
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+#if VSOC_PLATFORM_SDK_AFTER(J)
+    // The camera 3.0 implementation assumes that IMPLEMENTATION_DEFINED
+    // means HAL_PIXEL_FORMAT_RGBA_8888
+    case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+#endif
+      return 4;
+    case HAL_PIXEL_FORMAT_RGB_888:
+      return 3;
+    case HAL_PIXEL_FORMAT_RGB_565:
+    case HAL_PIXEL_FORMAT_YV12:
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+#endif
+      return 2;
+#if VSOC_PLATFORM_SDK_AFTER(J)
+    case HAL_PIXEL_FORMAT_BLOB:
+      return 1;
+#endif
+    default:
+      ALOGE("%s: unknown format=%d", __FUNCTION__, format);
+      return 4;
+  }
+}
+
+static inline void formatToYcbcr(
+    int format, int width, int height, void* base_v, android_ycbcr* out) {
+  char* it = static_cast<char*>(base_v);
+  // Clear reserved fields;
+  memset(out, 0, sizeof(*out));
+  switch (format) {
+    case HAL_PIXEL_FORMAT_YV12:
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+#endif
+      out->ystride = VSoCFrameBuffer::align(width, 16);
+      out->cstride = VSoCFrameBuffer::align(out->ystride / 2, 16);
+      out->chroma_step = 1;
+      out->y = it;
+      it += out->ystride * height;
+      out->cr = it;
+      it += out->cstride * height / 2;
+      out->cb = it;
+      break;
+    default:
+      ALOGE("%s: can't deal with format=0x%x (%s)",
+            __FUNCTION__, format, pixel_format_to_string(format));
+  }
+}
+
+static inline int formatToBytesPerFrame(int format, int w, int h) {
+  int bytes_per_pixel = formatToBytesPerPixel(format);
+  int w16, h16;
+  int y_size, c_size;
+
+  switch (format) {
+#if VSOC_PLATFORM_SDK_AFTER(J)
+    // BLOB is used to allocate buffers for JPEG formatted data. Bytes per pixel
+    // is 1, the desired buffer size is in w, and h should be 1. We refrain from
+    // adding additional padding, although the caller is likely to round
+    // up to a page size.
+    case HAL_PIXEL_FORMAT_BLOB:
+      return bytes_per_pixel * w * h;
+#endif
+    case HAL_PIXEL_FORMAT_YV12:
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+#endif
+      android_ycbcr strides;
+      formatToYcbcr(format, w, h, NULL, &strides);
+      y_size = strides.ystride * h;
+      c_size = strides.cstride * h / 2;
+      return (y_size + 2 * c_size + VSoCFrameBuffer::kSwiftShaderPadding);
+    /*case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+    case HAL_PIXEL_FORMAT_RGB_888:
+    case HAL_PIXEL_FORMAT_RGB_565:*/
+    default:
+      w16 = VSoCFrameBuffer::align(w, 16);
+      h16 = VSoCFrameBuffer::align(h, 16);
+      return bytes_per_pixel * w16 * h16 + VSoCFrameBuffer::kSwiftShaderPadding;
+  }
+}
+
+// Calculates the yoffset from a framebuffer handle. I checks the given handle
+// for errors first. Returns the yoffset (non negative integer) or -1 if there
+// is an error.
+static inline int YOffsetFromHandle(buffer_handle_t buffer_hnd) {
+    if (!buffer_hnd) {
+    ALOGE("Attempt to post null buffer");
+    return -1;
+  }
+  if (private_handle_t::validate(buffer_hnd) < 0) {
+    ALOGE("Attempt to post non-vsoc handle");
+    return -1;
+  }
+  const private_handle_t* hnd =
+      reinterpret_cast<private_handle_t const*>(buffer_hnd);
+  if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {
+    ALOGE("Attempt to post non-framebuffer");
+    return -1;
+  }
+
+  const VSoCFrameBuffer& config = VSoCFrameBuffer::getInstance();
+  return hnd->frame_offset / config.line_length();
+}
+
+int fb_device_open(
+    const hw_module_t* module, const char* name, hw_device_t** device);
+
+int gralloc_lock(
+    gralloc_module_t const* module,
+    buffer_handle_t handle, int usage,
+    int l, int t, int w, int h,
+    void** vaddr);
+
+int gralloc_unlock(
+    gralloc_module_t const* module, buffer_handle_t handle);
+
+int gralloc_register_buffer(
+    gralloc_module_t const* module, buffer_handle_t handle);
+
+int gralloc_unregister_buffer(
+    gralloc_module_t const* module, buffer_handle_t handle);
+
+int gralloc_lock_ycbcr(
+    struct gralloc_module_t const* module,
+    buffer_handle_t handle, int usage,
+    int l, int t, int w, int h,
+    struct android_ycbcr *ycbcr);
diff --git a/guest/hals/gralloc/legacy/mapper.cpp b/guest/hals/gralloc/legacy/mapper.cpp
new file mode 100644
index 0000000..8339df9
--- /dev/null
+++ b/guest/hals/gralloc/legacy/mapper.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 <limits.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cutils/hashmap.h>
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+
+#include "guest/libs/legacy_framebuffer/RegionRegistry.h"
+
+#include "gralloc_vsoc_priv.h"
+#include "guest/libs/remoter/remoter_framework_pkt.h"
+
+#define DEBUG_REFERENCES 1
+#define DEBUG_MAX_LOCK_LEVEL 20
+
+/*****************************************************************************/
+
+int gralloc_register_buffer(gralloc_module_t const* /*module*/,
+                            buffer_handle_t handle) {
+  if (private_handle_t::validate(handle) < 0) {
+    return -EINVAL;
+  }
+
+  private_handle_t* hnd = (private_handle_t*)handle;
+  if (reference_region(__FUNCTION__, hnd)) {
+    return 0;
+  } else {
+    return -EIO;
+  }
+}
+
+int gralloc_unregister_buffer(gralloc_module_t const* /*module*/,
+                              buffer_handle_t handle) {
+  if (private_handle_t::validate(handle) < 0) {
+    return -EINVAL;
+  }
+  private_handle_t* hnd = (private_handle_t*)handle;
+  return unreference_region("gralloc_unregister_buffer", hnd);
+}
+
+int gralloc_lock(
+    gralloc_module_t const* /*module*/, buffer_handle_t handle, int /*usage*/,
+    int /*l*/, int /*t*/, int /*w*/, int /*h*/,
+    void** vaddr) {
+  if (private_handle_t::validate(handle) < 0) {
+    return -EINVAL;
+  }
+  if (!vaddr) {
+    return -EINVAL;
+  }
+  private_handle_t* hnd = (private_handle_t*)handle;
+#if DEBUG_REFERENCES
+  if (hnd->lock_level > DEBUG_MAX_LOCK_LEVEL) {
+    LOG_FATAL("%s: unbalanced lock detected. lock level = %d",
+              __FUNCTION__, hnd->lock_level);
+  }
+  ++hnd->lock_level;
+#endif
+  void* base = reference_region("gralloc_lock", hnd);
+  *vaddr = reinterpret_cast<unsigned char*>(base)
+      + hnd->frame_offset;
+  return 0;
+}
+
+int gralloc_unlock(
+    gralloc_module_t const* /*module*/, buffer_handle_t handle) {
+  if (private_handle_t::validate(handle) < 0) {
+    return -EINVAL;
+  }
+  private_handle_t* hnd = (private_handle_t*) handle;
+#if DEBUG_REFERENCES
+  if (hnd->lock_level <= 0) {
+    LOG_FATAL("%s unbalanced unlock detected. lock level = %d",
+              __FUNCTION__, hnd->lock_level);
+  }
+  --hnd->lock_level;
+#endif
+  unreference_region("gralloc_unlock", hnd);
+  return 0;
+}
+
+int gralloc_lock_ycbcr(
+    gralloc_module_t const* /*module*/, buffer_handle_t handle, int /*usage*/,
+    int /*l*/, int /*t*/, int /*w*/, int /*h*/,
+    struct android_ycbcr* ycbcr) {
+  if (private_handle_t::validate(handle) < 0) {
+    return -EINVAL;
+  }
+  private_handle_t* hnd = (private_handle_t*)handle;
+#if DEBUG_REFERENCES
+  if (hnd->lock_level > DEBUG_MAX_LOCK_LEVEL) {
+    LOG_FATAL("%s: unbalanced lock detected. lock level = %d",
+              __FUNCTION__, hnd->lock_level);
+  }
+  ++hnd->lock_level;
+#endif
+  void* base = reference_region("gralloc_lock_ycbcr", hnd);
+  formatToYcbcr(hnd->format, hnd->x_res, hnd->y_res, base, ycbcr);
+  return 0;
+}
diff --git a/guest/hals/gralloc/mapper.cpp b/guest/hals/gralloc/mapper.cpp
new file mode 100644
index 0000000..f8a06fb
--- /dev/null
+++ b/guest/hals/gralloc/mapper.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gralloc_vsoc_priv.h"
+
+#include <cutils/hashmap.h>
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <log/log.h>
+#include <string.h>
+#include <sys/mman.h>
+
+namespace {
+
+struct HmLockGuard {
+  HmLockGuard(Hashmap* map) : map_(map) {
+    hashmapLock(map_);
+  }
+  ~HmLockGuard() {
+    hashmapUnlock(map_);
+  }
+ private:
+  Hashmap* map_;
+};
+
+int offset_hash(void* key) {
+  return *reinterpret_cast<int*>(key);
+}
+
+bool offset_equals(void* key1, void* key2) {
+  return *reinterpret_cast<int*>(key1) ==
+         *reinterpret_cast<int*>(key2);
+}
+
+// Keeps track of how many times a buffer is locked in the current process.
+struct GrallocBuffer {
+  void* vaddr;
+  int ref_count;
+  GrallocBuffer() : vaddr(NULL), ref_count(0) {}
+
+  static Hashmap* mapped_buffers() {
+    static Hashmap* mapped_buffers =
+        hashmapCreate(19, offset_hash, offset_equals);
+    return mapped_buffers;
+  }
+};
+
+}
+
+void* reference_buffer(const vsoc_buffer_handle_t* hnd) {
+  Hashmap* map = GrallocBuffer::mapped_buffers();
+  HmLockGuard lock_guard(map);
+  GrallocBuffer* buffer = reinterpret_cast<GrallocBuffer*>(
+      hashmapGet(map, const_cast<int*>(&hnd->offset)));
+  if (!buffer) {
+    buffer = new GrallocBuffer();
+    hashmapPut(map, const_cast<int*>(&hnd->offset), buffer);
+  }
+
+  if (!buffer->vaddr) {
+    void* mapped =
+      mmap(NULL, hnd->size, PROT_READ | PROT_WRITE, MAP_SHARED, hnd->fd, 0);
+    if (mapped == MAP_FAILED) {
+      ALOGE("Unable to map buffer (offset: %d, size: %d): %s",
+            hnd->offset,
+            hnd->size,
+            strerror(errno));
+      return NULL;
+    }
+    // Set up the guard pages. The last page is always a guard
+    uintptr_t base = uintptr_t(mapped);
+    uintptr_t addr = base + hnd->size - PAGE_SIZE;
+    if (mprotect((void*)addr, PAGE_SIZE, PROT_NONE) == -1) {
+      ALOGW("Unable to protect last page of buffer (offset: %d, size: %d): %s",
+            hnd->offset,
+            hnd->size,
+            strerror(errno));
+    }
+    buffer->vaddr = mapped;
+  }
+  buffer->ref_count++;
+  return buffer->vaddr;
+}
+
+int unreference_buffer(const vsoc_buffer_handle_t* hnd) {
+  int result = 0;
+  Hashmap* map = GrallocBuffer::mapped_buffers();
+  HmLockGuard lock_guard(map);
+  GrallocBuffer* buffer = reinterpret_cast<GrallocBuffer*>(
+      hashmapGet(map, const_cast<int*>(&hnd->offset)));
+  if (!buffer) {
+    ALOGE("Unreferencing an unknown buffer (offset: %d, size: %d)",
+          hnd->offset,
+          hnd->size);
+    return -EINVAL;
+  }
+  if (buffer->ref_count == 0) {
+    ALOGE("Unbalanced reference/unreference on buffer (offset: %d, size: %d)",
+          hnd->offset,
+          hnd->size);
+    return -EINVAL;
+  }
+  buffer->ref_count--;
+  if (buffer->ref_count == 0) {
+    result = munmap(buffer->vaddr, hnd->size);
+    if (result) {
+      ALOGE("Unable to unmap buffer (offset: %d, size: %d): %s",
+            hnd->offset,
+            hnd->size,
+            strerror(errno));
+    }
+    buffer->vaddr = NULL;
+  }
+  return result;
+}
diff --git a/guest/hals/hwcomposer/Android.mk b/guest/hals/hwcomposer/Android.mk
new file mode 100644
index 0000000..e1312f1
--- /dev/null
+++ b/guest/hals/hwcomposer/Android.mk
@@ -0,0 +1,55 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := hwcomposer.vsoc-future
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+    hwcomposer.cpp
+
+LOCAL_C_INCLUDES += \
+    device/google/cuttlefish_kernel \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_common/guest/hals/hwcomposer \
+    hardware/libhardware/include \
+    system/core/base/include
+
+LOCAL_CFLAGS := \
+    -DLOG_TAG=\"hwcomposer_vsoc\" \
+    -Wno-missing-field-initializers \
+    -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    liblog \
+    vsoc_lib
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+LOCAL_VENDOR_MODULE := true
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/hals/hwcomposer/hwcomposer.cpp b/guest/hals/hwcomposer/hwcomposer.cpp
new file mode 100644
index 0000000..125b510
--- /dev/null
+++ b/guest/hals/hwcomposer/hwcomposer.cpp
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <log/log.h>
+
+#include "common/vsoc/lib/fb_bcast_region_view.h"
+#include "guest/hals/gralloc/gralloc_vsoc_priv.h"
+
+// This file contains just a skeleton hwcomposer, the first step in the
+// multisided vsoc hwcomposer for cuttlefish.
+
+using vsoc::framebuffer::FBBroadcastRegionView;
+
+// TODO(jemoreira): FBBroadcastRegionView may belong in the HWC region
+
+FBBroadcastRegionView* GetFBBroadcastRegionView() {
+  static FBBroadcastRegionView instance;
+  return &instance;
+}
+
+namespace {
+
+// Ensures that the layer does not include any inconsistencies
+int SanityCheckLayer(const hwc_layer_1& layer) {
+  // Check displayFrame
+  if (layer.displayFrame.left > layer.displayFrame.right ||
+      layer.displayFrame.top > layer.displayFrame.bottom) {
+    ALOGE(
+        "%s: Malformed rectangle (displayFrame): [left = %d, right = %d, top = "
+        "%d, bottom = %d]",
+        __FUNCTION__,
+        layer.displayFrame.left,
+        layer.displayFrame.right,
+        layer.displayFrame.top,
+        layer.displayFrame.bottom);
+    return -EINVAL;
+  }
+  // Check sourceCrop
+  if (layer.sourceCrop.left > layer.sourceCrop.right ||
+      layer.sourceCrop.top > layer.sourceCrop.bottom) {
+    ALOGE(
+        "%s: Malformed rectangle (sourceCrop): [left = %d, right = %d, top = "
+        "%d, bottom = %d]",
+        __FUNCTION__,
+        layer.sourceCrop.left,
+        layer.sourceCrop.right,
+        layer.sourceCrop.top,
+        layer.sourceCrop.bottom);
+    return -EINVAL;
+  }
+  const vsoc_buffer_handle_t* p_handle =
+      reinterpret_cast<const vsoc_buffer_handle_t*>(layer.handle);
+  if (!p_handle) {
+    ALOGE("Layer has a NULL buffer handle");
+    return -EINVAL;
+  }
+  if (layer.sourceCrop.left < 0 || layer.sourceCrop.top < 0 ||
+      layer.sourceCrop.right > p_handle->x_res ||
+      layer.sourceCrop.bottom > p_handle->y_res) {
+    ALOGE(
+        "%s: Invalid sourceCrop for buffer handle: sourceCrop = [left = %d, "
+        "right = %d, top = %d, bottom = %d], handle = [width = %d, height = "
+        "%d]",
+        __FUNCTION__,
+        layer.sourceCrop.left,
+        layer.sourceCrop.right,
+        layer.sourceCrop.top,
+        layer.sourceCrop.bottom,
+        p_handle->x_res,
+        p_handle->y_res);
+    return -EINVAL;
+  }
+  return 0;
+}
+
+struct vsoc_hwc_device {
+  hwc_composer_device_1_t base;
+  const hwc_procs_t* procs;
+  pthread_t vsync_thread;
+  int64_t vsync_base_timestamp;
+  int32_t vsync_period_ns;
+  FBBroadcastRegionView* fb_broadcast;
+  uint32_t frame_num;
+};
+
+void* vsync_thread(void* arg) {
+  vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(arg);
+  setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+  int64_t base_timestamp = pdev->vsync_base_timestamp;
+  int64_t last_logged = base_timestamp / 1e9;
+  int sent = 0;
+  int last_sent = 0;
+  static const int log_interval = 60;
+  while (true) {
+    struct timespec rt;
+    if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+      ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
+            strerror(errno));
+    }
+    int64_t timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+    // Given now's timestamp calculate the time of the next vsync.
+    timestamp += pdev->vsync_period_ns -
+                 (timestamp - base_timestamp) % pdev->vsync_period_ns;
+
+    rt.tv_sec = timestamp / 1e9;
+    rt.tv_nsec = timestamp % static_cast<int32_t>(1e9);
+    int err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &rt, NULL);
+    if ( err == -1) {
+      ALOGE("error in vsync thread: %s", strerror(errno));
+      if (errno == EINTR) {
+        continue;
+      }
+    }
+
+    pdev->procs->vsync(const_cast<hwc_procs_t*>(pdev->procs), 0, timestamp);
+    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;
+    }
+    ++sent;
+  }
+
+  return NULL;
+}
+
+int hwc_prepare(struct hwc_composer_device_1* /*dev*/,
+                size_t numDisplays,
+                hwc_display_contents_1_t** displays) {
+  if (!numDisplays || !displays) return 0;
+  hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
+  if (!list) return 0;
+
+  for (size_t i = 0; i < list->numHwLayers; i++) {
+    if (list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
+      continue;
+    }
+    list->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
+  }
+
+  return 0;
+}
+
+int hwc_set(struct hwc_composer_device_1* dev,
+            size_t numDisplays,
+            hwc_display_contents_1_t** displays) {
+  if (!numDisplays || !displays) return 0;
+  hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
+  if (!list) return 0;
+  if (!dev) {
+    ALOGE("%s: dev is NULL", __FUNCTION__);
+    return -EINVAL;
+  }
+
+  for (size_t i = 0; i < list->numHwLayers; i++) {
+    if (vsoc_buffer_handle_t::validate(list->hwLayers[i].handle)) {
+      return -EINVAL;
+    }
+    if (list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
+      if (SanityCheckLayer(list->hwLayers[i])) {
+        ALOGW("Skipping layer %zu due to failed sanity check", i);
+        continue;
+      }
+      vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(dev);
+      const vsoc_buffer_handle_t* fb_handle =
+          reinterpret_cast<const vsoc_buffer_handle_t*>(
+              list->hwLayers[i].handle);
+      pdev->fb_broadcast->BroadcastNewFrame(pdev->frame_num++,
+                                            fb_handle->offset);
+      break;
+    }
+  }
+
+  return 0;
+}
+
+int hwc_eventControl(struct hwc_composer_device_1* /*dev*/,
+                     int disp,
+                     int event,
+                     int /*enabled*/) {
+  if (event == HWC_EVENT_VSYNC || disp != HWC_DISPLAY_PRIMARY) {
+    return 0;
+  }
+  return -EINVAL;
+}
+
+int hwc_blank(struct hwc_composer_device_1* /*dev*/, int disp, int /*blank*/) {
+  if (disp != HWC_DISPLAY_PRIMARY) {
+    return -EINVAL;
+  }
+  return 0;
+}
+
+int hwc_query(struct hwc_composer_device_1* dev, int what, int* value) {
+  vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(dev);
+  switch (what) {
+    case HWC_BACKGROUND_LAYER_SUPPORTED:
+      // we don't support the background layer
+      *value = 0;
+      break;
+    case HWC_VSYNC_PERIOD:
+      *value = pdev->vsync_period_ns;
+      break;
+    case HWC_DISPLAY_TYPES_SUPPORTED:
+      // We only support the primary display
+      *value = HWC_DISPLAY_PRIMARY_BIT;
+      break;
+    default:
+      // unsupported query
+      ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
+      return -EINVAL;
+  }
+  return 0;
+}
+
+void hwc_registerProcs(struct hwc_composer_device_1* dev,
+                       hwc_procs_t const* procs) {
+  reinterpret_cast<vsoc_hwc_device*>(dev)->procs = procs;
+}
+
+void hwc_dump(struct hwc_composer_device_1* /*dev*/,
+              char* /*buff*/, int /*buff_len*/) {}
+
+int hwc_getDisplayConfigs(struct hwc_composer_device_1* /*dev*/,
+                          int disp,
+                          uint32_t* configs,
+                          size_t* numConfigs) {
+  if (*numConfigs == 0) return 0;
+
+  if (disp == HWC_DISPLAY_PRIMARY) {
+    configs[0] = 0;
+    *numConfigs = 1;
+    return 0;
+  }
+
+  return -EINVAL;
+}
+
+int32_t vsoc_hwc_attribute(vsoc_hwc_device* pdev, uint32_t attribute) {
+  switch (attribute) {
+    case HWC_DISPLAY_VSYNC_PERIOD:
+      return 1000000000/pdev->fb_broadcast->refresh_rate_hz();
+    case HWC_DISPLAY_WIDTH:
+      return pdev->fb_broadcast->x_res();
+    case HWC_DISPLAY_HEIGHT:
+      return pdev->fb_broadcast->y_res();
+    case HWC_DISPLAY_DPI_X:
+    case HWC_DISPLAY_DPI_Y:
+      // The number of pixels per thousand inches
+      return pdev->fb_broadcast->dpi() * 1000;
+    case HWC_DISPLAY_COLOR_TRANSFORM:
+      // TODO(jemoreira): Add the other color transformations
+      return HAL_COLOR_TRANSFORM_IDENTITY;
+    default:
+      ALOGE("unknown display attribute %u", attribute);
+      return -EINVAL;
+  }
+}
+
+int hwc_getDisplayAttributes(struct hwc_composer_device_1* dev,
+                             int disp,
+                             uint32_t /*config*/,
+                             const uint32_t* attributes,
+                             int32_t* values) {
+  vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(dev);
+
+  if (disp != HWC_DISPLAY_PRIMARY) {
+    ALOGE("Unknown display type %u", disp);
+    return -EINVAL;
+  }
+
+  for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
+    values[i] = vsoc_hwc_attribute(pdev, attributes[i]);
+  }
+
+  return 0;
+}
+
+int hwc_close(hw_device_t* device) {
+  vsoc_hwc_device* dev = reinterpret_cast<vsoc_hwc_device*>(device);
+  pthread_kill(dev->vsync_thread, SIGTERM);
+  pthread_join(dev->vsync_thread, NULL);
+  delete dev;
+  return 0;
+}
+
+int hwc_open(const struct hw_module_t* module, const char* name,
+             struct hw_device_t** device) {
+  ALOGI("Opening vsoc hwcomposer device: %s", __FUNCTION__);
+  if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
+    ALOGE("%s called with bad name %s", __FUNCTION__, name);
+    return -EINVAL;
+  }
+
+  vsoc_hwc_device* dev = new vsoc_hwc_device();
+  if (!dev) {
+    ALOGE("%s failed to allocate dev", __FUNCTION__);
+    return -ENOMEM;
+  }
+  memset(dev, 0, sizeof(*dev));
+
+  int refreshRate = 60;
+  dev->vsync_period_ns = 1000000000 / refreshRate;
+  struct timespec rt;
+  if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+    ALOGE("%s:%d error in clock_gettime: %s",
+          __FILE__,
+          __LINE__,
+          strerror(errno));
+  }
+  dev->vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+
+  dev->base.common.tag = HARDWARE_DEVICE_TAG;
+  dev->base.common.version = HWC_DEVICE_API_VERSION_1_1;
+  dev->base.common.module = const_cast<hw_module_t*>(module);
+  dev->base.common.close = hwc_close;
+
+  dev->base.prepare = hwc_prepare;
+  dev->base.set = hwc_set;
+  dev->base.query = hwc_query;
+  dev->base.registerProcs = hwc_registerProcs;
+  dev->base.dump = hwc_dump;
+  dev->base.blank = hwc_blank;
+  dev->base.eventControl = hwc_eventControl;
+  dev->base.getDisplayConfigs = hwc_getDisplayConfigs;
+  dev->base.getDisplayAttributes = hwc_getDisplayAttributes;
+
+  dev->fb_broadcast = GetFBBroadcastRegionView();
+  if (!dev->fb_broadcast->Open()) {
+    ALOGE("Unable to open framebuffer broadcaster (%s)", __FUNCTION__);
+    delete dev;
+    return -1;
+  }
+
+  int ret = pthread_create(&dev->vsync_thread, NULL, vsync_thread, dev);
+  if (ret) {
+    ALOGE("failed to start vsync thread: %s", strerror(ret));
+    delete dev;
+  } else {
+    *device = &dev->base.common;
+  }
+
+  return -ret;
+}
+
+struct hw_module_methods_t hwc_module_methods = { hwc_open };
+
+} // namespace
+
+hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG,
+                                     HWC_MODULE_API_VERSION_0_1,
+                                     HARDWARE_HAL_API_VERSION,
+                                     HWC_HARDWARE_MODULE_ID,
+                                     "Cuttlefish hwcomposer module",
+                                     "Google",
+                                     &hwc_module_methods,
+                                     NULL,
+                                     {0}}};
diff --git a/guest/hals/hwcomposer/legacy/Android.mk b/guest/hals/hwcomposer/legacy/Android.mk
new file mode 100644
index 0000000..2de1251
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/Android.mk
@@ -0,0 +1,82 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# HAL module implemenation stored in
+# hw/<OVERLAY_HARDWARE_MODULE_ID>.<ro.product.board>.so
+
+# Old hwcomposer, relies on GLES composition
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/hwcomposer.mk
+LOCAL_CFLAGS += -DUSE_OLD_HWCOMPOSER
+LOCAL_MODULE := hwcomposer.vsoc-deprecated
+
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+# New hwcomposer, performs software composition
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/hwcomposer.mk
+LOCAL_MODULE := hwcomposer.vsoc
+LOCAL_VENDOR_MODULE := true
+
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+# An executable to run some tests
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := hwc_tests.vsoc
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := \
+    libvsocframebuffer \
+    liblog \
+    libbase \
+    libcutils \
+    libutils \
+    libsync \
+    libhardware \
+    libjpeg \
+    $(VSOC_STLPORT_LIBS)
+
+LOCAL_STATIC_LIBRARIES := \
+    libyuv_static
+
+LOCAL_SRC_FILES := \
+    hwc_tests.cpp \
+    vsoc_composer.cpp \
+    base_composer.cpp \
+    geometry_utils.cpp
+
+
+LOCAL_CFLAGS += \
+    -DLOG_TAG=\"hwc_tests\" \
+    $(VSOC_VERSION_CFLAGS)
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    bionic \
+    $(VSOC_STLPORT_INCLUDES)
+
+include $(BUILD_EXECUTABLE)
diff --git a/guest/hals/hwcomposer/legacy/base_composer.cpp b/guest/hals/hwcomposer/legacy/base_composer.cpp
new file mode 100644
index 0000000..561ce6f
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/base_composer.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 <cutils/log.h>
+#include "guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h"
+#include "guest/hals/gralloc//legacy/gralloc_vsoc_priv.h"
+
+namespace cvd {
+
+namespace {
+
+int BroadcastFrameBufferChanged (int yoffset) {
+  return VSoCFrameBufferControl::getInstance().BroadcastFrameBufferChanged(
+      yoffset);
+}
+
+}
+
+BaseComposer::BaseComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns)
+    : fb_broadcaster_(BroadcastFrameBufferChanged),
+      vsync_base_timestamp_(vsync_base_timestamp),
+      vsync_period_ns_(vsync_period_ns) {
+  const gralloc_module_t* gralloc_module;
+  hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+                reinterpret_cast<const hw_module_t**>(&gralloc_module));
+}
+
+BaseComposer::~BaseComposer() {}
+
+FbBroadcaster BaseComposer::ReplaceFbBroadcaster(
+    FbBroadcaster fb_broadcaster) {
+  FbBroadcaster tmp = fb_broadcaster_;
+  fb_broadcaster_ = fb_broadcaster;
+  return tmp;
+}
+
+void BaseComposer::Dump(char* buff __unused, int buff_len __unused) {}
+
+
+int BaseComposer::PostFrameBuffer(buffer_handle_t buffer) {
+  const int yoffset = YOffsetFromHandle(buffer);
+  // If the broadcaster is NULL or could not get a good yoffset just ignore it.
+  if (fb_broadcaster_ && yoffset >= 0) {
+    int retval = fb_broadcaster_(yoffset);
+    if (retval){
+      ALOGI("Failed to post framebuffer");
+      return -1;
+    }
+  }
+
+  return yoffset;
+}
+
+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 PostFrameBuffer(layers[idx].handle);
+    }
+  }
+  return -1;
+}
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/base_composer.h b/guest/hals/hwcomposer/legacy/base_composer.h
new file mode 100644
index 0000000..5a084fd
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/base_composer.h
@@ -0,0 +1,51 @@
+#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 <hardware/gralloc.h>
+#include "hwcomposer_common.h"
+
+namespace cvd {
+
+typedef int (*FbBroadcaster)(int);
+
+class BaseComposer {
+ public:
+  BaseComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns);
+  ~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 the yoffset that was broadcasted or a negative number if there was
+  // an error.
+  int SetLayers(size_t num_layers, vsoc_hwc_layer* layers);
+  // Returns yoffset of the handle or negative on error.
+  int PostFrameBuffer(buffer_handle_t handle);
+  // Changes the broadcaster, gives the ability to report more than just the
+  // yoffset by using a wrapper like the StatsKeepingComposer. Returns the old
+  // broadcaster. Passing a NULL pointer will cause the composer to not
+  // broadcast at all.
+  FbBroadcaster ReplaceFbBroadcaster(FbBroadcaster);
+  void Dump(char* buff, int buff_len);
+ protected:
+  int64_t vsync_base_timestamp_;
+  int32_t vsync_period_ns_;
+ private:
+  FbBroadcaster fb_broadcaster_;
+};
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.cpp b/guest/hals/hwcomposer/legacy/geometry_utils.cpp
new file mode 100644
index 0000000..75b7a61
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/geometry_utils.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "geometry_utils.h"
+#include <algorithm>
+#include <utility>
+
+namespace cvd {
+
+bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2) {
+  int left1 = layer1.displayFrame.left;
+  int right1 = layer1.displayFrame.right;
+  int top1 = layer1.displayFrame.top;
+  int bottom1 = layer1.displayFrame.bottom;
+
+  int left2 = layer2.displayFrame.left;
+  int right2 = layer2.displayFrame.right;
+  int top2 = layer2.displayFrame.top;
+  int bottom2 = layer2.displayFrame.bottom;
+
+  bool overlap_x = left1 < right2 && left2 < right1;
+  bool overlap_y = top1 < bottom2 && top2 < bottom1;
+
+  return overlap_x && overlap_y;
+}
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/guest/hals/hwcomposer/legacy/geometry_utils.h
new file mode 100644
index 0000000..b6a037b
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/geometry_utils.h
@@ -0,0 +1,24 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hwcomposer_common.h"
+
+namespace cvd {
+
+bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/hwc_tests.cpp b/guest/hals/hwcomposer/legacy/hwc_tests.cpp
new file mode 100644
index 0000000..5cfe797
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/hwc_tests.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "vsoc_composer.h"
+#include <stdio.h>
+
+// This executable is only intended to perform simple tests on the hwcomposer
+// functionality. It should not be part of the images, but rather be included
+// (via scp) when needed to test specific scenarios that are hard to reproduce
+// in the normal operation of the device.
+
+class HWC_Tester : public cvd::VSoCComposer {
+ public:
+  HWC_Tester() : cvd::VSoCComposer(int64_t(0), int32_t(16000000)) {}
+  int RunTest() {
+    // Allocate two buffers (1x1 and 800x1280)
+    buffer_handle_t src_handle;
+    int src_stride;
+    int res = gralloc_dev_->device.alloc(&gralloc_dev_->device,
+                                         1,
+                                         1,
+                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                         GRALLOC_USAGE_SW_READ_OFTEN,
+                                         &src_handle,
+                                         &src_stride);
+    if (res) {
+      fprintf(stderr, "Error allocating source buffer, see logs for details\n");
+      return -1;
+    }
+    buffer_handle_t dst_handle;
+    int dst_stride;
+    res = gralloc_dev_->device.alloc(&gralloc_dev_->device,
+                                     800,
+                                     1280,
+                                     HAL_PIXEL_FORMAT_RGBA_8888,
+                                     GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                     &dst_handle,
+                                     &dst_stride);
+    if (res) {
+      fprintf(stderr,
+              "Error allocating destination buffer, see logs for details\n");
+      return -1;
+    }
+    // Create a mock layer requesting a sinple copy of the pixels so that DoCopy gets called
+    vsoc_hwc_layer src_layer;
+    src_layer.compositionType = HWC_OVERLAY;
+    src_layer.hints = 0;
+    src_layer.flags = 0;
+    src_layer.handle = src_handle;
+
+    // No transformation, just a copy
+    src_layer.transform = 0;
+    src_layer.blending = HWC_BLENDING_NONE;
+
+    src_layer.sourceCrop.top = 0;
+    src_layer.sourceCrop.left = 0;
+    src_layer.sourceCrop.bottom = 1;
+    src_layer.sourceCrop.right = 1;
+
+    src_layer.displayFrame.top = 0;
+    src_layer.displayFrame.left = 0;
+    src_layer.displayFrame.bottom = 1;
+    src_layer.displayFrame.right = 1;
+
+    src_layer.visibleRegionScreen.numRects = 0;
+    src_layer.visibleRegionScreen.rects = NULL;
+
+    src_layer.acquireFenceFd = -1;
+    src_layer.releaseFenceFd = -1;
+    // Call CompositeLayer
+    CompositeLayer(&src_layer, dst_handle);
+    // If we got this far without a SEGFAULT we call it success
+    printf("OK\n");
+    gralloc_dev_->device.free(&gralloc_dev_->device, src_handle);
+    gralloc_dev_->device.free(&gralloc_dev_->device, dst_handle);
+    return 0;
+  }
+};
+
+int main() {
+  HWC_Tester t;
+  return t.RunTest();
+}
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.cpp b/guest/hals/hwcomposer/legacy/hwcomposer.cpp
new file mode 100644
index 0000000..82b0aac
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/hwcomposer.cpp
@@ -0,0 +1,388 @@
+/*
+ * 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.
+ */
+
+// Versions of hwcomposer we implement:
+// JB: 0.3
+// JB-MR1 to N : 1.1
+// N-MR1 to ... : We report 1.1 but SurfaceFlinger has the option to use an
+// adapter to treat our 1.1 hwcomposer as a 2.0. If SF stops using that adapter
+// to support 1.1 implementations it can be copied into cuttlefish from
+// frameworks/native/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.*
+
+#include <guest/libs/platform_support/api_level_fixes.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#define HWC_REMOVE_DEPRECATED_VERSIONS 1
+
+#include <cutils/compiler.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer.h>
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h>
+#include <guest/hals/gralloc/legacy/gralloc_vsoc_priv.h>
+#include <guest/libs/remoter/remoter_framework_pkt.h>
+#include <sync/sync.h>
+
+#include "vsoc_composer.h"
+#include "geometry_utils.h"
+#include "hwcomposer_common.h"
+#include "base_composer.h"
+#include "stats_keeper.h"
+
+
+#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;
+  ComposerType* composer;
+};
+
+static void dump_layer(vsoc_hwc_layer const* l) {
+  ALOGI(
+      "\ttype=%d, flags=%08x, handle=%p, tr=%02x, blend=%04x, "
+      "{%d,%d,%d,%d}, {%d,%d,%d,%d}",
+      l->compositionType, l->flags, l->handle, l->transform, l->blending,
+      l->sourceCrop.left, l->sourceCrop.top, l->sourceCrop.right,
+      l->sourceCrop.bottom, l->displayFrame.left, l->displayFrame.top,
+      l->displayFrame.right, l->displayFrame.bottom);
+}
+
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+static int vsoc_hwc_prepare(vsoc_hwc_device* dev, hwc_layer_list_t* list) {
+#else
+static int vsoc_hwc_prepare(vsoc_hwc_device* dev, size_t numDisplays,
+                           hwc_display_contents_1_t** displays) {
+  if (!numDisplays || !displays) return 0;
+
+  hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
+
+  if (!list) return 0;
+#endif
+  int composited_layers_count =
+      reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)
+          ->composer->PrepareLayers(list->numHwLayers, &list->hwLayers[0]);
+  return 0;
+}
+
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+int vsoc_hwc_set(struct hwc_composer_device* dev, hwc_display_t dpy,
+                hwc_surface_t sur, hwc_layer_list_t* list) {
+  reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->SetLayers(
+      list->numHwLayers, &list->hwLayers[0]);
+  return 0;
+}
+#else
+static int vsoc_hwc_set(vsoc_hwc_device* dev, size_t numDisplays,
+                       hwc_display_contents_1_t** displays) {
+  if (!numDisplays || !displays) return 0;
+
+  hwc_display_contents_1_t* contents = displays[HWC_DISPLAY_PRIMARY];
+  if (!contents) return 0;
+
+  vsoc_hwc_layer* layers = &contents->hwLayers[0];
+  reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->SetLayers(
+      contents->numHwLayers, layers);
+
+  int closedFds = 0;
+  for (size_t index = 0; index < contents->numHwLayers; ++index) {
+    if (layers[index].acquireFenceFd != -1) {
+      close(layers[index].acquireFenceFd);
+      layers[index].acquireFenceFd = -1;
+      ++closedFds;
+    }
+  }
+  if (closedFds) {
+    ALOGI("Saw %d layers, closed=%d", contents->numHwLayers, closedFds);
+  }
+
+  // TODO(ghartman): This should be set before returning. On the next set it
+  // should be signalled when we load the new frame.
+  contents->retireFenceFd = -1;
+  return 0;
+}
+#endif
+
+static void vsoc_hwc_register_procs(vsoc_hwc_device* dev,
+                                   const hwc_procs_t* procs) {
+  struct vsoc_hwc_composer_device_1_t* pdev =
+      (struct vsoc_hwc_composer_device_1_t*)dev;
+  pdev->procs = procs;
+}
+
+static int vsoc_hwc_query(vsoc_hwc_device* dev, int what, int* value) {
+  struct vsoc_hwc_composer_device_1_t* pdev =
+      (struct vsoc_hwc_composer_device_1_t*)dev;
+
+  switch (what) {
+    case HWC_BACKGROUND_LAYER_SUPPORTED:
+      // we support the background layer
+      value[0] = 0;
+      break;
+    case HWC_VSYNC_PERIOD:
+      value[0] = pdev->vsync_period_ns;
+      break;
+    default:
+      // unsupported query
+      ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
+      return -EINVAL;
+  }
+  return 0;
+}
+
+static int vsoc_hwc_event_control(
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+    vsoc_hwc_device* dev, int event, int /*enabled*/) {
+#else
+    vsoc_hwc_device* dev, int /*dpy*/, int event, int /*enabled*/) {
+#endif
+  struct vsoc_hwc_composer_device_1_t* pdev =
+      (struct vsoc_hwc_composer_device_1_t*)dev;
+
+  if (event == HWC_EVENT_VSYNC) {
+    return 0;
+  }
+  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;
+  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;
+      }
+    }
+
+    pdev->procs->vsync(const_cast<hwc_procs_t*>(pdev->procs), 0, timestamp);
+    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;
+    }
+    ++sent;
+  }
+
+  return NULL;
+}
+
+static int vsoc_hwc_blank(vsoc_hwc_device* dev, int disp, int /*blank*/) {
+  struct vsoc_hwc_composer_device_1_t* pdev =
+      (struct vsoc_hwc_composer_device_1_t*)dev;
+  if (!IS_PRIMARY_DISPLAY(disp)) return -EINVAL;
+  return 0;
+}
+
+static void vsoc_hwc_dump(vsoc_hwc_device* dev, char* buff, int buff_len) {
+  reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->Dump(buff,
+                                                                      buff_len);
+}
+
+static int vsoc_hwc_get_display_configs(vsoc_hwc_device* dev, int disp,
+                                       uint32_t* configs, size_t* numConfigs) {
+  struct vsoc_hwc_composer_device_1_t* pdev =
+      (struct vsoc_hwc_composer_device_1_t*)dev;
+
+  if (*numConfigs == 0) return 0;
+
+  if (IS_PRIMARY_DISPLAY(disp)) {
+    configs[0] = 0;
+    *numConfigs = 1;
+    return 0;
+  }
+
+  return -EINVAL;
+}
+
+#if VSOC_PLATFORM_SDK_AFTER(J)
+static int32_t vsoc_hwc_attribute(struct vsoc_hwc_composer_device_1_t* pdev,
+                                 const uint32_t attribute) {
+  const VSoCFrameBuffer& config = VSoCFrameBuffer::getInstance();
+  switch (attribute) {
+    case HWC_DISPLAY_VSYNC_PERIOD:
+      return pdev->vsync_period_ns;
+    case HWC_DISPLAY_WIDTH:
+      return config.x_res();
+    case HWC_DISPLAY_HEIGHT:
+      return config.y_res();
+    case HWC_DISPLAY_DPI_X:
+      ALOGI("Reporting DPI_X of %d", config.dpi());
+      return config.dpi() * 1000;  // The number of pixels per thousand inches
+    case HWC_DISPLAY_DPI_Y:
+      ALOGI("Reporting DPI_Y of %d", config.dpi());
+      return config.dpi() * 1000;  // The number of pixels per thousand inches
+    default:
+      ALOGE("unknown display attribute %u", attribute);
+      return -EINVAL;
+  }
+}
+
+static int vsoc_hwc_get_display_attributes(vsoc_hwc_device* dev, int disp,
+                                          uint32_t config __unused,
+                                          const uint32_t* attributes,
+                                          int32_t* values) {
+  struct vsoc_hwc_composer_device_1_t* pdev =
+      (struct vsoc_hwc_composer_device_1_t*)dev;
+
+  if (!IS_PRIMARY_DISPLAY(disp)) {
+    ALOGE("unknown display type %u", disp);
+    return -EINVAL;
+  }
+
+  for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
+    values[i] = vsoc_hwc_attribute(pdev, attributes[i]);
+  }
+
+  return 0;
+}
+#endif
+
+static int vsoc_hwc_close(hw_device_t* device) {
+  struct vsoc_hwc_composer_device_1_t* dev =
+      (struct vsoc_hwc_composer_device_1_t*)device;
+  ALOGE("vsoc_hwc_close");
+  pthread_kill(dev->vsync_thread, SIGTERM);
+  pthread_join(dev->vsync_thread, NULL);
+  delete dev->composer;
+  delete dev;
+  return 0;
+}
+
+static int vsoc_hwc_open(const struct hw_module_t* module, const char* name,
+                        struct hw_device_t** device) {
+  ALOGI("%s", __FUNCTION__);
+  if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
+    ALOGE("%s called with bad name %s", __FUNCTION__, name);
+    return -EINVAL;
+  }
+
+  vsoc_hwc_composer_device_1_t* dev  = new vsoc_hwc_composer_device_1_t();
+  if (!dev) {
+    ALOGE("%s failed to allocate dev", __FUNCTION__);
+    return -ENOMEM;
+  }
+
+  int refreshRate = 60;
+  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->base.common.tag = HARDWARE_DEVICE_TAG;
+  dev->base.common.version = VSOC_HWC_DEVICE_API_VERSION;
+  dev->base.common.module = const_cast<hw_module_t*>(module);
+  dev->base.common.close = vsoc_hwc_close;
+
+  dev->base.prepare = vsoc_hwc_prepare;
+  dev->base.set = vsoc_hwc_set;
+  dev->base.query = vsoc_hwc_query;
+  dev->base.registerProcs = vsoc_hwc_register_procs;
+  dev->base.dump = vsoc_hwc_dump;
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+  static hwc_methods_t hwc_methods = {vsoc_hwc_event_control};
+  dev->base.methods = &hwc_methods;
+#else
+  dev->base.blank = vsoc_hwc_blank;
+  dev->base.eventControl = vsoc_hwc_event_control;
+  dev->base.getDisplayConfigs = vsoc_hwc_get_display_configs;
+  dev->base.getDisplayAttributes = vsoc_hwc_get_display_attributes;
+#endif
+  dev->composer =
+      new ComposerType(dev->vsync_base_timestamp, dev->vsync_period_ns);
+
+  int ret = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev);
+  if (ret) {
+    ALOGE("failed to start vsync thread: %s", strerror(ret));
+    ret = -ret;
+    delete dev;
+  } else {
+    *device = &dev->base.common;
+  }
+
+  return ret;
+}
+
+static struct hw_module_methods_t vsoc_hwc_module_methods = {
+    vsoc_hwc_open,
+};
+
+hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG,
+                                     HWC_MODULE_API_VERSION_0_1,
+                                     HARDWARE_HAL_API_VERSION,
+                                     HWC_HARDWARE_MODULE_ID,
+                                     "VSOC hwcomposer module",
+                                     "Google",
+                                     &vsoc_hwc_module_methods,
+                                     NULL,
+                                     {0}}};
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.mk b/guest/hals/hwcomposer/legacy/hwcomposer.mk
new file mode 100644
index 0000000..7383ae0
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/hwcomposer.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MULTILIB := first
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := \
+    libvsocframebuffer \
+    libbase \
+    liblog \
+    libcutils \
+    libutils \
+    libsync \
+    libhardware \
+    libjpeg \
+    $(VSOC_STLPORT_LIBS)
+
+LOCAL_STATIC_LIBRARIES := \
+    libyuv_static
+
+LOCAL_SRC_FILES := \
+    geometry_utils.cpp \
+    hwcomposer.cpp \
+    vsoc_composer.cpp \
+    stats_keeper.cpp \
+    base_composer.cpp
+
+LOCAL_CFLAGS += \
+    -DLOG_TAG=\"hwcomposer_legacy\" \
+    -DGATHER_STATS \
+    $(VSOC_VERSION_CFLAGS)
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    external/libyuv/files/include \
+    bionic \
+    $(VSOC_STLPORT_INCLUDES)
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer_common.h b/guest/hals/hwcomposer/legacy/hwcomposer_common.h
new file mode 100644
index 0000000..a6b8121
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/hwcomposer_common.h
@@ -0,0 +1,35 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <guest/libs/platform_support/api_level_fixes.h>
+
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
+typedef hwc_composer_device_t vsoc_hwc_device;
+typedef hwc_layer_t vsoc_hwc_layer;
+#define IS_TARGET_FRAMEBUFFER(x) false
+#define IS_PRIMARY_DISPLAY(x) true
+#define VSOC_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_0_3
+#else
+typedef hwc_composer_device_1_t vsoc_hwc_device;
+typedef hwc_layer_1_t vsoc_hwc_layer;
+#define IS_TARGET_FRAMEBUFFER(x) ((x) == HWC_FRAMEBUFFER_TARGET)
+#define IS_PRIMARY_DISPLAY(x) ((x) == HWC_DISPLAY_PRIMARY)
+#define VSOC_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_1_1
+#endif
diff --git a/guest/hals/hwcomposer/legacy/stats_keeper.cpp b/guest/hals/hwcomposer/legacy/stats_keeper.cpp
new file mode 100644
index 0000000..86b786b
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/stats_keeper.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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 <cutils/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "geometry_utils.h"
+#include "stats_keeper.h"
+
+using cvd::time::TimeDifference;
+using cvd::time::Nanoseconds;
+using cvd::time::Microseconds;
+using cvd::time::Seconds;
+using cvd::time::MonotonicTimePoint;
+using cvd::Mutex;
+using cvd::LockGuard;
+
+namespace cvd {
+
+namespace {
+
+// These functions assume that there is at least one suitable element inside
+// the multiset.
+template <class T>
+void MultisetDeleteOne(std::multiset<T>* mset, const T& key) {
+  mset->erase(mset->find(key));
+}
+template <class T>
+const T& MultisetMin(const std::multiset<T>& mset) {
+  return *mset.begin();
+}
+template <class T>
+const T& MultisetMax(const std::multiset<T>& mset) {
+  return *mset.rbegin();
+}
+
+}  // namespace
+
+StatsKeeper::StatsKeeper(TimeDifference timespan,
+                         int64_t vsync_base,
+                         int32_t vsync_period)
+    : period_length_(timespan, 1),
+      num_layers_(0),
+      num_hwcomposited_layers_(0),
+      num_prepare_calls_(0),
+      num_set_calls_(0),
+      prepare_call_total_time_(0),
+      set_call_total_time_(0),
+      total_layers_area(0),
+      total_invisible_area(0),
+      vsync_base_(vsync_base),
+      vsync_period_(vsync_period) {
+  last_composition_stats_.num_prepare_calls = 0;
+}
+
+StatsKeeper::~StatsKeeper() {}
+
+void StatsKeeper::RecordPrepareStart(int num_layers) {
+  last_composition_stats_.num_layers = num_layers;
+  last_composition_stats_.num_prepare_calls++;
+  num_prepare_calls_++;
+  last_composition_stats_.prepare_start = MonotonicTimePoint::Now();
+  // Calculate the (expected) time of last VSYNC event. We can only make a guess about it because
+  // the vsync thread could run late or surfaceflinger could run late and call prepare from a
+  // previous vsync cycle.
+  int64_t last_vsync =
+      Nanoseconds(last_composition_stats_.set_start.SinceEpoch()).count();
+  last_vsync -= (last_vsync - vsync_base_) % vsync_period_;
+  last_composition_stats_.last_vsync = MonotonicTimePoint() + Nanoseconds(last_vsync);
+}
+
+void StatsKeeper::RecordPrepareEnd(int num_hwcomposited_layers) {
+  last_composition_stats_.prepare_end = MonotonicTimePoint::Now();
+  last_composition_stats_.num_hwc_layers = num_hwcomposited_layers;
+}
+
+void StatsKeeper::RecordSetStart() {
+  last_composition_stats_.set_start = MonotonicTimePoint::Now();
+}
+
+void StatsKeeper::RecordSetEnd() {
+  last_composition_stats_.set_end = MonotonicTimePoint::Now();
+  LockGuard<Mutex> lock(mutex_);
+  num_set_calls_++;
+  while (!raw_composition_data_.empty() &&
+         period_length_ < last_composition_stats_.set_end -
+                              raw_composition_data_.front().time_point()) {
+    const CompositionData& front = raw_composition_data_.front();
+
+    num_prepare_calls_ -= front.num_prepare_calls();
+    --num_set_calls_;
+    num_layers_ -= front.num_layers();
+    num_hwcomposited_layers_ -= front.num_hwcomposited_layers();
+    prepare_call_total_time_ =
+        Nanoseconds(prepare_call_total_time_ - front.prepare_time());
+    set_call_total_time_ =
+        Nanoseconds(set_call_total_time_ - front.set_calls_time());
+
+    MultisetDeleteOne(&prepare_calls_per_set_calls_, front.num_prepare_calls());
+    MultisetDeleteOne(&layers_per_compositions_, front.num_layers());
+    MultisetDeleteOne(&prepare_call_times_, front.prepare_time());
+    MultisetDeleteOne(&set_call_times_, front.set_calls_time());
+    if (front.num_hwcomposited_layers() != 0) {
+      MultisetDeleteOne(
+          &set_call_times_per_hwcomposited_layer_ns_,
+          front.set_calls_time().count() / front.num_hwcomposited_layers());
+    }
+
+    raw_composition_data_.pop_front();
+  }
+  Nanoseconds last_prepare_call_time_(last_composition_stats_.prepare_end -
+                                      last_composition_stats_.prepare_start);
+  Nanoseconds last_set_call_total_time_(last_composition_stats_.set_end -
+                                        last_composition_stats_.set_start);
+  raw_composition_data_.push_back(CompositionData(
+      last_composition_stats_.set_end, last_composition_stats_.num_prepare_calls,
+      last_composition_stats_.num_layers, last_composition_stats_.num_hwc_layers, last_prepare_call_time_,
+      last_set_call_total_time_));
+
+  const CompositionData& back = raw_composition_data_.back();
+
+  // There may be several calls to prepare before a call to set, but the only
+  // valid call is the last one, so we need to compute these here:
+  num_layers_ += last_composition_stats_.num_layers;
+  num_hwcomposited_layers_ += last_composition_stats_.num_hwc_layers;
+  prepare_call_total_time_ =
+      Nanoseconds(prepare_call_total_time_ + last_prepare_call_time_);
+  set_call_total_time_ =
+      Nanoseconds(set_call_total_time_ + last_set_call_total_time_);
+  prepare_calls_per_set_calls_.insert(last_composition_stats_.num_prepare_calls);
+  layers_per_compositions_.insert(last_composition_stats_.num_layers);
+  prepare_call_times_.insert(last_prepare_call_time_);
+  set_call_times_.insert(last_set_call_total_time_);
+  if (last_composition_stats_.num_hwc_layers != 0) {
+    set_call_times_per_hwcomposited_layer_ns_.insert(
+        last_set_call_total_time_.count() / last_composition_stats_.num_hwc_layers);
+  }
+
+  // Reset the counter
+  last_composition_stats_.num_prepare_calls = 0;
+}
+
+void StatsKeeper::SynchronizedDump(char* buffer,
+                                            int buffer_size) const {
+  LockGuard<Mutex> lock(mutex_);
+  int chars_written = 0;
+// Make sure there is enough space to write the next line
+#define bprintf(...)                                                           \
+  (chars_written += (chars_written < buffer_size)                              \
+                        ? (snprintf(&buffer[chars_written],                    \
+                                    buffer_size - chars_written, __VA_ARGS__)) \
+                        : 0)
+
+  bprintf(
+      "HWComposer stats from the %lld seconds just before the last call to "
+      "set() (which happended %lld seconds ago):\n",
+      Seconds(period_length_).count(),
+      Seconds(MonotonicTimePoint::Now() - last_composition_stats_.set_end).count());
+  bprintf("  Layer count: %d\n", num_layers_);
+
+  if (num_layers_ == 0 || num_prepare_calls_ == 0 || num_set_calls_ == 0) {
+    return;
+  }
+
+  bprintf("  Layers composited by hwcomposer: %d (%d%%)\n",
+          num_hwcomposited_layers_,
+          100 * num_hwcomposited_layers_ / num_layers_);
+  bprintf("  Number of calls to prepare(): %d\n", num_prepare_calls_);
+  bprintf("  Number of calls to set(): %d\n", num_set_calls_);
+  if (num_set_calls_ > 0) {
+    bprintf(
+        "  Maximum number of calls to prepare() before a single call to set(): "
+        "%d\n",
+        MultisetMax(prepare_calls_per_set_calls_));
+  }
+  bprintf(
+      "  Time spent on prepare() (in microseconds):\n    max: %lld\n    "
+      "average: %lld\n    min: %lld\n    total: %lld\n",
+      Microseconds(MultisetMax(prepare_call_times_)).count(),
+      Microseconds(prepare_call_total_time_).count() / num_prepare_calls_,
+      Microseconds(MultisetMin(prepare_call_times_)).count(),
+      Microseconds(prepare_call_total_time_).count());
+  bprintf(
+      "  Time spent on set() (in microseconds):\n    max: %lld\n    average: "
+      "%lld\n    min: %lld\n    total: %lld\n",
+      Microseconds(MultisetMax(set_call_times_)).count(),
+      Microseconds(set_call_total_time_).count() / num_set_calls_,
+      Microseconds(MultisetMin(set_call_times_)).count(),
+      Microseconds(set_call_total_time_).count());
+  if (num_hwcomposited_layers_ > 0) {
+    bprintf(
+        "  Per layer compostition time:\n    max: %lld\n    average: %lld\n    "
+        "min: %lld\n",
+        Microseconds(MultisetMax(set_call_times_per_hwcomposited_layer_ns_))
+            .count(),
+        Microseconds(set_call_total_time_).count() / num_hwcomposited_layers_,
+        Microseconds(MultisetMin(set_call_times_per_hwcomposited_layer_ns_))
+            .count());
+  }
+  bprintf("Statistics from last 100 compositions:\n");
+  bprintf("  Total area: %lld square pixels\n", total_layers_area);
+  if (total_layers_area != 0) {
+    bprintf("  Total invisible area: %lld square pixels, %lld%%\n",
+            total_invisible_area,
+            100 * total_invisible_area / total_layers_area);
+  }
+#undef bprintf
+}
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/stats_keeper.h b/guest/hals/hwcomposer/legacy/stats_keeper.h
new file mode 100644
index 0000000..8612cd1
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/stats_keeper.h
@@ -0,0 +1,187 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h>
+#include <common/libs/time/monotonic_time.h>
+#include <common/libs/threads/cuttlefish_thread.h>
+#include <android-base/thread_annotations.h>
+#include <deque>
+#include <set>
+
+#include "hwcomposer_common.h"
+
+namespace cvd {
+
+class CompositionData {
+ public:
+  CompositionData(cvd::time::MonotonicTimePoint time_point,
+                  int num_prepares, int num_layers, int num_hwcomposited_layers,
+                  cvd::time::Nanoseconds prepare_time,
+                  cvd::time::Nanoseconds set_calls_time)
+      : time_point_(time_point),
+        num_prepare_calls_(num_prepares),
+        num_layers_(num_layers),
+        num_hwcomposited_layers_(num_hwcomposited_layers),
+        prepare_time_(prepare_time),
+        set_calls_time_(set_calls_time) {}
+
+  cvd::time::MonotonicTimePoint time_point() const {
+    return time_point_;
+  }
+
+  int num_prepare_calls() const { return num_prepare_calls_; }
+
+  int num_layers() const { return num_layers_; }
+
+  int num_hwcomposited_layers() const { return num_hwcomposited_layers_; }
+
+  cvd::time::Nanoseconds prepare_time() const {
+    return prepare_time_;
+  }
+
+  cvd::time::Nanoseconds set_calls_time() const {
+    return set_calls_time_;
+  }
+
+ private:
+  cvd::time::MonotonicTimePoint time_point_;
+  int num_prepare_calls_;
+  int num_layers_;
+  int num_hwcomposited_layers_;
+  cvd::time::Nanoseconds prepare_time_;
+  cvd::time::Nanoseconds set_calls_time_;
+};
+
+class StatsKeeper {
+ public:
+  // The timespan parameter indicates for how long we keep stats about the past
+  // compositions.
+  StatsKeeper(cvd::time::TimeDifference timespan,
+              int64_t vsync_base,
+              int32_t vsync_period);
+  StatsKeeper();
+  ~StatsKeeper();
+
+  // Record the time at which a call to prepare was made, takes the number of
+  // layers received (excluding the framebuffer) as a parameter.
+  void RecordPrepareStart(int num_layers);
+  // Record the time at which a call to prepare (was about to) returned, takes
+  // the number of layers marked for hardware composition as a parameter.
+  void RecordPrepareEnd(int num_hwcomposited_layers);
+  void RecordSetStart();
+  void RecordSetEnd() EXCLUDES(mutex_);
+
+  const CompositionStats& last_composition_stats() { return last_composition_stats_; }
+
+  // Calls to this function are synchronized with calls to 'RecordSetEnd' with a
+  // mutex. The other Record* functions do not need such synchronization because
+  // they access last_* variables only, which are not read by 'Dump'.
+  void SynchronizedDump(char* buffer, int buffer_size) const EXCLUDES(mutex_);
+
+ private:
+
+  cvd::time::TimeDifference period_length_;
+
+  // Base and period of the VSYNC signal, allows to accurately calculate the
+  // time of the last vsync broadcast.
+  int64_t vsync_base_;
+  int32_t vsync_period_;
+  // Data collected about ongoing composition. These variables are not accessed
+  // from Dump(), so they don't need to be guarded by a mutex.
+  CompositionStats last_composition_stats_;
+
+  // Aggregated performance data collected from past compositions. These
+  // variables are modified when a composition is completed and when old
+  // compositions need to be discarded in RecordSetEnd(), and is accessed from
+  // Dump(). Non-aggregated data is kept in the raw_composition_data_ deque to
+  // be able to discard old values from the aggregated data.
+  int num_layers_ GUARDED_BY(mutex_);
+  int num_hwcomposited_layers_ GUARDED_BY(mutex_);
+  int num_prepare_calls_ GUARDED_BY(mutex_);
+  int num_set_calls_ GUARDED_BY(mutex_);
+  cvd::time::Nanoseconds prepare_call_total_time_ GUARDED_BY(mutex_);
+  cvd::time::Nanoseconds set_call_total_time_ GUARDED_BY(mutex_);
+  // These are kept in multisets to be able to calculate mins and maxs of
+  // changing sets of (not necessarily different) values.
+  std::multiset<int> prepare_calls_per_set_calls_ GUARDED_BY(mutex_);
+  std::multiset<int> layers_per_compositions_ GUARDED_BY(mutex_);
+  std::multiset<cvd::time::Nanoseconds> prepare_call_times_
+      GUARDED_BY(mutex_);
+  std::multiset<cvd::time::Nanoseconds> set_call_times_
+      GUARDED_BY(mutex_);
+  std::multiset<int64_t> set_call_times_per_hwcomposited_layer_ns_
+      GUARDED_BY(mutex_);
+
+  // Time-ordered list of compositions, used to update the global aggregated
+  // performance data when old compositions fall out of the period of interest.
+  std::deque<CompositionData> raw_composition_data_ GUARDED_BY(mutex_);
+
+  // TODO(jemoreira): Add min/max/average composition times per layer area units
+
+  std::deque<std::pair<int64_t, int64_t> > composition_areas GUARDED_BY(mutex_);
+  int64_t total_layers_area GUARDED_BY(mutex_);
+  int64_t total_invisible_area GUARDED_BY(mutex_);
+
+  // Controls access to data from past compositions.
+  mutable cvd::Mutex mutex_;
+};
+
+template <class Composer>
+class StatsKeepingComposer {
+ public:
+  // Keep stats from the last 10 seconds.
+  StatsKeepingComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns)
+      : composer_(vsync_base_timestamp, vsync_period_ns),
+        stats_keeper_(cvd::time::TimeDifference(cvd::time::Seconds(10), 1),
+                      vsync_base_timestamp,
+                      vsync_period_ns) {
+    // Don't let the composer broadcast by itself, allow it to return to collect
+    // the timings and broadcast then.
+    composer_.ReplaceFbBroadcaster(NULL);
+  }
+  ~StatsKeepingComposer() {}
+
+  int PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+    stats_keeper_.RecordPrepareStart(num_layers);
+    int num_hwc_layers = composer_.PrepareLayers(num_layers, layers);
+    stats_keeper_.RecordPrepareEnd(num_hwc_layers);
+    return num_hwc_layers;
+  }
+
+  int SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+    stats_keeper_.RecordSetStart();
+    int yoffset = composer_.SetLayers(num_layers, layers);
+    stats_keeper_.RecordSetEnd();
+    if (yoffset >= 0) {
+      VSoCFrameBufferControl::getInstance().BroadcastFrameBufferChanged(
+          yoffset, &stats_keeper_.last_composition_stats());
+    } else {
+      ALOGE("%s: Error on SetLayers(), yoffset: %d", __FUNCTION__, yoffset);
+    }
+    return yoffset;
+  }
+
+  void Dump(char* buff, int buff_len) {
+    stats_keeper_.SynchronizedDump(buff, buff_len);
+  }
+
+ private:
+  StatsKeeper stats_keeper_;
+  Composer composer_;
+};
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.cpp b/guest/hals/hwcomposer/legacy/vsoc_composer.cpp
new file mode 100644
index 0000000..5fcaccb
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/vsoc_composer.cpp
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vsoc_composer.h"
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer.h>
+#include <guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h>
+#include <cutils/log.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <libyuv.h>
+#include <system/graphics.h>
+#include <algorithm>
+#include <cstdlib>
+#include <utility>
+#include <vector>
+#include "geometry_utils.h"
+#include "hwcomposer_common.h"
+
+namespace cvd {
+
+namespace {
+
+// Ensures that the layer does not include any inconsistencies
+int SanityCheckLayer(const vsoc_hwc_layer& layer) {
+  // Check displayFrame
+  if (layer.displayFrame.left > layer.displayFrame.right ||
+      layer.displayFrame.top > layer.displayFrame.bottom) {
+    ALOGE(
+        "%s: Malformed rectangle (displayFrame): [left = %d, right = %d, top = "
+        "%d, bottom = %d]",
+        __FUNCTION__,
+        layer.displayFrame.left,
+        layer.displayFrame.right,
+        layer.displayFrame.top,
+        layer.displayFrame.bottom);
+    return -EINVAL;
+  }
+  // Check sourceCrop
+  if (layer.sourceCrop.left > layer.sourceCrop.right ||
+      layer.sourceCrop.top > layer.sourceCrop.bottom) {
+    ALOGE(
+        "%s: Malformed rectangle (sourceCrop): [left = %d, right = %d, top = "
+        "%d, bottom = %d]",
+        __FUNCTION__,
+        layer.sourceCrop.left,
+        layer.sourceCrop.right,
+        layer.sourceCrop.top,
+        layer.sourceCrop.bottom);
+    return -EINVAL;
+  }
+  const private_handle_t* p_handle =
+      reinterpret_cast<const private_handle_t*>(layer.handle);
+  if (!p_handle) {
+    ALOGE("Layer has a NULL buffer handle");
+    return -EINVAL;
+  }
+  if (layer.sourceCrop.left < 0 || layer.sourceCrop.top < 0 ||
+      layer.sourceCrop.right > p_handle->x_res ||
+      layer.sourceCrop.bottom > p_handle->y_res) {
+    ALOGE(
+        "%s: Invalid sourceCrop for buffer handle: sourceCrop = [left = %d, "
+        "right = %d, top = %d, bottom = %d], handle = [width = %d, height = "
+        "%d]",
+        __FUNCTION__,
+        layer.sourceCrop.left,
+        layer.sourceCrop.right,
+        layer.sourceCrop.top,
+        layer.sourceCrop.bottom,
+        p_handle->x_res,
+        p_handle->y_res);
+    return -EINVAL;
+  }
+  return 0;
+}
+
+bool LayerNeedsScaling(const vsoc_hwc_layer& layer) {
+  int from_w = layer.sourceCrop.right - layer.sourceCrop.left;
+  int from_h = layer.sourceCrop.bottom - layer.sourceCrop.top;
+  int to_w = layer.displayFrame.right - layer.displayFrame.left;
+  int to_h = layer.displayFrame.bottom - layer.displayFrame.top;
+
+  bool not_rot_scale = from_w != to_w || from_h != to_h;
+  bool rot_scale = from_w != to_h || from_h != to_w;
+
+  bool needs_rot = layer.transform & HAL_TRANSFORM_ROT_90;
+
+  return needs_rot ? rot_scale : not_rot_scale;
+}
+
+bool LayerNeedsBlending(const vsoc_hwc_layer& layer) {
+  return layer.blending != HWC_BLENDING_NONE;
+}
+
+bool LayerNeedsAttenuation(const vsoc_hwc_layer& layer) {
+  return layer.blending == HWC_BLENDING_COVERAGE;
+}
+
+struct BufferSpec;
+typedef int (*ConverterFunction)(const BufferSpec& src, const BufferSpec& dst,
+                                 bool v_flip);
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip);
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool v_flip);
+ConverterFunction GetConverter(uint32_t format) {
+  switch (format) {
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+    case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+      return &DoCopy;
+
+    case HAL_PIXEL_FORMAT_YV12:
+      return &ConvertFromYV12;
+
+    // Unsupported formats
+    // TODO(jemoreira): Conversion from these formats should be implemented as
+    // we find evidence of its usage.
+    // case HAL_PIXEL_FORMAT_BGRA_8888:
+
+    // case HAL_PIXEL_FORMAT_RGB_888:
+    // case HAL_PIXEL_FORMAT_RGB_565:
+
+    // case HAL_PIXEL_FORMAT_sRGB_A_8888:
+    // case HAL_PIXEL_FORMAT_sRGB_X_8888:
+
+    // case HAL_PIXEL_FORMAT_Y8:
+    // case HAL_PIXEL_FORMAT_Y16:
+
+    // case HAL_PIXEL_FORMAT_RAW_SENSOR:
+    // case HAL_PIXEL_FORMAT_BLOB:
+
+    // case HAL_PIXEL_FORMAT_YCbCr_420_888:
+    // case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+    // case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+    // case HAL_PIXEL_FORMAT_YCbCr_422_I:
+    default:
+      ALOGW("Unsupported format: 0x%04x, returning null converter function",
+            format);
+  }
+  return NULL;
+}
+
+// Whether we support a given format
+bool IsFormatSupported(uint32_t format) {
+  return GetConverter(format) != NULL;
+}
+
+bool CanCompositeLayer(const vsoc_hwc_layer& layer) {
+  if (layer.handle == NULL) {
+    ALOGW("%s received a layer with a null handler", __FUNCTION__);
+    return false;
+  }
+  int format = reinterpret_cast<const private_handle_t*>(layer.handle)->format;
+  if (!IsFormatSupported(format)) {
+    ALOGD("Unsupported pixel format: 0x%x, doing software composition instead",
+          format);
+    return false;
+  }
+  return true;
+}
+
+/*******************************************************************************
+Libyuv's convert functions only allow the combination of any rotation (multiple
+of 90 degrees) and a vertical flip, but not horizontal flips.
+Surfaceflinger's transformations are expressed in terms of a vertical flip, a
+horizontal flip and/or a single 90 degrees clockwise rotation (see
+NATIVE_WINDOW_TRANSFORM_HINT documentation on system/window.h for more insight).
+The following code allows to turn a horizontal flip into a 180 degrees rotation
+and a vertical flip.
+*******************************************************************************/
+libyuv::RotationMode GetRotationFromTransform(uint32_t transform) {
+  uint32_t rotation =
+      (transform & HAL_TRANSFORM_ROT_90) ? 1 : 0;          // 1 * ROT90 bit
+  rotation += (transform & HAL_TRANSFORM_FLIP_H) ? 2 : 0;  // 2 * VFLIP bit
+  return static_cast<libyuv::RotationMode>(90 * rotation);
+}
+
+bool GetVFlipFromTransform(uint32_t transform) {
+  // vertical flip xor horizontal flip
+  return ((transform & HAL_TRANSFORM_FLIP_V) >> 1) ^
+         (transform & HAL_TRANSFORM_FLIP_H);
+}
+
+struct BufferSpec {
+  uint8_t* buffer;
+  size_t size;
+  int width;
+  int height;
+  int stride;
+  int crop_x;
+  int crop_y;
+  int crop_width;
+  int crop_height;
+  uint32_t format;
+
+  BufferSpec(uint8_t* buffer, size_t size, int width, int height, int stride)
+      : buffer(buffer),
+        size(size),
+        width(width),
+        height(height),
+        stride(stride),
+        crop_x(0),
+        crop_y(0),
+        crop_width(width),
+        crop_height(height),
+        format(HAL_PIXEL_FORMAT_RGBA_8888) {}
+};
+
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  // use the stride in pixels as the source width
+  int stride_in_pixels = src.stride / formatToBytesPerPixel(src.format);
+
+  // The following calculation of plane offsets and alignments are based on
+  // swiftshader's Sampler::setTextureLevel() implementation
+  // (Renderer/Sampler.cpp:225)
+  uint8_t* src_y = src.buffer;
+  int stride_y = stride_in_pixels;
+  uint8_t* src_v = src_y + stride_y * src.height;
+  int stride_v = VSoCFrameBuffer::align(stride_y / 2, 16);
+  uint8_t* src_u = src_v + stride_v * src.height / 2;
+  int stride_u = VSoCFrameBuffer::align(stride_y / 2, 16);
+
+  // Adjust for crop
+  src_y += src.crop_y * stride_y + src.crop_x;
+  src_v += (src.crop_y / 2) * stride_v + (src.crop_x / 2);
+  src_u += (src.crop_y / 2) * stride_u + (src.crop_x / 2);
+  uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+                        dst.crop_x * formatToBytesPerPixel(dst.format);
+
+  // YV12 is the same as I420, with the U and V planes swapped
+  return libyuv::I420ToARGB(src_y, stride_y, src_v, stride_v, src_u, stride_u,
+                            dst_buffer, dst.stride, dst.crop_width,
+                            v_flip ? -dst.crop_height : dst.crop_height);
+}
+
+int DoConversion(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  return (*GetConverter(src.format))(src, dst, v_flip);
+}
+
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  // Point to the upper left corner of the crop rectangle
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+                        dst.crop_x * formatToBytesPerPixel(dst.format);
+  int width = src.crop_width;
+  int height = src.crop_height;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  // HAL formats are named based on the order of the pixel componets on the
+  // byte stream, while libyuv formats are named based on the order of those
+  // pixel components in an integer written from left to right. So
+  // libyuv::FOURCC_ARGB is equivalent to HAL_PIXEL_FORMAT_BGRA_8888.
+  return libyuv::ARGBCopy(src_buffer, src.stride, dst_buffer, dst.stride, width,
+                          height);
+}
+
+int DoRotation(const BufferSpec& src, const BufferSpec& dst,
+               libyuv::RotationMode rotation, bool v_flip) {
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+                        dst.crop_x * formatToBytesPerPixel(dst.format);
+  int width = src.crop_width;
+  int height = src.crop_height;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  return libyuv::ARGBRotate(src_buffer, src.stride, dst_buffer, dst.stride,
+                            width, height, rotation);
+}
+
+int DoScaling(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+                        dst.crop_x * formatToBytesPerPixel(dst.format);
+  int src_width = src.crop_width;
+  int src_height = src.crop_height;
+  int dst_width = dst.crop_width;
+  int dst_height = dst.crop_height;
+
+  if (v_flip) {
+    src_height = -src_height;
+  }
+
+  return libyuv::ARGBScale(src_buffer, src.stride, src_width, src_height,
+                           dst_buffer, dst.stride, dst_width, dst_height,
+                           libyuv::kFilterBilinear);
+}
+
+int DoAttenuation(const BufferSpec& src, const BufferSpec& dest, bool v_flip) {
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dest.buffer + dest.crop_y * dest.stride +
+                        dest.crop_x * formatToBytesPerPixel(dest.format);
+  int width = dest.crop_width;
+  int height = dest.crop_height;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  return libyuv::ARGBAttenuate(src_buffer, src.stride, dst_buffer, dest.stride,
+                               width, height);
+}
+
+int DoBlending(const BufferSpec& src, const BufferSpec& dest, bool v_flip) {
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+                        src.crop_x * formatToBytesPerPixel(src.format);
+  uint8_t* dst_buffer = dest.buffer + dest.crop_y * dest.stride +
+                        dest.crop_x * formatToBytesPerPixel(dest.format);
+  int width = dest.crop_width;
+  int height = dest.crop_height;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  // libyuv's ARGB format is hwcomposer's BGRA format, since blending only cares
+  // for the position of alpha in the pixel and not the position of the colors
+  // this function is perfectly usable.
+  return libyuv::ARGBBlend(src_buffer, src.stride, dst_buffer, dest.stride,
+                           dst_buffer, dest.stride, width, height);
+}
+
+}  // namespace
+
+// Returns a handle to the appropriate framebuffer to use:
+// - the one provided by surfaceflinger if it is doing any GLES composition
+// - the next hwc-only framebuffer otherwise
+// Takes care of rotating the hwc-only framebuffers
+buffer_handle_t VSoCComposer::FindFrameBuffer(int num_layers,
+                                             vsoc_hwc_layer* layers) {
+  buffer_handle_t* fb_handle = NULL;
+  bool use_hwc_fb = true;
+  // The framebuffer target is usually the last layer in the list, so iterate in
+  // reverse
+  for (int idx = num_layers - 1; idx >= 0; --idx) {
+    if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+      fb_handle = &layers[idx].handle;
+    } else if (layers[idx].compositionType != HWC_OVERLAY) {
+      use_hwc_fb = false;
+      // Just in case the FB target was not found yet
+      if (fb_handle) break;
+    }
+  }
+  if (use_hwc_fb && !hwc_framebuffers_.empty()) {
+    fb_handle = &hwc_framebuffers_[next_hwc_framebuffer_];
+    next_hwc_framebuffer_ =
+        (next_hwc_framebuffer_ + 1) % hwc_framebuffers_.size();
+  }
+
+  return *fb_handle;
+}
+
+void VSoCComposer::CompositeLayer(vsoc_hwc_layer* src_layer,
+                                 buffer_handle_t dst_handle) {
+  libyuv::RotationMode rotation =
+      GetRotationFromTransform(src_layer->transform);
+
+  const private_handle_t* src_priv_handle =
+      reinterpret_cast<const private_handle_t*>(src_layer->handle);
+  const private_handle_t* dst_priv_handle =
+      reinterpret_cast<const private_handle_t*>(dst_handle);
+
+  bool needs_conversion = src_priv_handle->format != dst_priv_handle->format;
+  bool needs_scaling = LayerNeedsScaling(*src_layer);
+  bool needs_rotation = rotation != libyuv::kRotate0;
+  bool needs_transpose = needs_rotation && rotation != libyuv::kRotate180;
+  bool needs_vflip = GetVFlipFromTransform(src_layer->transform);
+  bool needs_attenuation = LayerNeedsAttenuation(*src_layer);
+  bool needs_blending = LayerNeedsBlending(*src_layer);
+  bool needs_copy = !(needs_conversion || needs_scaling || needs_rotation ||
+                      needs_vflip || needs_attenuation || needs_blending);
+
+  uint8_t* src_buffer;
+  uint8_t* dst_buffer;
+  int retval = gralloc_module_->lock(
+      gralloc_module_, src_layer->handle, GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
+      src_priv_handle->x_res, src_priv_handle->y_res,
+      reinterpret_cast<void**>(&src_buffer));
+  if (retval) {
+    ALOGE("Got error code %d from lock function", retval);
+    return;
+  }
+  retval = gralloc_module_->lock(gralloc_module_, dst_handle,
+                                 GRALLOC_USAGE_SW_WRITE_OFTEN, 0, 0,
+                                 dst_priv_handle->x_res, dst_priv_handle->y_res,
+                                 reinterpret_cast<void**>(&dst_buffer));
+  if (retval) {
+    ALOGE("Got error code %d from lock function", retval);
+    // TODO(jemoreira): Use a lock_guard-like object.
+    gralloc_module_->unlock(gralloc_module_, src_priv_handle);
+    return;
+  }
+
+  BufferSpec src_layer_spec(src_buffer, src_priv_handle->total_size,
+                            src_priv_handle->x_res, src_priv_handle->y_res,
+                            src_priv_handle->stride_in_pixels *
+                                formatToBytesPerPixel(src_priv_handle->format));
+  src_layer_spec.crop_x = src_layer->sourceCrop.left;
+  src_layer_spec.crop_y = src_layer->sourceCrop.top;
+  src_layer_spec.crop_width =
+      src_layer->sourceCrop.right - src_layer->sourceCrop.left;
+  src_layer_spec.crop_height =
+      src_layer->sourceCrop.bottom - src_layer->sourceCrop.top;
+  src_layer_spec.format = src_priv_handle->format;
+
+  BufferSpec dst_layer_spec(dst_buffer, dst_priv_handle->total_size,
+                            dst_priv_handle->x_res, dst_priv_handle->y_res,
+                            dst_priv_handle->stride_in_pixels *
+                                formatToBytesPerPixel(dst_priv_handle->format));
+  dst_layer_spec.crop_x = src_layer->displayFrame.left;
+  dst_layer_spec.crop_y = src_layer->displayFrame.top;
+  dst_layer_spec.crop_width =
+      src_layer->displayFrame.right - src_layer->displayFrame.left;
+  dst_layer_spec.crop_height =
+      src_layer->displayFrame.bottom - src_layer->displayFrame.top;
+  dst_layer_spec.format = dst_priv_handle->format;
+
+  // Add the destination layer to the bottom of the buffer stack
+  std::vector<BufferSpec> dest_buffer_stack(1, dst_layer_spec);
+
+  // If more than operation is to be performed, a temporary buffer is needed for
+  // each additional operation
+
+  // N operations need N destination buffers, the destination layer (the
+  // framebuffer) is one of them, so only N-1 temporary buffers are needed.
+  // Vertical flip is not taken into account because it can be done together
+  // with any other operation.
+  int needed_tmp_buffers = (needs_conversion ? 1 : 0) +
+                           (needs_scaling ? 1 : 0) + (needs_rotation ? 1 : 0) +
+                           (needs_attenuation ? 1 : 0) +
+                           (needs_blending ? 1 : 0) + (needs_copy ? 1 : 0) - 1;
+
+  int x_res = src_layer->displayFrame.right - src_layer->displayFrame.left;
+  int y_res = src_layer->displayFrame.bottom - src_layer->displayFrame.top;
+  size_t output_frame_size =
+      x_res * y_res * formatToBytesPerPixel(dst_priv_handle->format);
+  while (needed_tmp_buffers > 0) {
+    BufferSpec tmp(
+        RotateTmpBuffer(needed_tmp_buffers), output_frame_size, x_res, y_res,
+        // There should be no danger of overflow aligning the stride because
+        // these sizes are taken from the displayFrame rectangle which is always
+        // smaller than the framebuffer, the framebuffer in turn has aligned
+        // stride and these buffers are the size of the framebuffer.
+        VSoCFrameBuffer::align(
+            x_res * formatToBytesPerPixel(dst_priv_handle->format), 16));
+    dest_buffer_stack.push_back(tmp);
+    needed_tmp_buffers--;
+  }
+
+  // Conversion and scaling should always be the first operations, so that every
+  // other operation works on equally sized frames (garanteed to fit in the tmp
+  // buffers)
+
+  // TODO(jemoreira): We are converting to ARGB as the first step under the
+  // assumption that scaling ARGB is faster than scaling I420 (the most common).
+  // This should be confirmed with testing.
+  if (needs_conversion) {
+    BufferSpec& dst_buffer_spec = dest_buffer_stack.back();
+    if (needs_scaling || needs_transpose) {
+      // If a rotation or a scaling operation are needed the dimensions at the
+      // top of the buffer stack are wrong (wrong sizes for scaling, swapped
+      // width and height for 90 and 270 rotations).
+      // Make width and height match the crop sizes on the source
+      int src_width = src_layer_spec.crop_width;
+      int src_height = src_layer_spec.crop_height;
+      int dst_stride = VSoCFrameBuffer::align(
+          src_width * formatToBytesPerPixel(dst_priv_handle->format), 16);
+      size_t needed_size = dst_stride * src_height;
+      dst_buffer_spec.width = src_width;
+      dst_buffer_spec.height = src_height;
+      // Ajust the stride accordingly
+      dst_buffer_spec.stride = dst_stride;
+      // Crop sizes also need to be adjusted
+      dst_buffer_spec.crop_width = src_width;
+      dst_buffer_spec.crop_height = src_height;
+      dst_buffer_spec.size = needed_size;
+      // crop_x and y are fine at 0, format is already set to match destination
+
+      // In case of a scale, the source frame may be bigger than the default tmp
+      // buffer size
+      if (needed_size > tmp_buffer_.size() / kNumTmpBufferPieces) {
+        dst_buffer_spec.buffer = GetSpecialTmpBuffer(needed_size);
+      }
+    }
+    retval = DoConversion(src_layer_spec, dst_buffer_spec, needs_vflip);
+    if (retval) {
+      ALOGE("Got error code %d from DoConversion function", retval);
+    }
+    needs_vflip = false;
+    src_layer_spec = dst_buffer_spec;
+    dest_buffer_stack.pop_back();
+  }
+
+  if (needs_scaling) {
+    BufferSpec& dst_buffer_spec = dest_buffer_stack.back();
+    if (needs_transpose) {
+      // If a rotation is needed, the temporary buffer has the correct size but
+      // needs to be transposed and have its stride updated accordingly. The
+      // crop sizes also needs to be transposed, but not the x and y since they
+      // are both zero in a temporary buffer (and it is a temporary buffer
+      // because a rotation will be performed next).
+      std::swap(dst_buffer_spec.width, dst_buffer_spec.height);
+      std::swap(dst_buffer_spec.crop_width, dst_buffer_spec.crop_height);
+      // TODO (jemoreira): Aligment (To align here may cause the needed size to
+      // be bigger than the buffer, so care should be taken)
+      dst_buffer_spec.stride = dst_buffer_spec.width *
+                               formatToBytesPerPixel(dst_priv_handle->format);
+    }
+    retval = DoScaling(src_layer_spec, dst_buffer_spec, needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoScaling function", retval);
+    }
+    src_layer_spec = dst_buffer_spec;
+    dest_buffer_stack.pop_back();
+  }
+
+  if (needs_rotation) {
+    retval = DoRotation(src_layer_spec, dest_buffer_stack.back(), rotation,
+                        needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoTransform function", retval);
+    }
+    src_layer_spec = dest_buffer_stack.back();
+    dest_buffer_stack.pop_back();
+  }
+
+  if (needs_attenuation) {
+    retval =
+        DoAttenuation(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoBlending function", retval);
+    }
+    src_layer_spec = dest_buffer_stack.back();
+    dest_buffer_stack.pop_back();
+  }
+
+  if (needs_copy) {
+    retval = DoCopy(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoBlending function", retval);
+    }
+    src_layer_spec = dest_buffer_stack.back();
+    dest_buffer_stack.pop_back();
+  }
+
+  // Blending (if needed) should always be the last operation, so that it reads
+  // and writes in the destination layer and not some temporary buffer.
+  if (needs_blending) {
+    retval = DoBlending(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+    needs_vflip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoBlending function", retval);
+    }
+    // Don't need to assign destination to source in the last one
+    dest_buffer_stack.pop_back();
+  }
+
+  gralloc_module_->unlock(gralloc_module_, src_priv_handle);
+  gralloc_module_->unlock(gralloc_module_, dst_priv_handle);
+}
+
+/* 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),
+      tmp_buffer_(kNumTmpBufferPieces *
+                  VSoCFrameBuffer::getInstance().bufferSize()),
+      next_hwc_framebuffer_(0) {
+  hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+                reinterpret_cast<const hw_module_t**>(&gralloc_module_));
+  gralloc_module_->common.methods->open(
+      reinterpret_cast<const hw_module_t*>(gralloc_module_),
+      GRALLOC_HARDWARE_GPU0,
+      reinterpret_cast<hw_device_t**>(&gralloc_dev_));
+  for (int i = 0; i < VSoCFrameBuffer::kNumHwcBuffers; ++i) {
+    buffer_handle_t tmp;
+    gralloc_dev_->alloc_hwc_framebuffer(
+        reinterpret_cast<alloc_device_t*>(gralloc_dev_), &tmp);
+    hwc_framebuffers_.push_back(tmp);
+  }
+}
+
+VSoCComposer::~VSoCComposer() {
+  // Free the hwc fb handles
+  for (int idx = 0; idx < hwc_framebuffers_.size(); ++idx) {
+    gralloc_dev_->device.free(reinterpret_cast<alloc_device_t*>(gralloc_dev_),
+                              hwc_framebuffers_[idx]);
+  }
+
+  // close devices
+  gralloc_dev_->device.common.close(reinterpret_cast<hw_device_t*>(gralloc_dev_));
+}
+
+int VSoCComposer::PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+  int composited_layers_count = 0;
+
+  // Loop over layers in inverse order of z-index
+  for (size_t layer_index = num_layers; layer_index > 0;) {
+    // Decrement here to be able to compare unsigned integer with 0 in the
+    // loop condition
+    --layer_index;
+    if (IS_TARGET_FRAMEBUFFER(layers[layer_index].compositionType)) {
+      continue;
+    }
+    if (layers[layer_index].flags & HWC_SKIP_LAYER) {
+      continue;
+    }
+    if (layers[layer_index].compositionType == HWC_BACKGROUND) {
+      layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+      continue;
+    }
+    layers[layer_index].compositionType = HWC_OVERLAY;
+    // Hwcomposer cannot draw below software-composed layers, so we need
+    // to mark those HWC_FRAMEBUFFER as well.
+    for (size_t top_idx = layer_index + 1; top_idx < num_layers; ++top_idx) {
+      // layers marked as skip are in a state that makes them unreliable to
+      // read, so it's best to assume they cover the whole screen
+      if (layers[top_idx].flags & HWC_SKIP_LAYER ||
+          (layers[top_idx].compositionType == HWC_FRAMEBUFFER &&
+           LayersOverlap(layers[layer_index], layers[top_idx]))) {
+        layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+        break;
+      }
+    }
+    if (layers[layer_index].compositionType == HWC_OVERLAY &&
+        !CanCompositeLayer(layers[layer_index])) {
+      layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+    }
+    if (layers[layer_index].compositionType == HWC_OVERLAY) {
+      ++composited_layers_count;
+    }
+  }
+  return composited_layers_count;
+}
+
+int VSoCComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+  int targetFbs = 0;
+  buffer_handle_t fb_handle = FindFrameBuffer(num_layers, layers);
+  if (!fb_handle) {
+    ALOGE("%s: framebuffer handle is null", __FUNCTION__);
+    return -1;
+  }
+  // TODO(jemoreira): Lock all HWC_OVERLAY layers and the framebuffer before
+  // this loop and unlock them after. The way it's done now causes the target
+  // framebuffer to be locked and unlocked many times, if regions are
+  // implemented it will also be true for every layer that covers more than one
+  // region.
+  for (size_t idx = 0; idx < num_layers; idx++) {
+    if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+      ++targetFbs;
+    } else if (layers[idx].compositionType == HWC_OVERLAY &&
+               !(layers[idx].flags & HWC_SKIP_LAYER)) {
+      if (SanityCheckLayer(layers[idx])) {
+        ALOGE("Layer (%d) failed sanity check", idx);
+        return -EINVAL;
+      }
+      CompositeLayer(&layers[idx], fb_handle);
+    }
+  }
+  if (targetFbs != 1) {
+    ALOGW("Saw %d layers, posted=%d", num_layers, targetFbs);
+  }
+  return PostFrameBuffer(fb_handle);
+}
+
+uint8_t* VSoCComposer::RotateTmpBuffer(unsigned int order) {
+  return &tmp_buffer_[(order % kNumTmpBufferPieces) * tmp_buffer_.size() /
+                      kNumTmpBufferPieces];
+}
+
+uint8_t* VSoCComposer::GetSpecialTmpBuffer(size_t needed_size) {
+  special_tmp_buffer_.resize(needed_size);
+  return &special_tmp_buffer_[0];
+}
+
+}  // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.h b/guest/hals/hwcomposer/legacy/vsoc_composer.h
new file mode 100644
index 0000000..cd455a5
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/vsoc_composer.h
@@ -0,0 +1,51 @@
+#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 <hardware/gralloc.h>
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+#include "hwcomposer_common.h"
+#include "base_composer.h"
+
+#include <vector>
+
+namespace cvd {
+
+class VSoCComposer : public BaseComposer {
+ public:
+  VSoCComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns);
+  ~VSoCComposer();
+
+  // override
+  int PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers);
+  // override
+  int SetLayers(size_t num_layers, vsoc_hwc_layer* layers);
+
+ protected:
+  static const int kNumTmpBufferPieces;
+  uint8_t* RotateTmpBuffer(unsigned int order);
+  uint8_t* GetSpecialTmpBuffer(size_t needed_size);
+  buffer_handle_t FindFrameBuffer(int num_layers, vsoc_hwc_layer* layers);
+  void CompositeLayer(vsoc_hwc_layer* src_layer, buffer_handle_t dst_layer);
+  std::vector<uint8_t> tmp_buffer_;
+  std::vector<uint8_t> special_tmp_buffer_;
+  const gralloc_module_t* gralloc_module_;
+  priv_alloc_device_t* gralloc_dev_;
+  std::vector<buffer_handle_t> hwc_framebuffers_;
+  int next_hwc_framebuffer_;
+};
+
+}  // namespace cvd
diff --git a/guest/hals/lights/Android.mk b/guest/hals/lights/Android.mk
new file mode 100644
index 0000000..f5e2082
--- /dev/null
+++ b/guest/hals/lights/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# HAL module implemenation, not prelinked and stored in
+# hw/<LIGHTS_HARDWARE_MODULE_ID>.<ro.hardware>.so
+include $(CLEAR_VARS)
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MULTILIB := first
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := device/google/cuttlefish_common
+LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_SRC_FILES := lights_vsoc.c
+LOCAL_MODULE := lights.vsoc
+LOCAL_CFLAGS += -DLIGHT_BACKLIGHT -DLOG_TAG=\"VSoC-lights\" $(VSOC_VERSION_CFLAGS)
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/guest/hals/lights/lights_vsoc.c b/guest/hals/lights/lights_vsoc.c
new file mode 100644
index 0000000..d1c9c10
--- /dev/null
+++ b/guest/hals/lights/lights_vsoc.c
@@ -0,0 +1,73 @@
+/*
+ * 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 <cutils/log.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <hardware/lights.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+static int set_light(struct light_device_t* dev,
+                     struct light_state_t const* state) {
+  ALOGI("%s: dev %p state %p", __FUNCTION__, dev, state);
+  if (state) {
+    ALOGI("%s: state value %d\n", __FUNCTION__, state->color);
+  }
+  return 0;
+}
+
+static int close_lights(struct light_device_t* dev) {
+  free(dev);
+  return 0;
+}
+
+static int open_lights(const struct hw_module_t* module,
+                       char const* name __unused, struct hw_device_t** device) {
+  struct light_device_t* dev = malloc(sizeof(struct light_device_t));
+  if (dev == NULL) {
+    return -EINVAL;
+  }
+  memset(dev, 0, sizeof(*dev));
+
+  dev->common.tag = HARDWARE_DEVICE_TAG;
+  dev->common.version = 0;
+  dev->common.module = (struct hw_module_t*)module;
+  dev->common.close = (int (*)(struct hw_device_t*))close_lights;
+  dev->set_light = set_light;
+  *device = (struct hw_device_t*)dev;
+  return 0;
+}
+
+static struct hw_module_methods_t lights_module_methods = {
+    VSOC_STATIC_INITIALIZER(open) open_lights,
+};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+    VSOC_STATIC_INITIALIZER(tag) HARDWARE_MODULE_TAG,
+    VSOC_STATIC_INITIALIZER(version_major) 1,
+    VSOC_STATIC_INITIALIZER(version_minor) 0,
+    VSOC_STATIC_INITIALIZER(id) LIGHTS_HARDWARE_MODULE_ID,
+    VSOC_STATIC_INITIALIZER(name) "Android GCE lights Module",
+    VSOC_STATIC_INITIALIZER(author) "Google",
+    VSOC_STATIC_INITIALIZER(methods) & lights_module_methods,
+};
diff --git a/guest/hals/power/Android.mk b/guest/hals/power/Android.mk
new file mode 100644
index 0000000..3a7cfdb
--- /dev/null
+++ b/guest/hals/power/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# HAL module implementation stored in
+# hw/<POWERS_HARDWARE_MODULE_ID>.<ro.hardware>.so
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS += $(VSOC_VERSION_CFLAGS)
+
+LOCAL_C_INCLUDES := device/google/cuttlefish_common
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MULTILIB := first
+
+LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_SRC_FILES := power.c
+LOCAL_MODULE := power.vsoc
+LOCAL_MODULE_TAGS := optional
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/guest/hals/power/power.c b/guest/hals/power/power.c
new file mode 100644
index 0000000..5bd3b0a
--- /dev/null
+++ b/guest/hals/power/power.c
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ *
+ * Based on the HiKeyPowerHAL
+ */
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <cutils/properties.h>
+
+#define LOG_TAG "VSoCPowerHAL"
+#include <utils/Log.h>
+
+#include <hardware/hardware.h>
+#include <hardware/power.h>
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+struct vsoc_power_module {
+    struct power_module base;
+    pthread_mutex_t lock;
+};
+
+
+#if VSOC_PLATFORM_SDK_AFTER(N_MR1)
+
+static void vsoc_power_set_feature(struct power_module __unused *module,
+                                  feature_t __unused hint,
+                                  int __unused state) {
+    return;
+}
+
+#elif VSOC_PLATFORM_SDK_AFTER(L)
+
+static void vsoc_power_set_feature(struct power_module __unused *module,
+                                  power_hint_t __unused hint,
+                                  int __unused state) {
+    return;
+}
+
+#endif
+
+static void vsoc_power_hint(struct power_module __unused *module,
+                           power_hint_t __unused hint,
+                           void __unused *data) {
+    return;
+}
+
+static void vsoc_power_set_interactive(struct power_module __unused *module,
+                                      int __unused on) {
+    return;
+}
+
+static  void vsoc_power_init(struct power_module __unused *module) {
+    return;
+}
+
+
+/*
+ * The power module wasn't opened at all in versions prior to 'O'. The module
+ * pointer was reinterpretd as a device pointer. 'O' retains this behavior when
+ * open is set to NULL. This code is using that mode.
+ * For reference,
+ * 'O': hardware/interfaces/power/1.0/default/Power.cpp
+ * prior: frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
+ */
+static struct hw_module_methods_t power_module_methods = {
+    VSOC_STATIC_INITIALIZER(open) NULL
+};
+
+
+struct vsoc_power_module HAL_MODULE_INFO_SYM = {
+  VSOC_STATIC_INITIALIZER(base) {
+    .common = {
+        VSOC_STATIC_INITIALIZER(tag) HARDWARE_MODULE_TAG,
+        VSOC_STATIC_INITIALIZER(module_api_version) POWER_MODULE_API_VERSION_0_2,
+        VSOC_STATIC_INITIALIZER(hal_api_version) HARDWARE_HAL_API_VERSION,
+        VSOC_STATIC_INITIALIZER(id) POWER_HARDWARE_MODULE_ID,
+        VSOC_STATIC_INITIALIZER(name) "VSoC Power HAL",
+        VSOC_STATIC_INITIALIZER(author) "The Android Open Source Project",
+        VSOC_STATIC_INITIALIZER(methods) &power_module_methods,
+    },
+    VSOC_STATIC_INITIALIZER(init) vsoc_power_init,
+    VSOC_STATIC_INITIALIZER(setInteractive) vsoc_power_set_interactive,
+    VSOC_STATIC_INITIALIZER(powerHint) vsoc_power_hint,
+    // Before L_MR1 we don't have setFeature
+#if VSOC_PLATFORM_SDK_AFTER(L)
+    VSOC_STATIC_INITIALIZER(setFeature) vsoc_power_set_feature,
+#endif
+  },
+
+  VSOC_STATIC_INITIALIZER(lock) PTHREAD_MUTEX_INITIALIZER,
+};
+
diff --git a/guest/hals/ril/Android.mk b/guest/hals/ril/Android.mk
new file mode 100644
index 0000000..de5e079
--- /dev/null
+++ b/guest/hals/ril/Android.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# RIL (Radio Interface Layer) and basic modem emulator for GCE.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+  vsoc_ril.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+  liblog \
+  libcutils \
+  libutils \
+  libril \
+  libcuttlefish_fs \
+  cuttlefish_net \
+  cuttlefish_auto_resources \
+  libnetutils \
+  libbase
+
+LOCAL_C_INCLUDES := bionic device/google/cuttlefish_common
+LOCAL_MODULE:= libvsoc-ril
+LOCAL_MODULE_TAGS := optional
+LOCAL_VENDOR_MODULE := true
+LOCAL_CFLAGS += $(VSOC_VERSION_CFLAGS)
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/guest/hals/ril/vsoc_ril.cpp b/guest/hals/ril/vsoc_ril.cpp
new file mode 100644
index 0000000..70c6633
--- /dev/null
+++ b/guest/hals/ril/vsoc_ril.cpp
@@ -0,0 +1,2532 @@
+/*
+** Copyright 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 ioogle/s distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "guest/hals/ril/vsoc_ril.h"
+
+#include <cutils/properties.h>
+#include <netutils/ifc.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "common/libs/net/netlink_client.h"
+#include "common/libs/net/network_interface.h"
+#include "common/libs/net/network_interface_manager.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+#define GCE_RIL_VERSION_STRING "Android VSoC RIL 1.0"
+
+/* Modem Technology bits */
+#define MDM_GSM 0x01
+#define MDM_WCDMA 0x02
+#define MDM_CDMA 0x04
+#define MDM_EVDO 0x08
+#define MDM_LTE 0x10
+
+#if VSOC_PLATFORM_SDK_BEFORE(K)
+#define RADIO_TECH_3GPP 1
+#define RADIO_TECH_3GPP2 2
+#endif
+
+typedef enum {
+  SIM_ABSENT = 0,
+  SIM_NOT_READY = 1,
+  SIM_READY = 2,  // SIM_READY means the radio state is RADIO_STATE_SIM_READY
+  SIM_PIN = 3,
+  SIM_PUK = 4,
+  SIM_NETWORK_PERSONALIZATION = 5,
+  RUIM_ABSENT = 6,
+  RUIM_NOT_READY = 7,
+  RUIM_READY = 8,
+  RUIM_PIN = 9,
+  RUIM_PUK = 10,
+  RUIM_NETWORK_PERSONALIZATION = 11
+} SIM_Status;
+
+static const struct RIL_Env* gce_ril_env;
+
+static const struct timeval TIMEVAL_SIMPOLL = {3, 0};
+
+static time_t gce_ril_start_time;
+
+static void pollSIMState(void* param);
+
+RIL_RadioState gRadioPowerState = RADIO_STATE_OFF;
+
+struct DataCall {
+  enum AllowedAuthenticationType { kNone = 0, kPap = 1, kChap = 2, kBoth = 3 };
+
+  enum ConnectionType {
+    kConnTypeIPv4,
+    kConnTypeIPv6,
+    kConnTypeIPv4v6,
+    kConnTypePPP
+  };
+
+  enum LinkState {
+    kLinkStateInactive = 0,
+    kLinkStateDown = 1,
+    kLinkStateUp = 2,
+  };
+
+  RIL_RadioTechnology technology_;
+  RIL_DataProfile profile_;
+  std::string access_point_;
+  std::string username_;
+  std::string password_;
+  AllowedAuthenticationType auth_type_;
+  ConnectionType connection_type_;
+  LinkState link_state_;
+  RIL_DataCallFailCause fail_cause_;
+  std::string other_properties_;
+};
+
+static std::string gSimPIN = "0000";
+static const std::string gSimPUK = "11223344";
+static int gSimPINAttempts = 0;
+static const int gSimPINAttemptsMax = 3;
+static SIM_Status gSimStatus = SIM_NOT_READY;
+
+namespace {
+// Holds the address and prefix from the latest DHCP query.
+char g_address_prefix[80];
+
+// Holds the dotted decimal IP address of the gateway from the latest DHCP
+// query.
+char g_gateway[80];
+
+// Holds the IP address of the DNS server.
+char g_dns1[80];
+};  // namespace
+
+// Oddly "part of the VNDK" doesn't translate into "has a header."
+extern "C" {
+int do_dhcp(char* iface);
+void get_dhcp_info(uint32_t* ipaddr, uint32_t* gateway, uint32_t* prefixLength,
+                   uint32_t* dns1, uint32_t* dns2, uint32_t* server,
+                   uint32_t* lease);
+};
+
+// SetUpNetworkInterface configures IP and Broadcast addresses on a RIL
+// controlled network interface by using dhcp code that is part of the VNDK.
+// This call returns true, if operation was successful.
+bool SetUpNetworkInterface(const char* interface_name) {
+  if (ifc_init()) {
+    ALOGE("%s disabled because ifc_init failed", interface_name);
+    return false;
+  }
+  // do_dhcp doesn't really write to this
+  if (do_dhcp(const_cast<char*>(interface_name))) {
+    ALOGE("%s disabled because DHCP failed", interface_name);
+    return false;
+  }
+  struct in_addr ipaddr, gateway, dns1;
+  uint32_t prefix_length, unused;
+  get_dhcp_info(&ipaddr.s_addr, &gateway.s_addr, &prefix_length, &dns1.s_addr,
+                &unused, &unused, &unused);
+  snprintf(g_address_prefix, sizeof(g_address_prefix), "%s/%d",
+           inet_ntoa(ipaddr), prefix_length);
+  // snprintf always null terminates, strncpy doesn't
+  snprintf(g_gateway, sizeof(g_gateway), "%s", inet_ntoa(gateway));
+  snprintf(g_dns1, sizeof(g_dns1), "%s", inet_ntoa(dns1));
+  return true;
+}
+
+namespace {
+// This gets cast to a char* because some of the interfaces are lazy about
+// const. Declaring it this way ensures that we'll crash if someone tries to
+// write to the interface name.
+const char g_ril_interface[] = "rmnet0";
+};  // namespace
+
+// TearDownNetworkInterface disables network interface.
+// This call returns true, if operation was successful.
+bool TearDownNetworkInterface() {
+  auto nm(cvd::NetworkInterfaceManager::New(nullptr));
+  auto ni(nm->Open("rmnet0"));
+
+  if (ni) {
+    ni->SetOperational(false);
+    bool res = nm->ApplyChanges(*ni);
+    if (!res) ALOGE("Could not disable %s", g_ril_interface);
+    return res;
+  }
+  return false;
+}
+
+static int gNextDataCallId = 8;
+static std::map<int, DataCall> gDataCalls;
+static bool gRilConnected = false;
+
+static int request_or_send_data_calllist(RIL_Token* t) {
+#if VSOC_PLATFORM_SDK_AFTER(N_MR1)
+  RIL_Data_Call_Response_v11* responses =
+      new RIL_Data_Call_Response_v11[gDataCalls.size()];
+#else
+  RIL_Data_Call_Response_v6* responses =
+      new RIL_Data_Call_Response_v6[gDataCalls.size()];
+#endif
+
+  int index = 0;
+
+  ALOGV("Query data call list: %zu data calls tracked.", gDataCalls.size());
+
+  for (std::map<int, DataCall>::iterator iter = gDataCalls.begin();
+       iter != gDataCalls.end(); ++iter, ++index) {
+    responses[index].status = iter->second.fail_cause_;
+    responses[index].suggestedRetryTime = -1;
+    responses[index].cid = iter->first;
+    responses[index].active = iter->second.link_state_;
+
+    switch (iter->second.connection_type_) {
+      case DataCall::kConnTypeIPv4:
+        responses[index].type = (char*)"IP";
+        break;
+      case DataCall::kConnTypeIPv6:
+        responses[index].type = (char*)"IPV6";
+        break;
+      case DataCall::kConnTypeIPv4v6:
+        responses[index].type = (char*)"IPV4V6";
+        break;
+      case DataCall::kConnTypePPP:
+        responses[index].type = (char*)"PPP";
+        break;
+      default:
+        responses[index].type = (char*)"IP";
+        break;
+    }
+
+    responses[index].ifname = const_cast<char*>(g_ril_interface);
+    responses[index].addresses = g_address_prefix;
+    responses[index].dnses = g_dns1;
+    responses[index].gateways = g_gateway;
+#if VSOC_PLATFORM_SDK_AFTER(N_MR1)
+    responses[index].pcscf = (char*)"";
+    responses[index].mtu = 1440;
+#endif
+  }
+
+  bool new_conn_state = (gDataCalls.size() > 0);
+
+  if (gRilConnected != new_conn_state) {
+    time_t curr_time;
+    time(&curr_time);
+    double diff_in_secs = difftime(curr_time, gce_ril_start_time);
+
+    gRilConnected = new_conn_state;
+
+    if (new_conn_state) {
+      ALOGV("MOBILE_DATA_CONNECTED %.2lf seconds", diff_in_secs);
+    } else {
+      ALOGV("MOBILE_DATA_DISCONNECTED %.2lf seconds", diff_in_secs);
+    }
+
+    if (property_set("ril.net_connected", new_conn_state ? "1" : "0")) {
+      ALOGE("Couldn't set a system property ril.net_connected.");
+    }
+  }
+
+  if (t != NULL) {
+    gce_ril_env->OnRequestComplete(*t, RIL_E_SUCCESS, responses,
+                                   gDataCalls.size() * sizeof(*responses));
+  } else {
+    gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED,
+                                       responses,
+                                       gDataCalls.size() * sizeof(*responses));
+  }
+  delete[] responses;
+  return 0;
+}
+
+static void request_datacall_fail_cause(RIL_Token t) {
+  RIL_DataCallFailCause fail = PDP_FAIL_DATA_REGISTRATION_FAIL;
+
+  if (gDataCalls.size() > 0) {
+    fail = gDataCalls.rbegin()->second.fail_cause_;
+  }
+
+  ALOGV("Requesting last data call setup fail cause (%d)", fail);
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &fail, sizeof(fail));
+};
+
+static void on_data_calllist_changed(void* /*param*/) {
+  request_or_send_data_calllist(NULL);
+}
+
+static void request_data_calllist(void* /*data*/, size_t /*datalen*/,
+                                  RIL_Token t) {
+  request_or_send_data_calllist(&t);
+}
+
+static void request_setup_data_call(void* data, size_t datalen, RIL_Token t) {
+  const char* apn;
+  char* cmd;
+  int err;
+  char** details = static_cast<char**>(data);
+  const size_t fields = datalen / sizeof(details[0]);
+
+  // There are two different versions of this interface, one providing 7 strings
+  // and the other providing 8. The code below will assume the presence of 7
+  // strings in all cases, so bail out here if things appear to be wrong. We
+  // protect the 8 string case below.
+  if (fields < 7) {
+    ALOGE("%s returning: called with small datalen %zu", __FUNCTION__, datalen);
+    return;
+  }
+  DataCall call;
+  int tech = atoi(details[0]);
+  switch (tech) {
+    case 0:
+    case 2 + RADIO_TECH_1xRTT:
+      call.technology_ = RADIO_TECH_1xRTT;
+      break;
+
+    case 1:
+    case 2 + RADIO_TECH_EDGE:
+      call.technology_ = RADIO_TECH_EDGE;
+      break;
+
+    default:
+      call.technology_ = RIL_RadioTechnology(tech - 2);
+      break;
+  }
+
+  int profile = atoi(details[1]);
+  call.profile_ = RIL_DataProfile(profile);
+
+  if (details[2]) call.access_point_ = details[2];
+  if (details[3]) call.username_ = details[3];
+  if (details[4]) call.password_ = details[4];
+
+  int auth_type = atoi(details[5]);
+  call.auth_type_ = DataCall::AllowedAuthenticationType(auth_type);
+
+  if (!strcmp("IP", details[6])) {
+    call.connection_type_ = DataCall::kConnTypeIPv4;
+  } else if (!strcmp("IPV6", details[6])) {
+    call.connection_type_ = DataCall::kConnTypeIPv6;
+  } else if (!strcmp("IPV4V6", details[6])) {
+    call.connection_type_ = DataCall::kConnTypeIPv4v6;
+  } else if (!strcmp("PPP", details[6])) {
+    call.connection_type_ = DataCall::kConnTypePPP;
+  } else {
+    ALOGW("Unknown / unsupported connection type %s. Falling back to IPv4",
+          details[6]);
+    call.connection_type_ = DataCall::kConnTypeIPv4;
+  }
+
+  if (call.connection_type_ != DataCall::kConnTypeIPv4) {
+    ALOGE("Non-IPv4 connections are not supported by GCE RIL.");
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    return;
+  }
+
+  call.link_state_ = DataCall::kLinkStateUp;
+  call.fail_cause_ = PDP_FAIL_NONE;
+  if (fields > 7) {
+    if (details[7]) call.other_properties_ = details[7];
+  }
+
+  if (gDataCalls.empty()) {
+    SetUpNetworkInterface(g_ril_interface);
+  }
+
+  gDataCalls[gNextDataCallId] = call;
+  gNextDataCallId++;
+
+  ALOGV("Requesting data call setup to APN %s, technology %s, prof %s",
+        details[2], details[0], details[1]);
+
+  request_or_send_data_calllist(&t);
+
+  gRilConnected = (gDataCalls.size() > 0);
+}
+
+static void request_teardown_data_call(void* data, size_t /*datalen*/,
+                                       RIL_Token t) {
+  char** data_strs = (char**)data;
+  int call_id = atoi(data_strs[0]);
+  int reason = atoi(data_strs[1]);
+
+  ALOGV("Tearing down data call %d, reason: %d", call_id, reason);
+
+  gDataCalls.erase(call_id);
+  gRilConnected = (gDataCalls.size() > 0);
+
+  if (!gRilConnected) {
+    TearDownNetworkInterface();
+  }
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void set_radio_state(RIL_RadioState new_state, RIL_Token t) {
+  // From header:
+  // Toggle radio on and off (for "airplane" mode)
+  // If the radio is is turned off/on the radio modem subsystem
+  // is expected return to an initialized state. For instance,
+  // any voice and data calls will be terminated and all associated
+  // lists emptied.
+  gDataCalls.clear();
+
+  RIL_RadioState old_state;
+
+  old_state = gRadioPowerState;
+  gRadioPowerState = new_state;
+  gSimStatus = SIM_NOT_READY;
+  ALOGV("RIL_RadioState change %d to %d", old_state, new_state);
+
+  if (new_state == RADIO_STATE_OFF) {
+    TearDownNetworkInterface();
+  }
+
+  if (t != NULL) {
+    gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+  }
+
+  gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED,
+                                     NULL, 0);
+
+  pollSIMState(NULL);
+}
+
+// returns 1 if on, 0 if off, and -1 on error
+static char is_radio_on() {
+  RIL_RadioState state;
+
+  state = gRadioPowerState;
+
+  return state == RADIO_STATE_ON;
+}
+
+static void request_radio_power(void* data, size_t /*datalen*/, RIL_Token t) {
+  int on = ((int*)data)[0];
+  set_radio_state(on ? RADIO_STATE_ON : RADIO_STATE_OFF, t);
+}
+
+static void send_call_state_changed(void* /*param*/) {
+  gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
+                                     NULL, 0);
+}
+
+// TODO(ender): this should be a class member. Move where it belongs.
+struct CallState {
+  RIL_CallState state;  // e.g. RIL_CALL_HOLDING;
+  bool isInternational;
+  bool isMobileTerminated;
+  bool isVoice;
+  bool isMultiParty;
+
+  std::string number;
+  std::string name;
+  std::string dtmf;
+
+  bool canPresentNumber;
+  bool canPresentName;
+
+  CallState()
+      : state(RIL_CallState(0)),
+        isInternational(false),
+        isMobileTerminated(true),
+        isVoice(true),
+        isMultiParty(false),
+        canPresentNumber(true),
+        canPresentName(true) {}
+
+  CallState(const std::string& number)
+      : state(RIL_CALL_INCOMING),
+        isInternational(false),
+        isMobileTerminated(true),
+        isVoice(true),
+        isMultiParty(false),
+        number(number),
+        name(number),
+        canPresentNumber(true),
+        canPresentName(true) {}
+
+  bool isBackground() { return state == RIL_CALL_HOLDING; }
+
+  bool isActive() { return state == RIL_CALL_ACTIVE; }
+
+  bool isDialing() { return state == RIL_CALL_DIALING; }
+
+  bool isIncoming() { return state == RIL_CALL_INCOMING; }
+
+  bool isWaiting() { return state == RIL_CALL_WAITING; }
+
+  void addDtmfDigit(char c) {
+    dtmf.push_back(c);
+    ALOGV("Call to %s: DTMF %s", number.c_str(), dtmf.c_str());
+  }
+
+  bool makeBackground() {
+    if (state == RIL_CALL_ACTIVE) {
+      state = RIL_CALL_HOLDING;
+      return true;
+    }
+
+    return false;
+  }
+
+  bool makeActive() {
+    if (state == RIL_CALL_INCOMING || state == RIL_CALL_WAITING ||
+        state == RIL_CALL_DIALING || state == RIL_CALL_HOLDING) {
+      state = RIL_CALL_ACTIVE;
+      return true;
+    }
+
+    return false;
+  }
+};
+
+static int gLastActiveCallIndex = 1;
+static int gMicrophoneMute = 0;
+static std::map<int, CallState> gActiveCalls;
+
+static void request_get_current_calls(void* /*data*/, size_t /*datalen*/,
+                                      RIL_Token t) {
+  const int countCalls = gActiveCalls.size();
+
+  RIL_Call** pp_calls = (RIL_Call**)alloca(countCalls * sizeof(RIL_Call*));
+  RIL_Call* p_calls = (RIL_Call*)alloca(countCalls * sizeof(RIL_Call));
+
+  memset(p_calls, 0, countCalls * sizeof(RIL_Call));
+
+  /* init the pointer array */
+  for (int i = 0; i < countCalls; i++) {
+    pp_calls[i] = &(p_calls[i]);
+  }
+
+  // TODO(ender): This should be built from calls requested via RequestDial.
+  for (std::map<int, CallState>::iterator iter = gActiveCalls.begin();
+       iter != gActiveCalls.end(); ++iter, ++p_calls) {
+    p_calls->state = iter->second.state;
+    p_calls->index = iter->first;
+    p_calls->toa = iter->second.isInternational ? 145 : 129;
+    p_calls->isMpty = iter->second.isMultiParty;
+    p_calls->isMT = iter->second.isMobileTerminated;
+    p_calls->als = iter->first;
+    p_calls->isVoice = iter->second.isVoice;
+    p_calls->isVoicePrivacy = 0;
+    p_calls->number = strdup(iter->second.number.c_str());
+    p_calls->numberPresentation = iter->second.canPresentNumber ? 0 : 1;
+    p_calls->name = strdup(iter->second.name.c_str());
+    p_calls->namePresentation = iter->second.canPresentName ? 0 : 1;
+    p_calls->uusInfo = NULL;
+
+    ALOGV("Call to %s (%s): voice=%d mt=%d type=%d state=%d index=%d",
+          p_calls->name, p_calls->number, p_calls->isVoice, p_calls->isMT,
+          p_calls->toa, p_calls->state, p_calls->index);
+  }
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, pp_calls,
+                                 countCalls * sizeof(RIL_Call*));
+
+  ALOGV("Get Current calls: %d calls found.\n", countCalls);
+}
+
+static void simulate_pending_calls_answered(void* /*ignore*/) {
+  ALOGV("Simulating outgoing call answered.");
+  // This also resumes held calls.
+  for (std::map<int, CallState>::iterator iter = gActiveCalls.begin();
+       iter != gActiveCalls.end(); ++iter) {
+    if (iter->second.isDialing()) {
+      iter->second.makeActive();
+    }
+  }
+
+  // Only unsolicited here.
+  gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
+                                     NULL, 0);
+}
+
+static void request_dial(void* data, size_t /*datalen*/, RIL_Token t) {
+  RIL_Dial* p_dial = (RIL_Dial*)data;
+
+  ALOGV("Dialing %s, number presentation is %s.", p_dial->address,
+        (p_dial->clir == 0) ? "defined by operator"
+                            : (p_dial->clir == 1) ? "allowed" : "restricted");
+
+  CallState state(p_dial->address);
+  state.isMobileTerminated = false;
+  state.state = RIL_CALL_DIALING;
+
+  switch (p_dial->clir) {
+    case 0:  // default
+    case 1:  // allow
+      state.canPresentNumber = true;
+      break;
+
+    case 2:  // restrict
+      state.canPresentNumber = false;
+      break;
+  }
+
+  int call_index = gLastActiveCallIndex++;
+  gActiveCalls[call_index] = state;
+
+  static const struct timeval kAnswerTime = {5, 0};
+  gce_ril_env->RequestTimedCallback(simulate_pending_calls_answered, NULL,
+                                    &kAnswerTime);
+
+  // success or failure is ignored by the upper layer here.
+  // it will call GET_CURRENT_CALLS and determine success that way
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+void request_set_mute(void* data, size_t /*datalen*/, RIL_Token t) {
+  gMicrophoneMute = ((int*)data)[0] != 0;
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+void request_get_mute(RIL_Token t) {
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gMicrophoneMute,
+                                 sizeof(gMicrophoneMute));
+}
+
+// TODO(ender): this should be a class member. Move where it belongs.
+struct SmsMessage {
+  enum SmsStatus { kUnread = 0, kRead = 1, kUnsent = 2, kSent = 3 };
+
+  std::string message;
+  SmsStatus status;
+};
+
+static int gNextMessageId = 1;
+static std::map<int, SmsMessage> gMessagesOnSimCard;
+
+static void request_write_sms_to_sim(void* data, size_t /*datalen*/,
+                                     RIL_Token t) {
+  RIL_SMS_WriteArgs* p_args = (RIL_SMS_WriteArgs*)data;
+
+  SmsMessage message;
+  message.status = SmsMessage::SmsStatus(p_args->status);
+  message.message = p_args->pdu;
+
+  ALOGV("Storing SMS message: '%s' with state: %s.", message.message.c_str(),
+        (message.status < SmsMessage::kUnsent)
+            ? ((message.status == SmsMessage::kRead) ? "READ" : "UNREAD")
+            : ((message.status == SmsMessage::kSent) ? "SENT" : "UNSENT"));
+
+  // TODO(ender): simulate SIM FULL?
+  int index = gNextMessageId++;
+  gMessagesOnSimCard[index] = message;
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &index, sizeof(index));
+}
+
+static void request_delete_sms_on_sim(void* data, size_t /*datalen*/,
+                                      RIL_Token t) {
+  int index = *(int*)data;
+
+  ALOGV("Delete SMS message %d", index);
+
+  if (gMessagesOnSimCard.erase(index) == 0) {
+    // No such message
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    return;
+  }
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_hangup(void* data, size_t /*datalen*/, RIL_Token t) {
+  int* p_line = (int*)data;
+
+  ALOGV("Hanging up call %d.", *p_line);
+  std::map<int, CallState>::iterator iter = gActiveCalls.find(*p_line);
+
+  if (iter == gActiveCalls.end()) {
+    ALOGV("No such call: %d.", *p_line);
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+  } else {
+    gActiveCalls.erase(iter);
+    gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+  }
+}
+
+static void request_hangup_waiting(void* /*data*/, size_t /*datalen*/,
+                                   RIL_Token t) {
+  ALOGV("Hanging up background/held calls.");
+  for (std::map<int, CallState>::iterator iter = gActiveCalls.begin();
+       iter != gActiveCalls.end();) {
+    if (iter->second.isBackground()) {
+      // C++98 -- std::map::erase doesn't return iterator.
+      std::map<int, CallState>::iterator temp = iter++;
+      gActiveCalls.erase(temp);
+    } else {
+      ++iter;
+    }
+  }
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_hangup_current(RIL_Token t) {
+  ALOGV("Hanging up foreground/active calls.");
+  // This also resumes held calls.
+  for (std::map<int, CallState>::iterator iter = gActiveCalls.begin();
+       iter != gActiveCalls.end();) {
+    if (iter->second.isBackground()) {
+      iter->second.makeActive();
+      ++iter;
+    } else {
+      // C++98 -- std::map::erase doesn't return iterator.
+      std::map<int, CallState>::iterator temp = iter++;
+      gActiveCalls.erase(temp);
+    }
+  }
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_switch_current_and_waiting(RIL_Token t) {
+  ALOGV("Toggle foreground and background calls.");
+  // TODO(ender): fix all states. Max 2 calls.
+  //   BEFORE                               AFTER
+  // Call 1   Call 2                 Call 1       Call 2
+  // ACTIVE   HOLDING                HOLDING     ACTIVE
+  // ACTIVE   WAITING                HOLDING     ACTIVE
+  // HOLDING  WAITING                HOLDING     ACTIVE
+  // ACTIVE   IDLE                   HOLDING     IDLE
+  // IDLE     IDLE                   IDLE        IDLE
+  for (std::map<int, CallState>::iterator iter = gActiveCalls.begin();
+       iter != gActiveCalls.end(); ++iter) {
+    // TODO(ender): call could also be waiting or dialing or...
+    if (iter->second.isBackground()) {
+      iter->second.makeActive();
+    } else {
+      iter->second.makeBackground();
+    }
+  }
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_answer_incoming(RIL_Token t) {
+  ALOGV("Answering incoming call.");
+
+  // There's two types of incoming calls:
+  // - incoming: we are receiving this call while nothing happens,
+  // - waiting: we are receiving this call while we're already talking.
+  // We only accept the incoming ones.
+  for (std::map<int, CallState>::iterator iter = gActiveCalls.begin();
+       iter != gActiveCalls.end(); ++iter) {
+    if (iter->second.isIncoming()) {
+      iter->second.makeActive();
+    }
+  }
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_combine_multiparty_call(void* /*data*/, size_t /*datalen*/,
+                                            RIL_Token t) {
+  ALOGW("Conference calls are not supported.");
+  gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void request_split_multiparty_call(void* /*data*/, size_t /*datalen*/,
+                                          RIL_Token t) {
+  ALOGW("Conference calls are not supported.");
+  gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void request_udub_on_incoming_calls(RIL_Token t) {
+  // UDUB = user determined user busy.
+  // We don't exactly do that. We simply drop these calls.
+  ALOGV("Reporting busy signal to incoming calls.");
+  for (std::map<int, CallState>::iterator iter = gActiveCalls.begin();
+       iter != gActiveCalls.end();) {
+    // If we have an incoming call, there should be no waiting call.
+    // If we have a waiting call, then previous incoming call has been answered.
+    if (iter->second.isIncoming() || iter->second.isWaiting()) {
+      // C++98 -- std::map::erase doesn't return iterator.
+      std::map<int, CallState>::iterator temp = iter++;
+      gActiveCalls.erase(temp);
+    } else {
+      ++iter;
+    }
+  }
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_send_dtmf(void* data, size_t /*datalen*/, RIL_Token t) {
+  char c = ((char*)data)[0];
+  ALOGV("Sending DTMF digit '%c'", c);
+
+  for (std::map<int, CallState>::iterator iter = gActiveCalls.begin();
+       iter != gActiveCalls.end(); ++iter) {
+    if (iter->second.isActive()) {
+      iter->second.addDtmfDigit(c);
+      break;
+    }
+  }
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_send_dtmf_stop(RIL_Token t) {
+  ALOGV("DTMF tone end.");
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+// Check SignalStrength.java file for more details on how these map to signal
+// strength bars.
+const int kGatewaySignalStrengthMin = 4;
+const int kGatewaySignalStrengthMax = 30;
+const int kCDMASignalStrengthMin = -110;
+const int kCDMASignalStrengthMax = -60;
+const int kEVDOSignalStrengthMin = -160;
+const int kEVDOSignalStrengthMax = -70;
+const int kLTESignalStrengthMin = 4;
+const int kLTESignalStrengthMax = 30;
+
+static int gGatewaySignalStrength = kGatewaySignalStrengthMax;
+static int gCDMASignalStrength = kCDMASignalStrengthMax;
+static int gEVDOSignalStrength = kEVDOSignalStrengthMax;
+static int gLTESignalStrength = kLTESignalStrengthMax;
+
+static void request_signal_strength(void* /*data*/, size_t /*datalen*/,
+                                    RIL_Token t) {
+  // TODO(ender): possible to support newer APIs here.
+#if VSOC_PLATFORM_SDK_AFTER(N_MR1)
+  RIL_SignalStrength_v10 strength;
+#else
+  RIL_SignalStrength_v6 strength;
+#endif
+
+  gGatewaySignalStrength += (rand() % 3 - 1);
+  gCDMASignalStrength += (rand() % 3 - 1);
+  gEVDOSignalStrength += (rand() % 3 - 1);
+  gLTESignalStrength += (rand() % 3 - 1);
+
+  if (gGatewaySignalStrength < kGatewaySignalStrengthMin)
+    gGatewaySignalStrength = kGatewaySignalStrengthMin;
+  if (gGatewaySignalStrength > kGatewaySignalStrengthMax)
+    gGatewaySignalStrength = kGatewaySignalStrengthMax;
+  if (gCDMASignalStrength < kCDMASignalStrengthMin)
+    gCDMASignalStrength = kCDMASignalStrengthMin;
+  if (gCDMASignalStrength > kCDMASignalStrengthMax)
+    gCDMASignalStrength = kCDMASignalStrengthMax;
+  if (gEVDOSignalStrength < kEVDOSignalStrengthMin)
+    gEVDOSignalStrength = kEVDOSignalStrengthMin;
+  if (gEVDOSignalStrength > kEVDOSignalStrengthMax)
+    gEVDOSignalStrength = kEVDOSignalStrengthMax;
+  if (gLTESignalStrength < kLTESignalStrengthMin)
+    gLTESignalStrength = kLTESignalStrengthMin;
+  if (gLTESignalStrength > kLTESignalStrengthMax)
+    gLTESignalStrength = kLTESignalStrengthMax;
+
+  strength.GW_SignalStrength.signalStrength = gGatewaySignalStrength;
+  strength.GW_SignalStrength.bitErrorRate = 0;  // 0..7%
+
+  strength.CDMA_SignalStrength.dbm = gCDMASignalStrength;
+  strength.CDMA_SignalStrength.ecio = 0;  // Ec/Io; keep high to use dbm.
+
+  strength.EVDO_SignalStrength.dbm = gEVDOSignalStrength;
+  strength.EVDO_SignalStrength.ecio = 0;  // Ec/Io; keep high to use dbm.
+
+  strength.LTE_SignalStrength.signalStrength = gLTESignalStrength;
+  strength.LTE_SignalStrength.rsrp = INT_MAX;   // Invalid = Use signalStrength.
+  strength.LTE_SignalStrength.rsrq = INT_MAX;   // Invalid = Use signalStrength.
+  strength.LTE_SignalStrength.rssnr = INT_MAX;  // Invalid = Use signalStrength.
+  strength.LTE_SignalStrength.cqi = INT_MAX;    // Invalid = Use signalStrength.
+
+  ALOGV("Reporting signal strength: GW=%d CDMA=%d EVDO=%d LTE=%d",
+        gGatewaySignalStrength, gCDMASignalStrength, gEVDOSignalStrength,
+        gLTESignalStrength);
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &strength, sizeof(strength));
+}
+
+static std::map<RIL_PreferredNetworkType, int> gModemSupportedNetworkTypes;
+
+static void init_modem_supported_network_types() {
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_GSM_WCDMA] = MDM_GSM | MDM_WCDMA;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_GSM_ONLY] = MDM_GSM;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_WCDMA] = MDM_WCDMA;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_GSM_WCDMA_AUTO] =
+      MDM_GSM | MDM_WCDMA;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_CDMA_EVDO_AUTO] =
+      MDM_CDMA | MDM_EVDO;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_CDMA_ONLY] = MDM_CDMA;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_EVDO_ONLY] = MDM_EVDO;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO] =
+      MDM_GSM | MDM_WCDMA | MDM_CDMA | MDM_EVDO;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_LTE_CDMA_EVDO] =
+      MDM_LTE | MDM_CDMA | MDM_EVDO;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_LTE_GSM_WCDMA] =
+      MDM_LTE | MDM_GSM | MDM_WCDMA;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA] =
+      MDM_LTE | MDM_CDMA | MDM_EVDO | MDM_GSM | MDM_WCDMA;
+  gModemSupportedNetworkTypes[PREF_NET_TYPE_LTE_ONLY] = MDM_LTE;
+}
+
+static std::map<RIL_PreferredNetworkType, int> gModemTechnologies;
+
+RIL_RadioTechnology gDataTechnologiesPreferenceOrder[] = {
+    RADIO_TECH_LTE,    RADIO_TECH_EHRPD, RADIO_TECH_HSPAP,  RADIO_TECH_HSPA,
+    RADIO_TECH_HSDPA,  RADIO_TECH_HSUPA, RADIO_TECH_EVDO_B, RADIO_TECH_EVDO_A,
+    RADIO_TECH_EVDO_0, RADIO_TECH_1xRTT, RADIO_TECH_UMTS,   RADIO_TECH_EDGE,
+    RADIO_TECH_GPRS};
+
+RIL_RadioTechnology gVoiceTechnologiesPreferenceOrder[] = {
+    RADIO_TECH_LTE,    RADIO_TECH_EHRPD, RADIO_TECH_EVDO_B, RADIO_TECH_EVDO_A,
+    RADIO_TECH_EVDO_0, RADIO_TECH_1xRTT, RADIO_TECH_IS95B,  RADIO_TECH_IS95A,
+    RADIO_TECH_UMTS,   RADIO_TECH_GSM};
+
+static void init_modem_technologies() {
+  gModemTechnologies[PREF_NET_TYPE_GSM_WCDMA] =
+      (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE) |
+      (1 << RADIO_TECH_UMTS);
+  gModemTechnologies[PREF_NET_TYPE_GSM_ONLY] =
+      (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE);
+  gModemTechnologies[PREF_NET_TYPE_WCDMA] =
+      (1 << RADIO_TECH_EDGE) | (1 << RADIO_TECH_UMTS);
+  gModemTechnologies[PREF_NET_TYPE_GSM_WCDMA_AUTO] =
+      (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE) |
+      (1 << RADIO_TECH_UMTS);
+  gModemTechnologies[PREF_NET_TYPE_CDMA_EVDO_AUTO] =
+      (1 << RADIO_TECH_IS95A) | (1 << RADIO_TECH_IS95B) |
+      (1 << RADIO_TECH_1xRTT) | (1 << RADIO_TECH_EVDO_0) |
+      (1 << RADIO_TECH_EVDO_A) | (1 << RADIO_TECH_HSDPA) |
+      (1 << RADIO_TECH_HSUPA) | (1 << RADIO_TECH_HSPA) |
+      (1 << RADIO_TECH_EVDO_B);
+  gModemTechnologies[PREF_NET_TYPE_CDMA_ONLY] = (1 << RADIO_TECH_IS95A) |
+                                                (1 << RADIO_TECH_IS95B) |
+                                                (1 << RADIO_TECH_1xRTT);
+  gModemTechnologies[PREF_NET_TYPE_EVDO_ONLY] =
+      (1 << RADIO_TECH_EVDO_0) | (1 << RADIO_TECH_EVDO_A) |
+      (1 << RADIO_TECH_EVDO_A) | (1 << RADIO_TECH_HSDPA) |
+      (1 << RADIO_TECH_HSUPA) | (1 << RADIO_TECH_HSPA) |
+      (1 << RADIO_TECH_EVDO_B);
+  gModemTechnologies[PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO] =
+      (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE) |
+      (1 << RADIO_TECH_UMTS) | (1 << RADIO_TECH_IS95A) |
+      (1 << RADIO_TECH_IS95B) | (1 << RADIO_TECH_1xRTT) |
+      (1 << RADIO_TECH_EVDO_0) | (1 << RADIO_TECH_EVDO_A) |
+      (1 << RADIO_TECH_HSDPA) | (1 << RADIO_TECH_HSUPA) |
+      (1 << RADIO_TECH_HSPA) | (1 << RADIO_TECH_EVDO_B);
+  gModemTechnologies[PREF_NET_TYPE_LTE_CDMA_EVDO] =
+      (1 << RADIO_TECH_HSPAP) | (1 << RADIO_TECH_LTE) |
+      (1 << RADIO_TECH_EHRPD) | (1 << RADIO_TECH_IS95A) |
+      (1 << RADIO_TECH_IS95B) | (1 << RADIO_TECH_1xRTT) |
+      (1 << RADIO_TECH_EVDO_0) | (1 << RADIO_TECH_EVDO_A) |
+      (1 << RADIO_TECH_HSDPA) | (1 << RADIO_TECH_HSUPA) |
+      (1 << RADIO_TECH_HSPA) | (1 << RADIO_TECH_EVDO_B);
+  gModemTechnologies[PREF_NET_TYPE_LTE_GSM_WCDMA] =
+      (1 << RADIO_TECH_HSPAP) | (1 << RADIO_TECH_LTE) |
+      (1 << RADIO_TECH_EHRPD) | (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) |
+      (1 << RADIO_TECH_EDGE) | (1 << RADIO_TECH_UMTS);
+
+  gModemTechnologies[PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA] =
+      (1 << RADIO_TECH_HSPAP) | (1 << RADIO_TECH_LTE) |
+      (1 << RADIO_TECH_EHRPD) | (1 << RADIO_TECH_IS95A) |
+      (1 << RADIO_TECH_IS95B) | (1 << RADIO_TECH_1xRTT) |
+      (1 << RADIO_TECH_EVDO_0) | (1 << RADIO_TECH_EVDO_A) |
+      (1 << RADIO_TECH_HSDPA) | (1 << RADIO_TECH_HSUPA) |
+      (1 << RADIO_TECH_HSPA) | (1 << RADIO_TECH_EVDO_B) |
+      (1 << RADIO_TECH_GSM) | (1 << RADIO_TECH_GPRS) | (1 << RADIO_TECH_EDGE) |
+      (1 << RADIO_TECH_UMTS);
+  gModemTechnologies[PREF_NET_TYPE_LTE_ONLY] =
+      (1 << RADIO_TECH_HSPAP) | (1 << RADIO_TECH_LTE) | (1 << RADIO_TECH_EHRPD);
+}
+
+static const RIL_PreferredNetworkType gModemDefaultType =
+    PREF_NET_TYPE_LTE_GSM_WCDMA;
+static RIL_PreferredNetworkType gModemCurrentType = gModemDefaultType;
+static RIL_RadioTechnology gModemTechnology = RADIO_TECH_LTE;
+static RIL_RadioTechnology gModemVoiceTechnology = RADIO_TECH_LTE;
+
+// Report technology change.
+// Select best technology from the list of supported techs.
+// Demotes RADIO_TECH_GSM as it's voice-only.
+static RIL_RadioTechnology getBestDataTechnology(
+    RIL_PreferredNetworkType network_type) {
+  RIL_RadioTechnology technology = RADIO_TECH_GPRS;
+
+  std::map<RIL_PreferredNetworkType, int>::iterator iter =
+      gModemTechnologies.find(network_type);
+
+  ALOGV("Searching for best data technology for network type %d...",
+        network_type);
+
+  // Find which technology bits are lit. Pick the top most.
+  for (size_t tech_index = 0;
+       tech_index < sizeof(gDataTechnologiesPreferenceOrder) /
+                        sizeof(gDataTechnologiesPreferenceOrder[0]);
+       ++tech_index) {
+    if (iter->second & (1 << gDataTechnologiesPreferenceOrder[tech_index])) {
+      technology = gDataTechnologiesPreferenceOrder[tech_index];
+      break;
+    }
+  }
+
+  ALOGV("Best data technology: %d.", technology);
+  return technology;
+}
+
+static RIL_RadioTechnology getBestVoiceTechnology(
+    RIL_PreferredNetworkType network_type) {
+  RIL_RadioTechnology technology = RADIO_TECH_GSM;
+
+  std::map<RIL_PreferredNetworkType, int>::iterator iter =
+      gModemTechnologies.find(network_type);
+
+  ALOGV("Searching for best voice technology for network type %d...",
+        network_type);
+
+  // Find which technology bits are lit. Pick the top most.
+  for (size_t tech_index = 0;
+       tech_index < sizeof(gVoiceTechnologiesPreferenceOrder) /
+                        sizeof(gVoiceTechnologiesPreferenceOrder[0]);
+       ++tech_index) {
+    if (iter->second & (1 << gVoiceTechnologiesPreferenceOrder[tech_index])) {
+      technology = gVoiceTechnologiesPreferenceOrder[tech_index];
+      break;
+    }
+  }
+
+  ALOGV("Best voice technology: %d.", technology);
+  return technology;
+}
+
+static void setRadioTechnology(RIL_PreferredNetworkType network_type) {
+  RIL_RadioTechnology technology = getBestVoiceTechnology(network_type);
+
+  if (technology != gModemVoiceTechnology) {
+    gModemVoiceTechnology = technology;
+    gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED,
+                                       &gModemVoiceTechnology,
+                                       sizeof(gModemVoiceTechnology));
+  }
+}
+
+#if VSOC_PLATFORM_SDK_AFTER(L)
+static void request_get_radio_capability(RIL_Token t) {
+  ALOGV("Requesting radio capability.");
+  RIL_RadioCapability rc;
+  rc.version = RIL_RADIO_CAPABILITY_VERSION;
+  rc.session = 1;
+  rc.phase = RC_PHASE_CONFIGURED;
+  rc.rat = RAF_HSPAP;
+  strncpy(rc.logicalModemUuid, "com.google.cvdgce1.modem", MAX_UUID_LENGTH);
+  rc.status = RC_STATUS_SUCCESS;
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &rc, sizeof(rc));
+}
+
+static void request_set_radio_capability(void* data, size_t datalen,
+                                         RIL_Token t) {
+  RIL_RadioCapability* rc = (RIL_RadioCapability*)data;
+  ALOGV(
+      "RadioCapability version %d session %d phase %d rat %d "
+      "logicalModemUuid %s status %d",
+      rc->version, rc->session, rc->phase, rc->rat, rc->logicalModemUuid,
+      rc->status);
+  // TODO(ender): do something about these numbers.
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, rc, datalen);
+}
+#endif
+
+static void request_set_preferred_network_type(int /*request*/, void* data,
+                                               size_t /*datalen*/,
+                                               RIL_Token t) {
+  RIL_PreferredNetworkType desired_type = *(RIL_PreferredNetworkType*)(data);
+
+  // TODO(ender): telephony still believes this phone is GSM only.
+  ALOGV("Requesting modem technology change -> %d", desired_type);
+
+  if (gModemSupportedNetworkTypes.find(desired_type) ==
+      gModemSupportedNetworkTypes.end()) {
+    desired_type = gModemSupportedNetworkTypes.begin()->first;
+  }
+
+  if (gModemCurrentType == desired_type) {
+    ALOGV("Modem technology already set to %d.", desired_type);
+    gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+  }
+
+  int supported_technologies = gModemSupportedNetworkTypes[gModemDefaultType];
+  int desired_technologies = gModemSupportedNetworkTypes[desired_type];
+
+  ALOGV("Requesting modem technology change %d -> %d", gModemCurrentType,
+        desired_type);
+
+  // Check if we support this technology.
+  if ((supported_technologies & desired_technologies) != desired_technologies) {
+    ALOGV("Desired technology is not supported.");
+    gce_ril_env->OnRequestComplete(t, RIL_E_MODE_NOT_SUPPORTED, NULL, 0);
+    return;
+  }
+
+  gModemCurrentType = desired_type;
+  setRadioTechnology(desired_type);
+  ALOGV("Technology change successful.");
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_get_preferred_network_type(int /*request*/, void* /*data*/,
+                                               size_t /*datalen*/,
+                                               RIL_Token t) {
+  gce_ril_env->OnRequestComplete(
+      t, RIL_E_SUCCESS,
+      const_cast<RIL_PreferredNetworkType*>(&gModemDefaultType),
+      sizeof(gModemDefaultType));
+}
+
+enum RegistrationState {
+  kUnregistered = 0,
+  kRegisteredInHomeNetwork = 1,
+  kSearchingForOperators = 2,
+  kRegistrationDenied = 3,
+  kUnknown = 4,
+  kRegisteredInRoamingMode = 5,
+
+  kUnregistered_EmergencyCallsOnly = 10,
+  kSearchingForOperators_EmergencyCallsOnly = 12,
+  kRegistrationDenied_EmergencyCallsOnly = 13,
+  kUnknown_EmergencyCallsOnly = 14
+};
+
+static const char kCdmaMobileDeviceNumber[] = "5551234567";
+static const char kCdmaSID[] = "123";
+static const char kCdmaNID[] = "65535";  // special: indicates free roaming.
+
+static void request_registration_state(int request, void* /*data*/,
+                                       size_t /*datalen*/, RIL_Token t) {
+  char** responseStr = NULL;
+  int numElements = 0;
+
+  // See RIL_REQUEST_VOICE_REGISTRATION_STATE and
+  // RIL_REQUEST_DATA_REGISTRATION_STATE.
+  numElements = (request == RIL_REQUEST_VOICE_REGISTRATION_STATE) ? 15 : 6;
+  responseStr = (char**)malloc(numElements * sizeof(char*));
+
+  asprintf(&responseStr[0], "%d", kRegisteredInHomeNetwork);
+  responseStr[1] = NULL;  // LAC - needed for GSM / WCDMA only.
+  responseStr[2] = NULL;  // CID - needed for GSM / WCDMA only.
+
+  // This is (and always has been) a huge memory leak.
+  if (request == RIL_REQUEST_VOICE_REGISTRATION_STATE) {
+    ALOGV("Requesting voice registration state.");
+    asprintf(&responseStr[3], "%d", getBestVoiceTechnology(gModemCurrentType));
+    responseStr[4] = strdup("1");       // BSID
+    responseStr[5] = strdup("123");     // Latitude
+    responseStr[6] = strdup("222");     // Longitude
+    responseStr[7] = strdup("0");       // CSS Indicator
+    responseStr[8] = strdup(kCdmaSID);  // SID
+    responseStr[9] = strdup(kCdmaNID);  // NID
+    responseStr[10] = strdup("0");      // Roaming indicator
+    responseStr[11] = strdup("1");      // System is in PRL
+    responseStr[12] = strdup("0");      // Default Roaming indicator
+    responseStr[13] = strdup("0");      // Reason for denial
+    responseStr[14] = strdup("0");      // Primary Scrambling Code of Current
+  } else if (request == RIL_REQUEST_DATA_REGISTRATION_STATE) {
+    ALOGV("Requesting data registration state.");
+    asprintf(&responseStr[3], "%d", getBestDataTechnology(gModemCurrentType));
+    responseStr[4] = strdup("");   // DataServiceDenyReason
+    responseStr[5] = strdup("1");  // Max simultaneous data calls.
+  } else {
+    ALOGV("Unexpected request type: %d", request);
+    return;
+  }
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, responseStr,
+                                 numElements * sizeof(responseStr));
+}
+
+static void request_baseband_version(RIL_Token t) {
+  const char* response_str = "CVD_R1.0.0";
+
+  ALOGV("Requested phone baseband version.");
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, strdup(response_str),
+                                 sizeof(response_str));
+}
+
+// Returns true, if modem is CDMA capable.
+static bool isCDMA() {
+  switch (gModemCurrentType) {
+    case PREF_NET_TYPE_GSM_WCDMA:
+    case PREF_NET_TYPE_GSM_ONLY:
+    case PREF_NET_TYPE_WCDMA:
+    case PREF_NET_TYPE_GSM_WCDMA_AUTO:
+    case PREF_NET_TYPE_LTE_GSM_WCDMA:
+    case PREF_NET_TYPE_LTE_ONLY:
+      return false;
+
+    case PREF_NET_TYPE_CDMA_EVDO_AUTO:
+    case PREF_NET_TYPE_CDMA_ONLY:
+    case PREF_NET_TYPE_EVDO_ONLY:
+    case PREF_NET_TYPE_LTE_CDMA_EVDO:
+    case PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA:
+    case PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO:
+      return true;
+    default:
+      break;
+  }
+
+  ALOGE("INVALID MODEM TYPE: %d", gModemCurrentType);
+  return false;
+}
+
+// Returns true, if modem is GSM capable.
+// Note, this is not same as !isCDMA().
+static bool isGSM() {
+  switch (gModemCurrentType) {
+    case PREF_NET_TYPE_GSM_WCDMA:
+    case PREF_NET_TYPE_GSM_ONLY:
+    case PREF_NET_TYPE_WCDMA:
+    case PREF_NET_TYPE_GSM_WCDMA_AUTO:
+    case PREF_NET_TYPE_LTE_GSM_WCDMA:
+    case PREF_NET_TYPE_LTE_ONLY:
+    case PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO:
+      return true;
+
+    case PREF_NET_TYPE_CDMA_EVDO_AUTO:
+    case PREF_NET_TYPE_CDMA_ONLY:
+    case PREF_NET_TYPE_EVDO_ONLY:
+    case PREF_NET_TYPE_LTE_CDMA_EVDO:
+    case PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA:
+      return false;
+    default:
+      break;
+  }
+
+  ALOGE("INVALID MODEM TYPE: %d", gModemCurrentType);
+  return false;
+}
+
+static const char gIdentityGsmImei[] = "12345678902468";  // Luhn cksum = 0.
+static const char gIdentityGsmImeiSv[] = "01";            // Arbitrary version.
+static const char gIdentityCdmaEsn[] = "A0123456";        // 8 digits, ^[A-F].*
+static const char gIdentityCdmaMeid[] =
+    "A0123456789012";  // 14 digits, ^[A-F].*
+
+static void request_get_imei(RIL_Token t) {
+  ALOGV("Requesting IMEI");
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS,
+                                 const_cast<char*>(gIdentityGsmImei),
+                                 strlen(gIdentityGsmImei));
+}
+
+static void request_get_imei_sv(RIL_Token t) {
+  ALOGV("Requesting IMEI SV");
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS,
+                                 const_cast<char*>(gIdentityGsmImeiSv),
+                                 strlen(gIdentityGsmImeiSv));
+}
+
+static void request_device_identity(int /*request*/, void* /*data*/,
+                                    size_t /*datalen*/, RIL_Token t) {
+  char* response[4] = {NULL};
+
+  ALOGV("Requesting device identity...");
+
+  if (isCDMA()) {
+    response[2] = strdup(&gIdentityCdmaEsn[0]);
+    response[3] = strdup(&gIdentityCdmaMeid[0]);
+  }
+
+  if (isGSM()) {
+    response[0] = strdup(&gIdentityGsmImei[0]);
+    response[1] = strdup(&gIdentityGsmImeiSv[0]);
+  }
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
+
+  free(response[0]);
+  free(response[1]);
+}
+
+// Let's pretend we have SIM for CDMA (by default).
+static bool gCdmaHasSim = true;
+static RIL_CdmaSubscriptionSource gCdmaSubscriptionType =
+    CDMA_SUBSCRIPTION_SOURCE_RUIM_SIM;
+
+static void request_cdma_get_subscription_source(int /*request*/,
+                                                 void* /*data*/,
+                                                 size_t /*datalen*/,
+                                                 RIL_Token t) {
+  ALOGV("Requesting CDMA Subscription source.");
+
+  if (!isCDMA()) {
+    // No such radio.
+    gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+    return;
+  }
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gCdmaSubscriptionType,
+                                 sizeof(gCdmaSubscriptionType));
+}
+
+static void request_cdma_set_subscription_source(int /*request*/, void* data,
+                                                 size_t /*datalen*/,
+                                                 RIL_Token t) {
+  ALOGV("Setting CDMA Subscription source.");
+
+  if (!isCDMA()) {
+    // No such radio.
+    gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+    return;
+  }
+
+  RIL_CdmaSubscriptionSource new_source = *(RIL_CdmaSubscriptionSource*)(data);
+
+  if (new_source == CDMA_SUBSCRIPTION_SOURCE_RUIM_SIM && !gCdmaHasSim) {
+    // No such radio.
+    gce_ril_env->OnRequestComplete(t, RIL_E_SIM_ABSENT, NULL, 0);
+    return;
+  }
+
+  ALOGV("Changed CDMA subscription type from %d to %d", gCdmaSubscriptionType,
+        new_source);
+  gCdmaSubscriptionType = new_source;
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+  gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED,
+                                     &gCdmaSubscriptionType,
+                                     sizeof(gCdmaSubscriptionType));
+}
+
+static void request_cdma_subscription(int /*request*/, void* /*data*/,
+                                      size_t /*datalen*/, RIL_Token t) {
+  ALOGV("Requesting CDMA Subscription.");
+
+  if (!isCDMA()) {
+    // No such radio.
+    gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+    return;
+  }
+
+  char* responseStr[5] = {NULL};
+  responseStr[0] = strdup(&kCdmaMobileDeviceNumber[0]);  // MDN
+  responseStr[1] = strdup(&kCdmaSID[0]);                 // SID
+  responseStr[2] = strdup(&kCdmaNID[0]);                 // NID
+  responseStr[3] = strdup(&kCdmaMobileDeviceNumber[0]);  // MIN
+  responseStr[4] = strdup("1");                          // PRL Version
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, responseStr,
+                                 sizeof(responseStr));
+}
+
+static const int gMaxConcurrentVoiceCalls = 4;
+static const int gMaxConcurrentDataCalls = 4;
+static const int gMaxConcurrentStandbyConnections = 4;
+
+#if VSOC_PLATFORM_SDK_AFTER(K)
+static void request_hardware_config(RIL_Token t) {
+  RIL_HardwareConfig hw_cfg[2];
+
+  ALOGV("Requesting hardware configuration.");
+
+  strncpy(hw_cfg[0].uuid, "com.google.cvdgce1.modem", sizeof(hw_cfg[0].uuid));
+  strncpy(hw_cfg[1].uuid, "com.google.cvdgce1.sim", sizeof(hw_cfg[1].uuid));
+
+  int technologies = 0;  // = unknown.
+  std::map<RIL_PreferredNetworkType, int>::iterator iter =
+      gModemTechnologies.find(gModemDefaultType);
+  if (iter != gModemTechnologies.end()) {
+    technologies = iter->second;
+  }
+
+  hw_cfg[0].type = RIL_HARDWARE_CONFIG_MODEM;
+  hw_cfg[0].state = RIL_HARDWARE_CONFIG_STATE_ENABLED;
+  hw_cfg[0].cfg.modem.rilModel = 0;
+  hw_cfg[0].cfg.modem.rat = technologies;
+  hw_cfg[0].cfg.modem.maxVoice = gMaxConcurrentVoiceCalls;
+  hw_cfg[0].cfg.modem.maxData = gMaxConcurrentDataCalls;
+  hw_cfg[0].cfg.modem.maxStandby = gMaxConcurrentStandbyConnections;
+
+  hw_cfg[1].type = RIL_HARDWARE_CONFIG_SIM;
+  hw_cfg[1].state = RIL_HARDWARE_CONFIG_STATE_ENABLED;
+  memcpy(hw_cfg[1].cfg.sim.modemUuid, hw_cfg[0].uuid,
+         sizeof(hw_cfg[1].cfg.sim.modemUuid));
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &hw_cfg, sizeof(hw_cfg));
+}
+#endif
+
+// 0 = Home network only, 1 = preferred networks only, 2 = all networks.
+static int gCdmaRoamingPreference = 2;
+
+static void request_cdma_get_roaming_preference(int /*request*/, void* /*data*/,
+                                                size_t /*datalen*/,
+                                                RIL_Token t) {
+  if (!isCDMA()) {
+    // No such radio.
+    gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+    return;
+  }
+
+  ALOGV("Requesting CDMA Roaming preference");
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gCdmaRoamingPreference,
+                                 sizeof(gCdmaRoamingPreference));
+}
+
+static void request_cdma_set_roaming_preference(int /*request*/, void* data,
+                                                size_t /*datalen*/,
+                                                RIL_Token t) {
+  if (!isCDMA()) {
+    // No such radio.
+    gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+    return;
+  }
+
+  int pref = *(int*)data;
+  ALOGV("Changing CDMA roaming preference: %d -> %d", gCdmaRoamingPreference,
+        pref);
+
+  if ((pref < 0) || (pref > 2)) {
+    ALOGV("Unsupported roaming preference: %d", pref);
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    return;
+  }
+
+  gCdmaRoamingPreference = pref;
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_send_ussd(void* /*data*/, size_t /*datalen*/, RIL_Token t) {
+  ALOGV("Sending USSD code is currently not supported");
+  // TODO(ender): support this feature
+  gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
+}
+
+static void request_cancel_ussd(RIL_Token t) {
+  ALOGV("Cancelling USSD code is currently not supported");
+  // TODO(ender): support this feature
+  gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
+}
+
+static void request_exit_emergency_mode(void* /*data*/, size_t /*datalen*/,
+                                        RIL_Token t) {
+  ALOGV("Exiting emergency callback mode.");
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static RIL_RadioState gce_ril_current_state() {
+  ALOGV("Reporting radio state %d", gRadioPowerState);
+  return gRadioPowerState;
+}
+
+static int gce_ril_on_supports(int requestCode) {
+  ALOGE("%s: Request code %d not implemented", __FUNCTION__, requestCode);
+  return 1;
+}
+
+static void gce_ril_on_cancel(RIL_Token /*t*/) {
+  ALOGE("Cancel operation not implemented");
+}
+
+static const char* gce_ril_get_version(void) {
+  ALOGV("Reporting GCE version " GCE_RIL_VERSION_STRING);
+  return GCE_RIL_VERSION_STRING;
+}
+
+static int s_cell_info_rate_ms = INT_MAX;
+static int s_mcc = 0;
+static int s_mnc = 0;
+static int s_lac = 0;
+static int s_cid = 0;
+
+std::vector<RIL_NeighboringCell> gGSMNeighboringCells;
+
+static void request_get_neighboring_cell_ids(void* /*data*/, size_t /*datalen*/,
+                                             RIL_Token t) {
+  ALOGV("Requesting GSM neighboring cell ids");
+
+  if (!isGSM() || gGSMNeighboringCells.empty()) {
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    return;
+  }
+
+  RIL_NeighboringCell** cells =
+      new RIL_NeighboringCell*[gGSMNeighboringCells.size()];
+
+  for (size_t index = 0; index < gGSMNeighboringCells.size(); ++index) {
+    cells[index] = &gGSMNeighboringCells[index];
+  }
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, cells,
+                                 sizeof(RIL_NeighboringCell*));
+  delete[] cells;
+}
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR1)
+static void request_get_cell_info_list(void* /*data*/, size_t /*datalen*/,
+                                       RIL_Token t) {
+  struct timespec now;
+  uint64_t curTime;
+
+  ALOGV("Requesting Cell Info List");
+
+  clock_gettime(CLOCK_MONOTONIC, &now);
+  curTime = now.tv_sec * 1000000000LL + now.tv_nsec;
+
+#if VSOC_PLATFORM_SDK_AFTER(N_MR1)
+  RIL_CellInfo_v12 ci;
+#else
+  RIL_CellInfo ci;
+#endif
+
+  if (isGSM()) {
+    ci.cellInfoType = RIL_CELL_INFO_TYPE_GSM;
+    ci.registered = 1;
+    ci.timeStampType = RIL_TIMESTAMP_TYPE_ANTENNA;  // Our own timestamp.
+    ci.timeStamp = curTime - 1000;                  // Fake time in the past.
+    ci.CellInfo.gsm.cellIdentityGsm.mcc = s_mcc;
+    ci.CellInfo.gsm.cellIdentityGsm.mnc = s_mnc;
+    ci.CellInfo.gsm.cellIdentityGsm.lac = s_lac;
+    ci.CellInfo.gsm.cellIdentityGsm.cid = s_cid;
+    ci.CellInfo.gsm.signalStrengthGsm.signalStrength = 10;
+    ci.CellInfo.gsm.signalStrengthGsm.bitErrorRate = 0;
+
+    gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &ci, sizeof(ci));
+  } else if (isCDMA()) {
+    // TODO(ender): CDMA cell support. And LTE.
+    gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+  } else {
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+  }
+}
+#endif
+
+struct NetworkOperator {
+  std::string long_name;
+  std::string short_name;
+  bool is_accessible;
+
+  NetworkOperator() {}
+
+  NetworkOperator(const std::string& long_name, const std::string& short_name,
+                  bool is_accessible)
+      : long_name(long_name),
+        short_name(short_name),
+        is_accessible(is_accessible) {}
+};
+
+static std::map<std::string, NetworkOperator> gNetworkOperators;
+static std::string gCurrentNetworkOperator = "";
+
+enum OperatorSelectionMethod {
+  kOperatorAutomatic = 0,
+  kOperatorManual = 1,
+  kOperatorDeregistered = 2,
+  kOperatorManualThenAutomatic = 4
+};
+
+static void init_virtual_network() {
+  gGSMNeighboringCells.resize(1);
+  gGSMNeighboringCells[0].cid = (char*)"0000";
+  gGSMNeighboringCells[0].rssi = 75;
+  gNetworkOperators["310260"] =
+      NetworkOperator("Android Virtual Operator", "Android", true);
+  gNetworkOperators["310300"] =
+      NetworkOperator("Alternative Operator", "Alternative", true);
+  gNetworkOperators["310400"] =
+      NetworkOperator("Hermetic Network Operator", "Hermetic", false);
+}
+
+static OperatorSelectionMethod gOperatorSelectionMethod = kOperatorDeregistered;
+
+static void request_query_network_selection_mode(void* /*data*/,
+                                                 size_t /*datalen*/,
+                                                 RIL_Token t) {
+  ALOGV("Query operator selection mode (%d)", gOperatorSelectionMethod);
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gOperatorSelectionMethod,
+                                 sizeof(gOperatorSelectionMethod));
+}
+
+static void request_operator(void* /*data*/, size_t /*datalen*/, RIL_Token t) {
+  std::map<std::string, NetworkOperator>::iterator iter =
+      gNetworkOperators.find(gCurrentNetworkOperator);
+
+  ALOGV("Requesting current operator info");
+
+  if (iter == gNetworkOperators.end()) {
+    gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+    return;
+  }
+
+  const char* response[] = {iter->second.long_name.c_str(),
+                            iter->second.short_name.c_str(),
+                            iter->first.c_str()};
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response));
+}
+
+static void request_query_available_networks(void* /*data*/, size_t /*datalen*/,
+                                             RIL_Token t) {
+  const char** available_networks =
+      new const char*[gNetworkOperators.size() * 4];
+
+  ALOGV("Querying available networks.");
+
+  // TODO(ender): this should only respond once operator is selected and
+  // registered.
+  int index = 0;
+  for (std::map<std::string, NetworkOperator>::iterator iter =
+           gNetworkOperators.begin();
+       iter != gNetworkOperators.end(); ++iter) {
+    // TODO(ender): wrap in a neat structure maybe?
+    available_networks[index++] = iter->second.long_name.c_str();
+    available_networks[index++] = iter->second.short_name.c_str();
+    available_networks[index++] = iter->first.c_str();
+    if (!iter->second.is_accessible) {
+      available_networks[index++] = "forbidden";
+    } else if (iter->first == gCurrentNetworkOperator) {
+      available_networks[index++] = "current";
+    } else {
+      available_networks[index++] = "available";
+    }
+  }
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &available_networks,
+                                 4 * gNetworkOperators.size());
+  delete[] available_networks;
+}
+
+static void request_set_automatic_network_selection(RIL_Token t) {
+  ALOGV("Requesting automatic operator selection");
+  gCurrentNetworkOperator = gNetworkOperators.begin()->first;
+  gOperatorSelectionMethod = kOperatorAutomatic;
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void request_set_manual_network_selection(void* data, size_t /*datalen*/,
+                                                 RIL_Token t) {
+  char* mccmnc = (char*)data;
+
+  ALOGV("Requesting manual operator selection: %s", mccmnc);
+
+  std::map<std::string, NetworkOperator>::iterator iter =
+      gNetworkOperators.find(mccmnc);
+
+  if (iter == gNetworkOperators.end() || iter->second.is_accessible) {
+    gce_ril_env->OnRequestComplete(t, RIL_E_ILLEGAL_SIM_OR_ME, NULL, 0);
+    return;
+  }
+
+  gCurrentNetworkOperator = mccmnc;
+  gOperatorSelectionMethod = kOperatorManual;
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static const char kDefaultSMSC[] = "00";
+static int gNextSmsMessageId = 1;
+
+static void request_cdma_send_SMS(void* /*data*/, RIL_Token t) {
+  RIL_SMS_Response response = {0, 0, 0};
+  // RIL_CDMA_SMS_Message* rcsm = (RIL_CDMA_SMS_Message*) data;
+
+  ALOGW("CDMA SMS Send is currently not implemented.");
+
+  // Cdma Send SMS implementation will go here:
+  // But it is not implemented yet.
+  memset(&response, 0, sizeof(response));
+  response.messageRef = -1;  // This must be BearerData MessageId.
+  gce_ril_env->OnRequestComplete(t, RIL_E_SMS_SEND_FAIL_RETRY, &response,
+                                 sizeof(response));
+}
+
+static void request_send_SMS(void* data, RIL_Token t) {
+  RIL_SMS_Response response = {0, 0, 0};
+
+  ALOGV("Send GSM SMS Message");
+
+  // SMSC is an address of SMS center or NULL for default.
+  const char* smsc = ((const char**)data)[0];
+  if (smsc == NULL) smsc = &kDefaultSMSC[0];
+
+  // PDU in hex-encoded string.
+  const char* pdu = ((const char**)data)[1];
+  int pdu_length = strlen(pdu) / 2;
+
+  response.messageRef = gNextSmsMessageId++;
+  response.ackPDU = NULL;
+  response.errorCode = 0;
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
+
+  // response.messageRef = -1;
+  // gce_ril_env->OnRequestComplete(t, RIL_E_SMS_SEND_FAIL_RETRY, &response,
+  //                                sizeof(response));
+}
+
+#if VSOC_PLATFORM_SDK_AFTER(J_MR1)
+static void request_set_cell_info_list_rate(void* data, size_t /*datalen*/,
+                                            RIL_Token t) {
+  // For now we'll save the rate but no RIL_UNSOL_CELL_INFO_LIST messages
+  // will be sent.
+  ALOGV("Setting cell info list rate.");
+  s_cell_info_rate_ms = ((int*)data)[0];
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+static void request_ims_send_SMS(void* data, size_t /*datalen*/, RIL_Token t) {
+  RIL_IMS_SMS_Message* args = (RIL_IMS_SMS_Message*)data;
+  RIL_SMS_Response response{};
+
+  ALOGV("Send IMS SMS Message");
+
+  switch (args->tech) {
+    case RADIO_TECH_3GPP:
+      return request_send_SMS(args->message.gsmMessage, t);
+
+    case RADIO_TECH_3GPP2:
+      return request_cdma_send_SMS(args->message.gsmMessage, t);
+
+    default:
+      ALOGE("Invalid SMS format value: %d", args->tech);
+      response.messageRef = -2;
+      gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, &response,
+                                     sizeof(response));
+  }
+}
+#endif
+
+static void request_SMS_acknowledge(void* data, size_t /*datalen*/,
+                                    RIL_Token t) {
+  int* ack = (int*)data;
+
+  // TODO(ender): we should retain "incoming" sms for later reception.
+  ALOGV("SMS receipt %ssuccessful (reason %d).", ack[0] ? "" : "un", ack[1]);
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+struct SimFileCommand {
+  uint8_t command;
+  uint16_t efid;
+  uint8_t param1;
+  uint8_t param2;
+  uint8_t param3;
+
+  bool operator<(const SimFileCommand& other) const {
+    uint64_t sum1, sum2;
+    sum1 = (command * 1ull << 40) | (efid * 1ull << 24) | (param1 << 16) |
+           (param2 << 8) | (param3);
+    sum2 = (other.command * 1ull << 40) | (other.efid * 1ull << 24) |
+           (other.param1 << 16) | (other.param2 << 8) | (other.param3);
+    return sum1 < sum2;
+  }
+
+  SimFileCommand(uint8_t cmd, uint16_t efid, uint8_t p1, uint8_t p2, uint8_t p3)
+      : command(cmd), efid(efid), param1(p1), param2(p2), param3(p3) {}
+};
+
+struct SimFileResponse {
+  uint8_t sw1;
+  uint8_t sw2;
+  const char* data;
+
+  SimFileResponse() : sw1(0), sw2(0), data(NULL) {}
+
+  SimFileResponse(uint8_t sw1, uint8_t sw2, const char* data)
+      : sw1(sw1), sw2(sw2), data(data) {}
+};
+
+// TODO(ender): Double check & rewrite these.
+std::map<SimFileCommand, SimFileResponse> gSimFileSystem;
+
+static void init_sim_file_system() {
+  gSimFileSystem[SimFileCommand(192, 28436, 0, 0, 15)] =
+      SimFileResponse(144, 0, "000000146f1404001aa0aa01020000");
+  gSimFileSystem[SimFileCommand(176, 28436, 0, 0, 20)] =
+      SimFileResponse(144, 0, "416e64726f6964ffffffffffffffffffffffffff");
+  gSimFileSystem[SimFileCommand(192, 28433, 0, 0, 15)] =
+      SimFileResponse(144, 0, "000000016f11040011a0aa01020000");
+  gSimFileSystem[SimFileCommand(176, 28433, 0, 0, 1)] =
+      SimFileResponse(144, 0, "55");
+  gSimFileSystem[SimFileCommand(192, 12258, 0, 0, 15)] =
+      SimFileResponse(144, 0, "0000000a2fe204000fa0aa01020000");
+  gSimFileSystem[SimFileCommand(176, 12258, 0, 0, 10)] =
+      SimFileResponse(144, 0, "98101430121181157002");
+  gSimFileSystem[SimFileCommand(192, 28435, 0, 0, 15)] =
+      SimFileResponse(144, 0, "000000016f13040011a0aa01020000");
+  gSimFileSystem[SimFileCommand(176, 28435, 0, 0, 1)] =
+      SimFileResponse(144, 0, "55");
+  gSimFileSystem[SimFileCommand(192, 28472, 0, 0, 15)] =
+      SimFileResponse(144, 0, "0000000f6f3804001aa0aa01020000");
+  gSimFileSystem[SimFileCommand(176, 28472, 0, 0, 15)] =
+      SimFileResponse(144, 0, "ff30ffff3c003c03000c0000f03f00");
+  gSimFileSystem[SimFileCommand(192, 28617, 0, 0, 15)] =
+      SimFileResponse(144, 0, "000000086fc9040011a0aa01020104");
+  gSimFileSystem[SimFileCommand(178, 28617, 1, 4, 4)] =
+      SimFileResponse(144, 0, "01000000");
+  gSimFileSystem[SimFileCommand(192, 28618, 0, 0, 15)] =
+      SimFileResponse(144, 0, "0000000a6fca040011a0aa01020105");
+  gSimFileSystem[SimFileCommand(178, 28618, 1, 4, 5)] =
+      SimFileResponse(144, 0, "0000000000");
+  gSimFileSystem[SimFileCommand(192, 28589, 0, 0, 15)] =
+      SimFileResponse(144, 0, "000000046fad04000aa0aa01020000");
+  gSimFileSystem[SimFileCommand(176, 28589, 0, 0, 4)] =
+      SimFileResponse(144, 0, "00000003");
+  gSimFileSystem[SimFileCommand(192, 28438, 0, 0, 15)] =
+      SimFileResponse(144, 0, "000000026f1604001aa0aa01020000");
+  gSimFileSystem[SimFileCommand(176, 28438, 0, 0, 2)] =
+      SimFileResponse(144, 0, "0233");
+  gSimFileSystem[SimFileCommand(192, 28486, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28621, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28613, 0, 0, 15)] =
+      SimFileResponse(144, 0, "000000f06fc504000aa0aa01020118");
+  gSimFileSystem[SimFileCommand(178, 28613, 1, 4, 24)] = SimFileResponse(
+      144, 0, "43058441aa890affffffffffffffffffffffffffffffffff");
+  gSimFileSystem[SimFileCommand(192, 28480, 0, 0, 15)] =
+      SimFileResponse(144, 0, "000000806f40040011a0aa01020120");
+  // Primary phone number encapsulated
+  // [51][55][21][43][65][f7] = 1 555 1234 567$
+  gSimFileSystem[SimFileCommand(178, 28480, 1, 4, 32)] = SimFileResponse(
+      144, 0,
+      "ffffffffffffffffffffffffffffffffffff07915155214365f7ffffffffffff");
+  gSimFileSystem[SimFileCommand(192, 28615, 0, 0, 15)] =
+      SimFileResponse(144, 0, "000000406fc7040011a0aa01020120");
+  // Voice mail number encapsulated
+  // [56][6f][69][63][65][6d][61][69][6c] = 'Voicemail'
+  // [51][55][67][45][23][f1] = 1 555 7654 321$
+  gSimFileSystem[SimFileCommand(178, 28615, 1, 4, 32)] = SimFileResponse(
+      144, 0,
+      "566f6963656d61696cffffffffffffffffff07915155674523f1ffffffffffff");
+  gSimFileSystem[SimFileCommand(192, 12037, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28437, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28478, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28450, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28456, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28474, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28481, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28484, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28493, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(192, 28619, 0, 0, 15)] =
+      SimFileResponse(148, 4, NULL);
+  gSimFileSystem[SimFileCommand(176, 28506, 0, 0, 4)] =
+      SimFileResponse(144, 0, "00000013");
+}
+
+static void request_SIM_IO(void* data, size_t /*datalen*/, RIL_Token t) {
+  const RIL_SIM_IO_v6& args = *(RIL_SIM_IO_v6*)data;
+  RIL_SIM_IO_Response sr = {0, 0, 0};
+
+  ALOGV(
+      "Requesting SIM File IO: %d EFID %x, Params: %d, %d, %d, path: %s, "
+      "data %s PIN: %s AID: %s",
+      args.command, args.fileid, args.p1, args.p2, args.p3, args.path,
+      args.data, args.pin2, args.aidPtr);
+
+  SimFileCommand cmd(args.command, args.fileid, args.p1, args.p2, args.p3);
+
+  std::map<SimFileCommand, SimFileResponse>::iterator resp =
+      gSimFileSystem.find(cmd);
+
+  if (resp != gSimFileSystem.end()) {
+    sr.sw1 = resp->second.sw1;
+    sr.sw2 = resp->second.sw2;
+    if (resp->second.data) sr.simResponse = strdup(resp->second.data);
+    gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &sr, sizeof(sr));
+    return;
+  }
+
+  ALOGW("Unsupported SIM File IO command.");
+  gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void request_enter_sim_pin(void* data, size_t /*datalen*/, RIL_Token t) {
+  const char** pin_aid = (const char**)data;
+
+  ALOGV("Entering PIN: %s / %s", pin_aid[0], pin_aid[1]);
+
+  ++gSimPINAttempts;
+  int remaining_attempts = gSimPINAttemptsMax - gSimPINAttempts;
+
+  bool is_valid = false;
+
+  if (gSimStatus == SIM_PIN) {
+    is_valid = (gSimPIN == pin_aid[0]);
+  } else if (gSimStatus == SIM_PUK) {
+    is_valid = (gSimPUK == pin_aid[0]);
+  } else {
+    ALOGV("Unexpected SIM status for unlock: %d", gSimStatus);
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    return;
+  }
+
+  if (!is_valid) {
+    if (gSimPINAttempts == gSimPINAttemptsMax) {
+      if (gSimStatus == SIM_PIN) {
+        gSimStatus = SIM_PUK;
+        gSimPINAttempts = 0;
+      } else {
+        ALOGV("PIN and PUK verification failed; locking SIM card.");
+        gSimStatus = SIM_NOT_READY;
+        gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
+      }
+    }
+
+    gce_ril_env->OnRequestComplete(t, RIL_E_PASSWORD_INCORRECT,
+                                   &remaining_attempts,
+                                   sizeof(remaining_attempts));
+  } else {
+    if (gSimStatus == SIM_PUK) {
+      ALOGV("Resetting SIM PIN to %s", pin_aid[1]);
+      gSimPIN = pin_aid[1];
+    }
+
+    gSimPINAttempts = 0;
+    gSimStatus = SIM_READY;
+    gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &remaining_attempts,
+                                   sizeof(remaining_attempts));
+  }
+
+  pollSIMState(NULL);
+}
+
+/**
+ * No longer POLL.
+ */
+static void pollSIMState(void* /*param*/) {
+  // TODO(ender): check radio state?
+
+  ALOGV("Polling SIM Status.");
+
+  switch (gSimStatus) {
+    case SIM_ABSENT:
+    case SIM_PIN:
+    case SIM_PUK:
+    case SIM_NETWORK_PERSONALIZATION:
+    default:
+      ALOGV("SIM Absent or Locked");
+      break;
+
+    case SIM_NOT_READY:
+      // Transition directly to READY. Set default network operator.
+      if (gRadioPowerState == RADIO_STATE_ON) {
+        gSimStatus = SIM_READY;
+        gCurrentNetworkOperator = "310260";
+      }
+
+      gce_ril_env->RequestTimedCallback(pollSIMState, NULL, &TIMEVAL_SIMPOLL);
+      break;
+
+    case SIM_READY:
+      ALOGV("SIM Ready. Notifying network state changed.");
+      break;
+  }
+
+  if (gRadioPowerState != RADIO_STATE_OFF) {
+    gce_ril_env->OnUnsolicitedResponse(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED,
+                                       NULL, 0);
+    gce_ril_env->OnUnsolicitedResponse(
+        RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, NULL, 0);
+  }
+}
+
+std::map<SIM_Status, RIL_AppStatus> gRilAppStatus;
+
+static void init_sim_status() {
+  gRilAppStatus[SIM_ABSENT] = (RIL_AppStatus){RIL_APPTYPE_UNKNOWN,
+                                              RIL_APPSTATE_UNKNOWN,
+                                              RIL_PERSOSUBSTATE_UNKNOWN,
+                                              NULL,
+                                              NULL,
+                                              0,
+                                              RIL_PINSTATE_UNKNOWN,
+                                              RIL_PINSTATE_UNKNOWN};
+  gRilAppStatus[SIM_NOT_READY] =
+      (RIL_AppStatus){RIL_APPTYPE_SIM,
+                      RIL_APPSTATE_DETECTED,
+                      RIL_PERSOSUBSTATE_UNKNOWN,
+                      NULL,
+                      NULL,
+                      0,
+                      RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+                      RIL_PINSTATE_ENABLED_NOT_VERIFIED};
+  gRilAppStatus[SIM_READY] = (RIL_AppStatus){
+      RIL_APPTYPE_SIM,
+      RIL_APPSTATE_READY,
+      RIL_PERSOSUBSTATE_READY,
+      NULL,
+      NULL,
+      0,
+      RIL_PINSTATE_ENABLED_VERIFIED,
+      RIL_PINSTATE_ENABLED_VERIFIED,
+  };
+  gRilAppStatus[SIM_PIN] = (RIL_AppStatus){RIL_APPTYPE_SIM,
+                                           RIL_APPSTATE_PIN,
+                                           RIL_PERSOSUBSTATE_UNKNOWN,
+                                           NULL,
+                                           NULL,
+                                           0,
+                                           RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+                                           RIL_PINSTATE_UNKNOWN};
+  gRilAppStatus[SIM_PUK] = (RIL_AppStatus){RIL_APPTYPE_SIM,
+                                           RIL_APPSTATE_PUK,
+                                           RIL_PERSOSUBSTATE_UNKNOWN,
+                                           NULL,
+                                           NULL,
+                                           0,
+                                           RIL_PINSTATE_ENABLED_BLOCKED,
+                                           RIL_PINSTATE_UNKNOWN};
+  gRilAppStatus[SIM_NETWORK_PERSONALIZATION] =
+      (RIL_AppStatus){RIL_APPTYPE_SIM,
+                      RIL_APPSTATE_SUBSCRIPTION_PERSO,
+                      RIL_PERSOSUBSTATE_SIM_NETWORK,
+                      NULL,
+                      NULL,
+                      0,
+                      RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+                      RIL_PINSTATE_UNKNOWN};
+  gRilAppStatus[RUIM_ABSENT] = (RIL_AppStatus){RIL_APPTYPE_UNKNOWN,
+                                               RIL_APPSTATE_UNKNOWN,
+                                               RIL_PERSOSUBSTATE_UNKNOWN,
+                                               NULL,
+                                               NULL,
+                                               0,
+                                               RIL_PINSTATE_UNKNOWN,
+                                               RIL_PINSTATE_UNKNOWN};
+  gRilAppStatus[RUIM_NOT_READY] = (RIL_AppStatus){RIL_APPTYPE_RUIM,
+                                                  RIL_APPSTATE_DETECTED,
+                                                  RIL_PERSOSUBSTATE_UNKNOWN,
+                                                  NULL,
+                                                  NULL,
+                                                  0,
+                                                  RIL_PINSTATE_UNKNOWN,
+                                                  RIL_PINSTATE_UNKNOWN};
+  gRilAppStatus[RUIM_READY] = (RIL_AppStatus){RIL_APPTYPE_RUIM,
+                                              RIL_APPSTATE_READY,
+                                              RIL_PERSOSUBSTATE_READY,
+                                              NULL,
+                                              NULL,
+                                              0,
+                                              RIL_PINSTATE_UNKNOWN,
+                                              RIL_PINSTATE_UNKNOWN};
+  gRilAppStatus[RUIM_PIN] = (RIL_AppStatus){RIL_APPTYPE_RUIM,
+                                            RIL_APPSTATE_PIN,
+                                            RIL_PERSOSUBSTATE_UNKNOWN,
+                                            NULL,
+                                            NULL,
+                                            0,
+                                            RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+                                            RIL_PINSTATE_UNKNOWN};
+  gRilAppStatus[RUIM_PUK] = (RIL_AppStatus){RIL_APPTYPE_RUIM,
+                                            RIL_APPSTATE_PUK,
+                                            RIL_PERSOSUBSTATE_UNKNOWN,
+                                            NULL,
+                                            NULL,
+                                            0,
+                                            RIL_PINSTATE_ENABLED_BLOCKED,
+                                            RIL_PINSTATE_UNKNOWN};
+  gRilAppStatus[RUIM_NETWORK_PERSONALIZATION] =
+      (RIL_AppStatus){RIL_APPTYPE_RUIM,
+                      RIL_APPSTATE_SUBSCRIPTION_PERSO,
+                      RIL_PERSOSUBSTATE_SIM_NETWORK,
+                      NULL,
+                      NULL,
+                      0,
+                      RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+                      RIL_PINSTATE_UNKNOWN};
+}
+
+/**
+ * Get the current card status.
+ */
+static void getCardStatus(RIL_Token t) {
+  ALOGV("Querying SIM status.");
+  RIL_CardStatus_v6 card_status;
+
+  if (gSimStatus == SIM_ABSENT) {
+    card_status.card_state = RIL_CARDSTATE_ABSENT;
+    card_status.num_applications = 0;
+  } else {
+    card_status.card_state = RIL_CARDSTATE_PRESENT;
+    card_status.num_applications = 1;
+  }
+
+  card_status.universal_pin_state = RIL_PINSTATE_UNKNOWN;
+  card_status.gsm_umts_subscription_app_index = -1;
+  card_status.cdma_subscription_app_index = -1;
+  card_status.ims_subscription_app_index = -1;
+
+  // Initialize application status
+  for (int i = 0; i < RIL_CARD_MAX_APPS; i++) {
+    card_status.applications[i] = gRilAppStatus[SIM_ABSENT];
+  }
+
+  if (card_status.num_applications > 0) {
+    card_status.gsm_umts_subscription_app_index = 0;
+
+    card_status.applications[0] = gRilAppStatus[gSimStatus];
+    card_status.universal_pin_state = card_status.applications[0].pin1;
+    // To enable basic CDMA (currently neither supported nor functional):
+    //    card_status.num_applications = 2;
+    //    card_status.cdma_subscription_app_index = 1;
+    //    card_status.applications[1] =
+    //        gRilAppStatus[SIM_Status(gSimStatus + RUIM_ABSENT)];
+  }
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &card_status,
+                                 sizeof(card_status));
+}
+
+struct SimSession {
+  std::string aid;
+};
+
+static int gNextSimSessionId = 1;
+static std::map<int, SimSession> gSimSessions;
+
+static void request_sim_open_channel(void* data, size_t /*datalen*/,
+                                     RIL_Token t) {
+  char* aid = (char*)data;
+  SimSession session;
+
+  ALOGV("Requesting new SIM session");
+
+  if (aid != NULL) {
+    session.aid = aid;
+  }
+
+  int response = gNextSimSessionId++;
+  gSimSessions[response] = session;
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
+}
+
+static void request_sim_close_channel(void* data, size_t /*datalen*/,
+                                      RIL_Token t) {
+  int session = *(int*)(data);
+
+  ALOGV("Closing SIM session %d", session);
+
+  if (gSimSessions.erase(session) == 0) {
+    // No such session.
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+  } else {
+    gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+  }
+}
+
+#if VSOC_PLATFORM_SDK_AFTER(K)
+static void request_sim_apdu(void* data, size_t /*datalen*/, RIL_Token t) {
+  RIL_SIM_APDU* apdu = (RIL_SIM_APDU*)data;
+
+  ALOGV("Requesting APDU: Session %d CLA %d INST %d Params: %d %d %d, data %s",
+        apdu->sessionid, apdu->cla, apdu->instruction, apdu->p1, apdu->p2,
+        apdu->p3, apdu->data);
+
+  if (gSimSessions.find(apdu->sessionid) != gSimSessions.end()) {
+    RIL_SIM_IO_Response sr{};
+
+    // Fallback / default behavior.
+    sr.sw1 = 144;
+    sr.sw2 = 0;
+    sr.simResponse = NULL;
+
+    gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &sr, sizeof(sr));
+  } else {
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+  }
+}
+#endif
+
+// 0 = Lock is available, but disabled.
+// 1 = Lock is available and enabled,
+// 2 = lock is neither available nor enabled
+static const int kFacilityLockAllDisabled = 0;
+
+static void request_facility_lock(void* data, size_t /*datalen*/, RIL_Token t) {
+  char** data_vec = (char**)data;
+
+  int data_vec_len = atoi(data_vec[1]);
+  char* result_vec;
+
+  // TODO(ender): implement this; essentially: AT+CLCK
+  // See http://www.activexperts.com/sms-component/at/commands/?at=%2BCLCK
+  // and
+  // opt/telephony/src/java/com/android/internal/telephony/CommandsInterface.java
+  // opt/telephony/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+
+  ALOGV("Query Facility Lock Code: %s PIN2: %s Service(s): %s AID: %s",
+        data_vec[0], data_vec[1], data_vec[2], data_vec[3]);
+
+  // TODO(ender): there should be a bit vector of responses for each of the
+  // services requested.
+  // Depending on lock code, facilities may be unlocked or locked. We report
+  // these are all unlocked, regardless of the query.
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS,
+                                 const_cast<int*>(&kFacilityLockAllDisabled),
+                                 sizeof(kFacilityLockAllDisabled));
+}
+
+static void request_international_subscriber_id_number(RIL_Token t) {
+  // TODO(ender): Reuse MCC and MNC.
+  std::string subscriber_id = gCurrentNetworkOperator.c_str();
+  subscriber_id += "123456789";
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS,
+                                 strdup(subscriber_id.c_str()), sizeof(char*));
+}
+
+static bool gScreenIsOn = true;
+
+static void request_set_screen_state(void* data, size_t /*datalen*/,
+                                     RIL_Token t) {
+  gScreenIsOn = *(int*)data ? true : false;
+  ALOGV("Screen is %s", gScreenIsOn ? "on" : "off");
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+// Unsure which section this belongs in.
+
+static int gModemTtyMode = 1;  // 0 = off, 1 = full, 2 = HCO, 3 = VCO.
+static void request_set_tty_mode(void* data, size_t /*datalen*/, RIL_Token t) {
+  int new_tty_mode = *(int*)(data);
+  ALOGV("Switching modem TTY mode %d -> %d", gModemTtyMode, new_tty_mode);
+
+  if (new_tty_mode >= 0 && new_tty_mode <= 3) {
+    gModemTtyMode = new_tty_mode;
+    gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+  } else {
+    gce_ril_env->OnRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+  }
+}
+
+static void request_get_tty_mode(RIL_Token t) {
+  ALOGV("Querying TTY mode");
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &gModemTtyMode,
+                                 sizeof(gModemTtyMode));
+}
+
+static bool gImsRegistered = false;
+static int gImsFormat = RADIO_TECH_3GPP;
+
+static void request_ims_registration_state(RIL_Token t) {
+  ALOGV("Querying IMS mode");
+  int reply[2];
+  reply[0] = gImsRegistered;
+  reply[1] = gImsFormat;
+
+  ALOGV("Requesting IMS Registration state: %d, format=%d ", reply[0],
+        reply[1]);
+
+  gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, reply, sizeof(reply));
+}
+
+static void gce_ril_on_request(int request, void* data, size_t datalen,
+                               RIL_Token t) {
+  int err;
+
+  // Ignore all requests except RIL_REQUEST_GET_SIM_STATUS
+  // when RADIO_STATE_UNAVAILABLE.
+  if (gRadioPowerState == RADIO_STATE_UNAVAILABLE &&
+      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 (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;
+  }
+
+  ALOGV("Received request %d", request);
+
+  switch (request) {
+    case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS:
+      request_query_available_networks(data, datalen, t);
+      break;
+    case RIL_REQUEST_GET_IMEI:
+      request_get_imei(t);
+      break;
+    case RIL_REQUEST_GET_IMEISV:
+      request_get_imei_sv(t);
+      break;
+    case RIL_REQUEST_DEACTIVATE_DATA_CALL:
+      request_teardown_data_call(data, datalen, t);
+      break;
+    case RIL_REQUEST_SCREEN_STATE:
+      request_set_screen_state(data, datalen, t);
+      break;
+    case RIL_REQUEST_GET_SIM_STATUS:
+      getCardStatus(t);
+      break;
+    case RIL_REQUEST_GET_CURRENT_CALLS:
+      request_get_current_calls(data, datalen, t);
+      break;
+    case RIL_REQUEST_DIAL:
+      request_dial(data, datalen, t);
+      break;
+    case RIL_REQUEST_HANGUP:
+      request_hangup(data, datalen, t);
+      break;
+    case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
+      request_hangup_waiting(data, datalen, t);
+      break;
+    case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
+      request_hangup_current(t);
+      break;
+    case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
+      request_switch_current_and_waiting(t);
+      break;
+    case RIL_REQUEST_ANSWER:
+      request_answer_incoming(t);
+      break;
+    case RIL_REQUEST_SET_MUTE:
+      request_set_mute(data, datalen, t);
+      break;
+    case RIL_REQUEST_GET_MUTE:
+      request_get_mute(t);
+      break;
+    case RIL_REQUEST_CONFERENCE:
+      request_combine_multiparty_call(data, datalen, t);
+      break;
+    case RIL_REQUEST_SEPARATE_CONNECTION:
+      request_split_multiparty_call(data, datalen, t);
+      break;
+    case RIL_REQUEST_UDUB:
+      request_udub_on_incoming_calls(t);
+      break;
+    case RIL_REQUEST_SIGNAL_STRENGTH:
+      request_signal_strength(data, datalen, t);
+      break;
+    case RIL_REQUEST_VOICE_REGISTRATION_STATE:
+    case RIL_REQUEST_DATA_REGISTRATION_STATE:
+      request_registration_state(request, data, datalen, t);
+      break;
+    case RIL_REQUEST_OPERATOR:
+      request_operator(data, datalen, t);
+      break;
+    case RIL_REQUEST_RADIO_POWER:
+      request_radio_power(data, datalen, t);
+      break;
+    case RIL_REQUEST_DTMF:
+    case RIL_REQUEST_DTMF_START:
+      request_send_dtmf(data, datalen, t);
+      break;
+    case RIL_REQUEST_DTMF_STOP:
+      request_send_dtmf_stop(t);
+      break;
+    case RIL_REQUEST_SEND_SMS:
+      request_send_SMS(data, t);
+      break;
+    case RIL_REQUEST_CDMA_SEND_SMS:
+      request_cdma_send_SMS(data, t);
+      break;
+    case RIL_REQUEST_SETUP_DATA_CALL:
+      request_setup_data_call(data, datalen, t);
+      break;
+    case RIL_REQUEST_SMS_ACKNOWLEDGE:
+      request_SMS_acknowledge(data, datalen, t);
+      break;
+    case RIL_REQUEST_GET_IMSI:
+      request_international_subscriber_id_number(t);
+      break;
+    case RIL_REQUEST_QUERY_FACILITY_LOCK:
+      request_facility_lock(data, datalen, t);
+      break;
+    case RIL_REQUEST_SIM_IO:
+      request_SIM_IO(data, datalen, t);
+      break;
+    case RIL_REQUEST_SEND_USSD:
+      request_send_ussd(data, datalen, t);
+      break;
+    case RIL_REQUEST_CANCEL_USSD:
+      request_cancel_ussd(t);
+      break;
+    case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC:
+      request_set_automatic_network_selection(t);
+      break;
+    case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL:
+      request_set_manual_network_selection(data, datalen, t);
+      break;
+    case RIL_REQUEST_DATA_CALL_LIST:
+      request_data_calllist(data, datalen, t);
+      break;
+    case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE:
+      request_datacall_fail_cause(t);
+      break;
+    case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
+      request_query_network_selection_mode(data, datalen, t);
+      break;
+    case RIL_REQUEST_OEM_HOOK_RAW:
+    case RIL_REQUEST_OEM_HOOK_STRINGS:
+      ALOGV("OEM Hooks not supported!");
+      gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
+      break;
+    case RIL_REQUEST_WRITE_SMS_TO_SIM:
+      request_write_sms_to_sim(data, datalen, t);
+      break;
+    case RIL_REQUEST_DELETE_SMS_ON_SIM:
+      request_delete_sms_on_sim(data, datalen, t);
+      break;
+    case RIL_REQUEST_ENTER_SIM_PIN:
+    case RIL_REQUEST_ENTER_SIM_PUK:
+    case RIL_REQUEST_ENTER_SIM_PIN2:
+    case RIL_REQUEST_ENTER_SIM_PUK2:
+    case RIL_REQUEST_CHANGE_SIM_PIN:
+    case RIL_REQUEST_CHANGE_SIM_PIN2:
+      request_enter_sim_pin(data, datalen, t);
+      break;
+    case RIL_REQUEST_VOICE_RADIO_TECH: {
+      RIL_RadioTechnology tech = getBestVoiceTechnology(gModemCurrentType);
+      gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, &tech, sizeof(tech));
+      break;
+    }
+    case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE:
+      request_set_preferred_network_type(request, data, datalen, t);
+      break;
+    case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE:
+      request_get_preferred_network_type(request, data, datalen, t);
+      break;
+    case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS:
+      request_get_neighboring_cell_ids(data, datalen, t);
+      break;
+#if VSOC_PLATFORM_SDK_AFTER(J_MR1)
+    case RIL_REQUEST_GET_CELL_INFO_LIST:
+      request_get_cell_info_list(data, datalen, t);
+      break;
+    case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE:
+      request_set_cell_info_list_rate(data, datalen, t);
+      break;
+#endif
+    case RIL_REQUEST_BASEBAND_VERSION:
+      request_baseband_version(t);
+      break;
+    case RIL_REQUEST_SET_TTY_MODE:
+      request_set_tty_mode(data, datalen, t);
+      break;
+    case RIL_REQUEST_QUERY_TTY_MODE:
+      request_get_tty_mode(t);
+      break;
+
+#if VSOC_PLATFORM_SDK_AFTER(L)
+    case RIL_REQUEST_GET_RADIO_CAPABILITY:
+      request_get_radio_capability(t);
+      break;
+    case RIL_REQUEST_SET_RADIO_CAPABILITY:
+      request_set_radio_capability(data, datalen, t);
+      break;
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(K)
+    case RIL_REQUEST_GET_HARDWARE_CONFIG:
+      request_hardware_config(t);
+      break;
+    case RIL_REQUEST_IMS_REGISTRATION_STATE:
+      request_ims_registration_state(t);
+      break;
+    case RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL:
+      request_sim_apdu(data, datalen, t);
+      break;
+    case RIL_REQUEST_SIM_OPEN_CHANNEL:
+      request_sim_open_channel(data, datalen, t);
+      break;
+    case RIL_REQUEST_SIM_CLOSE_CHANNEL:
+      request_sim_close_channel(data, datalen, t);
+      break;
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2)
+    case RIL_REQUEST_IMS_SEND_SMS:
+      request_ims_send_SMS(data, datalen, t);
+      break;
+
+    case RIL_REQUEST_SET_INITIAL_ATTACH_APN:
+      ALOGW("INITIAL ATTACH APN");
+      gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+      break;
+
+#endif
+    case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING:
+      gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+      break;
+    case RIL_REQUEST_DEVICE_IDENTITY:
+      request_device_identity(request, data, datalen, t);
+      break;
+    case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE:
+      request_cdma_get_subscription_source(request, data, datalen, t);
+      break;
+    case RIL_REQUEST_CDMA_SUBSCRIPTION:
+      request_cdma_subscription(request, data, datalen, t);
+      break;
+    case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE:
+      request_cdma_set_subscription_source(request, data, datalen, t);
+      break;
+    case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE:
+      request_cdma_get_roaming_preference(request, data, datalen, t);
+      break;
+    case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE:
+      request_cdma_set_roaming_preference(request, data, datalen, t);
+      break;
+    case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE:
+      request_exit_emergency_mode(data, datalen, t);
+      break;
+    default:
+      ALOGE("Request %d not supported.", request);
+      gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
+      break;
+  }
+}
+
+#define GCE_RIL_VERSION 6
+
+static const RIL_RadioFunctions ril_callbacks = {
+    GCE_RIL_VERSION,     gce_ril_on_request, gce_ril_current_state,
+    gce_ril_on_supports, gce_ril_on_cancel,  gce_ril_get_version};
+
+extern "C" {
+
+const RIL_RadioFunctions* RIL_Init(const struct RIL_Env* env, int /*argc*/,
+                                   char** /*argv*/) {
+  time(&gce_ril_start_time);
+  gce_ril_env = env;
+
+  TearDownNetworkInterface();
+
+  init_modem_supported_network_types();
+  init_modem_technologies();
+  init_virtual_network();
+  init_sim_file_system();
+  init_sim_status();
+
+  return &ril_callbacks;
+}
+
+}  // extern "C"
diff --git a/guest/hals/ril/vsoc_ril.h b/guest/hals/ril/vsoc_ril.h
new file mode 100644
index 0000000..44000e7
--- /dev/null
+++ b/guest/hals/ril/vsoc_ril.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef GUEST_RIL_VSOC_RIL_H_
+#define GUEST_RIL_VSOC_RIL_H_
+
+#define RIL_SHLIB
+
+#define LOG_TAG "VSoCRil"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <cutils/log.h>
+#include <telephony/ril.h>
+#include <telephony/ril_cdma_sms.h>
+#include <sys/time.h>
+
+#endif  // GUEST_RIL_VSOC_RIL_H_
diff --git a/guest/hals/sensors/Android.mk b/guest/hals/sensors/Android.mk
new file mode 100644
index 0000000..ff2d4f1
--- /dev/null
+++ b/guest/hals/sensors/Android.mk
@@ -0,0 +1,59 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# HAL module implemenation stored in
+# hw/<SENSORS_HARDWARE_MODULE_ID>.<ro.hardware>.so
+include $(CLEAR_VARS)
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MULTILIB := first
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := \
+    $(VSOC_STLPORT_LIBS) \
+    libcuttlefish_fs \
+    cuttlefish_auto_resources \
+    liblog \
+    libcutils
+
+LOCAL_SRC_FILES := \
+    sensors.cpp \
+    sensors_hal.cpp \
+    vsoc_sensors.cpp \
+    vsoc_sensors_message.cpp
+
+LOCAL_CFLAGS := -DLOG_TAG=\"VSoC-Sensors\" \
+    $(VSOC_VERSION_CFLAGS) \
+    -Werror -Wall -Wno-missing-field-initializers -Wno-unused-parameter
+
+LOCAL_C_INCLUDES := \
+    $(VSOC_STLPORT_INCLUDES) \
+    device/google/cuttlefish_common \
+    system/extras
+
+LOCAL_STATIC_LIBRARIES := \
+    libcutils \
+    libcuttlefish_remoter_framework \
+    $(VSOC_STLPORT_STATIC_LIBS)
+
+LOCAL_MODULE := sensors.vsoc
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/sensors/sensors.cpp b/guest/hals/sensors/sensors.cpp
new file mode 100644
index 0000000..eb1dda6
--- /dev/null
+++ b/guest/hals/sensors/sensors.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/hals/sensors/vsoc_sensors.h"
+
+#include <limits>
+
+#include "guest/hals/sensors/sensors.h"
+
+namespace cvd {
+namespace {
+const cvd::time::Milliseconds kDefaultSamplingRate(200);
+
+#if !VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_3)
+namespace {
+const int SENSOR_FLAG_WAKE_UP = 0;
+}
+#endif
+
+timespec infinity() {
+  timespec ts;
+  ts.tv_sec = std::numeric_limits<time_t>::max();
+  ts.tv_nsec = 0;
+  return ts;
+}
+}  // namespace
+
+const cvd::time::MonotonicTimePoint SensorState::kInfinity =
+    cvd::time::MonotonicTimePoint(infinity());
+
+SensorState::SensorState(SensorInfo info)
+    : enabled_(false),
+      event_(),
+      deadline_(kInfinity),
+      sampling_period_(kDefaultSamplingRate) {
+  event_.sensor = info.handle;
+  event_.type = info.type;
+}
+
+SensorInfo::SensorInfo(const char* name, const char* vendor, int version,
+                       int handle, int type, float max_range, float resolution,
+                       float power, int32_t min_delay,
+                       uint32_t fifo_reserved_event_count,
+                       uint32_t fifo_max_event_count, const char* string_type,
+                       const char* required_permission, int32_t max_delay,
+                       uint32_t reporting_mode) {
+  this->name = name;
+  this->vendor = vendor;
+  this->version = version;
+  this->handle = handle;
+  this->type = type;
+  this->maxRange = max_range;
+  this->resolution = resolution;
+  this->power = power;
+  this->minDelay = min_delay;
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_1)
+  this->fifoReservedEventCount = fifo_reserved_event_count;
+  this->fifoMaxEventCount = fifo_max_event_count;
+#endif
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_2)
+  this->stringType = string_type;
+  this->requiredPermission = required_permission;
+#endif
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_3)
+  this->maxDelay = max_delay;
+  this->flags = reporting_mode;
+#endif
+}
+
+namespace sc = sensors_constants;
+
+SensorInfo AccelerometerSensor() {
+  uint32_t flags = sc::kAccelerometerReportingMode |
+      (sc::kAccelerometerIsWakeup ? SENSOR_FLAG_WAKE_UP : 0);
+
+  return SensorInfo(sc::kAccelerometerName, sc::kVendor, sc::kVersion,
+                    sc::kAccelerometerHandle, SENSOR_TYPE_ACCELEROMETER,
+                    sc::kAccelerometerMaxRange, sc::kAccelerometerResolution,
+                    sc::kAccelerometerPower, sc::kAccelerometerMinDelay,
+                    sc::kFifoReservedEventCount, sc::kFifoMaxEventCount,
+                    sc::kAccelerometerStringType, sc::kRequiredPermission,
+                    sc::kMaxDelay, flags);
+}
+
+SensorInfo GyroscopeSensor() {
+  uint32_t flags = sc::kGyroscopeReportingMode |
+      (sc::kGyroscopeIsWakeup ? SENSOR_FLAG_WAKE_UP : 0);
+
+  return SensorInfo(
+      sc::kGyroscopeName, sc::kVendor, sc::kVersion, sc::kGyroscopeHandle,
+      SENSOR_TYPE_GYROSCOPE, sc::kGyroscopeMaxRange, sc::kGyroscopeResolution,
+      sc::kGyroscopePower, sc::kGyroscopeMinDelay, sc::kFifoReservedEventCount,
+      sc::kFifoMaxEventCount, sc::kGyroscopeStringType, sc::kRequiredPermission,
+      sc::kMaxDelay, flags);
+}
+
+SensorInfo LightSensor() {
+  uint32_t flags = sc::kLightReportingMode |
+      (sc::kLightIsWakeup ? SENSOR_FLAG_WAKE_UP : 0);
+
+  return SensorInfo(sc::kLightName, sc::kVendor, sc::kVersion, sc::kLightHandle,
+                    SENSOR_TYPE_LIGHT, sc::kLightMaxRange, sc::kLightResolution,
+                    sc::kLightPower, sc::kLightMinDelay,
+                    sc::kFifoReservedEventCount, sc::kFifoMaxEventCount,
+                    sc::kLightStringType, sc::kRequiredPermission,
+                    sc::kMaxDelay, flags);
+}
+
+SensorInfo MagneticFieldSensor() {
+  uint32_t flags = sc::kMagneticFieldReportingMode |
+      (sc::kMagneticFieldIsWakeup ? SENSOR_FLAG_WAKE_UP : 0);
+
+  return SensorInfo(sc::kMagneticFieldName, sc::kVendor, sc::kVersion,
+                    sc::kMagneticFieldHandle, SENSOR_TYPE_MAGNETIC_FIELD,
+                    sc::kMagneticFieldMaxRange, sc::kMagneticFieldResolution,
+                    sc::kMagneticFieldPower, sc::kMagneticFieldMinDelay,
+                    sc::kFifoReservedEventCount, sc::kFifoMaxEventCount,
+                    sc::kMagneticFieldStringType, sc::kRequiredPermission,
+                    sc::kMaxDelay, flags);
+}
+
+SensorInfo PressureSensor() {
+  uint32_t flags = sc::kPressureReportingMode |
+      (sc::kPressureIsWakeup ? SENSOR_FLAG_WAKE_UP : 0);
+
+  return SensorInfo(
+      sc::kPressureName, sc::kVendor, sc::kVersion, sc::kPressureHandle,
+      SENSOR_TYPE_PRESSURE, sc::kPressureMaxRange, sc::kPressureResolution,
+      sc::kPressurePower, sc::kPressureMinDelay, sc::kFifoReservedEventCount,
+      sc::kFifoMaxEventCount, sc::kPressureStringType, sc::kRequiredPermission,
+      sc::kMaxDelay, flags);
+}
+
+SensorInfo ProximitySensor() {
+  uint32_t flags = sc::kProximityReportingMode |
+      (sc::kProximityIsWakeup ? SENSOR_FLAG_WAKE_UP : 0);
+
+  return SensorInfo(
+      sc::kProximityName, sc::kVendor, sc::kVersion, sc::kProximityHandle,
+      SENSOR_TYPE_PROXIMITY, sc::kProximityMaxRange, sc::kProximityResolution,
+      sc::kProximityPower, sc::kProximityMinDelay, sc::kFifoReservedEventCount,
+      sc::kFifoMaxEventCount, sc::kProximityStringType, sc::kRequiredPermission,
+      sc::kMaxDelay, flags);
+}
+
+SensorInfo AmbientTempSensor() {
+  uint32_t flags = sc::kAmbientTempReportingMode |
+      (sc::kAmbientTempIsWakeup ? SENSOR_FLAG_WAKE_UP : 0);
+
+  return SensorInfo(sc::kAmbientTempName, sc::kVendor, sc::kVersion,
+                    sc::kAmbientTempHandle, SENSOR_TYPE_AMBIENT_TEMPERATURE,
+                    sc::kAmbientTempMaxRange, sc::kAmbientTempResolution,
+                    sc::kAmbientTempPower, sc::kAmbientTempMinDelay,
+                    sc::kFifoReservedEventCount, sc::kFifoMaxEventCount,
+                    sc::kAmbientTempStringType, sc::kRequiredPermission,
+                    sc::kMaxDelay, flags);
+}
+
+SensorInfo DeviceTempSensor() {
+  uint32_t flags = sc::kDeviceTempReportingMode |
+      (sc::kDeviceTempIsWakeup ? SENSOR_FLAG_WAKE_UP : 0);
+
+  return SensorInfo(sc::kDeviceTempName, sc::kVendor, sc::kVersion,
+                    sc::kDeviceTempHandle, SENSOR_TYPE_TEMPERATURE,
+                    sc::kDeviceTempMaxRange, sc::kDeviceTempResolution,
+                    sc::kDeviceTempPower, sc::kDeviceTempMinDelay,
+                    sc::kFifoReservedEventCount, sc::kFifoMaxEventCount,
+                    sc::kDeviceTempStringType, sc::kRequiredPermission,
+                    sc::kMaxDelay, flags);
+}
+
+SensorInfo RelativeHumiditySensor() {
+  uint32_t flags = sc::kRelativeHumidityReportingMode |
+      (sc::kRelativeHumidityIsWakeup ? SENSOR_FLAG_WAKE_UP : 0);
+
+  return SensorInfo(sc::kRelativeHumidityName, sc::kVendor, sc::kVersion,
+                    sc::kRelativeHumidityHandle, SENSOR_TYPE_RELATIVE_HUMIDITY,
+                    sc::kRelativeHumidityMaxRange,
+                    sc::kRelativeHumidityResolution, sc::kRelativeHumidityPower,
+                    sc::kRelativeHumidityMinDelay, sc::kFifoReservedEventCount,
+                    sc::kFifoMaxEventCount, sc::kRelativeHumidityStringType,
+                    sc::kRequiredPermission, sc::kMaxDelay,
+                    flags);
+}
+
+}  // namespace cvd
diff --git a/guest/hals/sensors/sensors.h b/guest/hals/sensors/sensors.h
new file mode 100644
index 0000000..c4305ce
--- /dev/null
+++ b/guest/hals/sensors/sensors.h
@@ -0,0 +1,231 @@
+/*
+ * 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/libs/time/monotonic_time.h"
+#include "guest/hals/sensors/sensors_hal.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+namespace cvd {
+
+// Stores static information about a sensor.
+// Must be completely compatible with sensor_t (i.e. no additional
+// information or virtual functions)
+// so we can cast a list of SensorInfo to a list of sensor_t.
+class SensorInfo : public sensor_t {
+ public:
+  // Dummy, empty set of sensor information (value-initialized).
+  SensorInfo() : sensor_t() {}
+
+ private:
+  SensorInfo(const char* name, const char* vendor, int version, int handle,
+             int type, float max_range, float resolution, float power,
+             int32_t min_delay, uint32_t fifo_reserved_event_count,
+             uint32_t fifo_max_event_count, const char* string_type,
+             const char* required_permission, int32_t max_delay,
+             uint32_t reporting_mode);
+
+  friend SensorInfo AccelerometerSensor();
+  friend SensorInfo GyroscopeSensor();
+  friend SensorInfo LightSensor();
+  friend SensorInfo MagneticFieldSensor();
+  friend SensorInfo PressureSensor();
+  friend SensorInfo ProximitySensor();
+  friend SensorInfo AmbientTempSensor();
+  friend SensorInfo DeviceTempSensor();
+  friend SensorInfo RelativeHumiditySensor();
+};
+
+SensorInfo AccelerometerSensor();
+SensorInfo GyroscopeSensor();
+SensorInfo LightSensor();
+SensorInfo MagneticFieldSensor();
+SensorInfo PressureSensor();
+SensorInfo ProximitySensor();
+SensorInfo AmbientTempSensor();
+SensorInfo DeviceTempSensor();
+SensorInfo RelativeHumiditySensor();
+
+// Stores the current state of a sensor.
+class SensorState {
+ public:
+  SensorState(SensorInfo info);
+  virtual ~SensorState() {}
+
+  // What this sensor is activated or not.
+  bool enabled_;
+  // Buffer of incoming events.
+  sensors_event_t event_;
+  // The deadline at which we should report the next sensor event
+  // to the framework in order to meet our frequency constraints.
+  // For disabled sensors, should be 'infinity'.
+  cvd::time::MonotonicTimePoint deadline_;
+  // Delay time between consecutive sensor samples, in ns.
+  cvd::time::Nanoseconds sampling_period_;
+
+  // Time 'infinity'.
+  static const cvd::time::MonotonicTimePoint kInfinity;
+};
+
+namespace sensors_constants {
+// TODO: Verify these numbers.
+// Vendor of the hardware part.
+const char kVendor[] = "Google";
+// Version of the hardware part + driver. The value of this field
+// must increase when the driver is updated in a way that
+// changes the output of the sensor.
+const int kVersion = VSOC_SENSOR_DEVICE_VERSION;
+// Number of events reserved for this sensor in batch mode FIFO.
+// If it has its own FIFO, the size of that FIFO.
+const uint32_t kFifoReservedEventCount = 15;
+// Maximum events that can be batched. In a shared FIFO,
+// the size of that FIFO.
+const uint32_t kFifoMaxEventCount = 15;
+// Permission required to use this sensor, or empty string
+// if none required.
+const char kRequiredPermission[] = "";
+// Defined only for continuous mode and on-change sensors.
+// Delay corresponding with lowest frequency supported.
+const int32_t kMaxDelay = 5000000;
+
+// Name of this sensor. Must be unique.
+const char kAccelerometerName[] = "acceleration";
+const char kGyroscopeName[] = "gyroscope";
+const char kLightName[] = "light";
+const char kMagneticFieldName[] = "magnetic_field";
+const char kPressureName[] = "pressure";
+const char kProximityName[] = "proximity";
+const char kAmbientTempName[] = "ambient_temp";
+const char kDeviceTempName[] = "device_temp";
+const char kRelativeHumidityName[] = "relative_humidity";
+
+// Handle that identifies the sensor. This is used as an array index,
+// so must be unique in the range [0, # sensors)
+
+const int kAccelerometerHandle = 0;
+const int kGyroscopeHandle = 1;
+const int kLightHandle = 2;
+const int kMagneticFieldHandle = 3;
+const int kPressureHandle = 4;
+const int kProximityHandle = 5;
+const int kAmbientTempHandle = 6;
+const int kDeviceTempHandle = 7;
+const int kRelativeHumidityHandle = 8;
+
+// For continuous sensors, minimum sample period (in microseconds).
+// On-Change (0), One-shot (-1), and special (0).
+const int32_t kAccelerometerMinDelay = 4444;
+const int32_t kGyroscopeMinDelay = 4444;
+const int32_t kLightMinDelay = 0;
+const int32_t kMagneticFieldMinDelay = 14285;
+const int32_t kPressureMinDelay = 28571;
+const int32_t kProximityMinDelay = 0;
+const int32_t kAmbientTempMinDelay = 4444;
+const int32_t kDeviceTempMinDelay = 4444;
+const int32_t kRelativeHumidityMinDelay = 4444;
+
+// Maximum range of this sensor's value in SI units.
+const float kAccelerometerMaxRange = 39.226593f;
+const float kGyroscopeMaxRange = 8.726639f;
+const float kLightMaxRange = 10000.0f;
+const float kMagneticFieldMaxRange = 4911.9995f;
+const float kPressureMaxRange = 1100.0f;
+const float kProximityMaxRange = 5.0f;
+const float kAmbientTempMaxRange = 80.0f;
+const float kDeviceTempMaxRange = 80.0f;
+const float kRelativeHumidityMaxRange = 100;
+
+// Smallest difference between two values reported by this sensor.
+const float kAccelerometerResolution = 0.45f;
+const float kGyroscopeResolution = 10.0f;
+const float kLightResolution = 10.0f;
+const float kMagneticFieldResolution = 1.0f;
+const float kPressureResolution = 1.0f;
+const float kProximityResolution = 1.0f;
+const float kAmbientTempResolution = 1.0f;
+const float kDeviceTempResolution = 1.0f;
+const float kRelativeHumidityResolution = 1.0f;
+
+// Rough estimate of this sensor's power consumption in mA.
+const float kAccelerometerPower = 0.45f;
+const float kGyroscopePower = 3.6f;
+const float kLightPower = 0.175f;
+const float kMagneticFieldPower = 5.0f;
+const float kPressurePower = 0.004f;
+const float kProximityPower = 12.675f;
+const float kAmbientTempPower = 1.0f;
+const float kDeviceTempPower = 1.0f;
+const float kRelativeHumidityPower = 1.0f;
+
+// Type of this sensor, represented as a string.
+
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_2)
+const char kAccelerometerStringType[] = SENSOR_STRING_TYPE_ACCELEROMETER;
+const char kGyroscopeStringType[] = SENSOR_STRING_TYPE_GYROSCOPE;
+const char kLightStringType[] = SENSOR_STRING_TYPE_LIGHT;
+const char kMagneticFieldStringType[] = SENSOR_STRING_TYPE_MAGNETIC_FIELD;
+const char kPressureStringType[] = SENSOR_STRING_TYPE_PRESSURE;
+const char kProximityStringType[] = SENSOR_STRING_TYPE_PROXIMITY;
+const char kAmbientTempStringType[] = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE;
+const char kDeviceTempStringType[] = SENSOR_STRING_TYPE_TEMPERATURE;
+const char kRelativeHumidityStringType[] = SENSOR_STRING_TYPE_RELATIVE_HUMIDITY;
+#else
+const char kAccelerometerStringType[] = "";
+const char kGyroscopeStringType[] = "";
+const char kLightStringType[] = "";
+const char kMagneticFieldStringType[] = "";
+const char kPressureStringType[] = "";
+const char kProximityStringType[] = "";
+const char kAmbientTempStringType[] = "";
+const char kDeviceTempStringType[] = "";
+const char kRelativeHumidityStringType[] = "";
+#endif
+
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_3)
+const uint32_t kAccelerometerReportingMode = SENSOR_FLAG_CONTINUOUS_MODE;
+const uint32_t kGyroscopeReportingMode = SENSOR_FLAG_CONTINUOUS_MODE;
+const uint32_t kLightReportingMode = SENSOR_FLAG_ON_CHANGE_MODE;
+const uint32_t kMagneticFieldReportingMode = SENSOR_FLAG_CONTINUOUS_MODE;
+const uint32_t kPressureReportingMode = SENSOR_FLAG_CONTINUOUS_MODE;
+const uint32_t kProximityReportingMode = SENSOR_FLAG_ON_CHANGE_MODE;
+const uint32_t kAmbientTempReportingMode = SENSOR_FLAG_ON_CHANGE_MODE;
+const uint32_t kDeviceTempReportingMode = SENSOR_FLAG_ON_CHANGE_MODE;
+const uint32_t kRelativeHumidityReportingMode = SENSOR_FLAG_ON_CHANGE_MODE;
+#else
+const uint32_t kAccelerometerReportingMode = 0;
+const uint32_t kGyroscopeReportingMode = 0;
+const uint32_t kLightReportingMode = 0;
+const uint32_t kMagneticFieldReportingMode = 0;
+const uint32_t kPressureReportingMode = 0;
+const uint32_t kProximityReportingMode = 0;
+const uint32_t kAmbientTempReportingMode = 0;
+const uint32_t kDeviceTempReportingMode = 0;
+const uint32_t kRelativeHumidityReportingMode = 0;
+#endif
+
+const bool kAccelerometerIsWakeup = false;
+const bool kGyroscopeIsWakeup = false;
+const bool kLightIsWakeup = false;
+const bool kMagneticFieldIsWakeup = false;
+const bool kPressureIsWakeup = false;
+const bool kProximityIsWakeup = true;
+const bool kAmbientTempIsWakeup = false;
+const bool kDeviceTempIsWakeup = false;
+const bool kRelativeHumidityIsWakeup = false;
+
+}  // namespace sensors_constants
+}  // namespace cvd
+
diff --git a/guest/hals/sensors/sensors_hal.cpp b/guest/hals/sensors/sensors_hal.cpp
new file mode 100644
index 0000000..4761d3d
--- /dev/null
+++ b/guest/hals/sensors/sensors_hal.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/hals/sensors/sensors_hal.h"
+
+#include "guest/hals/sensors/vsoc_sensors.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+static hw_module_methods_t hal_module_methods = {
+  VSOC_STATIC_INITIALIZER(open) cvd::GceSensors::Open,
+};
+
+sensors_module_t HAL_MODULE_INFO_SYM = {
+  VSOC_STATIC_INITIALIZER(common){
+    VSOC_STATIC_INITIALIZER(tag) HARDWARE_MODULE_TAG,
+    VSOC_STATIC_INITIALIZER(module_api_version) 1,
+    VSOC_STATIC_INITIALIZER(hal_api_version) 0,
+    VSOC_STATIC_INITIALIZER(id) SENSORS_HARDWARE_MODULE_ID,
+    VSOC_STATIC_INITIALIZER(name) "Android-GCE SENSORS Module",
+    VSOC_STATIC_INITIALIZER(author) "Google",
+    VSOC_STATIC_INITIALIZER(methods) & hal_module_methods,
+  },
+  VSOC_STATIC_INITIALIZER(get_sensors_list) cvd::GceSensors::GetSensorsList,
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_4)
+  VSOC_STATIC_INITIALIZER(set_operation_mode) cvd::GceSensors::SetOperationMode,
+#endif
+};
diff --git a/guest/hals/sensors/sensors_hal.h b/guest/hals/sensors/sensors_hal.h
new file mode 100644
index 0000000..4e83790
--- /dev/null
+++ b/guest/hals/sensors/sensors_hal.h
@@ -0,0 +1,45 @@
+/*
+ * 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 <cutils/log.h>
+#include <hardware/hardware.h>
+#include <hardware/sensors.h>
+
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+#if VSOC_PLATFORM_SDK_BEFORE(K)
+#define VSOC_SENSOR_DEVICE_VERSION HARDWARE_MAKE_API_VERSION(0, 1)
+#elif VSOC_PLATFORM_SDK_BEFORE(L)
+#define VSOC_SENSOR_DEVICE_VERSION SENSORS_DEVICE_API_VERSION_1_1
+#elif VSOC_PLATFORM_SDK_BEFORE(M)
+#define VSOC_SENSOR_DEVICE_VERSION SENSORS_DEVICE_API_VERSION_1_3
+#else
+#define VSOC_SENSOR_DEVICE_VERSION SENSORS_DEVICE_API_VERSION_1_4
+#endif
+
+#define VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(X) \
+    (defined (SENSORS_DEVICE_API_VERSION_##X) && \
+     (VSOC_SENSOR_DEVICE_VERSION >= SENSORS_DEVICE_API_VERSION_##X))
+
+#define SENSORS_DEBUG 0
+
+#if SENSORS_DEBUG
+#  define D(...) ALOGD(__VA_ARGS__)
+#else
+#  define D(...) ((void)0)
+#endif
+
diff --git a/guest/hals/sensors/vsoc_sensors.cpp b/guest/hals/sensors/vsoc_sensors.cpp
new file mode 100644
index 0000000..86809a0
--- /dev/null
+++ b/guest/hals/sensors/vsoc_sensors.cpp
@@ -0,0 +1,527 @@
+/*
+ * 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 <cstdint>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/system_properties.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "common/libs/fs/shared_select.h"
+#include "common/libs/threads/thunkers.h"
+#include "guest/hals/sensors/sensors_hal.h"
+#include "guest/hals/sensors/vsoc_sensors.h"
+#include "guest/hals/sensors/vsoc_sensors_message.h"
+#include "guest/libs/platform_support/api_level_fixes.h"
+#include "guest/libs/remoter/remoter_framework_pkt.h"
+
+using cvd::LockGuard;
+using cvd::Mutex;
+using cvd::time::Milliseconds;
+using cvd::time::MonotonicTimePoint;
+using cvd::time::Nanoseconds;
+
+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;
+const Nanoseconds GceSensors::kInjectedEventWaitTime =
+    Nanoseconds(Milliseconds(20));
+
+GceSensors::GceSensors()
+  : sensors_poll_device_1(), deadline_change_(&sensor_state_lock_) {
+  if (total_sensor_count_ == -1) {
+    RegisterSensors();
+  }
+
+  // Create a pair of FDs that would be used to control the
+  // receiver thread.
+  if (control_sender_socket_->IsOpen() || control_receiver_socket_->IsOpen()) {
+    ALOGE("%s: Receiver control FDs are opened", __FUNCTION__);
+  }
+  if (!cvd::SharedFD::Pipe(&control_receiver_socket_,
+                           &control_sender_socket_)) {
+    ALOGE("%s: Unable to create thread control FDs: %d -> %s", __FUNCTION__,
+          errno, strerror(errno));
+  }
+
+  // Create the correct number of holding buffers for this client.
+  sensor_states_.resize(total_sensor_count_);
+  int i;
+  for (i = 0; i < total_sensor_count_; i++) {
+    sensor_states_[i] = new SensorState(sensor_infos_[i]);
+  }
+}
+
+GceSensors::~GceSensors() {
+  int i;
+  for (i = 0; i < total_sensor_count_; i++) {
+    delete sensor_states_[i];
+  }
+}
+
+int GceSensors::GetSensorsList(struct sensors_module_t* /*module*/,
+                               struct sensor_t const** list) {
+  *list = sensor_infos_;
+  return total_sensor_count_;
+}
+
+int GceSensors::SetOperationMode(unsigned int /* is_loopback_mode */) {
+  return -EINVAL;
+}
+
+int GceSensors::Open(const struct hw_module_t* module, const char* name,
+                     struct hw_device_t** device) {
+  int status = -EINVAL;
+
+  if (!strcmp(name, SENSORS_HARDWARE_POLL)) {
+    // Create a new GceSensors object and set all the fields/functions
+    // to their default values.
+    GceSensors* rval = new GceSensors;
+
+    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>;
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_0)
+    rval->batch = SensorsThunker1<int(int, int, int64_t,
+                                      int64_t)>::call<&GceSensors::Batch>;
+#endif
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_1)
+    rval->flush = SensorsThunker1<int(int)>::call<&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>;
+#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);
+    if (err) {
+      ALOGE("GceSensors::%s: Unable to start receiver thread (%s)",
+            __FUNCTION__, strerror(err));
+    }
+
+    *device = &rval->common;
+    status = 0;
+  }
+  return status;
+}
+
+int GceSensors::Close() {
+  // Make certain the receiver thread wakes up.
+  SensorControlMessage msg;
+  msg.message_type = THREAD_STOP;
+  SendControlMessage(msg);
+  pthread_join(receiver_thread_, NULL);
+  delete this;
+  return 0;
+}
+
+int GceSensors::Activate(int handle, int enabled) {
+  if (handle < 0 || handle >= total_sensor_count_) {
+    ALOGE("GceSensors::%s: Bad handle %d", __FUNCTION__, handle);
+    return -1;
+  }
+
+  {
+    LockGuard<Mutex> guard(sensor_state_lock_);
+    // Update the report deadline, if changed.
+    if (enabled && !sensor_states_[handle]->enabled_) {
+      sensor_states_[handle]->deadline_ =
+          MonotonicTimePoint::Now() + sensor_states_[handle]->sampling_period_;
+    } else if (!enabled && sensor_states_[handle]->enabled_) {
+      sensor_states_[handle]->deadline_ = SensorState::kInfinity;
+    }
+    sensor_states_[handle]->enabled_ = enabled;
+    UpdateDeadline();
+  }
+
+  D("sensor_activate(): handle %d, enabled %d", handle, enabled);
+  if (!UpdateRemoterState(handle)) {
+    ALOGE("Failed to notify remoter about new sensor enable/disable.");
+  }
+  return 0;
+}
+
+int GceSensors::SetDelay(int handle, int64_t sampling_period_ns) {
+  if (handle < 0 || handle >= total_sensor_count_) {
+    ALOGE("GceSensors::%s: Bad handle %d", __FUNCTION__, handle);
+    return -1;
+  }
+  int64_t min_delay_ns = sensor_infos_[handle].minDelay * 1000;
+  if (sampling_period_ns < min_delay_ns) {
+    sampling_period_ns = min_delay_ns;
+  }
+
+  {
+    LockGuard<Mutex> guard(sensor_state_lock_);
+    sensor_states_[handle]->deadline_ -=
+        sensor_states_[handle]->sampling_period_;
+    sensor_states_[handle]->sampling_period_ = Nanoseconds(sampling_period_ns);
+    sensor_states_[handle]->deadline_ +=
+        sensor_states_[handle]->sampling_period_;
+    // If our sampling period has decreased, our deadline
+    // could have already passed. If so, report immediately, but not in the
+    // past.
+    MonotonicTimePoint now = MonotonicTimePoint::Now();
+    if (sensor_states_[handle]->deadline_ < now) {
+      sensor_states_[handle]->deadline_ = now;
+    }
+    UpdateDeadline();
+  }
+
+  D("sensor_set_delay(): handle %d, delay (ms) %" PRId64, handle,
+    Milliseconds(Nanoseconds(sampling_period_ns)).count());
+  if (!UpdateRemoterState(handle)) {
+    ALOGE("Failed to notify remoter about new sensor delay.");
+  }
+  return 0;
+}
+
+int GceSensors::Poll(sensors_event_t* data, int count_unsafe) {
+  if (count_unsafe <= 0) {
+    ALOGE("Framework polled with bad count (%d)", count_unsafe);
+    return -1;
+  }
+  size_t count = size_t(count_unsafe);
+
+  // Poll will block until 1 of 2 things happens:
+  //    1. The next deadline for some active sensor
+  //        occurs.
+  //    2. The next deadline changes (either because
+  //        a sensor was activated/deactivated or its
+  //        delay changed).
+  // In both cases, any sensors whose report deadlines
+  // have passed will report their data (or mock data),
+  // and poll will either return (if at least one deadline
+  // has passed), or repeat by blocking until the next deadline.
+  LockGuard<Mutex> guard(sensor_state_lock_);
+  current_deadline_ = UpdateDeadline();
+  // Sleep until we have something to report
+  while (!fifo_.size()) {
+    deadline_change_.WaitUntil(current_deadline_);
+    current_deadline_ = UpdateDeadline();
+  }
+  // Copy the events from the buffer
+  int num_copied = std::min(fifo_.size(), count);
+  FifoType::iterator first_uncopied = fifo_.begin() + num_copied;
+  std::copy(fifo_.begin(), first_uncopied, data);
+  fifo_.erase(fifo_.begin(), first_uncopied);
+  D("Reported %d sensor events. First: %d %f %f %f", num_copied, data->sensor,
+    data->data[0], data->data[1], data->data[2]);
+  return num_copied;
+}
+
+
+void *GceSensors::Receiver() {
+  // Initialize the server.
+  sensor_listener_socket_ = cvd::SharedFD::SocketSeqPacketServer(
+      gce_sensors_message::kSensorsHALSocketName, 0777);
+  if (!sensor_listener_socket_->IsOpen()) {
+    ALOGE("GceSensors::%s: Could not listen for sensor connections. (%s).",
+          __FUNCTION__, sensor_listener_socket_->StrError());
+    return NULL;
+  }
+  D("GceSensors::%s: Listening for sensor connections at %s", __FUNCTION__,
+    gce_sensors_message::kSensorsHALSocketName);
+  // Announce that we are ready for the remoter to connect.
+  if (!NotifyRemoter()) {
+    ALOGI("Failed to notify remoter that HAL is ready.");
+  } else {
+    ALOGI("Notified remoter that HAL is ready.");
+  }
+
+  typedef std::vector<cvd::SharedFD> FDVec;
+  FDVec connected;
+  // Listen for incoming sensor data and control messages
+  // from the HAL.
+  while (true) {
+    cvd::SharedFDSet fds;
+    for (FDVec::iterator it = connected.begin(); it != connected.end(); ++it) {
+      fds.Set(*it);
+    }
+    fds.Set(control_receiver_socket_);
+    // fds.Set(sensor_listener_socket_);
+    int res = cvd::Select(&fds, NULL, NULL, NULL);
+    if (res == -1) {
+      ALOGE("%s: select returned %d and failed %d -> %s", __FUNCTION__, res,
+            errno, strerror(errno));
+      break;
+    } else if (res == 0) {
+      ALOGE("%s: select timed out", __FUNCTION__);
+      break;
+    } else if (fds.IsSet(sensor_listener_socket_)) {
+      connected.push_back(cvd::SharedFD::Accept(*sensor_listener_socket_));
+      ALOGI("GceSensors::%s: new client connected", __FUNCTION__);
+    } else if (fds.IsSet(control_receiver_socket_)) {
+      // We received a control message.
+      SensorControlMessage msg;
+      int res =
+          control_receiver_socket_->Read(&msg, sizeof(SensorControlMessage));
+      if (res == -1) {
+        ALOGE("GceSensors::%s: Failed to receive control message.",
+              __FUNCTION__);
+      } else if (res == 0) {
+        ALOGE("GceSensors::%s: Control connection closed.", __FUNCTION__);
+      }
+      if (msg.message_type == SENSOR_STATE_UPDATE) {
+        // Forward the update to the remoter.
+        remoter_request_packet packet;
+        remoter_request_packet_init(&packet, kRemoterSensorState, 0);
+        {
+          LockGuard<Mutex> guard(sensor_state_lock_);
+          packet.params.sensor_state_params.type =
+              sensor_infos_[msg.sensor_handle].type;
+          packet.params.sensor_state_params.enabled =
+              sensor_states_[msg.sensor_handle]->enabled_;
+          packet.params.sensor_state_params.delay_ns =
+              sensor_states_[msg.sensor_handle]->sampling_period_.count();
+          packet.params.sensor_state_params.handle = msg.sensor_handle;
+        }
+        struct msghdr msg;
+        iovec msg_iov[1];
+        msg_iov[0].iov_base = &packet;
+        msg_iov[0].iov_len = sizeof(remoter_request_packet);
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_iov = msg_iov;
+        msg.msg_iovlen = arraysize(msg_iov);
+        msg.msg_control = NULL;
+        msg.msg_controllen = 0;
+        msg.msg_flags = 0;
+
+        for (FDVec::iterator it = connected.begin(); it != connected.end();
+             ++it) {
+          cvd::SharedFD &fd = *it;
+          if (fd->SendMsg(&msg, 0) == -1) {
+            ALOGE("GceSensors::%s. Could not send sensor state (%s).",
+                  __FUNCTION__, fd->StrError());
+          }
+        }
+      }
+      if (msg.message_type == THREAD_STOP) {
+        D("Received terminate control message.");
+        return NULL;
+      }
+    } else {
+      for (FDVec::iterator it = connected.begin(); it != connected.end();
+           ++it) {
+        cvd::SharedFD &fd = *it;
+        if (fds.IsSet(fd)) {
+          // We received a sensor update from remoter.
+          sensors_event_t event;
+          struct msghdr msg;
+          iovec msg_iov[1];
+          msg_iov[0].iov_base = &event;
+          msg_iov[0].iov_len = sizeof(event);
+          msg.msg_name = NULL;
+          msg.msg_namelen = 0;
+          msg.msg_iov = msg_iov;
+          msg.msg_iovlen = arraysize(msg_iov);
+          msg.msg_control = NULL;
+          msg.msg_controllen = 0;
+          msg.msg_flags = 0;
+          int res = fd->RecvMsg(&msg, 0);
+          if (res <= 0) {
+            if (res == 0) {
+              ALOGE("GceSensors::%s: Sensors HAL connection closed.",
+                    __FUNCTION__);
+            } else {
+              ALOGE("GceSensors::%s: Failed to receive sensor message",
+                    __FUNCTION__);
+            }
+            connected.erase(std::find(connected.begin(), connected.end(), fd));
+            break;
+          }
+
+          // We received an event from the remoter.
+          if (event.sensor < 0 || event.sensor >= total_sensor_count_) {
+            ALOGE("Remoter sent us an invalid sensor event! (handle %d)",
+                  event.sensor);
+            connected.erase(std::find(connected.begin(), connected.end(), fd));
+            break;
+          }
+
+          D("Received sensor event: %d %f %f %f", event.sensor, event.data[0],
+            event.data[1], event.data[2]);
+
+          {
+            LockGuard<Mutex> guard(sensor_state_lock_);
+            // Increase the delay so that the HAL knows
+            // it shouldn't report on its own for a while.
+            SensorState *holding_buffer = sensor_states_[event.sensor];
+            int wait_periods =
+                std::max(kInjectedEventWaitPeriods,
+                         (int)(kInjectedEventWaitTime.count() /
+                               holding_buffer->sampling_period_.count()));
+            holding_buffer->deadline_ =
+                MonotonicTimePoint::Now() +
+                holding_buffer->sampling_period_ * wait_periods;
+            holding_buffer->event_.data[0] = event.data[0];
+            holding_buffer->event_.data[1] = event.data[1];
+            holding_buffer->event_.data[2] = event.data[2];
+            // Signal the HAL to report the newly arrived event.
+            fifo_.push_back(event);
+            deadline_change_.NotifyOne();
+          }
+        }
+      }
+    }
+  }
+  return NULL;
+}
+
+bool GceSensors::NotifyRemoter() {
+  remoter_request_packet packet;
+  remoter_request_packet_init(&packet, kRemoterHALReady, 0);
+  packet.send_response = 0;
+  strncpy(packet.params.hal_ready_params.unix_socket,
+          gce_sensors_message::kSensorsHALSocketName,
+          sizeof(packet.params.hal_ready_params.unix_socket));
+  AutoCloseFileDescriptor remoter_socket(remoter_connect());
+  if (remoter_socket.IsError()) {
+    D("GceSensors::%s: Could not connect to remoter to notify ready (%s).",
+      __FUNCTION__, strerror(errno));
+    return false;
+  }
+  int err =
+      remoter_do_single_request_with_socket(remoter_socket, &packet, NULL);
+  if (err == -1) {
+    D("GceSensors::%s: Notify remoter ready: Failed after connect (%s).",
+      __FUNCTION__, strerror(errno));
+    return false;
+  }
+  D("GceSensors::%s: Notify remoter ready Succeeded.", __FUNCTION__);
+  return true;
+}
+
+static bool CompareTimestamps(const sensors_event_t& a,
+                              const sensors_event_t& b) {
+  return a.timestamp < b.timestamp;
+}
+
+MonotonicTimePoint GceSensors::UpdateDeadline() {
+  // Get the minimum of all the current deadlines.
+  MonotonicTimePoint now = MonotonicTimePoint::Now();
+  MonotonicTimePoint min = SensorState::kInfinity;
+  int i = 0;
+  bool sort_fifo = false;
+
+  for (i = 0; i < total_sensor_count_; i++) {
+    SensorState* holding_buffer = sensor_states_[i];
+    // Ignore disabled sensors.
+    if (!holding_buffer->enabled_) {
+      continue;
+    }
+    while (holding_buffer->deadline_ < now) {
+      sensors_event_t data = holding_buffer->event_;
+      data.timestamp = holding_buffer->deadline_.SinceEpoch().count();
+      fifo_.push_back(data);
+      holding_buffer->deadline_ += holding_buffer->sampling_period_;
+      sort_fifo = true;
+    }
+    // Now check if we should update the wake time based on the next event
+    // from this sensor.
+    if (sensor_states_[i]->deadline_ < min) {
+      min = sensor_states_[i]->deadline_;
+    }
+  }
+  // We added one or more sensor readings, so do a sort.
+  // This is likely to be cheaper than a traditional priority queue because
+  // a priority queue would try to keep its state correct for each addition.
+  if (sort_fifo) {
+    std::sort(fifo_.begin(), fifo_.end(), CompareTimestamps);
+  }
+  // If we added events or the deadline is lower notify the thread in Poll().
+  // If the deadline went up, don't do anything.
+  if (fifo_.size() || (min < current_deadline_)) {
+    deadline_change_.NotifyOne();
+  }
+  return min;
+}
+
+bool GceSensors::UpdateRemoterState(int handle) {
+  SensorControlMessage msg;
+  msg.message_type = SENSOR_STATE_UPDATE;
+  msg.sensor_handle = handle;
+  return SendControlMessage(msg);
+}
+
+bool GceSensors::SendControlMessage(SensorControlMessage msg) {
+  if (!control_sender_socket_->IsOpen()) {
+    ALOGE("%s: Can't send control message %d, control socket not open.",
+          __FUNCTION__, msg.message_type);
+    return false;
+  }
+  if (control_sender_socket_->Write(&msg, sizeof(SensorControlMessage)) == -1) {
+    ALOGE("GceSensors::%s. Could not send control message %d (%s).",
+          __FUNCTION__, msg.message_type, control_sender_socket_->StrError());
+    return false;
+  }
+  return true;
+}
+
+int GceSensors::RegisterSensors() {
+  if (total_sensor_count_ != -1) {
+    return -1;
+  }
+  total_sensor_count_ = 9;
+  sensor_infos_ = new SensorInfo[total_sensor_count_];
+  sensor_infos_[sensors_constants::kAccelerometerHandle] =
+      AccelerometerSensor();
+  sensor_infos_[sensors_constants::kGyroscopeHandle] = GyroscopeSensor();
+  sensor_infos_[sensors_constants::kLightHandle] = LightSensor();
+  sensor_infos_[sensors_constants::kMagneticFieldHandle] =
+      MagneticFieldSensor();
+  sensor_infos_[sensors_constants::kPressureHandle] = PressureSensor();
+  sensor_infos_[sensors_constants::kProximityHandle] = ProximitySensor();
+  sensor_infos_[sensors_constants::kAmbientTempHandle] = AmbientTempSensor();
+  sensor_infos_[sensors_constants::kDeviceTempHandle] = DeviceTempSensor();
+  sensor_infos_[sensors_constants::kRelativeHumidityHandle] =
+      RelativeHumiditySensor();
+  int i;
+  for (i = 0; i < total_sensor_count_; i++) {
+    D("Found sensor %s with handle %d", sensor_infos_[i].name,
+      sensor_infos_[i].handle);
+  }
+  return total_sensor_count_;
+}
+
+}  // namespace cvd
diff --git a/guest/hals/sensors/vsoc_sensors.h b/guest/hals/sensors/vsoc_sensors.h
new file mode 100644
index 0000000..dec7102
--- /dev/null
+++ b/guest/hals/sensors/vsoc_sensors.h
@@ -0,0 +1,241 @@
+/*
+ * 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 <vector>
+
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "common/libs/fs/shared_fd.h"
+#include "guest/hals/sensors/sensors.h"
+#include "guest/hals/sensors/sensors_hal.h"
+
+namespace cvd {
+
+// Used for sending control messages to the receiver thread.
+// The sensor_handle field may be left unused if it is not needed.
+enum ControlMessageType {
+  THREAD_STOP,
+  SENSOR_STATE_UPDATE
+};
+typedef struct {
+  ControlMessageType message_type;
+  uint8_t sensor_handle;
+} SensorControlMessage;
+
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_0)
+// Last updated to HAL 1.4
+// Version history:
+//   Before jb, jb-mr1 SENSORS_DEVICE_API_VERSION_0_1 (no version in sensors.h)
+//   jb-mr2: SENSORS_DEVICE_API_VERSION_1_0
+//   k: SENSORS_DEVICE_API_VERSION_1_1
+//   l, l-mr1: SENSORS_DEVICE_API_VERSION_1_3
+//   m, n, n-mr1: SENSORS_DEVICE_API_VERSION_1_4
+#else
+// Pre-1.0 sensors do not define the sensors_poll_device_1 type.
+typedef sensors_poll_device_t sensors_poll_device_1;
+#endif
+
+class GceSensors : public sensors_poll_device_1 {
+ public:
+  GceSensors();
+  ~GceSensors();
+
+  /**
+   ** SENSOR HAL API FUNCTIONS FOR MODULE
+   **/
+
+  // Gets a list of all supported sensors and stores in list.
+  // Returns the number of supported sensors.
+  static int GetSensorsList(struct sensors_module_t* module,
+    struct sensor_t const** list);
+
+  // Place the module in a specific mode. The following modes are defined
+  //
+  //  0 - Normal operation. Default state of the module.
+  //  1 - Loopback mode. Data is injected for the supported
+  //      sensors by the sensor service in this mode.
+  // @return 0 on success
+  //         -EINVAL if requested mode is not supported
+  //         -EPERM if operation is not allowed
+  static int SetOperationMode(unsigned int mode);
+
+
+  /**
+   ** SENSOR HAL API FUNCTIONS FOR DEVICE
+   **/
+  // Opens the device.
+  static int Open(const struct hw_module_t* module, const char* name,
+    struct hw_device_t** device);
+
+  // Closes the device, closing all sensors.
+  int Close();
+
+  // Activate (or deactivate) the sensor with the given handle.
+  //
+  // One-shot sensors deactivate themselves automatically upon receiving an
+  // event, and they must still accept to be deactivated through a call to
+  // activate(..., enabled=0).
+  // Non-wake-up sensors never prevent the SoC from going into suspend mode;
+  // that is, the HAL shall not hold a partial wake-lock on behalf of
+  // applications.
+  //
+  // If enabled is 1 and the sensor is already activated, this function is a
+  // no-op and succeeds.
+  //
+  // If enabled is 0 and the sensor is already deactivated, this function is a
+  // no-op and succeeds.
+  //
+  // This function returns 0 on success and a negative error number otherwise.
+  int Activate(int handle, int enabled);
+
+  // Sets the delay (in ns) for the sensor with the given handle.
+  // Deprecated as of HAL 1.1
+  // Called after activate()
+  int SetDelay(int handle, int64_t sampling_period_ns);
+
+  // Returns an array of sensor data by filling the data argument.
+  // This function must block until events are available. It will return
+  // the number of events read on success, or a negative number in case of
+  // an error.
+  int Poll(sensors_event_t* data, int count);
+
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_0)
+  // Sets a sensor’s parameters, including sampling frequency and maximum
+  // report latency. This function can be called while the sensor is
+  // activated, in which case it must not cause any sensor measurements to
+  // be lost: transitioning from one sampling rate to the other cannot cause
+  // lost events, nor can transitioning from a high maximum report latency to
+  // a low maximum report latency.
+  //
+  // Before SENSORS_DEVICE_API_VERSION_1_3, flags included:
+  //   SENSORS_BATCH_DRY_RUN
+  //   SENSORS_BATCH_WAKE_UPON_FIFO_FULL
+  //
+  // After SENSORS_DEVICE_API_VERSION_1_3 see WAKE_UPON_FIFO_FULL
+  // in sensor_t.flags
+  int Batch(int sensor_handle, int flags, int64_t sampling_period_ns,
+            int64_t max_report_latency_ns) {
+    // TODO: Add support for maximum report latency with max_report_latency_ns.
+    return SetDelay(sensor_handle, sampling_period_ns);
+  }
+#endif
+
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_1)
+  // Adds a META_DATA_FLUSH_COMPLETE event (sensors_event_meta_data_t)
+  // to the end of the "batch mode" FIFO for the specified sensor and flushes
+  // the FIFO.
+  //
+  // If the FIFO is empty or if the sensor doesn't support batching (FIFO
+  // size zero), it should return SUCCESS along with a trivial
+  // META_DATA_FLUSH_COMPLETE event added to the event stream. This applies to
+  // all sensors other than one-shot sensors.
+  //
+  // If the sensor is a one-shot sensor, flush must return -EINVAL and not
+  // generate any flush complete metadata.
+  //
+  // If the sensor is not active at the time flush() is called, flush() should
+  // return -EINVAL.
+  int Flush(int sensor_handle) {
+    return -EINVAL;
+  }
+#endif
+
+#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_4)
+  // Inject a single sensor sample to be to this device.
+  // data points to the sensor event to be injected
+  // @return 0 on success
+  //  -EPERM if operation is not allowed
+  //  -EINVAL if sensor event cannot be injected
+  int InjectSensorData(const sensors_event_t *data) {
+    return -EINVAL;
+  }
+#endif
+
+ private:
+  typedef std::vector<SensorState*> SensorStateVector;
+  typedef std::vector<sensors_event_t> FifoType;
+  // Total number of sensors supported by this HAL.
+  static int total_sensor_count_;
+  // Vector of static sensor information for sensors supported by this HAL.
+  // Indexed by the handle. Length must always be equal to total_sensor_count_.
+  static SensorInfo* sensor_infos_;
+  // Vector of sensor state information, indexed by the handle.
+  // Assumption here is that the sensor handles will start at 0 and be
+  // contiguous up to the number of supported sensors.
+  SensorStateVector sensor_states_;
+  // Keep track of the time when the thread in Poll() is scheduled to wake.
+  cvd::time::MonotonicTimePoint current_deadline_;
+
+  // Ordered set of sensor values.
+  // TODO(ghartman): Simulate FIFO overflow.
+  FifoType fifo_;
+  // Thread to handle new connections.
+  pthread_t receiver_thread_;
+  // Socket to receive sensor events on.
+  cvd::SharedFD sensor_listener_socket_;
+  // Socket for listener thread to receive control messages.
+  cvd::SharedFD control_receiver_socket_;
+  // Socket to send control messages to listener thread.
+  cvd::SharedFD control_sender_socket_;
+
+  // Lock to protect shared state, including
+  // sensor_states_ and next_deadline_.
+  // Associated with deadline_change_ condition variable.
+  cvd::Mutex sensor_state_lock_;
+  // Condition variable to signal changes in the deadline.
+  cvd::ConditionVariable deadline_change_;
+
+  // When events are arriving from a client, we report only
+  // when they arrive, rather than at a fixed cycle. After not
+  // receiving a real event for both a given number of periods
+  // and a given time period, we will give up and resume
+  // sending mock events.
+  const static int kInjectedEventWaitPeriods;
+  const static cvd::time::Nanoseconds kInjectedEventWaitTime;
+
+  /**
+   ** UTILITY FUNCTIONS
+   **/
+
+  // Receive data from remoter.
+  void* Receiver();
+
+  // Notifies the remoter that the HAL is awake and ready.
+  inline bool NotifyRemoter();
+
+  // Looks through all active sensor deadlines, and finds the one that
+  // is coming up next. If this is not next_deadline_, then the deadline
+  // has changed. Update it and signal the Poll thread.
+  // This should be called anytime the next deadline may have changed.
+  // Can only be called while holding sensor_state_lock_.
+  // Returns true if the deadline has changed.
+  cvd::time::MonotonicTimePoint UpdateDeadline();
+
+  // Sends an update for the sensor with the given handle to the remoter.
+  // Update will be enqueued for receiver, not send immediately.
+  inline bool UpdateRemoterState(int handle);
+
+  // Sends a control event to the listener.
+  inline bool SendControlMessage(SensorControlMessage msg);
+
+  // Populates the list of static sensor info. Returns the number
+  // of sensors supported. Should only be called once.
+  static inline int RegisterSensors();
+
+};
+
+} //namespace cvd
+
diff --git a/guest/hals/sensors/vsoc_sensors_message.cpp b/guest/hals/sensors/vsoc_sensors_message.cpp
new file mode 100644
index 0000000..1998762
--- /dev/null
+++ b/guest/hals/sensors/vsoc_sensors_message.cpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include "guest/hals/sensors/vsoc_sensors_message.h"
+
+const char* gce_sensors_message::kSensorsHALSocketName =
+    "/var/run/system/sensors_hal_socket";
diff --git a/guest/hals/sensors/vsoc_sensors_message.h b/guest/hals/sensors/vsoc_sensors_message.h
new file mode 100644
index 0000000..3a57700
--- /dev/null
+++ b/guest/hals/sensors/vsoc_sensors_message.h
@@ -0,0 +1,24 @@
+/*
+ * 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 <hardware/hardware.h>
+#include <hardware/sensors.h>
+
+struct gce_sensors_message : sensors_event_t {
+  static const char* kSensorsHALSocketName;
+};
+
diff --git a/guest/libs/legacy_framebuffer/Android.mk b/guest/libs/legacy_framebuffer/Android.mk
new file mode 100644
index 0000000..22723de
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/Android.mk
@@ -0,0 +1,59 @@
+# 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.
+
+# Temporary, this library should go away once the new HALS are in place.
+
+vsocframebuffer_common_src_files := \
+    vsoc_framebuffer.cpp \
+    vsoc_framebuffer_control.cpp \
+    RegionRegistry.cpp
+
+vsocframebuffer_common_c_flags := -Wall -Werror $(VSOC_VERSION_CFLAGS)
+
+vsocframebuffer_common_c_includes := \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_kernel
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libvsocframebuffer
+LOCAL_VENDOR_MODULE := true
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := ${vsocframebuffer_common_src_files}
+LOCAL_CFLAGS += ${vsocframebuffer_common_c_flags}
+LOCAL_C_INCLUDES := ${vsocframebuffer_common_c_includes} \
+    $(VSOC_STLPORT_INCLUDES)
+
+LOCAL_STATIC_LIBRARIES := \
+    libjsoncpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    liblog \
+    libutils \
+    libcutils \
+    cuttlefish_auto_resources \
+    libcuttlefish_fs \
+    vsoc_lib \
+    $(VSOC_STLPORT_LIBS)
+
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/libs/legacy_framebuffer/RegionRegistry.cpp b/guest/libs/legacy_framebuffer/RegionRegistry.cpp
new file mode 100644
index 0000000..24010b0
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/RegionRegistry.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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 <limits.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cutils/hashmap.h>
+#define LOG_TAG "VSoCGrallocRegionRegistry"
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+
+#include <linux/ashmem.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+
+// TODO(ghartman): Make the configurable through a property
+static const bool g_log_refs = false;
+
+struct GrallocRegion {
+  void* base_;
+  int   num_references_;
+
+  GrallocRegion() : base_(0), num_references_(0) { }
+  // Copy constructors are ok.
+};
+
+
+static const char* get_buffer_name(
+    const private_handle_t* hnd, char output[ASHMEM_NAME_LEN]) {
+  output[0] = '\0';
+  if (!hnd) {
+    ALOGE("Attempted to log gralloc name hnd=NULL");
+    return output;
+  }
+  if (hnd->fd == -1) {
+    ALOGE("Attempted to log gralloc name hnd=%p with fd == -1", hnd);
+    return output;
+  }
+  int rval = ioctl(hnd->fd, ASHMEM_GET_NAME, output);
+  if (rval == -1) {
+    output[0] = '\0';
+  }
+  return output;
+}
+
+
+static int str_hash(void* str) {
+  return hashmapHash(str, strlen(reinterpret_cast<const char*>(str)));
+}
+
+
+static bool str_equal(void* a, void* b) {
+  return strcmp(
+      reinterpret_cast<const char*>(a),
+      reinterpret_cast<const char*>(b)) == 0;
+}
+
+
+static Hashmap* get_regions() {
+  static Hashmap* regionMap = hashmapCreate(19, str_hash, str_equal);
+  return regionMap;
+}
+
+
+static GrallocRegion* lock_region_for_handle(
+    const private_handle_t* hnd, char region_name[ASHMEM_NAME_LEN]) {
+  region_name[0] = '\0';
+  get_buffer_name(hnd, region_name);
+  Hashmap* hash = get_regions();
+  hashmapLock(hash);
+  GrallocRegion* region = reinterpret_cast<GrallocRegion*>(
+      hashmapGet(hash, region_name));
+  if (!region) {
+    region = new GrallocRegion;
+    hashmapPut(hash, strdup(region_name), region);
+  }
+  return region;
+}
+
+
+/* The current implementation uses only a single lock for all regions.
+ * This method takes a region to simplfy the refactoring if we go to
+ * finer-grained locks.
+ */
+static inline void unlock_region(GrallocRegion* ) {
+  hashmapUnlock(get_regions());
+}
+
+
+void* reference_region(const char* op, const private_handle_t* hnd) {
+  char name_buf[ASHMEM_NAME_LEN];
+  GrallocRegion* region = lock_region_for_handle(hnd, name_buf);
+  if (!region->base_) {
+    void* mappedAddress = mmap(
+        0, hnd->total_size, PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0);
+    if (mappedAddress == MAP_FAILED) {
+      ALOGE("Could not mmap %s", strerror(errno));
+      unlock_region(region);
+      return NULL;
+    }
+    if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {
+      // Set up the guard pages. The last page is always a guard
+      uintptr_t base = uintptr_t(mappedAddress);
+      uintptr_t addr = base + hnd->total_size - PAGE_SIZE;
+      if (mprotect((void*)addr, PAGE_SIZE, PROT_NONE) == -1) {
+        ALOGE("mprotect base=%p, pg=%p failed (%s)",
+              (void*)base, (void*)addr, strerror(errno));
+      }
+    }
+    region->base_ = mappedAddress;
+    ALOGI("Mapped %s hnd=%p fd=%d base=%p format=%s(0x%x) width=%d height=%d",
+          name_buf, hnd, hnd->fd, region->base_,
+          pixel_format_to_string(hnd->format), hnd->format,
+          hnd->x_res, hnd->y_res);
+  }
+
+  void* rval = region->base_;
+  ++region->num_references_;
+  ALOGI_IF(g_log_refs, "Referencing name=%s op=%s addr=%p new numRefs=%d",
+        name_buf, op, region->base_, region->num_references_);
+  unlock_region(region);
+  return rval;
+}
+
+
+int unreference_region(const char* op, const private_handle_t* hnd) {
+  char name_buf[ASHMEM_NAME_LEN];
+
+  GrallocRegion* region = lock_region_for_handle(hnd, name_buf);
+  if (!region->base_) {
+    ALOGE("Unmapping region with no map hnd=%p", hnd);
+    unlock_region(region);
+    return -1;
+  }
+  if (region->num_references_ < 1) {
+    ALOGE(
+        "unmap with hnd=%p, numReferences=%d", hnd, region->num_references_);
+    unlock_region(region);
+    return -1;
+  }
+  --region->num_references_;
+  if (!region->num_references_) {
+    ALOGI("Unmapped %s hnd=%p fd=%d base=%p", name_buf, hnd,
+          hnd->fd, region->base_);
+    if (munmap(region->base_, hnd->total_size) < 0) {
+      ALOGE("Could not unmap %s", strerror(errno));
+    }
+    region->base_ = 0;
+  }
+  ALOGI_IF(g_log_refs, "Unreferencing name=%s op=%s addr=%p new numRefs=%d",
+        name_buf, op, region->base_, region->num_references_);
+  unlock_region(region);
+  return 0;
+}
diff --git a/guest/libs/legacy_framebuffer/RegionRegistry.h b/guest/libs/legacy_framebuffer/RegionRegistry.h
new file mode 100644
index 0000000..210ba9a
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/RegionRegistry.h
@@ -0,0 +1,30 @@
+#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.
+ */
+struct private_handle_t;
+
+/**
+ * Map the memory associated with hnd->fd or, if already mapped, increment
+ * its reference count.
+ */
+void* reference_region(
+    const char* op, const private_handle_t* hnd);
+
+/**
+ * Decrement the reference count associated with hnd->fd, unmapping its
+ * memory iff the reference count reaches 0.
+ */
+int unreference_region(const char* op, const private_handle_t* hnd);
diff --git a/guest/libs/legacy_framebuffer/vsoc_framebuffer.cpp b/guest/libs/legacy_framebuffer/vsoc_framebuffer.cpp
new file mode 100644
index 0000000..32071a0
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/vsoc_framebuffer.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <guest/libs/platform_support/api_level_fixes.h>
+
+#define LOG_TAG "VSoCFrameBuffer"
+
+#include "vsoc_framebuffer.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <utils/String8.h>
+#include <cutils/log.h>
+#include <system/graphics.h>
+
+#include "common/vsoc/lib/fb_bcast_region_view.h"
+
+const char* const VSoCFrameBuffer::kFrameBufferPath =
+    "/dev/userspace_framebuffer";
+
+
+static vsoc::framebuffer::FBBroadcastRegionView* GetFBBroadcastRegionView() {
+  static vsoc::framebuffer::FBBroadcastRegionView instance;
+  return &instance;
+}
+
+const VSoCFrameBuffer & VSoCFrameBuffer::getInstance() {
+  static VSoCFrameBuffer instance;
+  instance.Configure();
+  return instance;
+}
+
+
+VSoCFrameBuffer::VSoCFrameBuffer()
+  : fb_region_view_(GetFBBroadcastRegionView()), line_length_(-1) { }
+
+
+void VSoCFrameBuffer::Configure() {
+  if (!fb_region_view_->Open()) {
+    SLOGE("Failed to open broadcaster region");
+  }
+  line_length_ = align(x_res() * sizeof(Pixel));
+}
+
+
+int VSoCFrameBuffer::dpi() const {
+  return fb_region_view_->dpi();
+}
+
+
+int VSoCFrameBuffer::x_res() const {
+  return fb_region_view_->x_res();
+}
+
+
+int VSoCFrameBuffer::y_res() const {
+  return fb_region_view_->y_res();
+}
+
+
+bool VSoCFrameBuffer::OpenFrameBuffer(int* frame_buffer_fd) {
+  int fb_fd;
+  if ((fb_fd = open(VSoCFrameBuffer::kFrameBufferPath, O_RDWR)) < 0) {
+    SLOGE("Failed to open '%s' (%s)",
+          VSoCFrameBuffer::kFrameBufferPath, strerror(errno));
+    return false;
+  }
+
+  const VSoCFrameBuffer& config = VSoCFrameBuffer::getInstance();
+
+  if (ftruncate(fb_fd, config.total_buffer_size()) < 0) {
+    SLOGE("Failed to truncate framebuffer (%s)", strerror(errno));
+    return false;
+  }
+
+  *frame_buffer_fd = fb_fd;
+  return true;
+}
+
+
+bool VSoCFrameBuffer::OpenAndMapFrameBuffer(void** fb_memory,
+					    int* frame_buffer_fd) {
+  int fb_fd;
+  if (!VSoCFrameBuffer::OpenFrameBuffer(&fb_fd)) { return false; }
+
+  size_t fb_size = VSoCFrameBuffer::getInstance().total_buffer_size();
+
+  void* mmap_res = mmap(0, fb_size, PROT_READ, MAP_SHARED, fb_fd, 0);
+  if (mmap_res == MAP_FAILED) {
+    SLOGE("Failed to mmap framebuffer (%s)", strerror(errno));
+    close(fb_fd);
+    return false;
+  }
+
+  // Modify the pointers only after mmap succeeds.
+  *fb_memory = mmap_res;
+  *frame_buffer_fd = fb_fd;
+
+  return true;
+}
+
+bool VSoCFrameBuffer::UnmapAndCloseFrameBuffer(void* fb_memory,
+                                                    int frame_buffer_fd) {
+  size_t fb_size = VSoCFrameBuffer::getInstance().total_buffer_size();
+  return munmap(fb_memory, fb_size) == 0 && close(frame_buffer_fd) == 0;
+}
+
+
+int VSoCFrameBuffer::hal_format() const {
+  switch(bits_per_pixel()) {
+    case 32:
+      if (kRedShift) {
+        return HAL_PIXEL_FORMAT_BGRA_8888;
+      } else {
+        return HAL_PIXEL_FORMAT_RGBX_8888;
+      }
+    default:
+      return HAL_PIXEL_FORMAT_RGB_565;
+  }
+}
+
+const char* pixel_format_to_string(int format) {
+  switch (format) {
+    // Formats that are universal across versions
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+      return "RGBA_8888";
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+      return "RGBX_8888";
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+      return "BGRA_8888";
+    case HAL_PIXEL_FORMAT_RGB_888:
+      return "RGB_888";
+    case HAL_PIXEL_FORMAT_RGB_565:
+      return "RGB_565";
+    case HAL_PIXEL_FORMAT_YV12:
+      return "YV12";
+    case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+      return "YCrCb_420_SP";
+    case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+      return "YCbCr_422_SP";
+    case HAL_PIXEL_FORMAT_YCbCr_422_I:
+      return "YCbCr_422_I";
+
+#if VSOC_PLATFORM_SDK_AFTER(J)
+    // First supported on JBMR1 (API 17)
+    case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+      return "IMPLEMENTATION_DEFINED";
+    case HAL_PIXEL_FORMAT_BLOB:
+      return "BLOB";
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(J_MR1)
+    // First supported on JBMR2 (API 18)
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+      return "YCbCr_420_888";
+    case HAL_PIXEL_FORMAT_Y8:
+      return "Y8";
+    case HAL_PIXEL_FORMAT_Y16:
+      return "Y16";
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(K)
+    // Support was added in L (API 21)
+    case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+      return "RAW_OPAQUE";
+    // This is an alias for RAW_SENSOR in L and replaces it in M.
+    case HAL_PIXEL_FORMAT_RAW16:
+      return "RAW16";
+    case HAL_PIXEL_FORMAT_RAW10:
+      return "RAW10";
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(L_MR1)
+    case HAL_PIXEL_FORMAT_YCbCr_444_888:
+      return "YCbCr_444_888";
+    case HAL_PIXEL_FORMAT_YCbCr_422_888:
+      return "YCbCr_422_888";
+    case HAL_PIXEL_FORMAT_RAW12:
+      return "RAW12";
+    case HAL_PIXEL_FORMAT_FLEX_RGBA_8888:
+      return "FLEX_RGBA_8888";
+    case HAL_PIXEL_FORMAT_FLEX_RGB_888:
+      return "FLEX_RGB_888";
+#endif
+
+    // Formats that have been removed
+#if VSOC_PLATFORM_SDK_BEFORE(K)
+    // Support was dropped on K (API 19)
+    case HAL_PIXEL_FORMAT_RGBA_5551:
+      return "RGBA_5551";
+    case HAL_PIXEL_FORMAT_RGBA_4444:
+      return "RGBA_4444";
+#endif
+#if VSOC_PLATFORM_SDK_BEFORE(L)
+    // Renamed to RAW_16 in L. Both were present for L, but it was completely
+    // removed in M.
+    case HAL_PIXEL_FORMAT_RAW_SENSOR:
+      return "RAW_SENSOR";
+#endif
+#if VSOC_PLATFORM_SDK_AFTER(J_MR2) && VSOC_PLATFORM_SDK_BEFORE(M)
+    // Supported K, L, and LMR1. Not supported on JBMR0, JBMR1, JBMR2, and M
+    case HAL_PIXEL_FORMAT_sRGB_X_8888:
+      return "sRGB_X_8888";
+    case HAL_PIXEL_FORMAT_sRGB_A_8888:
+      return "sRGB_A_8888";
+#endif
+  }
+  return "UNKNOWN";
+}
diff --git a/guest/libs/legacy_framebuffer/vsoc_framebuffer.h b/guest/libs/legacy_framebuffer/vsoc_framebuffer.h
new file mode 100644
index 0000000..9f1d405
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/vsoc_framebuffer.h
@@ -0,0 +1,110 @@
+#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 <UniquePtr.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <climits>
+
+struct private_handle_t;
+struct remoter_request_packet;
+
+namespace vsoc {
+namespace framebuffer {
+class FBBroadcastRegionView;
+}
+}
+
+inline size_t roundUpToPageSize(size_t x) {
+  return (x + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1);
+}
+
+class VSoCFrameBuffer {
+public:
+  static const VSoCFrameBuffer& getInstance();
+
+  static int align(int input, int alignment = kAlignment) {
+    return (input + alignment - 1) & -alignment;
+  }
+
+  int bits_per_pixel() const {
+    return kBitsPerPixel;
+  }
+
+  size_t bufferSize() const {
+    return line_length_ * y_res();
+  }
+
+  int dpi() const;
+
+  int hal_format() const;
+
+  int line_length() const { return line_length_; }
+
+  int total_buffer_size() const {
+    return roundUpToPageSize(line_length_ * y_res_virtual() +
+                             VSoCFrameBuffer::kSwiftShaderPadding);
+  }
+
+  int x_res() const;
+
+  int y_res() const;
+
+  int y_res_virtual() const {
+    return y_res() * kNumBuffers;
+  }
+
+  static const int kAlignment = 8;
+  static const int kNumHwcBuffers = 3;
+  // Without sync fences enabled surfaceflinger uses only 2 framebuffers,
+  // regardless of how many are available
+  static const int kNumSfBuffers = 3;
+  static const int kNumBuffers = kNumHwcBuffers + kNumSfBuffers;
+  static const char* const kFrameBufferPath;
+
+  static const int kRedShift = 0;
+  static const int kRedBits = 8;
+  static const int kGreenShift = 8;
+  static const int kGreenBits = 8;
+  static const int kBlueShift = 16;
+  static const int kBlueBits = 8;
+  static const int kAlphaShift = 24;
+  static const int kAlphaBits = 8;
+  typedef uint32_t Pixel;
+  static const int kSwiftShaderPadding = 4;
+
+  // Opens the framebuffer file. Ensures the file has the appropriate size by
+  // calling ftruncate.
+  static bool OpenFrameBuffer(int* frame_buffer_fd);
+
+  // Maps the framebuffer into memory. It's the caller's responsibility to
+  // unmap the memory and close the file when done.
+  static bool OpenAndMapFrameBuffer(void** fb_memory, int* frame_buffer_fd);
+  static bool UnmapAndCloseFrameBuffer(void* fb_memory, int frame_buffer_fd);
+
+private:
+  VSoCFrameBuffer();
+  void Configure();
+
+  vsoc::framebuffer::FBBroadcastRegionView* fb_region_view_;
+  static const int kBitsPerPixel = sizeof(Pixel) * CHAR_BIT;
+  // Length of a scan-line in bytes.
+  int line_length_;
+  DISALLOW_COPY_AND_ASSIGN(VSoCFrameBuffer);
+};
+
+const char* pixel_format_to_string(int format);
diff --git a/guest/libs/legacy_framebuffer/vsoc_framebuffer_control.cpp b/guest/libs/legacy_framebuffer/vsoc_framebuffer_control.cpp
new file mode 100644
index 0000000..83c9b00
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/vsoc_framebuffer_control.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <utils/String8.h>
+
+#define LOG_TAG "VSoCFrameBufferControl"
+#include <cutils/log.h>
+#include <system/graphics.h>
+
+#include "guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h"
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+
+enum { NOT_YET = 0, IN_PROGRESS, DONE };
+
+struct FrameBufferControl {
+  pthread_mutex_t mutex;
+  pthread_cond_t cond_var;
+  uint32_t seq_num;
+  volatile int yoffset;
+  volatile int initialized;
+  volatile uint32_t buffer_bits;
+  CompositionStats stats;
+};
+
+// __sync_lock_test_and_set is described to work on intel, but not on many other
+// targets.
+#define ATOMICALLY_SET(x, val) __sync_lock_test_and_set(&(x), (val))
+#define ATOMICALLY_COMPARE_AND_SWAP(x, old, val) \
+  __sync_val_compare_and_swap(&(x), (old), (val))
+// fetch the value, don't modify it (or with 0)
+#define ATOMICALLY_GET(x) __sync_or_and_fetch(&(x), 0)
+
+const char* const VSoCFrameBufferControl::kFrameBufferControlPath =
+    "/dev/framebuffer_control";
+
+VSoCFrameBufferControl& VSoCFrameBufferControl::getInstance() {
+  static VSoCFrameBufferControl instance;
+  // If not initialized before and fails to initialize now
+  if (!instance.Initialize()) {
+    LOG_ALWAYS_FATAL(
+        "Unable to initialize the framebuffer control structure (%s)... "
+        "aborting!",
+        strerror(errno));
+  }
+  return instance;
+}
+
+uint32_t VSoCFrameBufferControl::GetAndSetNextAvailableBufferBit(uint32_t filter) {
+  if (pthread_mutex_lock(&control_memory_->mutex)) {
+    ALOGE("Failed to acquire lock on framebuffer control mutex (%s) - %s",
+          strerror(errno), __FUNCTION__);
+    return 0;
+  }
+  uint32_t bit = control_memory_->buffer_bits;
+  bit &= filter;
+  if (bit == filter) {
+    // All bits in the filter are already set in the set
+    bit = 0LU;
+  } else {
+    // Set available bits to 1
+    bit = (bit^filter);
+    // isolate first available bit
+    bit &= ~bit + 1LU;
+    // set it on bit set on shared memory
+    control_memory_->buffer_bits |= bit;
+  }
+
+  pthread_mutex_unlock(&control_memory_->mutex);
+  return bit;
+}
+
+int VSoCFrameBufferControl::UnsetBufferBits(uint32_t bits) {
+  if (pthread_mutex_lock(&control_memory_->mutex)) {
+    ALOGE("Failed to acquire lock on framebuffer control mutex (%s) - %s",
+          strerror(errno), __FUNCTION__);
+    return -1;
+  }
+
+  control_memory_->buffer_bits &= ~bits;
+
+  pthread_mutex_unlock(&control_memory_->mutex);
+  return 0;
+}
+
+VSoCFrameBufferControl::VSoCFrameBufferControl()
+    : control_fd_(-1), control_memory_(NULL) {}
+
+namespace {
+
+bool MapFrameBufferControl(FrameBufferControl** control_memory_ptr,
+                           int* fbc_fd) {
+  size_t control_size = sizeof(FrameBufferControl);
+  mode_t fb_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+  int control_fd;
+  if ((control_fd = open(VSoCFrameBufferControl::kFrameBufferControlPath, O_RDWR,
+                         fb_mode)) < 0) {
+    ALOGE("Failed to open framebuffer control at %s (%s)",
+          VSoCFrameBufferControl::kFrameBufferControlPath, strerror(errno));
+    return false;
+  }
+
+  if (ftruncate(control_fd, sizeof(FrameBufferControl)) < 0) {
+    ALOGE("Failed to truncate framebuffer control at %s (%s)",
+          VSoCFrameBufferControl::kFrameBufferControlPath, strerror(errno));
+    return false;
+  }
+
+  void* control_memory;
+  control_memory =
+      mmap(0, control_size, PROT_READ | PROT_WRITE, MAP_SHARED, control_fd, 0);
+  if (control_memory == MAP_FAILED) {
+    ALOGE("Failed to mmap framebuffer control (%s)", strerror(errno));
+    close(control_fd);
+    return false;
+  }
+
+  *control_memory_ptr = reinterpret_cast<FrameBufferControl*>(control_memory);
+  *fbc_fd = control_fd;
+
+  return true;
+}
+
+void UnmapFrameBufferControl(FrameBufferControl** control_memory_ptr,
+                             int* fbc_fd) {
+  munmap(*control_memory_ptr, sizeof(FrameBufferControl));
+  *control_memory_ptr = NULL;
+  close(*fbc_fd);
+  *fbc_fd = -1;
+}
+}
+
+bool VSoCFrameBufferControl::Initialize() {
+  if (control_fd_ >= 0) {
+    return true;
+  }
+
+  if (!MapFrameBufferControl(&control_memory_, &control_fd_)) {
+    return false;
+  }
+
+  int initializing_state = ATOMICALLY_COMPARE_AND_SWAP(
+      control_memory_->initialized, NOT_YET, IN_PROGRESS);
+  switch (initializing_state) {
+    case DONE:
+      return true;
+
+    case IN_PROGRESS: {  // wait 1 sec and try again
+      do {
+        sleep(1);
+        initializing_state = ATOMICALLY_GET(control_memory_->initialized);
+        if (initializing_state != DONE) {
+          ALOGW(
+              "Framebuffer control structure has not yet been initialized "
+              "after one second. Value of initialized flag: %d",
+              initializing_state);
+        }
+      } while (initializing_state != DONE);
+      return true;
+    }
+
+    case NOT_YET: {  // flag set to IN_PROGRESS, proceed to initialize
+      pthread_mutexattr_t mutex_attr;
+      pthread_mutexattr_init(&mutex_attr);
+      pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
+      int retval = pthread_mutex_init(&(control_memory_->mutex), &mutex_attr);
+      if (retval) {
+        ALOGE("Failed to acquire lock on framebuffer control mutex (%s) - %s",
+              strerror(errno), __FUNCTION__);
+        UnmapFrameBufferControl(&control_memory_, &control_fd_);
+        return false;
+      }
+      pthread_mutexattr_destroy(&mutex_attr);
+
+      pthread_condattr_t cond_attr;
+      pthread_condattr_init(&cond_attr);
+      pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
+      retval = pthread_cond_init(&(control_memory_->cond_var), &cond_attr);
+      if (retval) {
+        ALOGE("Failed to initialize cond var for framebuffer control (%s)",
+              strerror(errno));
+        pthread_mutex_destroy(&(control_memory_->mutex));
+        UnmapFrameBufferControl(&control_memory_, &control_fd_);
+        return false;
+      }
+      pthread_condattr_destroy(&cond_attr);
+
+      ATOMICALLY_SET(control_memory_->buffer_bits, 0LU);
+      ATOMICALLY_SET(control_memory_->seq_num, 0);
+      ATOMICALLY_SET(control_memory_->initialized, DONE);
+
+      return true;
+    }
+
+    default: {  // unrecognized value
+      ALOGE("Framebuffer control memory is corrupt, initialized = %d",
+            initializing_state);
+      UnmapFrameBufferControl(&control_memory_, &control_fd_);
+      return false;
+    }
+  }
+
+  return true;
+}
+
+int VSoCFrameBufferControl::GetCurrentYOffset() const {
+  if (!control_memory_) return -1;
+  return control_memory_->yoffset;
+}
+
+int VSoCFrameBufferControl::WaitForFrameBufferChangeSince(
+    uint32_t previous_fb_seq,
+    int* yoffset_p,
+    uint32_t* fb_seq_p,
+    CompositionStats* stats_p) {
+  if (pthread_mutex_lock(&control_memory_->mutex)) {
+    ALOGE("Failed to acquire lock on framebuffer control mutex (%s) - %s",
+          strerror(errno), __FUNCTION__);
+    return -1;
+  }
+  int retval = 0;
+
+  while (control_memory_->seq_num == previous_fb_seq) {
+    retval =
+        pthread_cond_wait(&control_memory_->cond_var, &control_memory_->mutex);
+  }
+
+  if (fb_seq_p) {
+    *fb_seq_p = control_memory_->seq_num;
+  }
+  if (yoffset_p) {
+    *yoffset_p = control_memory_->yoffset;
+  }
+  if (stats_p) {
+    *stats_p = control_memory_->stats;
+  }
+
+  pthread_mutex_unlock(&control_memory_->mutex);
+  return retval;
+}
+
+int VSoCFrameBufferControl::WaitForFrameBufferChange(int* yoffset_p) {
+  return WaitForFrameBufferChangeSince(
+      control_memory_->seq_num, yoffset_p, NULL, NULL);
+}
+
+int VSoCFrameBufferControl::BroadcastFrameBufferChanged(int yoffset) {
+  return BroadcastFrameBufferChanged(yoffset, NULL);
+}
+
+// increments the framebuffer sequential number, ensuring it's never zero
+static inline uint32_t seq_inc(uint32_t num) {
+  ++num;
+  return num? num: 1;
+}
+
+int VSoCFrameBufferControl::BroadcastFrameBufferChanged(
+    int yoffset, const CompositionStats* stats) {
+  if (pthread_mutex_lock(&control_memory_->mutex)) {
+    ALOGE("Failed to acquire lock on framebuffer control mutex (%s)",
+          strerror(errno));
+    return -1;
+  }
+  control_memory_->yoffset = yoffset;
+  control_memory_->seq_num = seq_inc(control_memory_->seq_num);
+  if (stats) { control_memory_->stats = *stats; }
+  pthread_cond_broadcast(&control_memory_->cond_var);
+  pthread_mutex_unlock(&control_memory_->mutex);
+
+  return 0;
+}
diff --git a/guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h b/guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h
new file mode 100644
index 0000000..5e1ec7e
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/vsoc_framebuffer_control.h
@@ -0,0 +1,88 @@
+#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 <common/libs/time/monotonic_time.h>
+
+struct FrameBufferControl;
+
+struct CompositionStats {
+  cvd::time::MonotonicTimePoint prepare_start;
+  cvd::time::MonotonicTimePoint prepare_end;
+  cvd::time::MonotonicTimePoint set_start;
+  cvd::time::MonotonicTimePoint set_end;
+  cvd::time::MonotonicTimePoint last_vsync;
+  // There may be more than one call to prepare, the timestamps are with regards to the last one (the one that precedes the set call)
+  int num_prepare_calls;
+  int num_layers;
+  // The number of layers composed by the hwcomposer
+  int num_hwc_layers;
+};
+
+class VSoCFrameBufferControl {
+ public:
+  static VSoCFrameBufferControl& getInstance();
+
+  static const char* const kFrameBufferControlPath;
+
+  // The framebuffer control structure mantains a bit set to keep track of the
+  // buffers that have been allocated already. This function atomically finds an
+  // unset (0) bit in the set, sets it to 1 and returns it. It will only
+  // consider bits already set in the filter parameter.
+  uint32_t GetAndSetNextAvailableBufferBit(uint32_t filter);
+  // Returns 0 on success
+  int UnsetBufferBits(uint32_t bits);
+
+  // Returns the yoffset of the last framebuffer update or a negative number on
+  // error.
+  int GetCurrentYOffset() const;
+  // Returns the value returned by the pthread_cond_wait, or -1 if the control
+  // structure has not been initialized by the hwcomposer yet.
+  int WaitForFrameBufferChange(int* yoffset_p);
+  // Uses a sequential number to determine whether the client was notified of
+  // the last framebuffer change and therefore needs to wait for a new one or if
+  // it can just return with the last one. It also provides the timings of the
+  // composition. Any NULL input parameters will be ignored. The sequential
+  // numbers are guaranteed to never be zero, so a value of zero can be used to
+  // get the last frame without waiting (useful when we want to get a frame for
+  // the first time).
+  int WaitForFrameBufferChangeSince(uint32_t previous_fb_seq,
+                                    int* yoffset_p,
+                                    uint32_t* fb_seq_p,
+                                    CompositionStats* stats_p);
+
+  // Returns 0 on success, a negative number on error.
+  int BroadcastFrameBufferChanged(int yoffset);
+
+  // Returns 0 on success, a negative number on error.
+  int BroadcastFrameBufferChanged(int yoffset, const CompositionStats* stats);
+
+ private:
+  VSoCFrameBufferControl();
+
+  // Map the control structure to memory and initialize its contents.
+  bool Initialize();
+
+  // FD for the frame buffer control.
+  int control_fd_;
+  // Pointer to the mapped frame buffer control.
+  FrameBufferControl* control_memory_;
+
+  // Disallow copy and assign
+  VSoCFrameBufferControl(const VSoCFrameBufferControl&) {}
+  VSoCFrameBufferControl& operator=(const VSoCFrameBufferControl&) {
+    return *this;
+  }
+};
diff --git a/guest/libs/platform_support/api_level_fixes.h b/guest/libs/platform_support/api_level_fixes.h
new file mode 100644
index 0000000..d0da546
--- /dev/null
+++ b/guest/libs/platform_support/api_level_fixes.h
@@ -0,0 +1,112 @@
+/*
+ * 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
+
+// Fixes for various things that go wrong between Android versions.
+// By convention this should be the very first include: it tweaks some
+// flags that affect the processing of the system headers.
+//
+// Code that needs to cope with platform changes should use the
+// VSOC_PLATFORM_SDK_BEFORE and VSOC_PLATFORM_SDK_AFTER macros below.
+// It's fine to provide declarations for broadly used things in this file
+// if that's easier.
+//
+// To use this header add $(VSOC_VERSION_CFLAGS) to the LOCAL_CFLAGS
+// in the corresponding Android.mk. There is an error check to catch
+// cases where this wasn't done.
+//
+// Code should not examine the SDK_PLATFORM_VERSION, and generally shouldn't
+// look at the VSOC_PLATFORM_SDK_* values. While these currently track
+// PLATFORM_SDK_VERSION, that's an implementation detail that will probably
+// change: Android will eventually break things without bumping
+// PLATFORM_SDK_VERSION.
+//
+// This is also why there is no SDK_PLATFORM_VERSION_IS(). Convert these
+// statements into BEFORE and/or AFTER.
+//
+// To check for master/AOSP use VSOC_PLATFORM_VERSION_AFTER(LAST_SHIPPED)
+#include <time.h>
+
+#ifndef VSOC_PLATFORM_SDK_VERSION
+#error VSOC_PLATFORM_SDK_VERSION is not set. Check your Android.mk
+#endif
+
+// Hide some C++ annotations that we'd like to use but need to avoid on older
+// compilers.
+#if __cplusplus <= 199711L
+#define override
+#endif
+
+#define VSOC_PLATFORM_SDK_J                16
+#define VSOC_PLATFORM_SDK_J_MR1            17
+#define VSOC_PLATFORM_SDK_J_MR2            18
+#define VSOC_PLATFORM_SDK_K                19
+// Version 20 reserved for KitKat wearables only. See
+// http://developer.android.com/guide/topics/manifest/uses-sdk-element.html
+#define VSOC_PLATFORM_SDK_L                21
+#define VSOC_PLATFORM_SDK_L_MR1            22
+#define VSOC_PLATFORM_SDK_M                23
+#define VSOC_PLATFORM_SDK_N                24
+#define VSOC_PLATFORM_SDK_N_MR1            25
+#define VSOC_PLATFORM_SDK_O                26
+#define VSOC_PLATFORM_SDK_O_MR1            27
+#define VSOC_PLATFORM_SDK_LAST_SHIPPED     27
+
+#define VSOC_PLATFORM_SDK_BEFORE(X) (VSOC_PLATFORM_SDK_VERSION < VSOC_PLATFORM_SDK_##X)
+#define VSOC_PLATFORM_SDK_AFTER(X) (VSOC_PLATFORM_SDK_VERSION > VSOC_PLATFORM_SDK_##X)
+
+#if VSOC_PLATFORM_SDK_BEFORE(J_MR2)
+#define VSOC_STATIC_INITIALIZER(X) X:
+#else
+#define VSOC_STATIC_INITIALIZER(X) .X =
+#endif
+
+#if VSOC_PLATFORM_SDK_BEFORE(K)
+// audio_input_flags_t was first defind in K.
+// JBMR2 and K use the same audio HAL version, so define a work-around here.
+typedef enum {
+  AUDIO_INPUT_FLAG_NONE       = 0x0,  // no attributes
+} audio_input_flags_t;
+#endif
+
+// cstdint doesn't provide PRI... before C++11. On some branches (K) inttypes.h
+// also doesn't behave as if it's C++. Just define the PRI... types here
+// the kludgy way so our source is clean from a C++11 point-of-view.
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS 1
+#endif
+#include <inttypes.h>
+
+#if VSOC_PLATFORM_SDK_BEFORE(L)
+#define HAL_PIXEL_FORMAT_RAW16 HAL_PIXEL_FORMAT_RAW_SENSOR
+#define VSOC_FDPRINTF fdprintf
+
+#define KLOG_ERROR_LEVEL   3
+#define KLOG_WARNING_LEVEL 4
+#define KLOG_NOTICE_LEVEL  5
+#define KLOG_INFO_LEVEL    6
+#define KLOG_DEBUG_LEVEL   7
+
+#else
+#define VSOC_FDPRINTF dprintf
+#endif
+
+#if VSOC_PLATFORM_SDK_BEFORE(M)
+__BEGIN_DECLS
+extern int clock_nanosleep(clockid_t, int, const struct timespec*,
+                             struct timespec*);
+__END_DECLS
+#endif
diff --git a/guest/libs/remoter/Android.mk b/guest/libs/remoter/Android.mk
new file mode 100644
index 0000000..bb0dba7
--- /dev/null
+++ b/guest/libs/remoter/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    remoter_framework_pkt.cpp
+LOCAL_MODULE := libcuttlefish_remoter_framework
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    liblog \
+    libcuttlefish_fs
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common
+LOCAL_CFLAGS := $(VSOC_VERSION_CFLAGS)
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_STATIC_LIBRARY)
+
+
diff --git a/guest/libs/remoter/remoter_framework_pkt.cpp b/guest/libs/remoter/remoter_framework_pkt.cpp
new file mode 100644
index 0000000..68564f1
--- /dev/null
+++ b/guest/libs/remoter/remoter_framework_pkt.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/libs/remoter/remoter_framework_pkt.h"
+
+void remoter_connect(cvd::SharedFD* dest) {
+  *dest = cvd::SharedFD::SocketLocalClient(
+      "remoter", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+  if ((*dest)->IsOpen()) {
+    ALOGE("Failed to connect to remoter (%s)", (*dest)->StrError());
+  } else {
+#ifdef DEBUG_CONNECTIONS
+    ALOGI("Connected to remoter (socket %d)", socket);
+#endif
+  }
+}
+
+int remoter_connect() {
+  int socket = socket_local_client(
+      "remoter", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+  if (socket < 0) {
+    ALOGE("Failed to connect to remoter (%s)", strerror(errno));
+    return -1;
+  } else {
+#ifdef DEBUG_CONNECTIONS
+    ALOGI("Connected to remoter (socket %d)", socket);
+#endif
+  }
+  return socket;
+}
+
diff --git a/guest/libs/remoter/remoter_framework_pkt.h b/guest/libs/remoter/remoter_framework_pkt.h
new file mode 100644
index 0000000..9fc6173
--- /dev/null
+++ b/guest/libs/remoter/remoter_framework_pkt.h
@@ -0,0 +1,284 @@
+/*
+ * 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 <stdlib.h>
+
+#include <cutils/sockets.h>
+#include <cutils/log.h>
+
+#include "common/libs/fs/shared_fd.h"
+
+// #define DEBUG_CONNECTIONS
+
+/*
+ * Packet structures for commands sent to the remoter from the GCE HAL.
+ * This is a private protocol between the HAL and the remoter.
+ */
+
+static const size_t kSensorNameMaxLen = 64;
+// Don't use PATH_MAX here because it would increate the size of every
+// packet that we send to the remoter.
+static const size_t kScreenRecordFilePathMaxLen = 128;
+static const size_t kUnixSocketPathMaxLen = 128;
+
+struct remoter_request_packet {
+  /* Length of the packet in bytes. */
+  uint32_t length;
+
+  /* Operation to perform. */
+  uint8_t operation;
+
+  /* Set to '1' if a response packet is desired */
+  uint8_t send_response;
+
+  /* Operation arguments. */
+  union {
+    /* Arguments for the frame buffer 'post' operation. */
+    struct {
+      /* Y offset in the double-buffer where this frame starts. */
+      uint32_t y_offset;
+    } fb_post_params;
+    /* Arguments for the frame buffer 'update rect' operation. */
+    struct {
+      uint32_t left;
+      uint32_t top;
+      uint32_t width;
+      uint32_t height;
+    } fb_update_rect_params;
+    struct {
+      uint32_t type;
+      bool enabled;
+      int64_t delay_ns;
+      int handle;
+    } sensor_state_params;
+    struct {
+      char filepath[kScreenRecordFilePathMaxLen];
+    } screenrecord_params;
+    struct {
+      char unix_socket[kUnixSocketPathMaxLen];
+    } hal_ready_params;
+  } params;
+} __attribute__((packed));
+
+enum {
+  kRemoterHALReady = 1,
+  kRemoterSensorState
+};
+
+/*
+ * If 'send_response' is set in a request then the remoter will respond
+ * with the following structure.
+ */
+struct remoter_response_packet {
+  uint32_t length;
+  uint8_t status;
+  union {
+    struct {
+      /* Number of 'struct sensor_list_element_packet's to follow */
+      uint8_t num_sensors;
+    } sensor_list_data;
+  } data;
+} __attribute__((packed));
+
+struct sensor_list_element_packet {
+  int handle;
+  int type;
+  char name[kSensorNameMaxLen];
+  char vendor[kSensorNameMaxLen];
+  int version;
+  float max_range;
+  float resolution;
+  float power;
+} __attribute__((packed));
+
+enum {
+  kResponseStatusOk = 1,
+  kResponseStatusFailed
+};
+
+static inline void remoter_request_packet_init(
+    struct remoter_request_packet* pkt, uint8_t operation,
+    uint8_t send_response) {
+  memset(pkt, 0, sizeof(*pkt));
+  pkt->length = sizeof(*pkt);
+  pkt->operation = operation;
+  pkt->send_response = send_response;
+}
+
+static inline void remoter_response_packet_init(
+    struct remoter_response_packet* pkt, uint8_t status) {
+  memset(pkt, 0, sizeof(*pkt));
+  pkt->length = sizeof(*pkt);
+  pkt->status = status;
+}
+
+void remoter_connect(cvd::SharedFD* dest);
+int remoter_connect();
+
+static inline int remoter_read_request(
+    const cvd::SharedFD& socket,
+    struct remoter_request_packet* request) {
+  int len;
+  int remaining_data;
+  /* Packets start with a 4 byte length (which includes the length). */
+
+  if ((len = socket->Read(request, sizeof(request->length))) < 0) {
+    ALOGE("%s: Failed to read remoter request (%s)",
+          __FUNCTION__, socket->StrError());
+    return -1;
+  } else if (len == 0) {
+    return 0;
+  } else if (len != sizeof(request->length)) {
+    ALOGE("%s: Failed to read remoter request (Short read)", __FUNCTION__);
+    return -1;
+  }
+
+  /* Extra paranoia. */
+  if (request->length != sizeof(*request)) {
+    ALOGE("%s: Malformed remoter request", __FUNCTION__);
+    return -1;
+  }
+  remaining_data = request->length - sizeof(request->length);
+  uint8_t* cursor = ((uint8_t*)request) + sizeof(request->length);
+  if ((len = socket->Read(cursor, remaining_data)) < 0) {
+    ALOGE("%s: Failed to read remoter request (%s)",
+          __FUNCTION__, socket->StrError());
+    return -1;
+  } else if (len == 0) {
+    return 0;
+  } else if (len != (int) remaining_data) {
+    ALOGE("%s: Failed to read remoter request (Short read)", __FUNCTION__);
+    return -1;
+  }
+  return 1;
+}
+
+static inline int remoter_read_response(
+    int socket, struct remoter_response_packet* response) {
+  int len;
+  int remaining_data;
+  /* Packets start with a 4 byte length (which includes the length). */
+
+#ifdef DEBUG_CONNECTIONS
+  ALOGI("remoter_read_response(): socket %d, length length = %d", socket,
+        sizeof(response->length));
+#endif
+  if ((len = TEMP_FAILURE_RETRY(
+      read(socket, response, sizeof(response->length)))) < 0) {
+    ALOGE("%s: Failed to read remoter response (%s)",
+          __FUNCTION__, strerror(errno));
+    return -1;
+  } else if (len == 0) {
+    return 0;
+  } else if (len != sizeof(response->length)) {
+    ALOGE("%s: Failed to read remoter response (Short read)", __FUNCTION__);
+    return -1;
+  }
+
+  /* Extra paranoia. */
+  if (response->length != sizeof(*response)) {
+    ALOGE("%s: Malformed remoter response", __FUNCTION__);
+    return -1;
+  }
+  remaining_data = response->length - sizeof(response->length);
+  uint8_t* cursor = ((uint8_t*)response) + sizeof(response->length);
+#ifdef DEBUG_CONNECTIONS
+  ALOGI("remoter_read_request(): socket %d, data length = %d",
+        socket, remaining_data);
+#endif
+  if ((len = TEMP_FAILURE_RETRY(read(socket, cursor, remaining_data))) < 0) {
+    ALOGE("%s: Failed to read remoter response (%s)",
+          __FUNCTION__, strerror(errno));
+    return -1;
+  } else if (len == 0) {
+    return 0;
+  } else if (len != (int) remaining_data) {
+    ALOGE("%s: Failed to read remoter response (Short read)", __FUNCTION__);
+    return -1;
+  }
+  return 1;
+}
+
+static inline int remoter_send_request(
+    int socket, struct remoter_request_packet* request) {
+#ifdef DEBUG_CONNECTIONS
+  ALOGI(
+      "remoter_send_request(): socket %d, length %u", socket, sizeof(*request));
+#endif
+  int len = TEMP_FAILURE_RETRY(write(socket, request, sizeof(*request)));
+  if (len <= 0) {
+    ALOGE("Failed to write request to remoter (%s)", strerror(errno));
+    return -1;
+  } else if (len != sizeof(*request)) {
+    ALOGE("Failed to write request to remoter (short write)");
+    return -1;
+  }
+  return 0;
+}
+
+static inline int remoter_send_response(
+    const cvd::SharedFD& socket,
+    struct remoter_response_packet* response) {
+  int len = socket->Write(response, sizeof(*response));
+  if (len <=0) {
+    ALOGE("%s: Failed to send response to remoter (%s)",
+          __FUNCTION__, strerror(errno));
+    return -1;
+  }
+  return 0;
+}
+
+static inline int remoter_do_single_request_with_socket(
+    int socket, struct remoter_request_packet* request,
+    struct remoter_response_packet* response) {
+
+  if (request->send_response && !response) {
+    ALOGE("%s: Request specifies a response but no response ptr set",
+          __FUNCTION__);
+    return -1;
+  } else if (!request->send_response && response) {
+    ALOGE("%s: Request specifies no response but has response ptr set",
+          __FUNCTION__);
+    return -1;
+  }
+
+  if (remoter_send_request(socket, request) < 0) {
+    return -1;
+  }
+
+  if (response && (remoter_read_response(socket, response) <= 0)) {
+    return -1;
+  }
+  return 0;
+}
+
+static inline int remoter_do_single_request(
+    struct remoter_request_packet* request,
+    struct remoter_response_packet* response) {
+  int socket;
+  if ((socket = remoter_connect()) < 0) {
+    return -1;
+  }
+
+  if (remoter_do_single_request_with_socket(socket, request, response) < 0) {
+    close(socket);
+    return -1;
+  }
+  close(socket);
+  return 0;
+}
+
diff --git a/guest/libs/wpa_supplicant_8_lib/Android.mk b/guest/libs/wpa_supplicant_8_lib/Android.mk
new file mode 100644
index 0000000..eba81f4
--- /dev/null
+++ b/guest/libs/wpa_supplicant_8_lib/Android.mk
@@ -0,0 +1,79 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(WPA_SUPPLICANT_VERSION),VER_0_8_X)
+
+ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),)
+  CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y
+endif
+
+# Use a custom libnl on releases before N
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?))
+EXTERNAL_VSOC_LIBNL_INCLUDE := external/gce/libnl/include
+else
+EXTERNAL_VSOC_LIBNL_INCLUDE :=
+endif
+
+
+WPA_SUPPL_DIR = external/wpa_supplicant_8
+WPA_SRC_FILE :=
+
+include $(WPA_SUPPL_DIR)/wpa_supplicant/android.config
+
+WPA_SUPPL_DIR_INCLUDE = $(WPA_SUPPL_DIR)/src \
+	$(WPA_SUPPL_DIR)/src/common \
+	$(WPA_SUPPL_DIR)/src/drivers \
+	$(WPA_SUPPL_DIR)/src/l2_packet \
+	$(WPA_SUPPL_DIR)/src/utils \
+	$(WPA_SUPPL_DIR)/src/wps \
+	$(WPA_SUPPL_DIR)/wpa_supplicant \
+	$(EXTERNAL_VSOC_LIBNL_INCLUDE)
+
+WPA_SUPPL_DIR_INCLUDE += external/libnl/include
+
+ifdef CONFIG_DRIVER_NL80211
+WPA_SRC_FILE += driver_cmd_nl80211.c
+endif
+
+ifeq ($(TARGET_ARCH),arm)
+# To force sizeof(enum) = 4
+L_CFLAGS += -mabi=aapcs-linux
+endif
+
+ifdef CONFIG_ANDROID_LOG
+L_CFLAGS += -DCONFIG_ANDROID_LOG
+endif
+
+########################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := lib_driver_cmd_simulated
+LOCAL_SHARED_LIBRARIES := libc libcutils
+
+LOCAL_CFLAGS := $(L_CFLAGS) \
+    $(VSOC_VERSION_CFLAGS)
+
+LOCAL_SRC_FILES := $(WPA_SRC_FILE)
+
+LOCAL_C_INCLUDES := \
+  device/google/cuttlefish_common \
+  $(WPA_SUPPL_DIR_INCLUDE)\
+
+include $(BUILD_STATIC_LIBRARY)
+
+########################
+
+endif
diff --git a/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.c b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.c
new file mode 100644
index 0000000..ff7ba23
--- /dev/null
+++ b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.c
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+/*
+ * Driver interaction with extended Linux CFG8021
+ */
+
+#include "driver_cmd_nl80211.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if VSOC_PLATFORM_SDK_AFTER(L_MR1)
+// Android M exposes headers more directly.
+#include <netinet/in.h>
+#include <linux/if.h>
+#include "driver_nl80211.h"
+#elif VSOC_PLATFORM_SDK_AFTER(J_MR2)
+// Android versions K and L put structures in hardware_legacy
+#include "hardware_legacy/driver_nl80211.h"
+#else
+// Android version J does not expose structures directly. These structures are
+// manually defined later.
+#include <netinet/in.h>
+#include <linux/if.h>
+#endif
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "android_drv.h"
+
+
+int wpa_driver_nl80211_driver_cmd(
+    void* priv, char* cmd, char* buf, size_t buf_len) {
+  struct i802_bss* bss = priv;
+  struct wpa_driver_nl80211_data* drv = bss->drv;
+  struct ifreq ifr;
+  android_wifi_priv_cmd priv_cmd;
+  int ret = 0;
+
+  D("%s: called", __FUNCTION__);
+  if (os_strcasecmp(cmd, "STOP") == 0) {
+    linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
+    wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
+  } else if (os_strcasecmp(cmd, "START") == 0) {
+    linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+    wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
+  } else if (os_strcasecmp(cmd, "MACADDR") == 0) {
+    u8 macaddr[ETH_ALEN] = {};
+
+    ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, macaddr);
+    if (!ret)
+      ret = os_snprintf(
+          buf, buf_len, "Macaddr = " MACSTR "\n", MAC2STR(macaddr));
+  } else if (os_strcasecmp(cmd, "RELOAD") == 0) {
+    wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+  } else {  // Use private command
+    return 0;
+  }
+  return ret;
+}
+
+
+int wpa_driver_set_p2p_noa(void* priv, u8 count, int start, int duration) {
+  D("%s: called", __FUNCTION__);
+  return 0;
+}
+
+
+int wpa_driver_get_p2p_noa(void* priv, u8* buf, size_t len) {
+  D("%s: called", __FUNCTION__);
+  return 0;
+}
+
+
+int wpa_driver_set_p2p_ps(void* priv, int legacy_ps, int opp_ps, int ctwindow) {
+  D("%s: called", __FUNCTION__);
+  return -1;
+}
+
+
+int wpa_driver_set_ap_wps_p2p_ie(
+    void* priv, const struct wpabuf* beacon,
+    const struct wpabuf* proberesp, const struct wpabuf* assocresp) {
+  D("%s: called", __FUNCTION__);
+  return 0;
+}
diff --git a/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.h b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.h
new file mode 100644
index 0000000..5d2046e
--- /dev/null
+++ b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.h
@@ -0,0 +1,107 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <guest/libs/platform_support/api_level_fixes.h>
+
+#include <memory.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common.h"
+#include "linux_ioctl.h"
+#include "wpa_supplicant_i.h"
+
+#define LOG_TAG "VSoCWpaSupplicant8Driver"
+
+#include "cutils/log.h"
+
+#define VSOC_WPA_SUPPLICANT_DEBUG 0
+
+#if VSOC_WPA_SUPPLICANT_DEBUG
+#  define D(...) ALOGD(__VA_ARGS__)
+#else
+#  define D(...) ((void)0)
+#endif
+
+
+typedef struct android_wifi_priv_cmd {
+  char* buf;
+  int used_len;
+  int total_len;
+} android_wifi_priv_cmd;
+
+#if VSOC_PLATFORM_SDK_BEFORE(K)
+
+#include "driver.h"
+
+struct i802_bss {
+  struct wpa_driver_nl80211_data* drv;
+  struct i802_bss* next;
+  int ifindex;
+  char ifname[IFNAMSIZ + 1];
+  char brname[IFNAMSIZ];
+
+  unsigned int beacon_set:1;
+  unsigned int added_if_into_bridge:1;
+  unsigned int added_bridge:1;
+  unsigned int in_deinit:1;
+
+  u8 addr[ETH_ALEN];
+
+  int freq;
+
+  void* ctx;
+  struct nl_handle* nl_preq;
+  struct nl_handle* nl_mgmt;
+  struct nl_cb* nl_cb;
+
+  struct nl80211_wiphy_data *wiphy_data;
+  struct dl_list wiphy_list;
+};
+
+struct nl80211_global {
+  struct dl_list interfaces;
+  int if_add_ifindex;
+  struct netlink_data *netlink;
+  struct nl_cb* nl_cb;
+  struct nl_handle* nl;
+  int nl80211_id;
+  int ioctl_sock;  // socket for ioctl() use
+
+  struct nl_handle* nl_event;
+};
+
+struct wpa_driver_nl80211_data {
+  struct nl80211_global* global;
+  struct dl_list list;
+  struct dl_list wiphy_list;
+  char phyname[32];
+  void* ctx;
+  int ifindex;
+  int if_removed;
+  int if_disabled;
+  int ignore_if_down_event;
+  struct rfkill_data* rfkill;
+  struct wpa_driver_capa capa;
+  u8* extended_capa;
+  u8* extended_capa_mask;
+  unsigned int extended_capa_len;
+  int has_capability;
+  // More internal data follows.
+};
+
+#endif  // VSOC_PLATFORM_SDK_AFTER(J)
diff --git a/guest/monitoring/dumpstate_ext/Android.mk b/guest/monitoring/dumpstate_ext/Android.mk
new file mode 100644
index 0000000..3fcd22f
--- /dev/null
+++ b/guest/monitoring/dumpstate_ext/Android.mk
@@ -0,0 +1,58 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common
+LOCAL_CFLAGS := $(VSOC_VERSION_CFLAGS) -DLOG_TAG=\"VSoC-dumpstate\"
+LOCAL_SRC_FILES := dumpstate.cpp
+LOCAL_MODULE := libdumpstate.vsoc
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libdumpstateaidl \
+    libdumpstateutil \
+    libz
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.hardware.dumpstate@1.0-service.cuttlefish
+LOCAL_INIT_RC := android.hardware.dumpstate@1.0-service.cuttlefish.rc
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := \
+    dumpstate_device.cpp \
+    service.cpp
+LOCAL_CFLAGS := $(VSOC_VERSION_CFLAGS) -DLOG_TAG=\"VSoC-dumpstate\"
+LOCAL_SHARED_LIBRARIES := \
+    android.hardware.dumpstate@1.0 \
+    libbase \
+    libcutils \
+    libdumpstateutil \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
+    liblog \
+    libutils
+LOCAL_C_INCLUDES := \
+    device/google/cuttlefish_common \
+    frameworks/native/cmds/dumpstate
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_EXECUTABLE)
diff --git a/guest/monitoring/dumpstate_ext/android.hardware.dumpstate@1.0-service.cuttlefish.rc b/guest/monitoring/dumpstate_ext/android.hardware.dumpstate@1.0-service.cuttlefish.rc
new file mode 100644
index 0000000..ad68f35
--- /dev/null
+++ b/guest/monitoring/dumpstate_ext/android.hardware.dumpstate@1.0-service.cuttlefish.rc
@@ -0,0 +1,4 @@
+service dumpstate-1-0 /vendor/bin/hw/android.hardware.dumpstate@1.0-service.cuttlefish
+    class hal
+    user system
+    group system
diff --git a/guest/monitoring/dumpstate_ext/dumpstate.cpp b/guest/monitoring/dumpstate_ext/dumpstate.cpp
new file mode 100644
index 0000000..89e820b
--- /dev/null
+++ b/guest/monitoring/dumpstate_ext/dumpstate.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/libs/platform_support/api_level_fixes.h"
+
+#if VSOC_PLATFORM_SDK_BEFORE(N)
+extern "C" {
+#endif
+#include <dumpstate.h>
+#if VSOC_PLATFORM_SDK_BEFORE(N)
+}
+#endif
+
+void dumpstate_board() { Dumpstate& ds = Dumpstate::GetInstance(); };
diff --git a/guest/monitoring/dumpstate_ext/dumpstate_device.cpp b/guest/monitoring/dumpstate_ext/dumpstate_device.cpp
new file mode 100644
index 0000000..d8481c7
--- /dev/null
+++ b/guest/monitoring/dumpstate_ext/dumpstate_device.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/monitoring/dumpstate_ext/dumpstate_device.h"
+
+#include <log/log.h>
+#include <DumpstateUtil.h>
+
+using android::os::dumpstate::DumpFileToFd;
+
+namespace android {
+namespace hardware {
+namespace dumpstate {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::dumpstate::V1_0::IDumpstateDevice follow.
+Return<void> DumpstateDevice::dumpstateBoard(const hidl_handle& handle) {
+  if (handle == nullptr || handle->numFds < 1) {
+    ALOGE("no FDs\n");
+    return Void();
+  }
+
+  int fd = handle->data[0];
+  if (fd < 0) {
+    ALOGE("invalid FD: %d\n", handle->data[0]);
+    return Void();
+  }
+
+  DumpFileToFd(fd, "GCE INITIAL METADATA", "/initial.metadata");
+
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace dumpstate
+}  // namespace hardware
+}  // namespace android
diff --git a/guest/monitoring/dumpstate_ext/dumpstate_device.h b/guest/monitoring/dumpstate_ext/dumpstate_device.h
new file mode 100644
index 0000000..581a96b
--- /dev/null
+++ b/guest/monitoring/dumpstate_ext/dumpstate_device.h
@@ -0,0 +1,46 @@
+/*
+ * 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/hardware/dumpstate/1.0/IDumpstateDevice.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace dumpstate {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::sp;
+
+struct DumpstateDevice : public IDumpstateDevice {
+  // Methods from ::android::hardware::dumpstate::V1_0::IDumpstateDevice follow.
+  Return<void> dumpstateBoard(const hidl_handle& h) override;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace dumpstate
+}  // namespace hardware
+}  // namespace android
diff --git a/guest/monitoring/dumpstate_ext/service.cpp b/guest/monitoring/dumpstate_ext/service.cpp
new file mode 100644
index 0000000..67eebe5
--- /dev/null
+++ b/guest/monitoring/dumpstate_ext/service.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "guest/monitoring/dumpstate_ext/dumpstate_device.h"
+
+using ::android::OK;
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
+using ::android::hardware::dumpstate::V1_0::implementation::DumpstateDevice;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::sp;
+
+int main() {
+  sp<IDumpstateDevice> dumpstate = new DumpstateDevice;
+  // This method MUST be called before interacting with any HIDL interfaces.
+  configureRpcThreadpool(1, true);
+  if (dumpstate->registerAsService() != OK) {
+    ALOGE("Could not register service.");
+    return 1;
+  }
+  joinRpcThreadpool();
+}
diff --git a/guest/monitoring/vsoc_service/Android.mk b/guest/monitoring/vsoc_service/Android.mk
new file mode 100644
index 0000000..fb9f6cb
--- /dev/null
+++ b/guest/monitoring/vsoc_service/Android.mk
@@ -0,0 +1,26 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CERTIFICATE := platform
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_PACKAGE_NAME := VSoCService
+LOCAL_PROGUARD_FLAGS := -include build/core/proguard.flags
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_PACKAGE)
diff --git a/guest/monitoring/vsoc_service/AndroidManifest.xml b/guest/monitoring/vsoc_service/AndroidManifest.xml
new file mode 100644
index 0000000..85abd09
--- /dev/null
+++ b/guest/monitoring/vsoc_service/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.google.gce.gceservice"
+    android:sharedUserId="android.uid.system">
+
+    <uses-sdk android:minSdkVersion="5" />
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
+    <uses-permission android:name="android.permission.DUMP" />
+
+    <application
+        android:label="GceService"
+        android:allowBackup="false">
+
+        <receiver android:name=".GceBroadcastReceiver">
+            <intent-filter android:priority="1000">
+                <!--
+                   Do not register for other events here.
+                   Use GceService.registerBroadcastReceivers() instead.
+                -->
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+
+        <service android:name=".GceService">
+            <intent-filter>
+                <action android:name="com.android.google.gce.gceservice.CONFIGURE" />
+                <action android:name="com.android.google.gce.gceservice.CONNECTIVITY_CHANGE" />
+                <action android:name="com.android.google.gce.gceservice.BLUETOOTH_CHANGED" />
+            </intent-filter>
+        </service>
+    </application>
+</manifest>
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/BluetoothChecker.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/BluetoothChecker.java
new file mode 100644
index 0000000..23d4fec
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/BluetoothChecker.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.bluetooth.BluetoothAdapter;
+import android.util.Log;
+
+/*
+ * A job that checks for Bluetooth being enabled before reporting VIRTUAL_DEVICE_BOOT_COMPLETED. Our
+ * devices should always boot with bt enabled, it can be configured in
+ * gce_x86/overlay_<device>/frameworks/base/packages/SettingsProvider/res/values/defaults.xml
+ */
+public class BluetoothChecker extends JobBase {
+    private static final String LOG_TAG = "GceBluetoothChecker";
+    private final GceFuture<Boolean> mEnabled = new GceFuture<Boolean>("Bluetooth");
+
+
+    public BluetoothChecker() {
+        super(LOG_TAG);
+    }
+
+
+    @Override
+    public int execute() {
+        if (mEnabled.isDone()) {
+            return 0;
+        }
+        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (bluetoothAdapter == null) {
+            Log.e(LOG_TAG, "No bluetooth adapter found");
+            mEnabled.set(new Exception("No bluetooth adapter found"));
+        } else {
+            if (bluetoothAdapter.isEnabled()) {
+                Log.i(LOG_TAG, "Bluetooth enabled with name: " + bluetoothAdapter.getName());
+                mEnabled.set(true);
+            } else {
+                Log.i(LOG_TAG, "Bluetooth disabled with name: " + bluetoothAdapter.getName());
+            }
+        }
+        return 0;
+    }
+
+
+    @Override
+    public void onDependencyFailed(Exception e) {
+        mEnabled.set(e);
+    }
+
+
+    public GceFuture<Boolean> getEnabled() {
+        return mEnabled;
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/BootReporter.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/BootReporter.java
new file mode 100644
index 0000000..0a1eecc
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/BootReporter.java
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.os.Handler;
+import android.util.Log;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+/**
+ * Report boot status to console.
+ *
+ * This class sends messages to kernel log (and serial console) directly by
+ * writing to /dev/kmsg.
+ */
+public class BootReporter extends JobBase {
+    private static final String LOG_TAG = "GceBootReporter";
+    private static final int KLOG_NOTICE = 5;
+    private static final String KLOG_OUTPUT = "/dev/kmsg";
+    private static final String KLOG_FORMAT = "<%d>%s: %s\n";
+    private static final String VIRTUAL_DEVICE_BOOT_STARTED = "VIRTUAL_DEVICE_BOOT_STARTED";
+    private static final String VIRTUAL_DEVICE_BOOT_PENDING = "VIRTUAL_DEVICE_BOOT_PENDING";
+    private static final String VIRTUAL_DEVICE_BOOT_COMPLETED = "VIRTUAL_DEVICE_BOOT_COMPLETED";
+    private static final String VIRTUAL_DEVICE_BOOT_FAILED = "VIRTUAL_DEVICE_BOOT_FAILED";
+    private FileOutputStream mKmsgStream = null;
+    private PrintWriter mKmsgWriter = null;
+    private List<String> mMessageList = new ArrayList<String>();
+
+
+    /** Constructor. */
+    public BootReporter() {
+        super(LOG_TAG);
+
+        try {
+            mKmsgStream = new FileOutputStream(KLOG_OUTPUT);
+            mKmsgWriter = new PrintWriter(mKmsgStream);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Could not open output stream.", e);
+        }
+    }
+
+
+    /** Report boot failure.
+     *
+     * Send message to kernel log and serial console explaining boot failure.
+     */
+    @Override
+    public void onDependencyFailed(Exception e) {
+        reportMessage(String.format("%s: %s", VIRTUAL_DEVICE_BOOT_FAILED, e.getMessage()));
+    }
+
+
+    /** Report straggling jobs.
+     *
+     * Reports boot pending, if any of the parent jobs is still awaiting completion
+     * and reschedules itself for re-execution.
+     *
+     * If all jobs have completed, reports boot completed and stops.
+     */
+    @Override
+    public void onDependencyStraggling(ArrayList<GceFuture<?>> deps) {
+        reportMessage(String.format("%s: %s", VIRTUAL_DEVICE_BOOT_PENDING,
+                    GceFuture.toString(deps)));
+    }
+
+
+    /** Report successful boot completion.
+     *
+     * Issue message to serial port confirming successful boot completion and
+     * custom boot completion message, if specified by the user prior to reboot.
+     */
+    @Override
+    public int execute() {
+        // We suspect that something is throttling our messages and preventing
+        // the following message from being logged to bugreport.
+        // The log is present in logcat log (that we collect independently), yet
+        // occasionally most of the GCEService logs never make it to show up on
+        // bug report.
+        // This may or may not prove to be effective. We need to monitor bugreports
+        // for VIRTUAL_DEVICE_BOOT_COMPLETED messages are being dropped.
+        //
+        // Number chosen at random - yet guaranteed to be prime.
+        try {
+            Thread.sleep(937);
+        } catch (InterruptedException e) {}
+
+        reportMessage(VIRTUAL_DEVICE_BOOT_COMPLETED);
+        return 0;
+    }
+
+
+    private void reportMessage(String message) {
+        Log.i(LOG_TAG, message);
+        mKmsgWriter.printf(KLOG_FORMAT, KLOG_NOTICE, LOG_TAG, message);
+        mKmsgWriter.flush();
+        DateFormat df = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+        String date = df.format(Calendar.getInstance().getTime());
+        mMessageList.add("[" + date + "] "  + message);
+    }
+
+
+    public void reportBootStarted() {
+        reportMessage(VIRTUAL_DEVICE_BOOT_STARTED);
+    }
+
+    /** Get the list of reported messages so far.
+     */
+    public List<String> getMessageList() {
+      return mMessageList;
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/ConnectivityChecker.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/ConnectivityChecker.java
new file mode 100644
index 0000000..dfa23d6
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/ConnectivityChecker.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class ConnectivityChecker extends JobBase {
+    private static final String LOG_TAG = "GceConnChecker";
+
+    private final Context mContext;
+    private final GceFuture<Boolean> mConnected = new GceFuture<Boolean>("Connectivity");
+
+
+    public ConnectivityChecker(Context context) {
+        super(LOG_TAG);
+        mContext = context;
+    }
+
+
+    @Override
+    public int execute() {
+        ConnectivityManager connManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (mConnected.isDone()) return 0;
+
+        NetworkInfo[] networks = connManager.getAllNetworkInfo();
+        ArrayList<String> connected = new ArrayList<String>();
+        ArrayList<String> disconnected = new ArrayList<String>();
+
+        for (NetworkInfo network : networks) {
+            if (network.isConnected()) {
+                connected.add(network.getTypeName());
+                mConnected.set(true);
+                break;
+            } else {
+                disconnected.add(network.getTypeName());
+            }
+        }
+
+        Log.i(LOG_TAG, "Connectivity status: connected:" + connected + ", disconnected:" + disconnected);
+
+        return 0;
+    }
+
+
+    @Override
+    public void onDependencyFailed(Exception e) {
+        mConnected.set(e);
+    }
+
+
+    public GceFuture<Boolean> getConnected() {
+        return mConnected;
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceBroadcastReceiver.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceBroadcastReceiver.java
new file mode 100644
index 0000000..754eb8f
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceBroadcastReceiver.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.util.Log;
+
+import com.android.google.gce.gceservice.GceService;
+
+public class GceBroadcastReceiver extends BroadcastReceiver {
+    private static final String LOG_TAG = "GceBroadcastReceiver";
+
+
+    private void reportIntent(Context context, String intentType) {
+        Intent intent = new Intent(context, GceService.class);
+        intent.setAction(intentType);
+        context.startService(intent);
+    }
+
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent != null) {
+            final String action = intent.getAction();
+            Log.i(LOG_TAG, "Received broadcast: " + action);
+
+            if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+                reportIntent(context, GceService.INTENT_ACTION_CONFIGURE);
+            } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+                reportIntent(context, GceService.INTENT_ACTION_NETWORK_CHANGED);
+            } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+                reportIntent(context, GceService.INTENT_ACTION_BLUETOOTH_CHANGED);
+            }
+        }
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceFuture.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceFuture.java
new file mode 100644
index 0000000..ea41409
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceFuture.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+
+public class GceFuture<T> implements Future<T> {
+    private static final String LOG_TAG = "GceFuture";
+    private boolean mDone = false;
+    private Exception mException = null;
+    private T mResult = null;
+    private final String mName;
+
+
+    public GceFuture(String name) {
+        mName = name;
+    }
+
+
+    public String getName() {
+        return mName;
+    }
+
+
+    public void set(T value) {
+        synchronized(this) {
+            if (mDone) {
+                Exception e = new Exception();
+                Log.e(LOG_TAG, mName + ": Multiple return values from a future object.", e);
+                return;
+            }
+
+            mResult = value;
+            mDone = true;
+            notifyAll();
+        }
+    }
+
+
+    public void set(Exception e) {
+        synchronized(this) {
+            if (mDone) {
+                Log.w(LOG_TAG, mName + ": Discarding execution exception -- job done.", e);
+                return;
+            }
+
+            Log.w(LOG_TAG, mName + ": Could not complete job: " + e.getMessage(), e);
+            mException = e;
+            mDone = true;
+            notifyAll();
+        }
+    }
+
+
+    @Override
+    public boolean cancel(boolean canInterrupt) {
+        // We do not support interrupting jobs on purpose:
+        // this offers us little benefit (stripping maybe a second or two), at the expense
+        // of killing something that may cascade, like BroadcastReceiver.
+        synchronized(this) {
+            if (mDone) return false;
+            set(new CancellationException("cancelled"));
+        }
+
+        return true;
+    }
+
+
+    @Override
+    public boolean isCancelled() {
+        synchronized(this) {
+            return (mException != null) && (mException instanceof CancellationException);
+        }
+    }
+
+
+    @Override
+    public boolean isDone() {
+        synchronized(this) {
+            return mDone;
+        }
+    }
+
+
+    @Override
+    public T get() throws CancellationException, ExecutionException, InterruptedException {
+        try {
+            return get(-1, TimeUnit.SECONDS);
+        } catch (TimeoutException e) {
+            // This is a really interesting case to consider.
+            // Fatal error to add to the drama.
+            Log.wtf(LOG_TAG, mName + ": Unexpected condition: Infinite wait timed out.");
+            return null;
+        }
+    }
+
+
+    @Override
+    public T get(long timeout, TimeUnit units)
+    throws CancellationException, ExecutionException, InterruptedException, TimeoutException {
+        waitDone(timeout, units);
+
+        if (mException != null) {
+            if (mException instanceof CancellationException)
+                throw (CancellationException)mException;
+            throw new ExecutionException(mException);
+        }
+
+        return mResult;
+    }
+
+
+    /** Wait for final result.
+     *
+     * Result is considered available, when:
+     * - provider returned value,
+     * - this object was cancelled,
+     * - provider threw an exception.
+     */
+    private void waitDone(long timeout, TimeUnit units)
+            throws InterruptedException, TimeoutException {
+        while (!mDone) {
+            synchronized(this) {
+                if (timeout >= 0) {
+                    this.wait(units.toMillis(timeout));
+                    if (!mDone) throw new InterruptedException();
+                } else {
+                    this.wait();
+                }
+            }
+        }
+    }
+
+
+    /** Convert list of GceFuture objects to string representation.
+     */
+    public static String toString(ArrayList<GceFuture<?>> futures) {
+        StringBuilder b = new StringBuilder();
+        for (GceFuture<?> dep : futures) {
+            if (b.length() > 0) b.append(", ");
+            b.append(dep.getName());
+        }
+
+        return b.toString();
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceService.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceService.java
new file mode 100644
index 0000000..c6c2457
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceService.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.google.gce.gceservice;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.util.Log;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Service is started by the BootCompletedReceiver at the end of Android Boot process.
+ * Responsible for final configuration changes and emitting final BOOT_COMPLETED message.
+ */
+public class GceService extends Service {
+    private static final String LOG_TAG = "GceService";
+    /* Intent sent by the BootCompletedReceiver upon receiving ACTION_BOOT_COMPLETED broadcast. */
+    public static final String INTENT_ACTION_CONFIGURE = "com.android.google.gce.gceservice.CONFIGURE";
+    public static final String INTENT_ACTION_NETWORK_CHANGED = "com.android.google.gce.gceservice.NETWORK_CHANGED";
+    public static final String INTENT_ACTION_BLUETOOTH_CHANGED = "com.android.google.gce.gceservice.BLUETOOTH_CHANGED";
+    private static final int NETWORK_OR_BOOT_TIMEOUT = 30;
+
+    private final JobExecutor mExecutor = new JobExecutor();
+    private final ConnectivityChecker mConnChecker = new ConnectivityChecker(this);
+    private final LocationServicesManager mLocationServices = new LocationServicesManager(this);
+    private final PackageVerifierManager mPackageVerifier = new PackageVerifierManager(this);
+    private final PackageVerificationConsentEnforcer mConsentEnforcer = new PackageVerificationConsentEnforcer(this);
+    private final BootReporter mBootReporter = new BootReporter();
+    private final GceBroadcastReceiver mBroadcastReceiver = new GceBroadcastReceiver();
+    private final BluetoothChecker mBluetoothChecker = new BluetoothChecker();
+    private final TombstoneChecker mTombstoneChecker = new TombstoneChecker();
+
+    private GceWifiManager mWifiManager = null;
+    private String mMostRecentAction = null;
+    private BinderService mBinderService;
+
+
+    public GceService() {}
+
+
+    @Override
+    public void onCreate() {
+        try {
+            super.onCreate();
+            mBootReporter.reportBootStarted();
+            registerBroadcastReceivers();
+
+            mWifiManager = new GceWifiManager(this, mExecutor);
+
+            mExecutor.schedule(mLocationServices);
+            mExecutor.schedule(mPackageVerifier);
+            mExecutor.schedule(mConsentEnforcer);
+            mExecutor.schedule(mWifiManager);
+            mExecutor.schedule(mBluetoothChecker);
+            // TODO(ender): TombstoneChecker is disabled, because we no longer have the code that
+            // produces /ts_snap.txt file. We need to rethink how TombstoneChecker should work.
+            // mExecutor.schedule(mTombstoneChecker);
+
+            mExecutor.schedule(mBootReporter,
+                    mLocationServices.getLocationServicesReady(),
+                    mPackageVerifier.getPackageVerifierReady(),
+                    mConnChecker.getConnected(),
+                    mWifiManager.getInitialWifiStateChangeReady(),
+                    mBluetoothChecker.getEnabled()
+                    // mTombstoneChecker.getTombstoneResult()
+                    );
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Exception caught", e);
+        }
+    }
+
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+
+    /** Register broadcast listeners.
+     *
+     * Certain intents can no longer be used to start a service or activity, but
+     * can still be registered for, such as CONNECTIVITY_ACTION (Android N).
+     */
+    private void registerBroadcastReceivers() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        this.registerReceiver(mBroadcastReceiver, filter);
+    }
+
+
+    /** StartService entry point.
+     */
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (intent != null) {
+            mMostRecentAction = intent.getAction();
+        } else {
+            Log.w(LOG_TAG, "Previous execution failed. Retrying.");
+        }
+
+        if (mMostRecentAction == null) {
+            Log.e(LOG_TAG, "Missing intent action.");
+        }
+
+        if (INTENT_ACTION_CONFIGURE.equals(mMostRecentAction)) {
+            mExecutor.schedule(mConnChecker);
+        } else if (INTENT_ACTION_NETWORK_CHANGED.equals(mMostRecentAction)) {
+            mExecutor.schedule(mConnChecker);
+        } else if (INTENT_ACTION_BLUETOOTH_CHANGED.equals(mMostRecentAction)) {
+            mExecutor.schedule(mBluetoothChecker);
+        }
+
+        mBinderService = new BinderService();
+        ServiceManager.addService("gce", mBinderService, false);
+
+        /* If anything goes wrong, make sure we receive intent again. */
+        return Service.START_STICKY;
+    }
+
+    /** Dump the virtual device state
+     */
+    private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Boot reporter:");
+        List<String> messageList = mBootReporter.getMessageList();
+        for (int i = 0; i < messageList.size(); i++) {
+            pw.println("  " + messageList.get(i));
+        }
+        pw.println("");
+        pw.println("Current system service state:");
+        pw.println("  Location service ready: "
+            + mLocationServices.getLocationServicesReady().isDone());
+        pw.println("  Package verifier ready: "
+            + mPackageVerifier.getPackageVerifierReady().isDone());
+        pw.println("  Network connected: " + mConnChecker.getConnected().isDone());
+        pw.println("  WiFi configured: " + mWifiManager.getInitialWifiStateChangeReady().isDone());
+        pw.println("  Bluetooth enabled: " + mBluetoothChecker.getEnabled().isDone());
+        pw.println("  Tombstone dropped (on boot): "
+            + !mTombstoneChecker.getTombstoneResult().isDone());
+        pw.println("");
+    }
+
+    private final class BinderService extends Binder {
+        @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            dumpInternal(fd, pw, args);
+        }
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceWifiManager.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceWifiManager.java
new file mode 100644
index 0000000..da1b3cf
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/GceWifiManager.java
@@ -0,0 +1,283 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manage WIFI state.
+ */
+public class GceWifiManager extends JobBase {
+    private static final String LOG_TAG = "GceWifiManager";
+    /* Timeout after which another attempt to re-connect wifi will be made. */
+    private static final int WIFI_RECONNECTION_TIMEOUT_S = 3;
+    /* Maximum number of retries before giving up and marking WIFI as inoperable. */
+    private static final int WIFI_RECONNECTION_MAX_ATTEMPTS = 10;
+
+    /** Describes possible WIFI states.
+     * WifiState is:
+     * - UNKNOWN only at the initialization time, replaced with state from
+     *           from Android's WifiManager.
+     * - ENABLED when WIFI is connected and operational,
+     * - DISABLED when WIFI is turned off,
+     * - FAILED if GceWifiManager was unable to configure WIFI.
+     */
+    public enum WifiState {
+        DISABLED,
+        ENABLED;
+    };
+
+    private final JobExecutor mJobExecutor;
+    private final Context mContext;
+    private final WifiManager mWifiManager;
+    private final ConnectivityManager mConnManager;
+
+    private ConfigureWifi mConfigureWifiJob = new ConfigureWifi();
+    private SetWifiState mSetInitialWifiStateJob = new SetWifiState();
+
+
+    /** Constructor.
+    */
+    public GceWifiManager(Context context, JobExecutor executor) {
+        super(LOG_TAG);
+
+        mContext = context;
+        mWifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
+        mConnManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mJobExecutor = executor;
+    }
+
+
+    private boolean isMobileNetworkAvailable() {
+        for (NetworkInfo network : mConnManager.getAllNetworkInfo()) {
+            if (network.getType() == ConnectivityManager.TYPE_MOBILE) return true;
+        }
+        return false;
+    }
+
+
+    private WifiState getExpectedWifiState() {
+        // TODO(ender): we will probably want to define this differently once virtual WIFI is
+        // available again.
+        return WifiState.DISABLED;
+    }
+
+
+    /** Executed during initial configuration.
+    */
+    @Override
+    public synchronized int execute() {
+        WifiState initialState = getExpectedWifiState();
+        mSetInitialWifiStateJob.setState(initialState);
+
+        // Only configure wifi if expected state is ENABLED.
+        // Configuring wifi *requires* wpa_supplicant to be up.
+        // This means that in order to configure wifi, we have to enable it first.
+        if (initialState == WifiState.ENABLED) {
+            mJobExecutor.schedule(mConfigureWifiJob);
+            mJobExecutor.schedule(mSetInitialWifiStateJob, mConfigureWifiJob.getWifiConfigured());
+        } else {
+            // If initial state is DISABLED, there's no need to wait for Wifi configuration to
+            // complete. Just shut it off.
+            mJobExecutor.schedule(mSetInitialWifiStateJob);
+        }
+        return 0;
+    }
+
+
+    @Override
+    public void onDependencyFailed(Exception e) {
+        Log.e(LOG_TAG, "Initial WIFI configuration failed due to dependency.", e);
+        getInitialWifiStateChangeReady().set(e);
+    }
+
+
+    public GceFuture<Boolean> getInitialWifiStateChangeReady() {
+        return mSetInitialWifiStateJob.getWifiReady();
+    }
+
+
+    /* Configure WIFI network stack.
+     *
+     * Adds network configuration that covers AndroidWifi virtual hotspot.
+     */
+    private class ConfigureWifi extends JobBase {
+        private final GceFuture<Boolean> mWifiConfigured =
+                new GceFuture<Boolean>("WIFI Configured");
+        private boolean mReportedWaitingForSupplicant = false;
+
+
+        public ConfigureWifi() {
+            super(LOG_TAG);
+        }
+
+
+        @Override
+        public int execute() {
+            if (mWifiConfigured.isDone()) return 0;
+
+            if (!mWifiManager.pingSupplicant()) {
+                if (!mWifiManager.isWifiEnabled()) {
+                    mWifiManager.setWifiEnabled(true);
+                }
+                if (!mReportedWaitingForSupplicant) {
+                    Log.i(LOG_TAG, "Supplicant not ready.");
+                    mReportedWaitingForSupplicant = true;
+                }
+                return WIFI_RECONNECTION_TIMEOUT_S;
+            }
+
+            WifiConfiguration conf = new WifiConfiguration();
+            conf.SSID = "\"AndroidWifi\"";
+            conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+            int network_id = mWifiManager.addNetwork(conf);
+            if (network_id < 0) {
+                Log.e(LOG_TAG, "Could not update wifi network.");
+                mWifiConfigured.set(new Exception("Could not add WIFI network"));
+            } else {
+                mWifiManager.enableNetwork(network_id, false);
+                mWifiConfigured.set(true);
+            }
+            return 0;
+        }
+
+
+        @Override
+        public void onDependencyFailed(Exception e) {
+            Log.e(LOG_TAG, "Could not configure WIFI.", e);
+            mWifiConfigured.set(e);
+        }
+
+
+        public GceFuture<Boolean> getWifiConfigured() {
+            return mWifiConfigured;
+        }
+    }
+
+
+    /* Modifies Wifi state:
+     * - if wifi disable requested (state == false), simply turns off wifi.
+     * - if wifi enable requested (state == true), turns on wifi and arms the
+     *   connection timeout (see startWifiReconnectionTimeout).
+     */
+    private class SetWifiState extends JobBase {
+        private final GceFuture<Boolean> mWifiReady =
+                new GceFuture<Boolean>("WIFI Ready");
+        private WifiState mDesiredState = WifiState.DISABLED;
+        private int mWifiStateChangeAttempt = 0;
+        private boolean mReportedWifiNotConnected = false;
+
+
+        public SetWifiState() {
+            super(LOG_TAG);
+        }
+
+
+        public void setState(WifiState state) {
+            mDesiredState = state;
+        }
+
+
+        public synchronized void cancel() {
+            if (!mWifiReady.isDone()) {
+                mWifiReady.cancel(false);
+            }
+        }
+
+
+        @Override
+        public synchronized int execute() {
+            WifiState currentState = mWifiManager.isWifiEnabled() ?
+                    WifiState.ENABLED : WifiState.DISABLED;
+
+            // Could be cancelled or exception.
+            if (mWifiReady.isDone()) return 0;
+
+            if (mWifiStateChangeAttempt >= WIFI_RECONNECTION_MAX_ATTEMPTS) {
+                mWifiReady.set(new Exception(
+                        String.format("Unable to change wifi state after %d attempts.",
+                            WIFI_RECONNECTION_MAX_ATTEMPTS)));
+                return 0;
+            }
+
+            if (currentState == mDesiredState) {
+                switch (currentState) {
+                    case ENABLED:
+                        // Wifi is enabled, but probably not yet connected. Check.
+                        WifiInfo info = mWifiManager.getConnectionInfo();
+                        if (info.getSupplicantState() != SupplicantState.COMPLETED) {
+                            if (!mReportedWifiNotConnected) {
+                                Log.w(LOG_TAG, "Wifi not yet connected.");
+                                mReportedWifiNotConnected = true;
+                            }
+                        } else {
+                            Log.i(LOG_TAG, "Wifi connected.");
+                            mWifiReady.set(true);
+                        }
+                        break;
+
+                    case DISABLED:
+                        // There's nothing extra to check for disable wifi.
+                        mWifiReady.set(true);
+                        break;
+                }
+
+                if (mWifiReady.isDone()) {
+                    return 0;
+                }
+            }
+
+            // At this point we know that:
+            // - current state is different that desired state, or
+            // - current state is enabled, but wifi is not yet connected.
+            ++mWifiStateChangeAttempt;
+
+            switch (mDesiredState) {
+                case DISABLED:
+                    mWifiManager.setWifiEnabled(false);
+                    break;
+
+                case ENABLED:
+                    mWifiManager.setWifiEnabled(true);
+                    mWifiManager.reconnect();
+                    break;
+            }
+            return WIFI_RECONNECTION_TIMEOUT_S;
+        }
+
+
+        @Override
+        public void onDependencyFailed(Exception e) {
+            Log.e(LOG_TAG, "Wifi state change failed due to failing dependency.", e);
+            mWifiReady.set(e);
+        }
+
+
+        public GceFuture<Boolean> getWifiReady() {
+            return mWifiReady;
+        }
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/JobBase.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/JobBase.java
new file mode 100644
index 0000000..aceb70f
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/JobBase.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.util.Log;
+import java.util.ArrayList;
+
+public abstract class JobBase {
+    private final String mTag;
+
+    JobBase(String tag) {
+        mTag = tag;
+    }
+
+
+    /** Invoked, when job could not be started due to dependency failure.
+     *
+     * Supplied exception describes reason of failure.
+     */
+    public abstract void onDependencyFailed(Exception exception);
+
+
+    /** Invoked, when one or more tasks have taken substantial time to complete.
+     *
+     * This is not a direct indication of an error, but merely an information
+     * about what stops current job from being executed.
+     *
+     * @param stragglingDependencies list of dependencies that have not completed
+     *        in last 10 seconds.
+     */
+    public void onDependencyStraggling(ArrayList<GceFuture<?>> stragglingDependencies) {
+        Log.i(mTag, "Waiting for: " + GceFuture.toString(stragglingDependencies));
+    }
+
+
+    /** Invoked, when all dependencies are ready and job is clear to commence.
+     *
+     * Returns number of second before re-execution, or 0, if job is done.
+     */
+    public abstract int execute();
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/JobExecutor.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/JobExecutor.java
new file mode 100644
index 0000000..9cb3225
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/JobExecutor.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.util.Log;
+import com.android.google.gce.gceservice.JobBase;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.HashSet;
+
+public class JobExecutor {
+    private static final int THREAD_POOL_SIZE = 8;
+    private static final String LOG_TAG = "GceExecutor";
+    private static final int ITERATION_PERIOD_MS = 10 * 1000;
+    private final ScheduledExecutorService mExecutor;
+    private final Set<String> mStartedJobs;
+
+    public JobExecutor() {
+        mExecutor = Executors.newScheduledThreadPool(THREAD_POOL_SIZE);
+        mStartedJobs = new HashSet<String>();
+    }
+
+    /** Schedule job for (periodic) execution.
+     *
+     * First execution is performed at the earliest possibility.
+     * Execution stops when a call to execute() returns a nonpositive number.
+     *
+     * Note: iteration time is |delaySeconds| + total time needed to execute job.
+     */
+    public void schedule(final JobBase job, final GceFuture<?>... futures) {
+        mExecutor.schedule(new Runnable() {
+            private boolean mDependenciesReady = false;
+            private ArrayList<GceFuture<?>> mFutures =
+                    new ArrayList<GceFuture<?>>(Arrays.asList(futures));
+
+            public void run() {
+                if (!mDependenciesReady) {
+                    while (!mFutures.isEmpty()) {
+                        ArrayList<GceFuture<?>> stragglers = new ArrayList<GceFuture<?>>();
+                        long endTime = System.currentTimeMillis() + ITERATION_PERIOD_MS;
+                        for (GceFuture<?> future : mFutures) {
+                            try {
+                                // Wait for at most ITERATION_PERIOD_MS. Check all futures,
+                                // collect only those that still failed to complete.
+                                future.get(
+                                        Math.max(0, endTime - System.currentTimeMillis()),
+                                        TimeUnit.MILLISECONDS);
+                            } catch (TimeoutException e) {
+                                stragglers.add(future);
+                            } catch (InterruptedException e) {
+                                // In theory we should re-try this one.
+                                // In practice this hardly ever happens, so let's just
+                                // give it another go in the second loop.
+                                stragglers.add(future);
+                            } catch (Exception e) {
+                                Log.e(LOG_TAG, String.format(
+                                        "Could not start job %s.", job.getClass().getName()),
+                                        e);
+                                job.onDependencyFailed(e);
+                                return;
+                            }
+                        }
+
+                        mFutures = stragglers;
+                        if (!mFutures.isEmpty()) {
+                            job.onDependencyStraggling(mFutures);
+                        }
+                    }
+                    mDependenciesReady = true;
+                }
+
+                try {
+                    String jobName = job.getClass().getName();
+                    if (!mStartedJobs.contains(jobName)) {
+                        mStartedJobs.add(jobName);
+                    }
+                    int delaySeconds = job.execute();
+                    if (delaySeconds > 0) {
+                        mExecutor.schedule(this, delaySeconds, TimeUnit.SECONDS);
+                    } else {
+                        mStartedJobs.remove(jobName);
+                    }
+                } catch (Exception e) {
+                    Log.e(LOG_TAG, String.format("Job %s threw an exception and will not be re-scheduled.",
+                                job.getClass().getName()), e);
+                }
+            }
+        }, 0, TimeUnit.SECONDS);
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/LocationServicesManager.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/LocationServicesManager.java
new file mode 100644
index 0000000..f9ad388
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/LocationServicesManager.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.util.Log;
+import com.android.google.gce.gceservice.GceFuture;
+import com.android.google.gce.gceservice.JobBase;
+
+/**
+ * Configure Location Services on Android Jellybean.
+ * No action on more recent versions of Android.
+ */
+class LocationServicesManager extends JobBase {
+    private static final String LOG_TAG = "GceLocationServicesManager";
+    private static final String ACTION_LOCATION_SERVICES_CONSENT_INTENT =
+            "com.google.android.gsf.action.SET_USE_LOCATION_FOR_SERVICES";
+    private static final String EXTRA_LOCATION_SERVICES_CONSENT_DISABLE =
+            "disable";
+    private final Context mContext;
+    private final GceFuture<Boolean> mResult = new GceFuture<Boolean>("Location Services");
+
+
+    LocationServicesManager(Context context) {
+        super(LOG_TAG);
+        mContext = context;
+    }
+
+
+    public int execute() {
+        /* Check if we're running Jellybean.
+         * Sadly, we can't use version name Build.VERSION_CODES.JELLY_BEAN_MR2
+         * because MR1 and MR0 don't know this number.
+         */
+        if (Build.VERSION.SDK_INT <= 18) {
+            Intent intent = new Intent();
+            intent.setAction(ACTION_LOCATION_SERVICES_CONSENT_INTENT);
+            intent.setFlags(intent.getFlags() |
+                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            intent.putExtra(EXTRA_LOCATION_SERVICES_CONSENT_DISABLE, false);
+            mContext.startActivity(intent);
+        }
+
+        mResult.set(true);
+        return 0;
+    }
+
+
+    public void onDependencyFailed(Exception e) {
+        Log.e(LOG_TAG, "Could not configure LocationServices.", e);
+        mResult.set(e);
+    }
+
+
+    public GceFuture<Boolean> getLocationServicesReady() {
+        return mResult;
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/PackageVerificationConsentEnforcer.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/PackageVerificationConsentEnforcer.java
new file mode 100644
index 0000000..e45d656
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/PackageVerificationConsentEnforcer.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Log;
+
+/**
+ * Forces pacakge verification to be off on N and N-MR1 by adjusting package_verifier_user_consent.
+ *
+ * This is needed because CVDs don't have a touch screen, and the consent
+ * dialog will block apk installs.
+ *
+ * Possible values for consent seem to be:
+ *   -1 The user refused
+ *    0 Ask the user
+ *    1 The user accepted
+ *
+ * This code polls because Android may overwrite a non-zero value with a 0
+ * at some point after boot completes. However, this happens only on some
+ * boots, so it can't be a blocker for boot complete.
+ */
+class PackageVerificationConsentEnforcer extends JobBase {
+    private static final String LOG_TAG = "GcePVCR";
+    private static final String PACKAGE_VERIFIER_USER_CONSENT = "package_verifier_user_consent";
+    private final Context mContext;
+
+    // Chosen to avoid the possible values (see top comment).
+    private int mLastObservedValue = -2;
+
+
+    public PackageVerificationConsentEnforcer(Context context) {
+        super(LOG_TAG);
+        mContext = context;
+    }
+
+
+    public int execute() {
+        if (android.os.Build.VERSION.SDK_INT < 24) {
+            // Skip older android versions.
+            return 0;
+        }
+
+        try {
+            ContentResolver contentResolver = mContext.getContentResolver();
+            int value = Settings.Secure.getInt(contentResolver, PACKAGE_VERIFIER_USER_CONSENT);
+            if (value != mLastObservedValue) {
+                mLastObservedValue = value;
+            }
+
+            if (value == 0) {
+                Settings.Secure.putInt(mContext.getContentResolver(), PACKAGE_VERIFIER_USER_CONSENT, -1);
+            }
+        } catch (SettingNotFoundException e) {
+        }
+
+        return 1;
+    }
+
+
+    public void onDependencyFailed(Exception e) {
+        Log.e(LOG_TAG, "Could not start Consent Enforcer.", e);
+    }
+}
+
+
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/PackageVerifierManager.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/PackageVerifierManager.java
new file mode 100644
index 0000000..e10845a
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/PackageVerifierManager.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+package com.android.google.gce.gceservice;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Log;
+
+/**
+ * Disable package verifier.
+ */
+public class PackageVerifierManager extends JobBase {
+    private static final String LOG_TAG = "GcePackageVerifierManager";
+    private static final String SETTING_PACKAGE_VERIFIER_ENABLE = "verifier_verify_adb_installs";
+    private final Context mContext;
+    private final GceFuture<Boolean> mResult =
+            new GceFuture<Boolean>("Package Verifier");
+
+
+    PackageVerifierManager(Context context) {
+        super(LOG_TAG);
+        mContext = context;
+    }
+
+
+    private boolean getAndLogPackageVerifierState() {
+        int package_verifier_state = 1;
+        try {
+            ContentResolver contentResolver = mContext.getContentResolver();
+            package_verifier_state = Settings.Secure.getInt(contentResolver, SETTING_PACKAGE_VERIFIER_ENABLE);
+        } catch (SettingNotFoundException e) {
+            Log.w(LOG_TAG, "Could not read package verifier state. Assuming it's enabled.");
+        }
+
+        return package_verifier_state != 0;
+    }
+
+
+    public int execute() {
+        if (getAndLogPackageVerifierState()) {
+            Settings.Secure.putInt(mContext.getContentResolver(), SETTING_PACKAGE_VERIFIER_ENABLE, 0);
+            // One more call, just to log the state.
+            getAndLogPackageVerifierState();
+        }
+
+        mResult.set(true);
+        return 0;
+    }
+
+
+    public void onDependencyFailed(Exception e) {
+        Log.e(LOG_TAG, "Could not disable Package Verifier.", e);
+        mResult.set(e);
+    }
+
+
+    public GceFuture<Boolean> getPackageVerifierReady() {
+        return mResult;
+    }
+}
diff --git a/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/TombstoneChecker.java b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/TombstoneChecker.java
new file mode 100644
index 0000000..dec0837
--- /dev/null
+++ b/guest/monitoring/vsoc_service/java/com/android/google/gce/gceservice/TombstoneChecker.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.google.gce.gceservice;
+
+import android.util.Log;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Scanner;
+
+/** A job that checks for any new tombstones before reporting VIRTUAL_DEVICE_BOOT_COMPLETED.
+ *
+ */
+public class TombstoneChecker extends JobBase {
+    private static final String LOG_TAG = "GceTombstoneChecker";
+    private static final String sSnapshotDir = "/data/tombstones";
+    private static final String sSnapshotFile = "/ts_snap.txt";
+    private static final String sTsExceptionMessage = "GceTombstoneChecker internal error. ";
+    private static final String sTsFilePrefix = "tombstone";
+    private final GceFuture<Boolean> mPassed = new GceFuture<Boolean>("GceTombstoneChecker");
+    private ArrayList<Record> mPreBootRecords = new ArrayList<Record>();
+    private ArrayList<Record> mPostBootRecords = new ArrayList<Record>();
+
+    public TombstoneChecker() {
+        super(LOG_TAG);
+    }
+
+
+    @Override
+    public int execute() {
+        if (mPassed.isDone()) {
+            return 0;
+        }
+
+        try {
+            readPreBootSnapshot();
+            capturePostBootSnapshot();
+            if (seenNewTombstones()) {
+                Log.e(LOG_TAG, "Tombstones created during boot. ");
+                for (int i = 0; i < mPostBootRecords.size(); i++) {
+                    Log.i(LOG_TAG, mPostBootRecords.get(i).getFileName());
+                }
+                mPassed.set(new Exception("Tombstones created. "));
+            } else {
+                mPassed.set(true);
+            }
+        } catch(Exception e) {
+            Log.e(LOG_TAG, sTsExceptionMessage + e);
+            mPassed.set(new Exception(sTsExceptionMessage, e));
+        }
+
+        return 0;
+    }
+
+    @Override
+    public void onDependencyFailed(Exception e) {
+        mPassed.set(e);
+    }
+
+    public GceFuture<Boolean> getTombstoneResult() {
+        return mPassed;
+    }
+
+    private void capturePostBootSnapshot() throws Exception {
+        File dir = new File(sSnapshotDir);
+        File[] files = dir.listFiles();
+
+        // In K & L, /data/tombstones directory is not created during boot. So
+        // dir.listFiles() can return null.
+        if (files == null) {
+            return;
+        }
+
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].isFile() && files[i].getName().startsWith(sTsFilePrefix)) {
+                long ctime = files[i].lastModified() / 1000;
+                mPostBootRecords.add(new Record(files[i].getName(), ctime));
+            }
+        }
+        Collections.sort(mPostBootRecords);
+
+        return;
+    }
+
+    private void readPreBootSnapshot() throws Exception {
+        File file = new File(sSnapshotFile);
+        if (!file.isFile()) {
+            throw new FileNotFoundException(sSnapshotFile);
+        }
+
+        Scanner scanner = new Scanner(file);
+        while (scanner.hasNext()) {
+            String[] fields = scanner.nextLine().split(" ");
+            mPreBootRecords.add(new Record(fields[0], Long.parseLong(fields[1])));
+        }
+        Collections.sort(mPreBootRecords);
+
+        return;
+    }
+
+    private boolean seenNewTombstones() {
+        return !isEqual(mPreBootRecords, mPostBootRecords);
+    }
+
+    private boolean isEqual(ArrayList<Record> preBoot, ArrayList<Record> postBoot) {
+        postBoot.removeAll(preBoot);
+        if (postBoot.size() != 0) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private class Record implements Comparable<Record> {
+        private String mFilename;
+        private long mCtime;
+
+        public Record(String filename, long ctime) {
+            this.mFilename = filename;
+            this.mCtime = ctime;
+        }
+
+        public String getFileName() {
+            return mFilename;
+        }
+
+        public int compareTo(Record r) {
+            if (this == r) {
+                return 0;
+            }
+
+            return (mFilename.compareTo(r.mFilename));
+        }
+
+        public boolean equals(Object o) {
+            if (o == null) {
+                return false;
+            }
+
+            if (this == o) {
+                return true;
+            }
+
+            Record r = (Record) o;
+            return (mFilename.equals(r.mFilename) && (mCtime == r.mCtime));
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(mFilename).append(" ").append(String.valueOf(mCtime));
+            return sb.toString();
+        }
+    }
+}
diff --git a/guest/monitoring/vsoc_service/proguard.flags b/guest/monitoring/vsoc_service/proguard.flags
new file mode 100644
index 0000000..c09a728
--- /dev/null
+++ b/guest/monitoring/vsoc_service/proguard.flags
@@ -0,0 +1,26 @@
+# 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.
+
+# Keep enough data for stack traces
+-renamesourcefileattribute SourceFile
+-keepattributes SourceFile,LineNumberTable,*Annotation*
+
+# Keep classes and methods that have the guava @VisibleForTesting annotation
+-keep @com.google.common.annotations.VisibleForTesting class *
+-keepclassmembers class * {
+  @com.google.common.annotations.VisibleForTesting *;
+}
+
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
diff --git a/guest/vsoc/lib/Android.mk b/guest/vsoc/lib/Android.mk
new file mode 100644
index 0000000..5ea78d7
--- /dev/null
+++ b/guest/vsoc/lib/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := vsoc_guest_region_e2e_test
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := guest_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
+
+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/gralloc_region_view.cpp b/guest/vsoc/lib/gralloc_region_view.cpp
new file mode 100644
index 0000000..2ec1aba
--- /dev/null
+++ b/guest/vsoc/lib/gralloc_region_view.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "guest/vsoc/lib/gralloc_region_view.h"
+
+#include <atomic>
+#include <common/vsoc/lib/lock_guard.h>
+#include <log/log.h>
+#include <sys/types.h>
+#include <uapi/vsoc_shm.h>
+
+using vsoc::gralloc::GrallocRegionView;
+using vsoc::layout::gralloc::BufferEntry;
+using vsoc::layout::gralloc::GrallocBufferLayout;
+using vsoc::layout::gralloc::GrallocManagerLayout;
+
+namespace {
+template <typename T>
+inline T gralloc_align(T val) {
+  return (val + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+}
+
+// Use the thread id to identify the original creator of a buffer.
+inline uint32_t gralloc_owned_value() {
+  return gettid();
+}
+
+}  // namespace
+
+GrallocRegionView::GrallocRegionView() {
+  // The construction in the singleton is thread safe, so we call Open here to
+  // make sure it opens thread safe too. The singleton will return null if the
+  // region failed to open.
+  Open();
+}
+
+bool GrallocRegionView::Open() {
+  if (is_open_) {
+    return true;
+  }
+  if (!vsoc::ManagerRegionView<GrallocManagerLayout>::Open()) {
+    return false;
+  }
+  std::shared_ptr<vsoc::RegionControl> managed_region =
+      vsoc::RegionControl::Open(
+          GrallocManagerLayout::ManagedRegion::region_name);
+  if (!managed_region) {
+    LOG_FATAL("Unable to open managed region");
+    return false;
+  }
+  offset_of_buffer_memory_ = gralloc_align<vsoc_reg_off_t>(
+      managed_region->region_desc().offset_of_region_data);
+  total_buffer_memory_ =
+      managed_region->region_size() - offset_of_buffer_memory_;
+
+  // TODO(jemoreira): Handle the case of unexpected values in the region.
+  is_open_ = true;
+  return true;
+}
+
+int GrallocRegionView::AllocateBuffer(size_t size, uint32_t* begin_offset) {
+  size = gralloc_align<size_t>(size);
+  // Cache the value of buffer_count in shared memory.
+  uint32_t buffer_count_local = 0;
+  {
+    vsoc::LockGuard<vsoc::layout::GuestLock>(&data()->new_buffer_lock);
+    buffer_count_local = data()->buffer_count;
+  }
+  // Find a free buffer entry of the appropriate size.
+  for (uint32_t idx = 0; idx < buffer_count_local; ++idx) {
+    BufferEntry& entry = data()->buffers_table[idx];
+    if (entry.owned_by == VSOC_REGION_FREE && entry.buffer_size() == size) {
+      int fd = control_->CreateFdScopedPermission(
+          GrallocManagerLayout::ManagedRegion::region_name,
+          pointer_to_region_offset(&entry.owned_by),
+          gralloc_owned_value(),
+          entry.buffer_begin,
+          entry.buffer_end);
+      if (fd >= 0) {
+        if (begin_offset) {
+          *begin_offset = entry.buffer_begin;
+        }
+        return fd;
+      }
+    }
+  }
+
+  // We couldn't find any suitable buffer, create one
+  {
+    vsoc::LockGuard<vsoc::layout::GuestLock>(&data()->new_buffer_lock);
+    // don't use the cached value here!!!
+    uint32_t idx = data()->buffer_count;
+    if (pointer_to_region_offset(&data()->buffers_table[idx + 1]) >
+        control_->region_size()) {
+      ALOGE(
+          "Out of memory in gralloc_manager (total: %d, used: %d, "
+          "requested: %d)",
+          static_cast<int>(control_->region_size()),
+          static_cast<int>(
+              pointer_to_region_offset(&data()->buffers_table[idx])),
+          static_cast<int>(sizeof(data()->buffers_table[idx])));
+      return -ENOMEM;
+    }
+    if (total_buffer_memory_ - data()->allocated_buffer_memory < size) {
+      ALOGE(
+          "Out of memory in gralloc_memory (total: %d, used: %d, requested: %d)",
+          static_cast<int>(total_buffer_memory_),
+          static_cast<int>(data()->allocated_buffer_memory),
+          static_cast<int>(size));
+      return -ENOMEM;
+    }
+    // Initialize the buffer entry and acquire ownership
+    // Do it before increasing buffer_count so that another thread looking for
+    // free entries doesn't find this one
+    BufferEntry& new_entry = data()->buffers_table[idx];
+    new_entry.buffer_begin =
+        offset_of_buffer_memory_ + data()->allocated_buffer_memory;
+    data()->allocated_buffer_memory += size;
+    new_entry.buffer_end = new_entry.buffer_begin + size;
+    int fd = control_->CreateFdScopedPermission(
+        GrallocManagerLayout::ManagedRegion::region_name,
+        pointer_to_region_offset(&new_entry.owned_by),
+        gralloc_owned_value(),
+        new_entry.buffer_begin,
+        new_entry.buffer_end);
+    if (fd < 0) {
+      LOG_FATAL(
+          "Unexpected error while creating fd scoped permission over "
+          "uncontested memory: %s",
+          strerror(-fd));
+      return fd;
+    }
+    // Increment buffer_count now that the entry can't be taken from us
+    data()->buffer_count++;
+    if (begin_offset) {
+      *begin_offset = new_entry.buffer_begin;
+    }
+    return fd;
+  }
+}
+
+/* static */
+// The C++03 standard does not guarantee this singleton implemention to be
+// thread safe, however magic statics are part of the gcc compiler since
+// version 4.3.
+GrallocRegionView* GrallocRegionView::GetInstance() {
+  static GrallocRegionView singleton;
+  if (!singleton.is_open_) {
+    return NULL;
+  }
+  return &singleton;
+}
diff --git a/guest/vsoc/lib/gralloc_region_view.h b/guest/vsoc/lib/gralloc_region_view.h
new file mode 100644
index 0000000..2432e8c
--- /dev/null
+++ b/guest/vsoc/lib/gralloc_region_view.h
@@ -0,0 +1,50 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <common/vsoc/shm/gralloc_layout.h>
+#include <guest/vsoc/lib/manager_region_view.h>
+#include <stdlib.h>
+
+namespace vsoc {
+namespace gralloc {
+
+class GrallocRegionView : public vsoc::ManagerRegionView<
+                          vsoc::layout::gralloc::GrallocManagerLayout> {
+ public:
+  // Allocates a gralloc buffer of (at least) the specified size. Returns a file
+  // descriptor that exposes the buffer when mmapped from 0 to (the page
+  // aligned) size (and fails to mmap anything outside of that range) or a
+  // negative number in case of error (e.g not enough free memory left).
+  // TODO(jemoreira): Include debug info like stride, width, height, etc
+  int AllocateBuffer(size_t size, uint32_t* begin_offset = nullptr);
+
+  static GrallocRegionView* GetInstance();
+ protected:
+  GrallocRegionView();
+  GrallocRegionView(const GrallocRegionView&) = delete;
+  GrallocRegionView & operator=(const GrallocRegionView&) = delete;
+
+  bool Open();
+
+  vsoc_reg_off_t offset_of_buffer_memory_{};
+  uint32_t total_buffer_memory_{};
+  bool is_open_{false};
+};
+
+} // namespace gralloc
+} // namespace vsoc
diff --git a/guest/vsoc/lib/guest_lock.cpp b/guest/vsoc/lib/guest_lock.cpp
new file mode 100644
index 0000000..67ce665
--- /dev/null
+++ b/guest/vsoc/lib/guest_lock.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/shm/lock.h"
+
+#include "common/vsoc/lib/compat.h"
+#include "common/vsoc/lib/single_sided_signal.h"
+
+namespace vsoc {
+namespace layout {
+
+void GuestLock::Lock() {
+  uint32_t tid = gettid();
+  uint32_t expected_value;
+  uint32_t* uaddr = reinterpret_cast<uint32_t*>(&lock_uint32_);
+
+  while (1) {
+    if (TryLock(tid, &expected_value)) {
+      return;
+    }
+    SingleSidedSignal::AwaitSignal(expected_value, uaddr);
+  }
+}
+
+void GuestLock::Unlock() {
+  Sides sides_to_signal = UnlockCommon(gettid());
+  if (sides_to_signal.value_ != Sides::NoSides) {
+    SingleSidedSignal::Signal(&lock_uint32_);
+  }
+}
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/guest/vsoc/lib/guest_region_e2e_test.cpp b/guest/vsoc/lib/guest_region_e2e_test.cpp
new file mode 100644
index 0000000..4b71249
--- /dev/null
+++ b/guest/vsoc/lib/guest_region_e2e_test.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * End-to-end test to ensure that mapping of vsoc regions works on the guest.
+ */
+
+#include "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(View *r) {
+  disable_tombstones();
+  // region.Open should never return.
+  EXPECT_FALSE(r->Open());
+}
+
+// Here is a summary of the two regions interrupt and write test:
+// 1. Write our strings to the first region
+// 2. Ensure that our peer hasn't signalled the second region. That would
+//    indicate that it didn't wait for our interrupt.
+// 3. Send the interrupt on the first region
+// 4. Wait for our peer's interrupt on the first region
+// 5. Confirm that we can see our peer's writes in the first region
+// 6. Initialize our strings in the second region
+// 7. Send an interrupt on the second region to our peer
+// 8. Wait for our peer's interrupt on the second region
+// 9. Confirm that we can see our peer's writes in the second region
+// 10. Repeat the process for signaling.
+// 11. Confirm that no interrupt is pending in the first region
+// 12. Confirm that no interrupt is pending in the second region
+
+template <typename View>
+void SetGuestStrings(View* in) {
+  size_t num_data = in->string_size();
+  EXPECT_LE(2U, num_data);
+  for (size_t i = 0; i < num_data; ++i) {
+    EXPECT_TRUE(!in->guest_string(i)[0] ||
+                !strcmp(in->guest_string(i), View::Layout::guest_pattern));
+    in->set_guest_string(i, View::Layout::guest_pattern);
+    EXPECT_STREQ(in->guest_string(i), View::Layout::guest_pattern);
+  }
+}
+
+template <typename View>
+void CheckPeerStrings(View* in) {
+  size_t num_data = in->string_size();
+  EXPECT_LE(2, num_data);
+  for (size_t i = 0; i < num_data; ++i) {
+    EXPECT_STREQ(View::Layout::host_pattern, in->host_string(i));
+  }
+}
+
+TEST(RegionTest, BasicPeerTests) {
+  vsoc::E2EPrimaryRegionView primary;
+  vsoc::E2ESecondaryRegionView secondary;
+  ASSERT_TRUE(primary.Open());
+  ASSERT_TRUE(secondary.Open());
+  LOG(INFO) << "Regions are open";
+  SetGuestStrings(&primary);
+  LOG(INFO) << "Primary guest strings are set";
+  EXPECT_FALSE(secondary.HasIncomingInterrupt());
+  LOG(INFO) << "Verified no early second interrupt";
+  EXPECT_TRUE(primary.MaybeInterruptPeer());
+  LOG(INFO) << "Interrupt sent. Waiting for first interrupt from peer";
+  primary.WaitForInterrupt();
+  LOG(INFO) << "First interrupt received";
+  CheckPeerStrings(&primary);
+  LOG(INFO) << "Verified peer's primary strings";
+  SetGuestStrings(&secondary);
+  LOG(INFO) << "Secondary guest strings are set";
+  EXPECT_TRUE(secondary.MaybeInterruptPeer());
+  LOG(INFO) << "Second interrupt sent";
+  secondary.WaitForInterrupt();
+  LOG(INFO) << "Second interrupt received";
+  CheckPeerStrings(&secondary);
+  LOG(INFO) << "Verified peer's secondary strings";
+
+  // Test signals
+  EXPECT_FALSE(secondary.HasIncomingInterrupt());
+  LOG(INFO) << "Verified no early second signal";
+  vsoc::layout::Sides side;
+  side.value_ = vsoc::layout::Sides::Peer;
+  primary.SendSignal(side, &primary.data()->guest_to_host_signal);
+  LOG(INFO) << "Signal sent. Waiting for first signal from peer";
+  primary.WaitForInterrupt();
+  int count = 0; // counts the number of signals received.
+  primary.ProcessSignalsFromPeer([&primary, &count](uint32_t* uaddr){
+      ++count;
+      EXPECT_TRUE(uaddr == &primary.data()->host_to_guest_signal);
+    });
+  EXPECT_TRUE(count == 1);
+  LOG(INFO) << "Signal received on primary region";
+  secondary.SendSignal(side, &secondary.data()->guest_to_host_signal);
+  LOG(INFO) << "Signal sent. Waiting for second signal from peer";
+  secondary.WaitForInterrupt();
+  count = 0;
+  secondary.ProcessSignalsFromPeer([&secondary, &count](uint32_t* uaddr){
+      ++count;
+      EXPECT_TRUE(uaddr == &secondary.data()->host_to_guest_signal);
+    });
+  EXPECT_TRUE(count == 1);
+  LOG(INFO) << "Signal received on secondary region";
+
+  EXPECT_FALSE(primary.HasIncomingInterrupt());
+  EXPECT_FALSE(secondary.HasIncomingInterrupt());
+  LOG(INFO) << "PASS: BasicPeerTests";
+}
+
+TEST(RegionTest, MissingRegionDeathTest) {
+  vsoc::E2EUnfindableRegionView test;
+  // EXPECT_DEATH creates a child for the test, so we do it out here.
+  // DeathTestGuestRegion will actually do the deadly call after ensuring
+  // that we don't create an unwanted tombstone.
+  EXPECT_EXIT(DeathTestView(&test), testing::ExitedWithCode(2),
+              ".*" DEATH_TEST_MESSAGE ".*");
+}
+
+class ManagedRegionTest {
+ public:
+  void testManagedRegionFailMap() {
+    vsoc::TypedRegionView<E2EManagedTestRegionLayout> 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:
+  vsoc::ManagerRegionView<E2EManagerTestRegionLayout> 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) {
+  android::base::InitLogging(argv);
+  testing::InitGoogleTest(&argc, argv);
+  int rval = RUN_ALL_TESTS();
+  if (!rval) {
+    vsoc::E2EPrimaryRegionView region;
+    region.Open();
+    region.guest_status(vsoc::layout::e2e_test::E2E_MEMORY_FILLED);
+    LOG(INFO) << "stage_1_guest_region_e2e_tests PASSED";
+  }
+  return rval;
+}
diff --git a/guest/vsoc/lib/manager_region_view.h b/guest/vsoc/lib/manager_region_view.h
new file mode 100644
index 0000000..6c65dec
--- /dev/null
+++ b/guest/vsoc/lib/manager_region_view.h
@@ -0,0 +1,58 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/vsoc/lib/typed_region_view.h"
+
+namespace vsoc {
+
+/**
+ * Adds methods to create file descriptor scoped permissions. Just like
+ * TypedRegionView it can be directly constructed or subclassed.
+ *
+ * The Layout type must (in addition to requirements for TypedRegionView) also
+ * provide a nested type for the layout of the managed region.
+ */
+template <typename Layout>
+class ManagerRegionView : public TypedRegionView<Layout> {
+ public:
+  ManagerRegionView() = default;
+  /**
+   * Creates a fd scoped permission on the managed region.
+   *
+   * The managed_region_fd is in/out parameter that can be a not yet open file
+   * descriptor. If the fd is not open yet it will open the managed region
+   * device and then create the permission. If the function returns EBUSY
+   * (meaning that we lost the race to acquire the memory) the same fd can (and
+   * is expected to) be used in a subsequent call to create a permission on
+   * another memory location.
+   *
+   * On success returns an open fd with the requested permission asociated to
+   * it. If another thread/process acquired ownership of *owner_ptr before this
+   * one returns -EBUSY, returns a different negative number otherwise.
+   */
+  int CreateFdScopedPermission(uint32_t* owner_ptr, uint32_t owned_val,
+                               vsoc_reg_off_t begin_offset,
+                               vsoc_reg_off_t end_offset) {
+    return this->control_->CreateFdScopedPermission(
+        Layout::ManagedRegion::region_name,
+        this->pointer_to_region_offset(owner_ptr), owned_val, begin_offset,
+        end_offset);
+  }
+};
+
+}  // namespace vsoc
diff --git a/guest/vsoc/lib/region_control.cpp b/guest/vsoc/lib/region_control.cpp
new file mode 100644
index 0000000..8c6864c
--- /dev/null
+++ b/guest/vsoc/lib/region_control.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/lib/region_view.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <uapi/vsoc_shm.h>
+
+using cvd::SharedFD;
+
+namespace {
+class GuestRegionControl : public vsoc::RegionControl {
+ public:
+  explicit GuestRegionControl(const SharedFD& region_fd,
+                              const vsoc_device_region& desc)
+      : region_fd_{region_fd} {
+    region_desc_ = desc;
+  }
+  virtual bool InterruptPeer() override;
+  virtual void InterruptSelf() override;
+  virtual void WaitForInterrupt() override;
+  virtual void* Map() override;
+
+ protected:
+  int CreateFdScopedPermission(const char* managed_region_name,
+                               vsoc_reg_off_t owner_offset, uint32_t owned_val,
+                               vsoc_reg_off_t begin_offset,
+                               vsoc_reg_off_t end_offset) override;
+  cvd::SharedFD region_fd_;
+};
+
+std::string device_path_from_name(const char* region_name) {
+  return std::string("/dev/") + region_name;
+}
+
+bool GuestRegionControl::InterruptPeer() {
+  int rval = region_fd_->Ioctl(VSOC_SEND_INTERRUPT_TO_HOST, 0);
+  if ((rval == -1) && (errno != EBUSY)) {
+    LOG(INFO) << __FUNCTION__ << ": ioctl failed (" << strerror(errno) << ")";
+  }
+  return !rval;
+}
+
+void GuestRegionControl::InterruptSelf() {
+  region_fd_->Ioctl(VSOC_SELF_INTERRUPT, 0);
+}
+
+void GuestRegionControl::WaitForInterrupt() {
+  region_fd_->Ioctl(VSOC_WAIT_FOR_INCOMING_INTERRUPT, 0);
+}
+
+int GuestRegionControl::CreateFdScopedPermission(
+    const char* managed_region_name, vsoc_reg_off_t owner_offset,
+    uint32_t owned_value, vsoc_reg_off_t begin_offset,
+    vsoc_reg_off_t end_offset) {
+  if (!region_fd_->IsOpen()) {
+    LOG(FATAL) << "Can't create permission before opening controller region";
+    return -EINVAL;
+  }
+  int managed_region_fd =
+      open(device_path_from_name(managed_region_name).c_str(), O_RDWR);
+  if (managed_region_fd < 0) {
+    int errno_ = errno;
+    LOG(FATAL) << "Can't open managed region: " << managed_region_name;
+    return -errno_;
+  }
+
+  fd_scoped_permission_arg perm;
+  perm.perm.begin_offset = begin_offset;
+  perm.perm.end_offset = end_offset;
+  perm.perm.owned_value = owned_value;
+  perm.perm.owner_offset = owner_offset;
+  perm.managed_region_fd = managed_region_fd;
+  LOG(INFO) << "owner offset: " << perm.perm.owner_offset;
+  int retval = region_fd_->Ioctl(VSOC_CREATE_FD_SCOPED_PERMISSION, &perm);
+  if (retval) {
+    int errno_ = errno;
+    close(managed_region_fd);
+    if (errno_ != EBUSY) {
+      LOG(FATAL) << "Unable to create fd scoped permission ("
+                 << strerror(errno_) << ")";
+    }
+    return -errno_;
+  }
+  return managed_region_fd;
+}
+
+void* GuestRegionControl::Map() {
+  region_base_ =
+      region_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE, MAP_SHARED, 0);
+  if (region_base_ == MAP_FAILED) {
+    LOG(FATAL) << "mmap failed (" << region_fd_->StrError() << ")";
+    region_base_ = nullptr;
+  }
+  return region_base_;
+}
+}  // namespace
+
+// domain is here to ensure that this method has the same signature as the
+// method on host regions.
+std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open(
+    const char* region_name) {
+  std::string path = device_path_from_name(region_name);
+  SharedFD fd = SharedFD::Open(path.c_str(), O_RDWR);
+  if (!fd->IsOpen()) {
+    LOG(FATAL) << "Unable to open region " << region_name << " ("
+               << fd->StrError() << ")";
+    return nullptr;
+  }
+  vsoc_device_region desc;
+  if (fd->Ioctl(VSOC_DESCRIBE_REGION, &desc)) {
+    LOG(FATAL) << "Unable to obtain region descriptor (" << fd->StrError()
+               << ")";
+    return nullptr;
+  }
+  return std::shared_ptr<vsoc::RegionControl>(new GuestRegionControl(fd, desc));
+}
diff --git a/guest/vsoc/lib/region_view.cpp b/guest/vsoc/lib/region_view.cpp
new file mode 100644
index 0000000..db1bec0
--- /dev/null
+++ b/guest/vsoc/lib/region_view.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/lib/region_view.h"
+#include "common/vsoc/lib/region_control.h"
+
+const vsoc_signal_table_layout& vsoc::RegionView::incoming_signal_table() {
+  return control_->region_desc().host_to_guest_signal_table;
+}
+
+// Returns a pointer to the table that will be used to post signals
+const vsoc_signal_table_layout& vsoc::RegionView::outgoing_signal_table() {
+  return control_->region_desc().guest_to_host_signal_table;
+}
diff --git a/host/Android.bp b/host/Android.bp
new file mode 100644
index 0000000..2280c23
--- /dev/null
+++ b/host/Android.bp
@@ -0,0 +1,20 @@
+//
+// 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.
+
+subdirs = [
+    "commands",
+    "frontend",
+    "libs",
+]
diff --git a/host/commands/Android.bp b/host/commands/Android.bp
new file mode 100644
index 0000000..618dd00
--- /dev/null
+++ b/host/commands/Android.bp
@@ -0,0 +1,18 @@
+//
+// 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.
+
+subdirs = [
+    "launch",
+]
diff --git a/host/commands/launch/Android.bp b/host/commands/launch/Android.bp
new file mode 100644
index 0000000..2a3eb0d
--- /dev/null
+++ b/host/commands/launch/Android.bp
@@ -0,0 +1,28 @@
+cc_binary_host {
+    name: "launch_cvd",
+    srcs: [
+        "main.cc",
+        "fb_bcast_region_handler.cc",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libicuuc",
+        "libbase",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libivserver",
+        "libvadb",
+        "libusbip",
+        "libgflags",
+        "libxml2",
+        "libjsoncpp",
+        "libmonitor",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/commands/launch/README.md b/host/commands/launch/README.md
new file mode 100644
index 0000000..05bb716
--- /dev/null
+++ b/host/commands/launch/README.md
@@ -0,0 +1,123 @@
+# Host-side binary for Android Virtual Device
+
+## Launcher package
+
+This is the cuttlefish launcher implementation, that integrates following
+features:
+
+* `libvirt` domain configuration,
+* `ivshmem` server,
+* USB forwarding.
+
+## Overview
+
+### ivshmem
+
+We are breaking from the general philosophy of ivshmem-server inter-vm
+communication. In this prototype there is no concept of inter-vm communication;
+guests can only talk to daemons running on host.
+
+### Requirements
+
+Cuttlefish requires the following packages to be installed on your system:
+
+Compiling:
+
+* `libjsoncpp-dev`
+* `libudev-dev`,
+* `libvirt-dev`,
+* `libxml2-dev`
+
+Running:
+
+* `linux-image-extra-virtual` to supply `vhci-hcd` module (module must be
+  loaded manually)
+* `libvirt-bin`
+* `libxml2`
+* `qemu-2.8` (or newer)
+
+### Building and Installing debian package
+
+To build debian package:
+
+```sh
+host$ cd dist
+host$ debuild --no-tgz-check -us -uc
+host$ cd ..
+host$ scp cuttlefish-common*.deb ${USER}@123.45.67.89:
+```
+
+This will create file named `cuttlefish-common_0.1-1_amd64.deb` in the root
+folder of your workspace. You will have to manually upload this file to
+your remote instance and install it as:
+
+```sh
+host$ ssh 123.45.67.89
+gce$ sudo apt install -f --reinstall ./cuttlefish-common*.deb
+```
+
+`apt` will pull in all necessary dependencies. After it's done, support files
+will be ready to use.
+
+### Host Configuration
+
+All configuration and basic deployment is covered by scripts in the
+`host/deploy` folder. To configure remote instance for cuttlefish, execute:
+
+```sh
+host$ cd host/deploy
+host$ python main.py config -i 123.45.67.89
+```
+
+The script will automatically update libvirt configuration files and user group
+membership, as well as create necessary folder for Cuttlefish images.
+
+### Uploading images
+
+To deploy cuttlefish images from build server, execute:
+
+```sh
+host$ cd host/deploy
+host$ python main.py deploy -i 123.45.67.89
+```
+
+By default, the script will pull the latest build of `cf_x86_phone-userdebug`
+target from `oc-gce-dev` branch, and latest kernel from cuttlefish kernel
+target and branch. Both system and kernel locations can be tuned by supplying
+relevant arguments via command line.
+
+Optionally, files can be populated and uploaded manually. Please ensure that
+at all times user `libvirt-qemu` can access each of these files by specifying
+correct ACL permissions using `setfacl` command, eg:
+
+```sh
+gce$ setfacl -m u:libvirt-qemu:rw /path/to/system.img
+```
+
+### Starting Cuttlefish
+
+To start cuttlefish, assuming you executed all the above:
+
+```sh
+gce$ sudo modprobe vhci-hcd
+gce$ sudo cf -system_image_dir /srv/cf/latest/ \
+        -kernel /srv/cf/latest/kernel \
+        -data_image /srv/cf/latest/data.img \
+        -cache_image /srv/cf/latest/cache.img \
+        --logtostderr
+```
+
+Shortly after, you should be able to execute `adb devices` and see your device
+listed. If device is reported as `????????`, or as:
+
+```log
+CUTTLEFISHCVD01 no permissions (verify udev rules); see [http://developer.android.com/tools/device.html]
+```
+
+you may have to re-start adb as root (we don't have udev rules updating virtual
+usb permissions yet):
+
+```sh
+gce$ adb kill-server
+gce$ sudo adb devices
+```
diff --git a/host/commands/launch/fb_bcast_region_handler.cc b/host/commands/launch/fb_bcast_region_handler.cc
new file mode 100644
index 0000000..4e89b79
--- /dev/null
+++ b/host/commands/launch/fb_bcast_region_handler.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <gflags/gflags.h>
+
+#include "common/vsoc/lib/fb_bcast_region_view.h"
+#include "host/commands/launch/pre_launch_initializers.h"
+#include "host/libs/config/host_config.h"
+
+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");
+
+void InitializeFBBroadcastRegion() {
+  vsoc::framebuffer::FBBroadcastRegionView region;
+  if (!region.Open(vsoc::GetDomain().c_str())) {
+    LOG(INFO) << "Framebuffer region was not found";
+    return;
+  }
+  auto dest = region.data();
+  dest->x_res = FLAGS_x_res;
+  dest->y_res = FLAGS_y_res;
+  dest->dpi = FLAGS_dpi;
+}
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
new file mode 100644
index 0000000..435d2b2
--- /dev/null
+++ b/host/commands/launch/main.cc
@@ -0,0 +1,377 @@
+/*
+ * 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 <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iomanip>
+#include <memory>
+#include <sstream>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_select.h"
+#include "host/commands/launch/pre_launch_initializers.h"
+#include "host/libs/config/file_partition.h"
+#include "host/libs/config/guest_config.h"
+#include "host/libs/config/host_config.h"
+#include "host/libs/ivserver/ivserver.h"
+#include "host/libs/ivserver/options.h"
+#include "host/libs/monitor/kernel_log_server.h"
+#include "host/libs/usbip/server.h"
+#include "host/libs/vadb/virtual_adb_server.h"
+
+namespace {
+std::string StringFromEnv(const char* varname, std::string defval) {
+  const char* const valstr = getenv(varname);
+  if (!valstr) {
+    return defval;
+  }
+  return valstr;
+}
+}  // namespace
+
+using vsoc::GetPerInstanceDefault;
+using vsoc::GetDefaultPerInstancePath;
+
+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_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(extra_kernel_command_line, "",
+              "Additional flags to put on the kernel command line");
+DECLARE_int32(instance);
+DEFINE_string(initrd, "", "Location of cuttlefish initrd file.");
+DEFINE_string(kernel, "", "Location of cuttlefish kernel file.");
+DEFINE_string(kernel_command_line, "",
+              "Location of a text file with the kernel command line.");
+DEFINE_string(launch_command, "virsh create /dev/fd/0",
+              "Command to start an instance");
+DEFINE_string(layout,
+              StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) +
+                  "/config/vsoc_mem.json",
+              "Location of the vsoc_mem.json file.");
+DEFINE_bool(log_xml, false, "Log the XML machine configuration");
+DEFINE_int32(memory_mb, 2048,
+             "Total amount of memory available for guest, MB.");
+std::string g_default_mempath{GetPerInstanceDefault("/var/run/shm/cvd-")};
+DEFINE_string(mempath, g_default_mempath.c_str(),
+              "Target location for the shmem file.");
+std::string g_default_mobile_interface{GetPerInstanceDefault("cvd-mobile-")};
+DEFINE_string(mobile_interface, g_default_mobile_interface.c_str(),
+              "Network interface to use for mobile networking");
+std::string g_default_qemusocket = GetDefaultPerInstancePath("ivshmem_socket_qemu");
+DEFINE_string(qemusocket, g_default_qemusocket.c_str(), "QEmu socket path");
+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(system_image_dir,
+              StringFromEnv("ANDROID_PRODUCT_OUT", StringFromEnv("HOME", ".")),
+              "Location of the system partition images.");
+DEFINE_string(vendor_image, "", "Location of the vendor partition image.");
+
+std::string g_default_uuid{
+    GetPerInstanceDefault("699acfc4-c8c4-11e7-882b-5065f31dc1")};
+DEFINE_string(uuid, g_default_uuid.c_str(),
+              "UUID to use for the device. Random if not specified");
+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.");
+
+namespace {
+Json::Value LoadLayoutFile(const std::string& file) {
+  char real_file_path[PATH_MAX];
+  if (realpath(file.c_str(), real_file_path) == nullptr) {
+    LOG(FATAL) << "Could not get real path for file " << file << ": "
+               << strerror(errno);
+  }
+
+  Json::Value result;
+  Json::Reader reader;
+  std::ifstream ifs(real_file_path);
+  if (!reader.parse(ifs, result)) {
+    LOG(FATAL) << "Could not read layout file " << file << ": "
+               << reader.getFormattedErrorMessages();
+  }
+  return result;
+}
+
+// VirtualUSBManager manages virtual USB device presence for Cuttlefish.
+class VirtualUSBManager {
+ public:
+  VirtualUSBManager(const std::string& usbsocket,
+                    const std::string& android_usbipsocket)
+      : adb_{usbsocket, android_usbipsocket},
+        usbip_{android_usbipsocket, adb_.Pool()} {}
+
+  ~VirtualUSBManager() = default;
+
+  // Initialize Virtual USB and start USB management thread.
+  void Start() {
+    CHECK(adb_.Init()) << "Could not initialize Virtual ADB server";
+    CHECK(usbip_.Init()) << "Could not start USB/IP server";
+    thread_.reset(new std::thread([this]() { Thread(); }));
+  }
+
+ private:
+  void Thread() {
+    for (;;) {
+      cvd::SharedFDSet fd_read;
+      fd_read.Zero();
+
+      adb_.BeforeSelect(&fd_read);
+      usbip_.BeforeSelect(&fd_read);
+
+      int ret = cvd::Select(&fd_read, nullptr, nullptr, nullptr);
+      if (ret <= 0) continue;
+
+      adb_.AfterSelect(fd_read);
+      usbip_.AfterSelect(fd_read);
+    }
+  }
+
+  vadb::VirtualADBServer adb_;
+  vadb::usbip::Server usbip_;
+  std::unique_ptr<std::thread> thread_;
+
+  VirtualUSBManager(const VirtualUSBManager&) = delete;
+  VirtualUSBManager& operator=(const VirtualUSBManager&) = delete;
+};
+
+// IVServerManager takes care of serving shared memory segments between
+// Cuttlefish and host-side daemons.
+class IVServerManager {
+ public:
+  IVServerManager(const Json::Value& json_root)
+      : server_(ivserver::IVServerOptions(FLAGS_layout, FLAGS_mempath,
+                                          FLAGS_qemusocket,
+                                          vsoc::GetDomain()),
+                json_root) {}
+
+  ~IVServerManager() = default;
+
+  // Start IVServer thread.
+  void Start() {
+    thread_.reset(new std::thread([this]() { server_.Serve(); }));
+  }
+
+ private:
+  ivserver::IVServer server_;
+  std::unique_ptr<std::thread> thread_;
+
+  IVServerManager(const IVServerManager&) = delete;
+  IVServerManager& operator=(const IVServerManager&) = delete;
+};
+
+// KernelLogMonitor receives and monitors kernel log for Cuttlefish.
+class KernelLogMonitor {
+ public:
+  KernelLogMonitor(const std::string& socket_name,
+                   const std::string& log_name,
+                   bool deprecated_boot_completed)
+      : klog_{socket_name, log_name, deprecated_boot_completed} {}
+
+  ~KernelLogMonitor() = default;
+
+  void Start() {
+    CHECK(klog_.Init()) << "Could not initialize kernel log server";
+    thread_.reset(new std::thread([this]() { Thread(); }));
+  }
+
+ private:
+  void Thread() {
+    for (;;) {
+      cvd::SharedFDSet fd_read;
+      fd_read.Zero();
+
+      klog_.BeforeSelect(&fd_read);
+
+      int ret = cvd::Select(&fd_read, nullptr, nullptr, nullptr);
+      if (ret <= 0) continue;
+
+      klog_.AfterSelect(fd_read);
+    }
+  }
+
+  monitor::KernelLogServer klog_;
+  std::unique_ptr<std::thread> thread_;
+
+  KernelLogMonitor(const KernelLogMonitor&) = delete;
+  KernelLogMonitor& operator=(const KernelLogMonitor&) = delete;
+};
+
+}  // anonymous namespace
+
+void subprocess(const char* const* command) {
+  pid_t pid = fork();
+  if (!pid) {
+    int rval = execv(command[0], const_cast<char* const*>(command));
+    // No need for an if: if exec worked it wouldn't have returned
+    LOG(ERROR) << "exec of " << command[0] << " failed (" << strerror(errno)
+               << ")";
+    exit(rval);
+  }
+  if (pid == -1) {
+    LOG(ERROR) << "fork of " << command[0] << " failed (" << strerror(errno)
+               << ")";
+  } else {
+    waitpid(pid, 0, 0);
+  }
+}
+
+int main(int argc, char** argv) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  LOG_IF(FATAL, FLAGS_system_image_dir.empty())
+      << "--system_image_dir must be specified.";
+
+  std::string per_instance_dir = vsoc::GetDefaultPerInstanceDir();
+  struct stat unused;
+  if ((stat(per_instance_dir.c_str(), &unused) == -1) && (errno == ENOENT)) {
+    LOG(INFO) << "Setting up " << per_instance_dir;
+    const char* mkdir_command[]{
+        "/usr/bin/sudo",          "/bin/mkdir", "-m", "0775",
+        per_instance_dir.c_str(), NULL};
+    subprocess(mkdir_command);
+    std::string owner_group{getenv("USER")};
+    owner_group += ":libvirt-qemu";
+    const char* chown_command[]{"/usr/bin/sudo", "/bin/chown",
+                                owner_group.c_str(), per_instance_dir.c_str(),
+                                NULL};
+    subprocess(chown_command);
+  }
+
+  // If user did not specify location of either of these files, expect them to
+  // be placed in --system_image_dir location.
+  if (FLAGS_kernel.empty()) {
+    FLAGS_kernel = FLAGS_system_image_dir + "/kernel";
+  }
+
+  if (FLAGS_kernel_command_line.empty()) {
+    FLAGS_kernel_command_line = FLAGS_system_image_dir + "/cmdline";
+  }
+
+  if (FLAGS_initrd.empty()) {
+    FLAGS_initrd = FLAGS_system_image_dir + "/ramdisk.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";
+  }
+
+  Json::Value json_root = LoadLayoutFile(FLAGS_layout);
+
+  // Each of these calls is free to fail and terminate launch if file does not
+  // exist or could not be created.
+  auto system_partition = config::FilePartition::ReuseExistingFile(
+      FLAGS_system_image_dir + "/system.img");
+  auto data_partition =
+      config::FilePartition::ReuseExistingFile(FLAGS_data_image);
+  auto cache_partition =
+      config::FilePartition::ReuseExistingFile(FLAGS_cache_image);
+  auto vendor_partition =
+      config::FilePartition::ReuseExistingFile(FLAGS_vendor_image);
+
+  std::ostringstream cmdline;
+  std::ifstream t(FLAGS_kernel_command_line);
+  if (!t) {
+    LOG(FATAL) << "Unable to open " << FLAGS_kernel_command_line;
+  }
+  cmdline << t.rdbuf();
+  t.close();
+  cmdline << " androidboot.serialno=" << FLAGS_serial_number;
+  if (FLAGS_extra_kernel_command_line.size()) {
+    cmdline << " " << FLAGS_extra_kernel_command_line;
+  }
+
+  std::string entropy_source = "/dev/urandom";
+
+  config::GuestConfig cfg;
+  cfg.SetID(FLAGS_instance)
+      .SetVCPUs(FLAGS_cpus)
+      .SetMemoryMB(FLAGS_memory_mb)
+      .SetKernelName(FLAGS_kernel)
+      .SetInitRDName(FLAGS_initrd)
+      .SetKernelArgs(cmdline.str())
+      .SetIVShMemSocketPath(FLAGS_qemusocket)
+      .SetIVShMemVectorCount(json_root["vsoc_device_regions"].size())
+      .SetSystemPartitionPath(system_partition->GetName())
+      .SetCachePartitionPath(cache_partition->GetName())
+      .SetDataPartitionPath(data_partition->GetName())
+      .SetVendorPartitionPath(vendor_partition->GetName())
+      .SetMobileBridgeName(FLAGS_mobile_interface)
+      .SetEntropySource(entropy_source)
+      .SetDisableDACSecurity(FLAGS_disable_dac_security)
+      .SetDisableAppArmorSecurity(FLAGS_disable_app_armor_security)
+      .SetUUID(FLAGS_uuid);
+  cfg.SetUSBV1SocketName(
+      GetDefaultPerInstancePath(cfg.GetInstanceName() + "-usb"));
+  cfg.SetKernelLogSocketName(
+      GetDefaultPerInstancePath(cfg.GetInstanceName() + "-kernel-log"));
+
+  std::string xml = cfg.Build();
+  if (FLAGS_log_xml) {
+    LOG(INFO) << "Using XML:\n" << xml;
+  }
+
+  VirtualUSBManager vadb(cfg.GetUSBV1SocketName(),
+                         GetPerInstanceDefault("android_usbip"));
+  vadb.Start();
+  IVServerManager ivshmem(json_root);
+  ivshmem.Start();
+  KernelLogMonitor kmon(cfg.GetKernelLogSocketName(),
+                        GetDefaultPerInstancePath("kernel.log"),
+                        FLAGS_deprecated_boot_completed);
+  kmon.Start();
+
+  sleep(1);
+
+  // Initialize the regions that require it before the VM starts.
+  PreLaunchInitializers::Initialize();
+
+  FILE* launch = popen(FLAGS_launch_command.c_str(), "w");
+  if (!launch) {
+    LOG(FATAL) << "Unable to execute " << FLAGS_launch_command;
+  }
+  int rval = fputs(xml.c_str(), launch);
+  if (rval == EOF) {
+    LOG(FATAL) << "Launch command exited while accepting XML";
+  }
+  int exit_code = pclose(launch);
+  if (exit_code) {
+    LOG(FATAL) << "Launch command exited with status " << exit_code;
+  }
+  pause();
+}
diff --git a/host/commands/launch/pre_launch_initializers.h b/host/commands/launch/pre_launch_initializers.h
new file mode 100644
index 0000000..5d43bce
--- /dev/null
+++ b/host/commands/launch/pre_launch_initializers.h
@@ -0,0 +1,30 @@
+#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.
+ */
+
+// Handles initialization of regions that require it strictly before the virtual
+// machine is started.
+// To add initializers for more regions declare here, implement in its own
+// source file and call from PreLaunchInitializers::Initialize().
+void InitializeFBBroadcastRegion();
+
+class PreLaunchInitializers {
+ public:
+  static void Initialize() {
+    InitializeFBBroadcastRegion();
+  }
+};
diff --git a/host/config/Android.mk b/host/config/Android.mk
new file mode 100644
index 0000000..6c176fe
--- /dev/null
+++ b/host/config/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Common definitions for all variants.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := vsoc_mem_json
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(HOST_OUT)/config
+LOCAL_MODULE_STEM := vsoc_mem.json
+LOCAL_SRC_FILES := vsoc_mem.json
+include $(BUILD_PREBUILT)
diff --git a/host/config/vsoc_mem.json b/host/config/vsoc_mem.json
new file mode 100644
index 0000000..411b3c5
--- /dev/null
+++ b/host/config/vsoc_mem.json
@@ -0,0 +1,175 @@
+{
+  "vsoc_device_regions" : [
+   {
+     "device_name" : "vsoc_input",
+     "__comment" : "Send input events to the guest",
+     "current_version" : 1,
+     "min_compatible_version" : 1,
+     "region_size" : 4096,
+
+     "guest_to_host_signal_table" : {
+       "__comment" : "sizeof each node is based on common/libs/shm_compatible/lock.h",
+       "num_nodes_lg2" : 0
+      },
+
+     "host_to_guest_signal_table" : {
+       "num_nodes_lg2" : 0
+      }
+   },
+
+   {
+     "device_name" : "hwcomposer",
+     "__comment" : "HWComposer related",
+     "current_version" : 1,
+     "min_compatible_version" : 1,
+     "region_size" : 16384,
+
+     "guest_to_host_signal_table" : {
+       "__comment" : "sizeof each node is based on common/libs/shm_compatible/lock.h",
+       "num_nodes_lg2" : 2
+      },
+
+     "host_to_guest_signal_table" : {
+       "num_nodes_lg2" : 2
+      }
+   },
+
+   {
+     "device_name" : "fb_broadcast",
+     "__comment" : "Broadcasts new frames",
+     "current_version" : 0,
+     "min_compatible_version" : 0,
+     "region_size": 4096,
+
+     "guest_to_host_signal_table" : {
+       "__comment" : "sizeof each node is based on common/libs/shm_compatible/lock.h",
+       "num_nodes_lg2" : 2
+      },
+
+     "host_to_guest_signal_table" : {
+       "num_nodes_lg2" : 2
+      }
+   },
+
+   {
+     "device_name" : "gralloc_manager",
+     "__comment" : "Manages allocation of gralloc buffers",
+     "current_version" : 0,
+     "min_compatible_version" : 0,
+     "region_size" : 40960,
+
+     "guest_to_host_signal_table" : {
+       "__comment" : "sizeof each node is based on common/libs/shm/lock.h",
+       "num_nodes_lg2" : 2
+      },
+
+     "host_to_guest_signal_table" : {
+       "num_nodes_lg2" : 2
+      }
+   },
+
+   {
+     "device_name" : "gralloc_memory",
+     "__comment" : "Holds the gralloc buffers",
+     "managed_by" : "gralloc_manager",
+     "current_version" : 0,
+     "min_compatible_version" : 0,
+     "region_size" : 419430400,
+
+     "guest_to_host_signal_table" : {
+       "__comment" : "sizeof each node is based on common/libs/shm/lock.h",
+       "num_nodes_lg2" : 0
+      },
+
+     "host_to_guest_signal_table" : {
+       "num_nodes_lg2" : 0
+      }
+   },
+
+   {
+     "device_name" : "wifi_exchange",
+     "__comment" : [
+       "WIFI exchange buffer for primary wireless network interface.",
+       "Must be able to accomodate at least one pagesize long packet each direction",
+       "Region size = 2 * CircularPacketQueue64k_size + 1024 bytes for control structures."
+     ],
+     "current_version" : 0,
+     "min_compatible_version" : 0,
+     "region_size" : 139264,
+
+     "guest_to_host_signal_table" : {
+       "num_nodes_lg2" : 2
+      },
+
+     "host_to_guest_signal_table" : {
+       "num_nodes_lg2" : 2
+      }
+   },
+
+
+   {
+    "device_name" : "e2e_primary",
+    "__comment" : "Sanity testing",
+    "current_version" : 1,
+    "min_compatible_version" : 1,
+    "region_size" : 16384,
+
+    "guest_to_host_signal_table" : {
+      "num_nodes_lg2" : 1
+    },
+
+    "host_to_guest_signal_table" : {
+      "num_nodes_lg2" : 1
+    }
+   },
+
+   {
+    "device_name" : "e2e_secondary",
+    "__comment" : "Sanity testing",
+    "current_version" : 1,
+    "min_compatible_version" : 1,
+    "region_size" : 16384,
+
+    "guest_to_host_signal_table" : {
+      "num_nodes_lg2" : 1
+    },
+
+    "host_to_guest_signal_table" : {
+      "num_nodes_lg2" : 1
+    }
+   },
+
+   {
+    "device_name" : "e2e_manager",
+    "__comment" : "Sanity testing of managed regions",
+    "current_version" : 1,
+    "min_compatible_version" : 1,
+    "region_size" : 4096,
+
+    "guest_to_host_signal_table" : {
+      "num_nodes_lg2" : 1
+    },
+
+    "host_to_guest_signal_table" : {
+      "num_nodes_lg2" : 1
+    }
+   },
+
+   {
+    "device_name" : "e2e_managed",
+    "__comment" : "Sanity testing of managed regions",
+    "current_version" : 1,
+    "min_compatible_version" : 1,
+    "region_size" : 16384,
+    "managed_by" : "e2e_manager",
+
+    "guest_to_host_signal_table" : {
+      "num_nodes_lg2" : 1
+    },
+
+    "host_to_guest_signal_table" : {
+      "num_nodes_lg2" : 1
+    }
+   }
+  ]
+}
diff --git a/host/frontend/Android.bp b/host/frontend/Android.bp
new file mode 100644
index 0000000..bf82b3c
--- /dev/null
+++ b/host/frontend/Android.bp
@@ -0,0 +1,18 @@
+//
+// 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.
+
+subdirs = [
+    "vnc_server",
+]
diff --git a/host/frontend/vnc_server/Android.bp b/host/frontend/vnc_server/Android.bp
new file mode 100644
index 0000000..b5ec78b
--- /dev/null
+++ b/host/frontend/vnc_server/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary_host {
+    name: "vnc_server",
+    srcs: [
+        "blackboard.cpp",
+        "frame_buffer_watcher.cpp",
+        "jpeg_compressor.cpp",
+        "main.cpp",
+        "simulated_hw_composer.cpp",
+        "tcp_socket.cpp",
+        "virtual_inputs.cpp",
+        "virtual_input_device.cpp",
+        "vnc_client_connection.cpp",
+        "vnc_server.cpp",
+        "vnc_utils.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "vsoc_lib",
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libjpeg",
+        "libgflags",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/frontend/vnc_server/README.md b/host/frontend/vnc_server/README.md
new file mode 100644
index 0000000..a702303
--- /dev/null
+++ b/host/frontend/vnc_server/README.md
@@ -0,0 +1,40 @@
+# Host side VNC server
+
+## Build instructions
+
+```shell
+bazel build host/frontend/vnc_server/:vnc_server
+```
+
+### Requirements
+
+* libjpeg-turbo8-dev
+
+## Run Instructions
+
+The vnc server receives frame updates from the hwcomposer through the shared
+memory, for which it needs to connect to the ivserver. To send input to the
+instance it temporarily connects to monkey, which is set up to run on the
+instance as a service and listens on port 6445, so you need to forward that port
+to a local socket.
+
+* Make sure to wait for monkey to start (about 16s), it's one of the last
+services to start since it's started by the zygote:
+
+```shell
+adb shell ps -e | grep monkey
+```
+
+* Forward the port to a local socket:
+
+```shell
+adb forward localfilesystem:/tmp/android-cuttlefish-1-input tcp:6445
+```
+
+* Run the server:
+
+```shell
+bazel-bin/host/vnc_server/vnc_server --port=6444 --input_socket=/tmp/android-cuttlefish-1-input
+```
+
+The VNC server will then be listening for connections on port 6444 on the host.
diff --git a/host/frontend/vnc_server/blackboard.cpp b/host/frontend/vnc_server/blackboard.cpp
new file mode 100644
index 0000000..b842648
--- /dev/null
+++ b/host/frontend/vnc_server/blackboard.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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/frontend/vnc_server/blackboard.h"
+
+#include <algorithm>
+#include <utility>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+#include "host/frontend/vnc_server/frame_buffer_watcher.h"
+
+DEFINE_bool(debug_blackboard, false,
+            "Turn on detailed logging for the blackboard");
+
+#define DLOG(LEVEL)                                 \
+  if (FLAGS_debug_blackboard) LOG(LEVEL)
+
+using cvd::vnc::BlackBoard;
+using cvd::vnc::Stripe;
+
+cvd::vnc::SeqNumberVec cvd::vnc::MakeSeqNumberVec() {
+  return SeqNumberVec(FrameBufferWatcher::StripesPerFrame());
+}
+
+void BlackBoard::NewStripeReady(int index, StripeSeqNumber seq_num) {
+  std::lock_guard<std::mutex> guard(m_);
+  DLOG(INFO) << "new stripe arrived from frame watcher";
+  auto& current_seq_num = most_recent_stripe_seq_nums_[index];
+  current_seq_num = std::max(current_seq_num, seq_num);
+  for (auto& client : clients_) {
+    if (client.second.ready_to_receive) {
+      client.second.new_frame_cv.notify_one();
+    }
+  }
+}
+
+void BlackBoard::Register(const VncClientConnection* conn) {
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    CHECK(!clients_.count(conn));
+    clients_[conn];  // constructs new state in place
+  }
+  new_client_cv_.notify_one();
+}
+
+void BlackBoard::Unregister(const VncClientConnection* conn) {
+  std::lock_guard<std::mutex> guard(m_);
+  CHECK(clients_.count(conn));
+  clients_.erase(clients_.find(conn));
+}
+
+bool BlackBoard::NoNewStripesFor(const SeqNumberVec& seq_nums) const {
+  CHECK(seq_nums.size() == most_recent_stripe_seq_nums_.size());
+  for (auto state_seq_num = seq_nums.begin(),
+            held_seq_num = most_recent_stripe_seq_nums_.begin();
+       state_seq_num != seq_nums.end(); ++state_seq_num, ++held_seq_num) {
+    if (*state_seq_num < *held_seq_num) {
+      return false;
+    }
+  }
+  return true;
+}
+
+cvd::vnc::StripePtrVec BlackBoard::WaitForSenderWork(
+    const VncClientConnection* conn) {
+  std::unique_lock<std::mutex> guard(m_);
+  auto& state = GetStateForClient(conn);
+  DLOG(INFO) << "Waiting for stripe...";
+  while (!state.closed &&
+         (!state.ready_to_receive || NoNewStripesFor(state.stripe_seq_nums))) {
+    state.new_frame_cv.wait(guard);
+  }
+  DLOG(INFO) << "At least one new stripe is available, should unblock " << conn;
+  state.ready_to_receive = false;
+  auto new_stripes = frame_buffer_watcher_->StripesNewerThan(
+      state.orientation, state.stripe_seq_nums);
+  for (auto& s : new_stripes) {
+    state.stripe_seq_nums[s->index] = s->seq_number;
+  }
+  return new_stripes;
+}
+
+void BlackBoard::WaitForAtLeastOneClientConnection() {
+  std::unique_lock<std::mutex> guard(m_);
+  while (clients_.empty()) {
+    new_client_cv_.wait(guard);
+  }
+}
+
+void BlackBoard::SetOrientation(const VncClientConnection* conn,
+                                ScreenOrientation orientation) {
+  std::lock_guard<std::mutex> guard(m_);
+  auto& state = GetStateForClient(conn);
+  state.orientation = orientation;
+  // After an orientation change the vnc client will need all stripes from
+  // the new orientation, regardless of age.
+  ResetToZero(&state.stripe_seq_nums);
+}
+
+void BlackBoard::SignalClientNeedsEntireScreen(
+    const VncClientConnection* conn) {
+  std::lock_guard<std::mutex> guard(m_);
+  ResetToZero(&GetStateForClient(conn).stripe_seq_nums);
+}
+
+void BlackBoard::ResetToZero(SeqNumberVec* seq_nums) {
+  seq_nums->assign(FrameBufferWatcher::StripesPerFrame(), StripeSeqNumber{});
+}
+
+void BlackBoard::FrameBufferUpdateRequestReceived(
+    const VncClientConnection* conn) {
+  std::lock_guard<std::mutex> guard(m_);
+  DLOG(INFO) << "Received frame buffer update request";
+  auto& state = GetStateForClient(conn);
+  state.ready_to_receive = true;
+  state.new_frame_cv.notify_one();
+}
+
+void BlackBoard::StopWaiting(const VncClientConnection* conn) {
+  std::lock_guard<std::mutex> guard(m_);
+  auto& state = GetStateForClient(conn);
+  state.closed = true;
+  // Wake up the thread that might be in WaitForSenderWork()
+  state.new_frame_cv.notify_one();
+}
+
+void BlackBoard::set_frame_buffer_watcher(
+    cvd::vnc::FrameBufferWatcher* frame_buffer_watcher) {
+  std::lock_guard<std::mutex> guard(m_);
+  frame_buffer_watcher_ = frame_buffer_watcher;
+}
+
+void BlackBoard::set_jpeg_quality_level(int quality_level) {
+  // NOTE all vnc clients share a common jpeg quality level because the
+  // server doesn't compress per-client. The quality level for all clients
+  // will be whatever the most recent set was by any client.
+  std::lock_guard<std::mutex> guard(m_);
+  if (quality_level < kJpegMinQualityEncoding ||
+      quality_level > kJpegMaxQualityEncoding) {
+    LOG(WARNING) << "Bogus jpeg quality level: " << quality_level
+                 << ". Quality must be in range [" << kJpegMinQualityEncoding
+                 << ", " << kJpegMaxQualityEncoding << "]";
+    return;
+  }
+  jpeg_quality_level_ = 55 + (5 * (quality_level + 32));
+  DLOG(INFO) << "jpeg quality level set to " << jpeg_quality_level_ << "%";
+}
+
+BlackBoard::ClientFBUState& BlackBoard::GetStateForClient(
+    const VncClientConnection* conn) {
+  CHECK(clients_.count(conn));
+  return clients_[conn];
+}
diff --git a/host/frontend/vnc_server/blackboard.h b/host/frontend/vnc_server/blackboard.h
new file mode 100644
index 0000000..1119dd3
--- /dev/null
+++ b/host/frontend/vnc_server/blackboard.h
@@ -0,0 +1,113 @@
+#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 <condition_variable>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include "common/libs/threads/thread_annotations.h"
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+namespace cvd {
+namespace vnc {
+
+class VncClientConnection;
+class FrameBufferWatcher;
+using StripePtrVec = std::vector<std::shared_ptr<const Stripe>>;
+using SeqNumberVec = std::vector<StripeSeqNumber>;
+
+SeqNumberVec MakeSeqNumberVec();
+
+class BlackBoard {
+ private:
+  struct ClientFBUState {
+    bool ready_to_receive{};
+    ScreenOrientation orientation{};
+    std::condition_variable new_frame_cv;
+    SeqNumberVec stripe_seq_nums = MakeSeqNumberVec();
+    bool closed{};
+  };
+
+ public:
+  class Registerer {
+   public:
+    Registerer(BlackBoard* bb, const VncClientConnection* conn)
+        : bb_{bb}, conn_{conn} {
+      bb->Register(conn);
+    }
+    ~Registerer() { bb_->Unregister(conn_); }
+    Registerer(const Registerer&) = delete;
+    Registerer& operator=(const Registerer&) = delete;
+
+   private:
+    BlackBoard* bb_{};
+    const VncClientConnection* conn_{};
+  };
+
+  BlackBoard() = default;
+  BlackBoard(const BlackBoard&) = delete;
+  BlackBoard& operator=(const BlackBoard&) = delete;
+
+  bool NoNewStripesFor(const SeqNumberVec& seq_nums) const REQUIRES(m_);
+  void NewStripeReady(int index, StripeSeqNumber seq_num);
+  void Register(const VncClientConnection* conn);
+  void Unregister(const VncClientConnection* conn);
+
+  StripePtrVec WaitForSenderWork(const VncClientConnection* conn);
+
+  void WaitForAtLeastOneClientConnection();
+
+  void FrameBufferUpdateRequestReceived(const VncClientConnection* conn);
+  // Setting orientation implies needing the entire screen
+  void SetOrientation(const VncClientConnection* conn,
+                      ScreenOrientation orientation);
+  void SignalClientNeedsEntireScreen(const VncClientConnection* conn);
+
+  void StopWaiting(const VncClientConnection* conn);
+
+  void set_frame_buffer_watcher(FrameBufferWatcher* frame_buffer_watcher);
+
+  // quality_level must be the value received from the client, in the range
+  // [kJpegMinQualityEncoding, kJpegMaxQualityEncoding], else it is ignored.
+  void set_jpeg_quality_level(int quality_level);
+
+  int jpeg_quality_level() const {
+    std::lock_guard<std::mutex> guard(m_);
+    return jpeg_quality_level_;
+  }
+
+ private:
+  ClientFBUState& GetStateForClient(const VncClientConnection* conn)
+      REQUIRES(m_);
+  static void ResetToZero(SeqNumberVec* seq_nums);
+
+  mutable std::mutex m_;
+  SeqNumberVec most_recent_stripe_seq_nums_ GUARDED_BY(m_) = MakeSeqNumberVec();
+  std::unordered_map<const VncClientConnection*, ClientFBUState> clients_
+      GUARDED_BY(m_);
+  int jpeg_quality_level_ GUARDED_BY(m_) = 100;
+  std::condition_variable new_client_cv_;
+  // NOTE the FrameBufferWatcher pointer itself should be
+  // guarded, but not the pointee.
+  FrameBufferWatcher* frame_buffer_watcher_ GUARDED_BY(m_){};
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/frame_buffer_watcher.cpp b/host/frontend/vnc_server/frame_buffer_watcher.cpp
new file mode 100644
index 0000000..a09269a
--- /dev/null
+++ b/host/frontend/vnc_server/frame_buffer_watcher.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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/frontend/vnc_server/frame_buffer_watcher.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <utility>
+
+#include <glog/logging.h>
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+using cvd::vnc::FrameBufferWatcher;
+
+FrameBufferWatcher::FrameBufferWatcher(BlackBoard* bb)
+    : bb_{bb}, hwcomposer{bb_} {
+  for (auto& stripes_vec : stripes_) {
+    std::generate_n(std::back_inserter(stripes_vec),
+                    SimulatedHWComposer::NumberOfStripes(),
+                    std::make_shared<Stripe>);
+  }
+  bb_->set_frame_buffer_watcher(this);
+  auto num_workers = std::max(std::thread::hardware_concurrency(), 1u);
+  std::generate_n(std::back_inserter(workers_), num_workers, [this] {
+    return std::thread{&FrameBufferWatcher::Worker, this};
+  });
+}
+
+FrameBufferWatcher::~FrameBufferWatcher() {
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    closed_ = true;
+  }
+  for (auto& tid : workers_) {
+    tid.join();
+  }
+}
+
+bool FrameBufferWatcher::closed() const {
+  std::lock_guard<std::mutex> guard(m_);
+  return closed_;
+}
+
+cvd::vnc::Stripe FrameBufferWatcher::Rotated(Stripe stripe) {
+  if (stripe.orientation == ScreenOrientation::Landscape) {
+    LOG(FATAL) << "Rotating a landscape stripe, this is a mistake";
+  }
+  auto w = stripe.width;
+  auto h = stripe.height;
+  const auto& raw = stripe.raw_data;
+  Message rotated(raw.size(), 0xAA);
+  for (std::uint16_t i = 0; i < w; ++i) {
+    for (std::uint16_t j = 0; j < h; ++j) {
+      auto to = (i * h + j) * BytesPerPixel();
+      auto from = (w - (i + 1) + w * j) * BytesPerPixel();
+      CHECK(from < raw.size());
+      CHECK(to < rotated.size());
+      std::memcpy(&rotated[to], &raw[from], BytesPerPixel());
+    }
+  }
+  std::swap(stripe.x, stripe.y);
+  std::swap(stripe.width, stripe.height);
+  stripe.raw_data = std::move(rotated);
+  stripe.orientation = ScreenOrientation::Landscape;
+  return stripe;
+}
+
+bool FrameBufferWatcher::StripeIsDifferentFromPrevious(
+    const Stripe& stripe) const {
+  return Stripes(stripe.orientation)[stripe.index]->raw_data != stripe.raw_data;
+}
+
+cvd::vnc::StripePtrVec FrameBufferWatcher::StripesNewerThan(
+    ScreenOrientation orientation, const SeqNumberVec& seq_numbers) const {
+  std::lock_guard<std::mutex> guard(stripes_lock_);
+  const auto& stripes = Stripes(orientation);
+  CHECK(seq_numbers.size() == stripes.size());
+  StripePtrVec new_stripes;
+  auto seq_number_it = seq_numbers.begin();
+  std::copy_if(stripes.begin(), stripes.end(), std::back_inserter(new_stripes),
+               [seq_number_it](const StripePtrVec::value_type& s) mutable {
+                 return *(seq_number_it++) < s->seq_number;
+               });
+  return new_stripes;
+}
+
+cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
+    ScreenOrientation orientation) {
+  return stripes_[static_cast<int>(orientation)];
+}
+
+const cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
+    ScreenOrientation orientation) const {
+  return stripes_[static_cast<int>(orientation)];
+}
+
+bool FrameBufferWatcher::UpdateMostRecentSeqNumIfStripeIsNew(
+    const Stripe& stripe) {
+  if (most_recent_identical_stripe_seq_nums_[stripe.index] <=
+      stripe.seq_number) {
+    most_recent_identical_stripe_seq_nums_[stripe.index] = stripe.seq_number;
+    return true;
+  }
+  return false;
+}
+
+bool FrameBufferWatcher::UpdateStripeIfStripeIsNew(
+    const std::shared_ptr<const Stripe>& stripe) {
+  std::lock_guard<std::mutex> guard(stripes_lock_);
+  if (UpdateMostRecentSeqNumIfStripeIsNew(*stripe)) {
+    Stripes(stripe->orientation)[stripe->index] = stripe;
+    return true;
+  }
+  return false;
+}
+
+void FrameBufferWatcher::CompressStripe(JpegCompressor* jpeg_compressor,
+                                        Stripe* stripe) {
+  stripe->jpeg_data = jpeg_compressor->Compress(
+      stripe->raw_data, bb_->jpeg_quality_level(), 0, 0, stripe->width,
+      stripe->height, stripe->width);
+}
+
+void FrameBufferWatcher::Worker() {
+  JpegCompressor jpeg_compressor;
+#ifdef FUZZ_TEST_VNC
+  std::default_random_engine e{std::random_device{}()};
+  std::uniform_int_distribution<int> random{0, 2};
+#endif
+  while (!closed()) {
+    auto portrait_stripe = hwcomposer.GetNewStripe();
+    if (closed()) {
+      break;
+    }
+    {
+      // TODO(haining) use if (with init) and else for c++17 instead of extra
+      // scope and continue
+      // if (std::lock_guard guard(stripes_lock_); /*condition*/) { }
+      std::lock_guard<std::mutex> guard(stripes_lock_);
+      if (!StripeIsDifferentFromPrevious(portrait_stripe)) {
+        UpdateMostRecentSeqNumIfStripeIsNew(portrait_stripe);
+        continue;
+      }
+    }
+    auto seq_num = portrait_stripe.seq_number;
+    auto index = portrait_stripe.index;
+    auto landscape_stripe = Rotated(portrait_stripe);
+    auto stripes = {std::make_shared<Stripe>(std::move(portrait_stripe)),
+                    std::make_shared<Stripe>(std::move(landscape_stripe))};
+    for (auto& stripe : stripes) {
+#ifdef FUZZ_TEST_VNC
+      if (random(e)) {
+        usleep(10000);
+      }
+#endif
+      CompressStripe(&jpeg_compressor, stripe.get());
+    }
+    bool any_new_stripes = false;
+    for (auto& stripe : stripes) {
+      any_new_stripes = UpdateStripeIfStripeIsNew(stripe) || any_new_stripes;
+    }
+    if (any_new_stripes) {
+      bb_->NewStripeReady(index, seq_num);
+    }
+  }
+}
+
+int FrameBufferWatcher::StripesPerFrame() {
+  return SimulatedHWComposer::NumberOfStripes();
+}
diff --git a/host/frontend/vnc_server/frame_buffer_watcher.h b/host/frontend/vnc_server/frame_buffer_watcher.h
new file mode 100644
index 0000000..40ab270
--- /dev/null
+++ b/host/frontend/vnc_server/frame_buffer_watcher.h
@@ -0,0 +1,76 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include "common/libs/threads/thread_annotations.h"
+#include "host/frontend/vnc_server/blackboard.h"
+#include "host/frontend/vnc_server/jpeg_compressor.h"
+#include "host/frontend/vnc_server/simulated_hw_composer.h"
+
+namespace cvd {
+namespace vnc {
+class FrameBufferWatcher {
+ public:
+  explicit FrameBufferWatcher(BlackBoard* bb);
+  FrameBufferWatcher(const FrameBufferWatcher&) = delete;
+  FrameBufferWatcher& operator=(const FrameBufferWatcher&) = delete;
+  ~FrameBufferWatcher();
+
+  StripePtrVec StripesNewerThan(ScreenOrientation orientation,
+                                const SeqNumberVec& seq_num) const;
+  static int StripesPerFrame();
+
+ private:
+  static Stripe Rotated(Stripe stripe);
+
+  bool closed() const;
+  bool StripeIsDifferentFromPrevious(const Stripe& stripe) const
+      REQUIRES(stripes_lock_);
+  // returns true if stripe is still considered new and seq number was updated
+  bool UpdateMostRecentSeqNumIfStripeIsNew(const Stripe& stripe)
+      REQUIRES(stripes_lock_);
+  // returns true if stripe is still considered new and was updated
+  bool UpdateStripeIfStripeIsNew(const std::shared_ptr<const Stripe>& stripe)
+      EXCLUDES(stripes_lock_);
+  // Compresses stripe->raw_data to stripe->jpeg_data
+  void CompressStripe(JpegCompressor* jpeg_compressor, Stripe* stripe);
+  void Worker();
+  void Updater();
+
+  StripePtrVec& Stripes(ScreenOrientation orientation) REQUIRES(stripes_lock_);
+  const StripePtrVec& Stripes(ScreenOrientation orientation) const
+      REQUIRES(stripes_lock_);
+
+  std::vector<std::thread> workers_;
+  mutable std::mutex stripes_lock_;
+  std::array<StripePtrVec, kNumOrientations> stripes_ GUARDED_BY(stripes_lock_);
+  SeqNumberVec most_recent_identical_stripe_seq_nums_
+      GUARDED_BY(stripes_lock_) = MakeSeqNumberVec();
+  mutable std::mutex m_;
+  bool closed_ GUARDED_BY(m_){};
+  BlackBoard* bb_{};
+  SimulatedHWComposer hwcomposer{bb_};
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/jpeg_compressor.cpp b/host/frontend/vnc_server/jpeg_compressor.cpp
new file mode 100644
index 0000000..711a762
--- /dev/null
+++ b/host/frontend/vnc_server/jpeg_compressor.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 <stdio.h>  // stdio.h must appear before jpeglib.h
+#include <jpeglib.h>
+
+#include <glog/logging.h>
+#include "host/frontend/vnc_server/jpeg_compressor.h"
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+using cvd::vnc::JpegCompressor;
+
+namespace {
+void InitCinfo(jpeg_compress_struct* cinfo, jpeg_error_mgr* err,
+               std::uint16_t width, std::uint16_t height, int jpeg_quality) {
+  cinfo->err = jpeg_std_error(err);
+  jpeg_create_compress(cinfo);
+
+  cinfo->image_width = width;
+  cinfo->image_height = height;
+  cinfo->input_components = cvd::vnc::BytesPerPixel();
+  cinfo->in_color_space = JCS_EXT_RGBX;
+
+  jpeg_set_defaults(cinfo);
+  jpeg_set_quality(cinfo, jpeg_quality, true);
+}
+}  // namespace
+
+cvd::vnc::Message JpegCompressor::Compress(const Message& frame,
+                                           int jpeg_quality, std::uint16_t x,
+                                           std::uint16_t y, std::uint16_t width,
+                                           std::uint16_t height,
+                                           int screen_width) {
+  jpeg_compress_struct cinfo{};
+  jpeg_error_mgr err{};
+  InitCinfo(&cinfo, &err, width, height, jpeg_quality);
+
+  auto* compression_buffer = buffer_.get();
+  auto compression_buffer_size = buffer_capacity_;
+  jpeg_mem_dest(&cinfo, &compression_buffer, &compression_buffer_size);
+  jpeg_start_compress(&cinfo, true);
+
+  while (cinfo.next_scanline < cinfo.image_height) {
+    auto row = static_cast<JSAMPROW>(const_cast<std::uint8_t*>(
+        &frame[(y * screen_width * BytesPerPixel()) +
+               (cinfo.next_scanline * BytesPerPixel() * screen_width) +
+               (x * BytesPerPixel())]));
+    jpeg_write_scanlines(&cinfo, &row, 1);
+  }
+  jpeg_finish_compress(&cinfo);
+  jpeg_destroy_compress(&cinfo);
+
+  UpdateBuffer(compression_buffer, compression_buffer_size);
+  return {compression_buffer, compression_buffer + compression_buffer_size};
+}
+
+void JpegCompressor::UpdateBuffer(std::uint8_t* compression_buffer,
+                                  unsigned long compression_buffer_size) {
+  if (buffer_.get() != compression_buffer) {
+    buffer_capacity_ = compression_buffer_size;
+    buffer_.reset(compression_buffer);
+  }
+}
diff --git a/host/frontend/vnc_server/jpeg_compressor.h b/host/frontend/vnc_server/jpeg_compressor.h
new file mode 100644
index 0000000..ae3af18
--- /dev/null
+++ b/host/frontend/vnc_server/jpeg_compressor.h
@@ -0,0 +1,52 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+namespace cvd {
+namespace vnc {
+
+// libjpeg-turbo with jpeg_mem_dest (using memory as a destination) is funky.
+// If you give it a buffer that is big enough it will use it.
+// If you give it a buffer that is too small, it will allocate a new buffer
+// but will NOT free the buffer you gave it.
+// This class keeps track of the capacity of the working buffer, and frees the
+// old buffer if libjpeg-turbo silently discards it.
+class JpegCompressor {
+ public:
+  Message Compress(const Message& frame, int jpeg_quality, std::uint16_t x,
+                   std::uint16_t y, std::uint16_t width, std::uint16_t height,
+                   int screen_width);
+
+ private:
+  void UpdateBuffer(std::uint8_t* compression_buffer,
+                    unsigned long compression_buffer_size);
+  struct Freer {
+    void operator()(void* p) const { std::free(p); }
+  };
+
+  std::unique_ptr<std::uint8_t, Freer> buffer_;
+  unsigned long buffer_capacity_{};
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/keysyms.h b/host/frontend/vnc_server/keysyms.h
new file mode 100644
index 0000000..41ff7c7
--- /dev/null
+++ b/host/frontend/vnc_server/keysyms.h
@@ -0,0 +1,54 @@
+#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 <cstdint>
+
+namespace cvd {
+namespace xk {
+
+constexpr uint32_t BackSpace = 0xff08, Tab = 0xff09, Return = 0xff0d,
+                   Enter = Return, Escape = 0xff1b, MultiKey = 0xff20,
+                   Insert = 0xff63, Delete = 0xffff, Pause = 0xff13,
+                   Home = 0xff50, End = 0xff57, PageUp = 0xff55,
+                   PageDown = 0xff56, Left = 0xff51, Up = 0xff52,
+                   Right = 0xff53, Down = 0xff54, F1 = 0xffbe, F2 = 0xffbf,
+                   F3 = 0xffc0, F4 = 0xffc1, F5 = 0xffc2, F6 = 0xffc3,
+                   F7 = 0xffc4, F8 = 0xffc5, F9 = 0xffc6, F10 = 0xffc7,
+                   F11 = 0xffc8, F12 = 0xffc9, F13 = 0xffca, F14 = 0xffcb,
+                   F15 = 0xffcc, F16 = 0xffcd, F17 = 0xffce, F18 = 0xffcf,
+                   F19 = 0xffd0, F20 = 0xffd1, F21 = 0xffd2, F22 = 0xffd3,
+                   F23 = 0xffd4, F24 = 0xffd5, ShiftLeft = 0xffe1,
+                   ShiftRight = 0xffe2, ControlLeft = 0xffe3,
+                   ControlRight = 0xffe4, MetaLeft = 0xffe7, MetaRight = 0xffe8,
+                   AltLeft = 0xffe9, AltRight = 0xffea, CapsLock = 0xffe5,
+                   NumLock = 0xff7f, ScrollLock = 0xff14, Keypad0 = 0xffb0,
+                   Keypad1 = 0xffb1, Keypad2 = 0xffb2, Keypad3 = 0xffb3,
+                   Keypad4 = 0xffb4, Keypad5 = 0xffb5, Keypad6 = 0xffb6,
+                   Keypad7 = 0xffb7, Keypad8 = 0xffb8, Keypad9 = 0xffb9,
+                   KeypadMultiply = 0xffaa, KeypadSubtract = 0xffad,
+                   KeypadAdd = 0xffab, KeypadDecimal = 0xffae,
+                   KeypadEnter = 0xff8d, KeypadDivide = 0xffaf,
+                   KeypadEqual = 0xffbd, PlusMinus = 0xb1, SysReq = 0xff15,
+                   LineFeed = 0xff0a, KeypadSeparator = 0xffac, Yen = 0xa5,
+                   Cancel = 0xff69, Undo = 0xff65, Redo = 0xff66, Find = 0xff68,
+                   Print = 0xff61, VolumeDown = 0x1008ff11, Mute = 0x1008ff12,
+                   VolumeUp = 0x1008ff13, Menu = 0xff67,
+                   VNCMenu = 0xffed;  // VNC seems to translate MENU to this
+
+}  // namespace xk
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/main.cpp b/host/frontend/vnc_server/main.cpp
new file mode 100644
index 0000000..65971d4
--- /dev/null
+++ b/host/frontend/vnc_server/main.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <algorithm>
+#include <string>
+
+#include <gflags/gflags.h>
+
+#include "common/libs/glog/logging.h"
+#include "host/frontend/vnc_server/vnc_server.h"
+#include "host/frontend/vnc_server/vnc_utils.h"
+#include "host/libs/config/host_config.h"
+
+DEFINE_bool(agressive, false, "Whether to use agressive server");
+DEFINE_int32(port, 6444, "Port where to listen for connections");
+
+int main(int argc, char* argv[]) {
+  using ::android::base::ERROR;
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+  if (!cvd::vnc::GetFBBroadcastRegionView()->Open(vsoc::GetDomain().c_str())) {
+    LOG(FATAL) << "Unable to open FBBroadcastRegion";
+  }
+  cvd::vnc::VncServer vnc_server(FLAGS_port, FLAGS_agressive);
+  vnc_server.MainLoop();
+}
diff --git a/host/frontend/vnc_server/mocks.h b/host/frontend/vnc_server/mocks.h
new file mode 100644
index 0000000..e69eb43
--- /dev/null
+++ b/host/frontend/vnc_server/mocks.h
@@ -0,0 +1,35 @@
+#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.
+ */
+
+struct GceFrameBuffer {
+  typedef uint32_t Pixel;
+
+  static const int kRedShift = 0;
+  static const int kRedBits = 8;
+  static const int kGreenShift = 8;
+  static const int kGreenBits = 8;
+  static const int kBlueShift = 16;
+  static const int kBlueBits = 8;
+  static const int kAlphaShift = 24;
+  static const int kAlphaBits = 8;
+};
+
+// Sensors
+struct gce_sensors_message {
+  static constexpr const char* const kSensorsHALSocketName = "";
+};
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
new file mode 100644
index 0000000..001006b
--- /dev/null
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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/frontend/vnc_server/simulated_hw_composer.h"
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "host/frontend/vnc_server/vnc_utils.h"
+#include "host/libs/config/host_config.h"
+#include "host/vsoc/lib/gralloc_buffer_region_view.h"
+
+using cvd::vnc::SimulatedHWComposer;
+using vsoc::gralloc::GrallocBufferRegionView;
+
+SimulatedHWComposer::SimulatedHWComposer(BlackBoard* bb)
+    :
+#ifdef FUZZ_TEST_VNC
+      engine_{std::random_device{}()},
+#endif
+      bb_{bb},
+      stripes_(kMaxQueueElements, &SimulatedHWComposer::EraseHalfOfElements) {
+  stripe_maker_ = std::thread(&SimulatedHWComposer::MakeStripes, this);
+}
+
+SimulatedHWComposer::~SimulatedHWComposer() {
+  close();
+  stripe_maker_.join();
+}
+
+cvd::vnc::Stripe SimulatedHWComposer::GetNewStripe() {
+  auto s = stripes_.Pop();
+#ifdef FUZZ_TEST_VNC
+  if (random_(engine_)) {
+    usleep(7000);
+    stripes_.Push(std::move(s));
+    s = stripes_.Pop();
+  }
+#endif
+  return s;
+}
+
+bool SimulatedHWComposer::closed() {
+  std::lock_guard<std::mutex> guard(m_);
+  return closed_;
+}
+
+void SimulatedHWComposer::close() {
+  std::lock_guard<std::mutex> guard(m_);
+  closed_ = true;
+}
+
+// Assuming the number of stripes is less than half the size of the queue
+// this will be safe as the newest stripes won't be lost. In the real
+// hwcomposer, where stripes are coming in a different order, the full
+// queue case would probably need a different approach to be safe.
+void SimulatedHWComposer::EraseHalfOfElements(
+    ThreadSafeQueue<Stripe>::QueueImpl* q) {
+  q->erase(q->begin(), std::next(q->begin(), kMaxQueueElements / 2));
+}
+
+void SimulatedHWComposer::MakeStripes() {
+  std::uint32_t previous_seq_num{};
+  auto screen_height = ActualScreenHeight();
+  Message raw_screen;
+  std::uint64_t stripe_seq_num = 1;
+  while (!closed()) {
+    bb_->WaitForAtLeastOneClientConnection();
+    vsoc_reg_off_t buffer_offset =
+        GetFBBroadcastRegionView()->WaitForNewFrameSince(&previous_seq_num);
+
+    const auto* frame_start =
+        GrallocBufferRegionView::GetInstance(vsoc::GetDomain().c_str())
+            ->OffsetToBufferPtr(buffer_offset);
+    raw_screen.assign(frame_start, frame_start + ScreenSizeInBytes());
+
+    for (int i = 0; i < kNumStripes; ++i) {
+      ++stripe_seq_num;
+      std::uint16_t y = (screen_height / kNumStripes) * i;
+
+      // Last frames on the right and/or bottom handle extra pixels
+      // when a screen dimension is not evenly divisible by Frame::kNumSlots.
+      std::uint16_t height =
+          screen_height / kNumStripes +
+          (i + 1 == kNumStripes ? screen_height % kNumStripes : 0);
+      const auto* raw_start =
+          &raw_screen[y * ActualScreenWidth() * BytesPerPixel()];
+      const auto* raw_end =
+          raw_start + (height * ActualScreenWidth() * BytesPerPixel());
+      // creating a named object and setting individual data members in order
+      // to make klp happy
+      // TODO (haining) construct this inside the call when not compiling
+      // on klp
+      Stripe s{};
+      s.index = i;
+      s.frame_id = previous_seq_num;
+      s.x = 0;
+      s.y = y;
+      s.width = ActualScreenWidth();
+      s.height = height;
+      s.raw_data.assign(raw_start, raw_end);
+      s.seq_number = StripeSeqNumber{stripe_seq_num};
+      s.orientation = ScreenOrientation::Portrait;
+      stripes_.Push(std::move(s));
+    }
+  }
+}
+
+int SimulatedHWComposer::NumberOfStripes() { return kNumStripes; }
diff --git a/host/frontend/vnc_server/simulated_hw_composer.h b/host/frontend/vnc_server/simulated_hw_composer.h
new file mode 100644
index 0000000..6078584
--- /dev/null
+++ b/host/frontend/vnc_server/simulated_hw_composer.h
@@ -0,0 +1,65 @@
+#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 <condition_variable>
+#include <mutex>
+#ifdef FUZZ_TEST_VNC
+#include <random>
+#endif
+#include <thread>
+
+#include "common/libs/thread_safe_queue/thread_safe_queue.h"
+#include "common/libs/threads/thread_annotations.h"
+#include "common/vsoc/lib/fb_bcast_region_view.h"
+#include "host/frontend/vnc_server/blackboard.h"
+
+namespace cvd {
+namespace vnc {
+class SimulatedHWComposer {
+ public:
+  SimulatedHWComposer(BlackBoard* bb);
+  SimulatedHWComposer(const SimulatedHWComposer&) = delete;
+  SimulatedHWComposer& operator=(const SimulatedHWComposer&) = delete;
+  ~SimulatedHWComposer();
+
+  Stripe GetNewStripe();
+
+  // NOTE not constexpr on purpose
+  static int NumberOfStripes();
+
+ private:
+  bool closed();
+  void close();
+  static void EraseHalfOfElements(ThreadSafeQueue<Stripe>::QueueImpl* q);
+  void MakeStripes();
+
+#ifdef FUZZ_TEST_VNC
+  std::default_random_engine engine_;
+  std::uniform_int_distribution<int> random_ =
+      std::uniform_int_distribution<int>{0, 2};
+#endif
+  static constexpr int kNumStripes = 8;
+  constexpr static std::size_t kMaxQueueElements = 64;
+  bool closed_ GUARDED_BY(m_){};
+  std::mutex m_;
+  BlackBoard* bb_{};
+  ThreadSafeQueue<Stripe> stripes_;
+  std::thread stripe_maker_;
+};
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/tcp_socket.cpp b/host/frontend/vnc_server/tcp_socket.cpp
new file mode 100644
index 0000000..92aed93
--- /dev/null
+++ b/host/frontend/vnc_server/tcp_socket.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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/frontend/vnc_server/tcp_socket.h"
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <cerrno>
+
+#include <glog/logging.h>
+
+using cvd::SharedFD;
+using cvd::vnc::ClientSocket;
+using cvd::vnc::Message;
+using cvd::vnc::ServerSocket;
+
+Message ClientSocket::Recv(size_t length) {
+  Message buf(length);
+  ssize_t total_read = 0;
+  while (total_read < static_cast<ssize_t>(length)) {
+    auto just_read = fd_->Read(&buf[total_read], buf.size() - total_read);
+    if (just_read <= 0) {
+      if (just_read < 0) {
+        LOG(ERROR) << "read() error: " << strerror(errno);
+      }
+      other_side_closed_ = true;
+      return Message{};
+    }
+    total_read += just_read;
+  }
+  CHECK(total_read == static_cast<ssize_t>(length));
+  return buf;
+}
+
+ssize_t ClientSocket::Send(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)) {
+    auto just_written = fd_->Write(data + written, size - written);
+    if (just_written <= 0) {
+      LOG(INFO) << "Couldn't write to vnc client: " << strerror(errno);
+      return just_written;
+    }
+    written += just_written;
+  }
+  return written;
+}
+
+ssize_t ClientSocket::Send(const Message& message) {
+  return Send(&message[0], message.size());
+}
+
+ServerSocket::ServerSocket(int port)
+    : fd_{SharedFD::SocketLocalServer(port, SOCK_STREAM)} {
+  if (!fd_->IsOpen()) {
+    LOG(FATAL) << "Couldn't open streaming server on port " << port;
+  }
+}
+
+ClientSocket ServerSocket::Accept() {
+  SharedFD client = SharedFD::Accept(*fd_);
+  if (!client->IsOpen()) {
+    LOG(FATAL) << "Error attemping to accept: " << strerror(errno);
+  }
+  return ClientSocket{client};
+}
diff --git a/host/frontend/vnc_server/tcp_socket.h b/host/frontend/vnc_server/tcp_socket.h
new file mode 100644
index 0000000..f926642
--- /dev/null
+++ b/host/frontend/vnc_server/tcp_socket.h
@@ -0,0 +1,80 @@
+#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 "host/frontend/vnc_server/vnc_utils.h"
+
+#include <unistd.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <mutex>
+
+namespace cvd {
+namespace vnc {
+
+class ServerSocket;
+
+// Recv and Send wait until all data has been received or sent.
+// Send is thread safe in this regard, Recv is not.
+class ClientSocket {
+ public:
+  ClientSocket(ClientSocket&& other) : fd_{other.fd_} {}
+
+  ClientSocket& operator=(ClientSocket&& other) {
+    fd_ = other.fd_;
+    return *this;
+  }
+
+  ClientSocket(const ClientSocket&) = delete;
+  ClientSocket& operator=(const ClientSocket&) = delete;
+
+  Message Recv(std::size_t length);
+  ssize_t Send(const std::uint8_t* data, std::size_t size);
+  ssize_t Send(const Message& message);
+
+  template <std::size_t N>
+  ssize_t Send(const std::uint8_t (&data)[N]) {
+    return Send(data, N);
+  }
+
+  bool closed() const { return other_side_closed_; }
+
+ private:
+  friend ServerSocket;
+  explicit ClientSocket(cvd::SharedFD fd) : fd_(fd) {}
+
+  cvd::SharedFD fd_;
+  bool other_side_closed_{};
+  std::mutex send_lock_;
+};
+
+class ServerSocket {
+ public:
+  explicit ServerSocket(int port);
+
+  ServerSocket(const ServerSocket&) = delete;
+  ServerSocket& operator=(const ServerSocket&) = delete;
+
+  ClientSocket Accept();
+
+ private:
+  cvd::SharedFD fd_;
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/virtual_input_device.cpp b/host/frontend/vnc_server/virtual_input_device.cpp
new file mode 100644
index 0000000..66cedc2
--- /dev/null
+++ b/host/frontend/vnc_server/virtual_input_device.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2014 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/virtual_input_device.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <vector>
+
+#include <glog/logging.h>
+#include "host/frontend/vnc_server/keysyms.h"
+
+namespace cvd {
+
+//////////////////////////
+// VirtualButton Support
+//////////////////////////
+
+void VirtualButton::HandleButtonPressEvent(bool button_down) {
+  SendCommand(std::string("key ") + "down " + input_keycode_ + "\n");
+}
+
+//////////////////////////
+// VirtualKeyboard Support
+//////////////////////////
+
+struct KeyEventToInput {
+  uint32_t xk;
+  std::string input_code;
+};
+
+static const KeyEventToInput key_table[] = {
+    {xk::AltLeft, "KEYCODE_ALT_LEFT"},
+    {xk::ControlLeft, "KEYCODE_CTRL_LEFT"},
+    {xk::ShiftLeft, "KEYCODE_SHIFT_LEFT"},
+    {xk::AltRight, "KEYCODE_ALT_RIGHT"},
+    {xk::ControlRight, "KEYCODE_CTRL_RIGHT"},
+    {xk::ShiftRight, "KEYCODE_SHIFT_RIGHT"},
+    {xk::MetaLeft, "KEYCODE_META_LEFT"},
+    {xk::MetaRight, "KEYCODE_META_RIGHT"},
+    // {xk::MultiKey, "KEYCODE_COMPOSE"},
+
+    {xk::CapsLock, "KEYCODE_CAPS_LOCK"},
+    {xk::NumLock, "KEYCODE_NUM_LOCK"},
+    {xk::ScrollLock, "KEYCODE_SCROLL_LOCK"},
+
+    {xk::BackSpace, "KEYCODE_DEL"},
+    {xk::Tab, "KEYCODE_TAB"},
+    {xk::Return, "KEYCODE_ENTER"},
+    {xk::Escape, "KEYCODE_ESCAPE"},
+
+    {' ', "KEYCODE_SPACE"},
+    {'!', "KEYCODE_1"},
+    {'"', "KEYCODE_APOSTROPHE"},
+    {'#', "KEYCODE_POUND"},
+    {'$', "KEYCODE_4"},
+    {'%', "KEYCODE_5"},
+    {'^', "KEYCODE_6"},
+    {'&', "KEYCODE_7"},
+    {'\'', "KEYCODE_APOSTROPHE"},
+    {'(', "KEYCODE_NUMPAD_LEFT_PAREN"},
+    {')', "KEYCODE_NUMPAD_RIGHT_PAREN"},
+    {'*', "KEYCODE_STAR"},
+    {'+', "KEYCODE_EQUALS"},
+    {',', "KEYCODE_COMMA"},
+    {'-', "KEYCODE_MINUS"},
+    {'.', "KEYCODE_PERIOD"},
+    {'/', "KEYCODE_SLASH"},
+    {'0', "KEYCODE_0"},
+    {'1', "KEYCODE_1"},
+    {'2', "KEYCODE_2"},
+    {'3', "KEYCODE_3"},
+    {'4', "KEYCODE_4"},
+    {'5', "KEYCODE_5"},
+    {'6', "KEYCODE_6"},
+    {'7', "KEYCODE_7"},
+    {'8', "KEYCODE_8"},
+    {'9', "KEYCODE_9"},
+    {':', "KEYCODE_SEMICOLON"},
+    {';', "KEYCODE_SEMICOLON"},
+    {'<', "KEYCODE_COMMA"},
+    {'=', "KEYCODE_EQUALS"},
+    {'>', "KEYCODE_PERIOD"},
+    {'?', "KEYCODE_SLASH"},
+    {'@', "KEYCODE_2"},
+    {'A', "KEYCODE_A"},
+    {'B', "KEYCODE_B"},
+    {'C', "KEYCODE_C"},
+    {'D', "KEYCODE_D"},
+    {'E', "KEYCODE_E"},
+    {'F', "KEYCODE_F"},
+    {'G', "KEYCODE_G"},
+    {'H', "KEYCODE_H"},
+    {'I', "KEYCODE_I"},
+    {'J', "KEYCODE_J"},
+    {'K', "KEYCODE_K"},
+    {'L', "KEYCODE_L"},
+    {'M', "KEYCODE_M"},
+    {'N', "KEYCODE_N"},
+    {'O', "KEYCODE_O"},
+    {'P', "KEYCODE_P"},
+    {'Q', "KEYCODE_Q"},
+    {'R', "KEYCODE_R"},
+    {'S', "KEYCODE_S"},
+    {'T', "KEYCODE_T"},
+    {'U', "KEYCODE_U"},
+    {'V', "KEYCODE_V"},
+    {'W', "KEYCODE_W"},
+    {'X', "KEYCODE_X"},
+    {'Y', "KEYCODE_Y"},
+    {'Z', "KEYCODE_Z"},
+    {'[', "KEYCODE_LEFT_BRACKET"},
+    {'\\', "KEYCODE_BACKSLASH"},
+    {']', "KEYCODE_RIGHT_BRACKET"},
+    {'-', "KEYCODE_MINUS"},
+    {'_', "KEYCODE_MINUS"},
+    {'`', "KEYCODE_GRAVE"},
+    {'a', "KEYCODE_A"},
+    {'b', "KEYCODE_B"},
+    {'c', "KEYCODE_C"},
+    {'d', "KEYCODE_D"},
+    {'e', "KEYCODE_E"},
+    {'f', "KEYCODE_F"},
+    {'g', "KEYCODE_G"},
+    {'h', "KEYCODE_H"},
+    {'i', "KEYCODE_I"},
+    {'j', "KEYCODE_J"},
+    {'k', "KEYCODE_K"},
+    {'l', "KEYCODE_L"},
+    {'m', "KEYCODE_M"},
+    {'n', "KEYCODE_N"},
+    {'o', "KEYCODE_O"},
+    {'p', "KEYCODE_P"},
+    {'q', "KEYCODE_Q"},
+    {'r', "KEYCODE_R"},
+    {'s', "KEYCODE_S"},
+    {'t', "KEYCODE_T"},
+    {'u', "KEYCODE_U"},
+    {'v', "KEYCODE_V"},
+    {'w', "KEYCODE_W"},
+    {'x', "KEYCODE_X"},
+    {'y', "KEYCODE_Y"},
+    {'z', "KEYCODE_Z"},
+    {'{', "KEYCODE_LEFT_BRACKET"},
+    {'\\', "KEYCODE_BACKSLASH"},
+    // {'|', "|"},
+    {'}', "KEYCODE_RIGHT_BRACKET"},
+    {'~', "KEYCODE_GRAVE"},
+
+    {xk::F1, "KEYCODE_F1"},
+    {xk::F2, "KEYCODE_F2"},
+    {xk::F3, "KEYCODE_F3"},
+    {xk::F4, "KEYCODE_F4"},
+    {xk::F5, "KEYCODE_F5"},
+    {xk::F6, "KEYCODE_F6"},
+    {xk::F7, "KEYCODE_F7"},
+    {xk::F8, "KEYCODE_F8"},
+    {xk::F9, "KEYCODE_F9"},
+    {xk::F10, "KEYCODE_F10"},
+    {xk::F11, "KEYCODE_F11"},
+    {xk::F12, "KEYCODE_F12"},
+    // {xk::F13, "KEYCODE_F13"},
+    // {xk::F14, "KEYCODE_F14"},
+    // {xk::F15, "KEYCODE_F15"},
+    // {xk::F16, "KEYCODE_F16"},
+    // {xk::F17, "KEYCODE_F17"},
+    // {xk::F18, "KEYCODE_F18"},
+    // {xk::F19, "KEYCODE_F19"},
+    // {xk::F20, "KEYCODE_F20"},
+    // {xk::F21, "KEYCODE_F21"},
+    // {xk::F22, "KEYCODE_F22"},
+    // {xk::F23, "KEYCODE_F23"},
+    // {xk::F24, "KEYCODE_F24"},
+
+    {xk::Keypad0, "KEYCODE_NUMPAD_0"},
+    {xk::Keypad1, "KEYCODE_NUMPAD_1"},
+    {xk::Keypad2, "KEYCODE_NUMPAD_2"},
+    {xk::Keypad3, "KEYCODE_NUMPAD_3"},
+    {xk::Keypad4, "KEYCODE_NUMPAD_4"},
+    {xk::Keypad5, "KEYCODE_NUMPAD_5"},
+    {xk::Keypad6, "KEYCODE_NUMPAD_6"},
+    {xk::Keypad7, "KEYCODE_NUMPAD_7"},
+    {xk::Keypad8, "KEYCODE_NUMPAD_8"},
+    {xk::Keypad9, "KEYCODE_NUMPAD_9"},
+    {xk::KeypadMultiply, "KEYCODE_NUMPAD_MULTIPLY"},
+    {xk::KeypadSubtract, "KEYCODE_NUMPAD_SUBTRACT"},
+    {xk::KeypadAdd, "KEYCODE_NUMPAD_ADD"},
+    {xk::KeypadDecimal, "KEYCODE_NUMPAD_DOT"},
+    {xk::KeypadEnter, "KEYCODE_NUMPAD_ENTER"},
+    {xk::KeypadDivide, "KEYCODE_NUMPAD_DIVIDE"},
+    {xk::KeypadEqual, "KEYCODE_NUMPAD_EQUALS"},
+    // {xk::PlusMinus, "KEYCODE_NUMPAD_PLUSMINUS"},
+
+    {xk::SysReq, "KEYCODE_SYSRQ"},
+    // {xk::LineFeed, "KEYCODE_LINEFEED"},
+    {xk::Home, "KEYCODE_HOME"},
+    {xk::Up, "KEYCODE_DPAD_UP"},
+    {xk::PageUp, "KEYCODE_PAGE_UP"},
+    {xk::Left, "KEYCODE_DPAD_LEFT"},
+    {xk::Right, "KEYCODE_DPAD_RIGHT"},
+    {xk::End, "KEYCODE_MOVE_END"},
+    {xk::Down, "KEYCODE_DPAD_DOWN"},
+    {xk::PageDown, "KEYCODE_PAGE_DOWN"},
+    {xk::Insert, "KEYCODE_INSERT"},
+    {xk::Delete, "KEYCODE_FORWARD_DEL"},
+    {xk::Pause, "KEYCODE_BREAK"},
+    {xk::KeypadSeparator, "KEYCODE_NUMPAD_COMMA"},
+    {xk::Yen, "KEYCODE_YEN"},
+    // {xk::Cancel, "KEYCODE_STOP"},
+    // {xk::Redo, "KEYCODE_AGAIN"},
+    // {xk::Undo, "KEYCODE_UNDO"},
+    // {xk::Find, "KEYCODE_FIND"},
+    // {xk::Print, "KEYCODE_PRINT"},
+    {xk::VolumeDown, "KEYCODE_VOLUME_DOWN"},
+    {xk::Mute, "KEYCODE_MUTE"},
+    {xk::VolumeUp, "KEYCODE_VOLUME_UP"},
+    {xk::Menu, "KEYCODE_MENU"},
+    {xk::VNCMenu, "KEYCODE_MENU"},
+};
+
+VirtualKeyboard::VirtualKeyboard(std::function<bool(std::string)> cmd_sender)
+    : VirtualInputDevice(cmd_sender) {
+  for (const auto& key : key_table) {
+    keymapping_[key.xk] = key.input_code;
+  }
+}
+
+void VirtualKeyboard::GenerateKeyPressEvent(int keycode, bool button_down) {
+  if (keymapping_.count(keycode)) {
+    SendCommand(std::string("key ") + (button_down ? "down " : "up ") +
+                keymapping_[keycode] + "\n");
+  } else {
+    LOG(INFO) << "Unknown keycode " << keycode;
+  }
+}
+
+//////////////////////////
+// VirtualTouchPad Support
+//////////////////////////
+
+std::string VirtualTouchPad::GetCommand(bool touch_down, int x, int y) {
+  std::string cmd;
+  if (touch_down == prev_touch_) {
+    if (touch_down && (x != prev_x_ || y != prev_y_)) {
+      cmd = "touch move ";
+    }  // else don't repeat last event or send non touch mouse movements
+  } else if (touch_down) {
+    cmd = "touch down ";
+  } else {
+    cmd = "touch up ";
+  }
+  prev_touch_ = touch_down;
+  prev_x_ = x;
+  prev_y_ = y;
+  return cmd;
+}
+
+void VirtualTouchPad::HandlePointerEvent(bool touch_down, int x, int y) {
+  SendCommand(GetCommand(touch_down, x, y) + std::to_string(x) + " " +
+              std::to_string(y) + "\n");
+}
+
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/virtual_input_device.h b/host/frontend/vnc_server/virtual_input_device.h
new file mode 100644
index 0000000..5fb0be5
--- /dev/null
+++ b/host/frontend/vnc_server/virtual_input_device.h
@@ -0,0 +1,78 @@
+#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 <functional>
+#include <map>
+#include <string>
+
+namespace cvd {
+
+// Base virtual input device class which contains a bunch of boiler-plate code.
+class VirtualInputDevice {
+ public:
+  VirtualInputDevice(std::function<bool(std::string)> cmd_sender)
+      : send_command_(cmd_sender) {}
+
+ protected:
+  bool SendCommand(std::string cmd) { return send_command_(cmd); }
+
+ private:
+  std::function<bool(std::string)> send_command_;
+};
+
+// Virtual touch-pad.
+class VirtualTouchPad : public VirtualInputDevice {
+ public:
+  VirtualTouchPad(std::function<bool(std::string)> cmd_sender)
+      : VirtualInputDevice(cmd_sender) {}
+
+  void HandlePointerEvent(bool touch_down, int x, int y);
+
+ private:
+  std::string GetCommand(bool touch_down, int x, int y);
+  bool prev_touch_ = false;
+  int prev_x_ = -1;
+  int prev_y_ = -1;
+};
+
+// Virtual button.
+class VirtualButton : public VirtualInputDevice {
+ public:
+  VirtualButton(std::string input_keycode,
+                std::function<bool(std::string)> cmd_sender)
+      : VirtualInputDevice(cmd_sender), input_keycode_(input_keycode) {}
+
+  void HandleButtonPressEvent(bool button_down);
+
+ private:
+  std::string input_keycode_;
+};
+
+// Virtual keyboard.
+class VirtualKeyboard : public VirtualInputDevice {
+ public:
+  VirtualKeyboard(std::function<bool(std::string)> cmd_sender);
+  virtual ~VirtualKeyboard() {}
+
+  void GenerateKeyPressEvent(int code, bool down);
+
+ private:
+  std::map<uint32_t, std::string> keymapping_;
+};
+
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
new file mode 100644
index 0000000..369fa96
--- /dev/null
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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/frontend/vnc_server/virtual_inputs.h"
+#include <gflags/gflags.h>
+
+#include <mutex>
+#include <thread>
+
+DEFINE_string(input_socket, "/tmp/android-cuttlefish-1-input",
+              "The name of unix socket where the monkey server is listening "
+              "for input commands");
+using cvd::vnc::VirtualInputs;
+
+VirtualInputs::VirtualInputs()
+    : virtual_keyboard_(
+          [this](std::string cmd) { return SendMonkeyComand(cmd); }),
+      virtual_touch_pad_(
+          [this](std::string cmd) { return SendMonkeyComand(cmd); }),
+      virtual_power_button_("KEYCODE_POWER", [this](std::string cmd) {
+        return SendMonkeyComand(cmd);
+      }) {
+  monkey_socket_ = cvd::SharedFD::SocketLocalClient(FLAGS_input_socket.c_str(),
+                                                    false, SOCK_STREAM);
+  if (!monkey_socket_->IsOpen()) {
+    // Monkey is known to die on the second conection, so let's wait a litttle
+    // bit and try again.
+    std::this_thread::sleep_for(std::chrono::milliseconds(500));
+    monkey_socket_ = cvd::SharedFD::SocketLocalClient(
+        FLAGS_input_socket.c_str(), false, SOCK_STREAM);
+    if (!monkey_socket_->IsOpen()) {
+      LOG(FATAL) << "Unable to connect to the mokey server";
+    }
+  }
+}
+
+namespace {
+constexpr char kCmdDone[] = "done\n";
+}  // anonymous namespace
+
+VirtualInputs::~VirtualInputs() {
+  if (monkey_socket_->IsOpen()) {
+    monkey_socket_->Send(kCmdDone, sizeof(kCmdDone) - 1, 0);
+  }
+}
+
+bool VirtualInputs::SendMonkeyComand(std::string cmd) {
+  return monkey_socket_->Send(cmd.c_str(), cmd.size(), 0) == cmd.size();
+  // TODO(jemoreira): If monkey is going to be used for a long time it may be
+  // useful to check the response to this commands.
+}
+
+void VirtualInputs::GenerateKeyPressEvent(int code, bool down) {
+  std::lock_guard<std::mutex> guard(m_);
+  virtual_keyboard_.GenerateKeyPressEvent(code, down);
+}
+
+void VirtualInputs::PressPowerButton(bool down) {
+  std::lock_guard<std::mutex> guard(m_);
+  virtual_power_button_.HandleButtonPressEvent(down);
+}
+
+void VirtualInputs::HandlePointerEvent(bool touch_down, int x, int y) {
+  std::lock_guard<std::mutex> guard(m_);
+  virtual_touch_pad_.HandlePointerEvent(touch_down, x, y);
+}
diff --git a/host/frontend/vnc_server/virtual_inputs.h b/host/frontend/vnc_server/virtual_inputs.h
new file mode 100644
index 0000000..d5d00b9
--- /dev/null
+++ b/host/frontend/vnc_server/virtual_inputs.h
@@ -0,0 +1,50 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <linux/input.h>
+
+#include <mutex>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/threads/thread_annotations.h"
+#include "host/frontend/vnc_server/virtual_input_device.h"
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+namespace cvd {
+namespace vnc {
+
+class VirtualInputs {
+ public:
+  VirtualInputs();
+  ~VirtualInputs();
+
+  void GenerateKeyPressEvent(int code, bool down);
+  void PressPowerButton(bool down);
+  void HandlePointerEvent(bool touch_down, int x, int y);
+
+ private:
+  cvd::SharedFD monkey_socket_;
+  bool SendMonkeyComand(std::string cmd);
+  std::mutex m_;
+  VirtualKeyboard virtual_keyboard_ GUARDED_BY(m_);
+  VirtualTouchPad virtual_touch_pad_ GUARDED_BY(m_);
+  VirtualButton virtual_power_button_ GUARDED_BY(m_);
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/vnc_client_connection.cpp b/host/frontend/vnc_server/vnc_client_connection.cpp
new file mode 100644
index 0000000..c9a87c8
--- /dev/null
+++ b/host/frontend/vnc_server/vnc_client_connection.cpp
@@ -0,0 +1,723 @@
+/*
+ * 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/frontend/vnc_server/vnc_client_connection.h"
+
+#include <netinet/in.h>
+#include <sys/time.h>
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+#include "host/frontend/vnc_server/keysyms.h"
+#include "host/frontend/vnc_server/mocks.h"
+#include "host/frontend/vnc_server/tcp_socket.h"
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+using cvd::vnc::Message;
+using cvd::vnc::Stripe;
+using cvd::vnc::StripePtrVec;
+using cvd::vnc::VncClientConnection;
+
+DEFINE_bool(debug_client, false, "Turn on detailed logging for the client");
+
+#define DLOG(LEVEL)                                 \
+  if (FLAGS_debug_client) LOG(LEVEL)
+
+
+namespace {
+class BigEndianChecker {
+ public:
+  BigEndianChecker() {
+    uint32_t u = 1;
+    is_big_endian_ = *reinterpret_cast<const char*>(&u) == 0;
+  }
+  bool operator()() const { return is_big_endian_; }
+
+ private:
+  bool is_big_endian_{};
+};
+
+const BigEndianChecker ImBigEndian;
+
+constexpr int32_t kDesktopSizeEncoding = -223;
+constexpr int32_t kTightEncoding = 7;
+
+// These are the lengths not counting the first byte. The first byte
+// indicates the message type.
+constexpr size_t kSetPixelFormatLength = 19;
+constexpr size_t kFramebufferUpdateRequestLength = 9;
+constexpr size_t kSetEncodingsLength = 3;  // more bytes follow
+constexpr size_t kKeyEventLength = 7;
+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() {
+  // Localhost is good enough for local development and to connect through ssh
+  // tunneling, for something else this probably needs to change.
+  return "localhost";
+}
+
+std::uint16_t uint16_tAt(const void* p) {
+  std::uint16_t u{};
+  std::memcpy(&u, p, sizeof u);
+  return ntohs(u);
+}
+
+std::uint32_t uint32_tAt(const void* p) {
+  std::uint32_t u{};
+  std::memcpy(&u, p, sizeof u);
+  return ntohl(u);
+}
+
+std::int32_t int32_tAt(const void* p) {
+  std::uint32_t u{};
+  std::memcpy(&u, p, sizeof u);
+  u = ntohl(u);
+  std::int32_t s{};
+  std::memcpy(&s, &u, sizeof s);
+  return s;
+}
+
+std::uint32_t RedVal(std::uint32_t pixel) {
+  return (pixel >> GceFrameBuffer::kRedShift) &
+         ((0x1 << GceFrameBuffer::kRedBits) - 1);
+}
+
+std::uint32_t BlueVal(std::uint32_t pixel) {
+  return (pixel >> GceFrameBuffer::kBlueShift) &
+         ((0x1 << GceFrameBuffer::kBlueBits) - 1);
+}
+
+std::uint32_t GreenVal(std::uint32_t pixel) {
+  return (pixel >> GceFrameBuffer::kGreenShift) &
+         ((0x1 << GceFrameBuffer::kGreenBits) - 1);
+}
+}  // namespace
+namespace cvd {
+namespace vnc {
+bool operator==(const VncClientConnection::FrameBufferUpdateRequest& lhs,
+                const VncClientConnection::FrameBufferUpdateRequest& rhs) {
+  return lhs.x_pos == rhs.x_pos && lhs.y_pos == rhs.y_pos &&
+         lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+bool operator!=(const VncClientConnection::FrameBufferUpdateRequest& lhs,
+                const VncClientConnection::FrameBufferUpdateRequest& rhs) {
+  return !(lhs == rhs);
+}
+}  // namespace vnc
+}  // namespace cvd
+
+VncClientConnection::VncClientConnection(ClientSocket client,
+                                         VirtualInputs* virtual_inputs,
+                                         BlackBoard* bb, bool aggressive)
+    : client_{std::move(client)},
+      sensor_event_hal_{cvd::SharedFD::SocketSeqPacketClient(
+          gce_sensors_message::kSensorsHALSocketName)},
+      virtual_inputs_{virtual_inputs},
+      bb_{bb} {
+  frame_buffer_request_handler_tid_ = std::thread(
+      &VncClientConnection::FrameBufferUpdateRequestHandler, this, aggressive);
+}
+
+VncClientConnection::~VncClientConnection() {
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    closed_ = true;
+  }
+  bb_->StopWaiting(this);
+  frame_buffer_request_handler_tid_.join();
+}
+
+void VncClientConnection::StartSession() {
+  LOG(INFO) << "Starting session";
+  SetupProtocol();
+  LOG(INFO) << "Protocol set up";
+  if (client_.closed()) {
+    return;
+  }
+  SetupSecurityType();
+  LOG(INFO) << "Security type set";
+  if (client_.closed()) {
+    return;
+  }
+  GetClientInit();
+  LOG(INFO) << "Gotten client init";
+  if (client_.closed()) {
+    return;
+  }
+  SendServerInit();
+  LOG(INFO) << "Sent server init";
+  if (client_.closed()) {
+    return;
+  }
+  NormalSession();
+  LOG(INFO) << "vnc session terminated";
+}
+
+bool VncClientConnection::closed() {
+  std::lock_guard<std::mutex> guard(m_);
+  return closed_;
+}
+
+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);
+  auto client_protocol = client_.Recv(kVersionLen);
+  if (std::memcmp(&client_protocol[0], kRFBVersion,
+                  std::min(kVersionLen, client_protocol.size())) != 0) {
+    client_protocol.push_back('\0');
+    LOG(ERROR) << "vnc client wants a different protocol: "
+               << reinterpret_cast<const char*>(&client_protocol[0]);
+  }
+}
+
+void VncClientConnection::SetupSecurityType() {
+  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);
+  auto client_security = client_.Recv(1);
+  if (client_.closed()) {
+    return;
+  }
+  if (client_security.front() != kNoneSecurity) {
+    LOG(ERROR) << "vnc client is asking for security type "
+               << static_cast<int>(client_security.front());
+  }
+  static constexpr std::uint8_t kZero[4] = {};
+  client_.Send(kZero);
+}
+
+void VncClientConnection::GetClientInit() {
+  auto client_shared = client_.Recv(1);
+}
+
+void VncClientConnection::SendServerInit() {
+  const std::string server_name = HostName();
+  std::lock_guard<std::mutex> guard(m_);
+  auto server_init = 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,
+      pixel_format_.red_max, pixel_format_.green_max, pixel_format_.blue_max,
+      pixel_format_.red_shift, pixel_format_.green_shift,
+      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);
+}
+
+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});
+}
+
+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);
+}
+
+void VncClientConnection::AppendJpegSize(Message* frame_buffer_update,
+                                         size_t jpeg_size) {
+  constexpr size_t kJpegSizeOneByteMax = 127;
+  constexpr size_t kJpegSizeTwoByteMax = 16383;
+  constexpr size_t kJpegSizeThreeByteMax = 4194303;
+
+  if (jpeg_size <= kJpegSizeOneByteMax) {
+    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));
+  } 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));
+  }
+}
+
+void VncClientConnection::AppendRawStripe(Message* frame_buffer_update,
+                                          const Stripe& stripe) const {
+  using Pixel = GceFrameBuffer::Pixel;
+  auto& fbu = *frame_buffer_update;
+  AppendRawStripeHeader(&fbu, stripe);
+  auto init_size = fbu.size();
+  fbu.insert(fbu.end(), stripe.raw_data.begin(), stripe.raw_data.end());
+  for (size_t i = init_size; i < fbu.size(); i += sizeof(Pixel)) {
+    CHECK((i + sizeof(Pixel)) < fbu.size());
+    Pixel raw_pixel{};
+    std::memcpy(&raw_pixel, &fbu[i], sizeof raw_pixel);
+    auto red = RedVal(raw_pixel);
+    auto green = GreenVal(raw_pixel);
+    auto blue = BlueVal(raw_pixel);
+    Pixel pixel = Pixel{red} << pixel_format_.red_shift |
+                  Pixel{blue} << pixel_format_.blue_shift |
+                  Pixel{green} << pixel_format_.green_shift;
+
+    if (bool(pixel_format_.big_endian) != ImBigEndian()) {
+      // flip them bits (refactor into function)
+      auto p = reinterpret_cast<char*>(&pixel);
+      std::swap(p[0], p[3]);
+      std::swap(p[1], p[2]);
+    }
+    CHECK(i + sizeof pixel <= fbu.size());
+    std::memcpy(&fbu[i], &pixel, sizeof pixel);
+  }
+}
+
+Message VncClientConnection::MakeRawFrameBufferUpdate(
+    const StripePtrVec& stripes) const {
+  auto fbu =
+      MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
+  for (auto& stripe : stripes) {
+    AppendRawStripe(&fbu, *stripe);
+  }
+  return fbu;
+}
+
+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);
+  AppendJpegSize(frame_buffer_update, stripe.jpeg_data.size());
+}
+
+void VncClientConnection::AppendJpegStripe(Message* frame_buffer_update,
+                                           const Stripe& stripe) {
+  AppendJpegStripeHeader(frame_buffer_update, stripe);
+  frame_buffer_update->insert(frame_buffer_update->end(),
+                              stripe.jpeg_data.begin(), stripe.jpeg_data.end());
+}
+
+Message VncClientConnection::MakeJpegFrameBufferUpdate(
+    const StripePtrVec& stripes) {
+  auto fbu =
+      MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
+  for (auto& stripe : stripes) {
+    AppendJpegStripe(&fbu, *stripe);
+  }
+  return fbu;
+}
+
+Message VncClientConnection::MakeFrameBufferUpdate(
+    const StripePtrVec& stripes) {
+  return use_jpeg_compression_ ? MakeJpegFrameBufferUpdate(stripes)
+                               : MakeRawFrameBufferUpdate(stripes);
+}
+
+void VncClientConnection::FrameBufferUpdateRequestHandler(bool aggressive) {
+  BlackBoard::Registerer reg(bb_, this);
+  const StripeSeqNumber kBeginningOfTime{};
+
+  while (!closed()) {
+    auto stripes = bb_->WaitForSenderWork(this);
+    if (closed()) {
+      break;
+    }
+    if (stripes.empty()) {
+      LOG(FATAL) << "Got 0 stripes";
+    }
+    {
+      // lock here so a portrait frame can't be sent after a landscape
+      // DesktopSize update, or vice versa.
+      std::lock_guard<std::mutex> guard(m_);
+      DLOG(INFO) << "Sending update in "
+                 << (current_orientation_ == ScreenOrientation::Portrait
+                         ? "portrait"
+                         : "landscape")
+                 << " mode";
+      client_.Send(MakeFrameBufferUpdate(stripes));
+    }
+    if (aggressive) {
+      bb_->FrameBufferUpdateRequestReceived(this);
+    }
+  }
+}
+
+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));
+}
+
+bool VncClientConnection::IsUrgent(
+    const FrameBufferUpdateRequest& update_request) const {
+  return !update_request.incremental ||
+         update_request != previous_update_request_;
+}
+
+void VncClientConnection::HandleFramebufferUpdateRequest() {
+  auto msg = client_.Recv(kFramebufferUpdateRequestLength);
+  if (msg.size() != kFramebufferUpdateRequestLength) {
+    return;
+  }
+  FrameBufferUpdateRequest fbur{msg[1] == 0, uint16_tAt(&msg[1]),
+                                uint16_tAt(&msg[3]), uint16_tAt(&msg[5]),
+                                uint16_tAt(&msg[7])};
+  if (IsUrgent(fbur)) {
+    bb_->SignalClientNeedsEntireScreen(this);
+  }
+  bb_->FrameBufferUpdateRequestReceived(this);
+  previous_update_request_ = fbur;
+}
+
+void VncClientConnection::HandleSetEncodings() {
+  auto msg = client_.Recv(kSetEncodingsLength);
+  if (msg.size() != kSetEncodingsLength) {
+    return;
+  }
+  auto count = uint16_tAt(&msg[1]);
+  auto encodings = client_.Recv(count * sizeof(int32_t));
+  if (encodings.size() % sizeof(int32_t) != 0) {
+    return;
+  }
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    use_jpeg_compression_ = false;
+  }
+  for (size_t i = 0; i < encodings.size(); i += sizeof(int32_t)) {
+    auto enc = int32_tAt(&encodings[i]);
+    DLOG(INFO) << "client requesting encoding: " << enc;
+    if (enc == kTightEncoding) {
+      // This is a deviation from the spec which says that if a jpeg quality
+      // level is not specified, tight encoding won't use jpeg.
+      std::lock_guard<std::mutex> guard(m_);
+      use_jpeg_compression_ = true;
+    }
+    if (kJpegMinQualityEncoding <= enc && enc <= kJpegMaxQualityEncoding) {
+      DLOG(INFO) << "jpeg compression level: " << enc;
+      bb_->set_jpeg_quality_level(enc);
+    }
+    if (enc == kDesktopSizeEncoding) {
+      supports_desktop_size_encoding_ = true;
+    }
+  }
+}
+
+void VncClientConnection::HandleSetPixelFormat() {
+  std::lock_guard<std::mutex> guard(m_);
+  auto msg = client_.Recv(kSetPixelFormatLength);
+  if (msg.size() != kSetPixelFormatLength) {
+    return;
+  }
+  pixel_format_.bits_per_pixel = msg[3];
+  pixel_format_.depth = msg[4];
+  pixel_format_.big_endian = msg[5];
+  pixel_format_.true_color = msg[7];
+  pixel_format_.red_max = uint16_tAt(&msg[8]);
+  pixel_format_.green_max = uint16_tAt(&msg[10]);
+  pixel_format_.blue_max = uint16_tAt(&msg[12]);
+  pixel_format_.red_shift = msg[13];
+  pixel_format_.green_shift = msg[14];
+  pixel_format_.blue_shift = msg[15];
+}
+
+void VncClientConnection::HandlePointerEvent() {
+  auto msg = client_.Recv(kPointerEventLength);
+  if (msg.size() != kPointerEventLength) {
+    return;
+  }
+  std::uint8_t button_mask = msg[0];
+  auto x_pos = uint16_tAt(&msg[1]);
+  auto y_pos = uint16_tAt(&msg[3]);
+  {
+    std::lock_guard<std::mutex> guard(m_);
+    if (current_orientation_ == ScreenOrientation::Landscape) {
+      std::tie(x_pos, y_pos) =
+          std::make_pair(ActualScreenWidth() - y_pos, x_pos);
+    }
+  }
+  virtual_inputs_->HandlePointerEvent(button_mask, x_pos, y_pos);
+}
+
+void VncClientConnection::UpdateAccelerometer(float x, float y, float z) {
+  // // Discard the event if we don't have a connection to the HAL.
+  // if (!sensor_event_hal_->IsOpen()) {
+  //   LOG(ERROR) << "sensor event client not open";
+  //   return;
+  // }
+  // timespec current_time{};
+  // clock_gettime(CLOCK_MONOTONIC, &current_time);
+  // // Construct the sensor message.
+  // gce_sensors_message message{};
+  // message.version = sizeof message;
+  // message.sensor = cvd::sensors_constants::kAccelerometerHandle;
+  // message.type = SENSOR_TYPE_ACCELEROMETER;
+  // message.timestamp = current_time.tv_sec * static_cast<int64_t>(1000000000)
+  // +
+  //                     current_time.tv_nsec;
+  // message.data[0] = x;
+  // message.data[1] = y;
+  // message.data[2] = z;
+
+  // std::array<iovec, 1> msg_iov{};
+  // msg_iov[0].iov_base = &message;
+  // msg_iov[0].iov_len = sizeof(sensors_event_t);
+
+  // msghdr msg;
+  // msg.msg_name = nullptr;
+  // msg.msg_namelen = 0;
+  // msg.msg_iov = msg_iov.data();
+  // msg.msg_iovlen = msg_iov.size();
+  // msg.msg_control = nullptr;
+  // msg.msg_controllen = 0;
+  // msg.msg_flags = 0;
+  // if (sensor_event_hal_->SendMsg(&msg, 0) == -1) {
+  //   LOG(ERROR) << __FUNCTION__ << ": Could not send sensor data. (%s)." <<
+  //         sensor_event_hal_->StrError();
+  // }
+}
+
+VncClientConnection::Coordinates VncClientConnection::CoordinatesForOrientation(
+    ScreenOrientation orientation) const {
+  // Compute the acceleration vector that we need to send to mimic
+  // this change.
+  constexpr float g = 9.81;
+  constexpr float angle = 20.0;
+  const float cos_angle = std::cos(angle / M_PI);
+  const float sin_angle = std::sin(angle / M_PI);
+  const float z = g * sin_angle;
+  switch (orientation) {
+    case ScreenOrientation::Portrait:
+      return {0, g * cos_angle, z};
+    case ScreenOrientation::Landscape:
+      return {g * cos_angle, 0, z};
+  }
+}
+
+int VncClientConnection::ScreenWidth() const {
+  return current_orientation_ == ScreenOrientation::Portrait
+             ? ActualScreenWidth()
+             : ActualScreenHeight();
+}
+
+int VncClientConnection::ScreenHeight() const {
+  return current_orientation_ == ScreenOrientation::Portrait
+             ? ActualScreenHeight()
+             : ActualScreenWidth();
+}
+
+void VncClientConnection::SetScreenOrientation(ScreenOrientation orientation) {
+  std::lock_guard<std::mutex> guard(m_);
+  auto coords = CoordinatesForOrientation(orientation);
+  UpdateAccelerometer(coords.x, coords.y, coords.z);
+  if (supports_desktop_size_encoding_) {
+    auto previous_orientation = current_orientation_;
+    current_orientation_ = orientation;
+    if (current_orientation_ != previous_orientation &&
+        supports_desktop_size_encoding_) {
+      SendDesktopSizeUpdate();
+      bb_->SetOrientation(this, current_orientation_);
+      // TODO not sure if I should be sending a frame update along with this,
+      // or just letting the next FBUR handle it. This seems to me like it's
+      // sending one more frame buffer update than was requested, which is
+      // maybe a violation of the spec?
+    }
+  }
+}
+
+bool VncClientConnection::RotateIfIsRotationCommand(std::uint32_t key) {
+  // Due to different configurations on different platforms we're supporting
+  // a set of options for rotating the screen. These are similar to what
+  // the emulator supports and has supported.
+  // ctrl+left and ctrl+right work on windows and linux
+  // command+left and command+right work on Mac
+  // ctrl+fn+F11 and ctrl+fn+F12 work when chromoting to ubuntu from a Mac
+  if (!control_key_down_ && !meta_key_down_) {
+    return false;
+  }
+  switch (key) {
+    case cvd::xk::Right:
+    case cvd::xk::F12:
+      DLOG(INFO) << "switching to portrait";
+      SetScreenOrientation(ScreenOrientation::Portrait);
+      break;
+    case cvd::xk::Left:
+    case cvd::xk::F11:
+      DLOG(INFO) << "switching to landscape";
+      SetScreenOrientation(ScreenOrientation::Landscape);
+      break;
+    default:
+      return false;
+  }
+  return true;
+}
+
+void VncClientConnection::HandleKeyEvent() {
+  auto msg = client_.Recv(kKeyEventLength);
+  if (msg.size() != kKeyEventLength) {
+    return;
+  }
+
+  auto key = uint32_tAt(&msg[3]);
+  bool key_down = msg[0];
+  switch (key) {
+    case cvd::xk::ControlLeft:
+    case cvd::xk::ControlRight:
+      control_key_down_ = key_down;
+      break;
+    case cvd::xk::MetaLeft:
+    case cvd::xk::MetaRight:
+      meta_key_down_ = key_down;
+      break;
+    case cvd::xk::F5:
+      key = cvd::xk::Menu;
+      break;
+    case cvd::xk::F7:
+      virtual_inputs_->PressPowerButton(key_down);
+      return;
+    default:
+      break;
+  }
+
+  if (RotateIfIsRotationCommand(key)) {
+    return;
+  }
+
+  virtual_inputs_->GenerateKeyPressEvent(key, key_down);
+}
+
+void VncClientConnection::HandleClientCutText() {
+  auto msg = client_.Recv(kClientCutTextLength);
+  if (msg.size() != kClientCutTextLength) {
+    return;
+  }
+  auto len = uint32_tAt(&msg[3]);
+  client_.Recv(len);
+}
+
+void VncClientConnection::NormalSession() {
+  static constexpr std::uint8_t kSetPixelFormatMessage{0};
+  static constexpr std::uint8_t kSetEncodingsMessage{2};
+  static constexpr std::uint8_t kFramebufferUpdateRequestMessage{3};
+  static constexpr std::uint8_t kKeyEventMessage{4};
+  static constexpr std::uint8_t kPointerEventMessage{5};
+  static constexpr std::uint8_t kClientCutTextMessage{6};
+  while (true) {
+    if (client_.closed()) {
+      return;
+    }
+    auto msg = client_.Recv(1);
+    if (client_.closed()) {
+      return;
+    }
+    auto msg_type = msg.front();
+    DLOG(INFO) << "Received message type " << msg_type;
+
+    switch (msg_type) {
+      case kSetPixelFormatMessage:
+        HandleSetPixelFormat();
+        break;
+
+      case kSetEncodingsMessage:
+        HandleSetEncodings();
+        break;
+
+      case kFramebufferUpdateRequestMessage:
+        HandleFramebufferUpdateRequest();
+        break;
+
+      case kKeyEventMessage:
+        HandleKeyEvent();
+        break;
+
+      case kPointerEventMessage:
+        HandlePointerEvent();
+        break;
+
+      case kClientCutTextMessage:
+        HandleClientCutText();
+        break;
+
+      default:
+        LOG(WARNING) << "message type not handled: "
+                     << static_cast<int>(msg_type);
+        break;
+    }
+  }
+}
diff --git a/host/frontend/vnc_server/vnc_client_connection.h b/host/frontend/vnc_server/vnc_client_connection.h
new file mode 100644
index 0000000..9b403bb
--- /dev/null
+++ b/host/frontend/vnc_server/vnc_client_connection.h
@@ -0,0 +1,171 @@
+#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/libs/fs/shared_fd.h"
+#include "common/libs/threads/thread_annotations.h"
+
+#include <cstdint>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "host/frontend/vnc_server/blackboard.h"
+#include "host/frontend/vnc_server/tcp_socket.h"
+#include "host/frontend/vnc_server/virtual_inputs.h"
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+namespace cvd {
+namespace vnc {
+
+class VncClientConnection {
+ public:
+  VncClientConnection(ClientSocket client, VirtualInputs* virtual_inputs,
+                      BlackBoard* bb, bool aggressive);
+  VncClientConnection(const VncClientConnection&) = delete;
+  VncClientConnection& operator=(const VncClientConnection&) = delete;
+  ~VncClientConnection();
+
+  void StartSession();
+
+ private:
+  struct PixelFormat {
+    std::uint8_t bits_per_pixel;
+    std::uint8_t depth;
+    std::uint8_t big_endian;
+    std::uint8_t true_color;
+    std::uint16_t red_max;
+    std::uint16_t green_max;
+    std::uint16_t blue_max;
+    std::uint8_t red_shift;
+    std::uint8_t green_shift;
+    std::uint8_t blue_shift;
+  };
+
+  struct FrameBufferUpdateRequest {
+    bool incremental;
+    std::uint16_t x_pos;
+    std::uint16_t y_pos;
+    std::uint16_t width;
+    std::uint16_t height;
+  };
+
+  friend bool operator==(const FrameBufferUpdateRequest&,
+                         const FrameBufferUpdateRequest&);
+  friend bool operator!=(const FrameBufferUpdateRequest&,
+                         const FrameBufferUpdateRequest&);
+
+  bool closed();
+  void SetupProtocol();
+  void SetupSecurityType();
+
+  void GetClientInit();
+
+  void SendServerInit() EXCLUDES(m_);
+  static Message MakeFrameBufferUpdateHeader(std::uint16_t num_stripes);
+
+  static void AppendRawStripeHeader(Message* frame_buffer_update,
+                                    const Stripe& stripe);
+  void AppendRawStripe(Message* frame_buffer_update, const Stripe& stripe) const
+      REQUIRES(m_);
+  Message MakeRawFrameBufferUpdate(const StripePtrVec& stripes) const
+      REQUIRES(m_);
+
+  static void AppendJpegSize(Message* frame_buffer_update, size_t jpeg_size);
+  static void AppendJpegStripeHeader(Message* frame_buffer_update,
+                                     const Stripe& stripe);
+  static void AppendJpegStripe(Message* frame_buffer_update,
+                               const Stripe& stripe);
+  static Message MakeJpegFrameBufferUpdate(const StripePtrVec& stripes);
+
+  Message MakeFrameBufferUpdate(const StripePtrVec& frame) REQUIRES(m_);
+
+  void FrameBufferUpdateRequestHandler(bool aggressive) EXCLUDES(m_);
+
+  void SendDesktopSizeUpdate() REQUIRES(m_);
+
+  bool IsUrgent(const FrameBufferUpdateRequest& update_request) const;
+  static StripeSeqNumber MostRecentStripeSeqNumber(const StripePtrVec& stripes);
+
+  void HandleFramebufferUpdateRequest() EXCLUDES(m_);
+
+  void HandleSetEncodings();
+
+  void HandleSetPixelFormat();
+
+  void HandlePointerEvent() EXCLUDES(m_);
+
+  void UpdateAccelerometer(float x, float y, float z);
+
+  struct Coordinates {
+    float x;
+    float y;
+    float z;
+  };
+
+  Coordinates CoordinatesForOrientation(ScreenOrientation orientation) const;
+
+  int ScreenWidth() const REQUIRES(m_);
+
+  int ScreenHeight() const REQUIRES(m_);
+
+  void SetScreenOrientation(ScreenOrientation orientation) EXCLUDES(m_);
+
+  // Returns true if key is special and the screen was rotated.
+  bool RotateIfIsRotationCommand(std::uint32_t key);
+
+  void HandleKeyEvent();
+
+  void HandleClientCutText();
+
+  void NormalSession();
+
+  mutable std::mutex m_;
+  ClientSocket client_;
+  cvd::SharedFD sensor_event_hal_;
+  bool control_key_down_ = false;
+  bool meta_key_down_ = false;
+  VirtualInputs* virtual_inputs_{};
+
+  FrameBufferUpdateRequest previous_update_request_{};
+  BlackBoard* bb_;
+  bool use_jpeg_compression_ GUARDED_BY(m_) = false;
+
+  std::thread frame_buffer_request_handler_tid_;
+  bool closed_ GUARDED_BY(m_){};
+
+  PixelFormat pixel_format_ GUARDED_BY(m_) = {
+      std::uint8_t{32},  // bits per pixel
+      std::uint8_t{8},   // depth
+      std::uint8_t{},    // big_endian
+      std::uint8_t{},    // true_color
+      std::uint16_t{},   // red_max, (maxes not used when true color flag is 0)
+      std::uint16_t{},   // green_max
+      std::uint16_t{},   // blue_max
+      std::uint8_t{},  // red_shift (shifts not used when true color flag is 0)
+      std::uint8_t{},  // green_shift
+      std::uint8_t{},  // blue_shift
+  };
+
+  bool supports_desktop_size_encoding_ = false;
+  ScreenOrientation current_orientation_ GUARDED_BY(m_) =
+      ScreenOrientation::Portrait;
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/vnc_server.cpp b/host/frontend/vnc_server/vnc_server.cpp
new file mode 100644
index 0000000..2c013af
--- /dev/null
+++ b/host/frontend/vnc_server/vnc_server.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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/frontend/vnc_server/vnc_server.h"
+
+#include <glog/logging.h>
+#include "host/frontend/vnc_server/blackboard.h"
+#include "host/frontend/vnc_server/frame_buffer_watcher.h"
+#include "host/frontend/vnc_server/jpeg_compressor.h"
+#include "host/frontend/vnc_server/tcp_socket.h"
+#include "host/frontend/vnc_server/virtual_inputs.h"
+#include "host/frontend/vnc_server/vnc_client_connection.h"
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+using cvd::vnc::VncServer;
+
+VncServer::VncServer(int port, bool aggressive)
+    : server_(port), frame_buffer_watcher_{&bb_}, aggressive_{aggressive} {}
+
+void VncServer::MainLoop() {
+  while (true) {
+    LOG(INFO) << "Awaiting connections";
+    auto connection = server_.Accept();
+    LOG(INFO) << "Accepted a client connection";
+    StartClient(std::move(connection));
+  }
+}
+
+void VncServer::StartClient(ClientSocket sock) {
+  std::thread t(&VncServer::StartClientThread, this, std::move(sock));
+  t.detach();
+}
+
+void VncServer::StartClientThread(ClientSocket sock) {
+  // NOTE if VncServer is expected to be destroyed, we have a problem here.
+  // All of the client threads will be pointing to the VncServer's
+  // 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_,
+                             aggressive_);
+  client.StartSession();
+}
diff --git a/host/frontend/vnc_server/vnc_server.h b/host/frontend/vnc_server/vnc_server.h
new file mode 100644
index 0000000..7b50367
--- /dev/null
+++ b/host/frontend/vnc_server/vnc_server.h
@@ -0,0 +1,56 @@
+#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 <string>
+#include <thread>
+#include <utility>
+
+#include "host/frontend/vnc_server/blackboard.h"
+#include "host/frontend/vnc_server/frame_buffer_watcher.h"
+#include "host/frontend/vnc_server/jpeg_compressor.h"
+#include "host/frontend/vnc_server/tcp_socket.h"
+#include "host/frontend/vnc_server/virtual_inputs.h"
+#include "host/frontend/vnc_server/vnc_client_connection.h"
+#include "host/frontend/vnc_server/vnc_utils.h"
+
+namespace cvd {
+namespace vnc {
+
+class VncServer {
+ public:
+  explicit VncServer(int port, bool aggressive);
+
+  VncServer(const VncServer&) = delete;
+  VncServer& operator=(const VncServer&) = delete;
+
+  [[noreturn]] void MainLoop();
+
+ private:
+  void StartClient(ClientSocket sock);
+
+  void StartClientThread(ClientSocket sock);
+
+  ServerSocket server_;
+  VirtualInputs virtual_inputs_;
+  BlackBoard bb_;
+  FrameBufferWatcher frame_buffer_watcher_;
+  bool aggressive_{};
+};
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/vnc_utils.cpp b/host/frontend/vnc_server/vnc_utils.cpp
new file mode 100644
index 0000000..883a9d1
--- /dev/null
+++ b/host/frontend/vnc_server/vnc_utils.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/vsoc/lib/fb_bcast_region_view.h"
+
+using vsoc::framebuffer::FBBroadcastRegionView;
+
+namespace cvd {
+namespace vnc {
+
+FBBroadcastRegionView* GetFBBroadcastRegionView() {
+  static FBBroadcastRegionView instance;
+  return &instance;
+}
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/frontend/vnc_server/vnc_utils.h b/host/frontend/vnc_server/vnc_utils.h
new file mode 100644
index 0000000..84a22dc
--- /dev/null
+++ b/host/frontend/vnc_server/vnc_utils.h
@@ -0,0 +1,86 @@
+#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 <array>
+#include <cstdint>
+#include <utility>
+#include <vector>
+
+#include "common/vsoc/lib/fb_bcast_region_view.h"
+
+namespace cvd {
+namespace vnc {
+
+// TODO(haining) when the hwcomposer gives a sequence number type, use that
+// instead. It might just work to replace this class with a type alias
+// using StripeSeqNumber = whatever_the_hwcomposer_uses;
+class StripeSeqNumber {
+ public:
+  StripeSeqNumber() = default;
+  explicit StripeSeqNumber(std::uint64_t t) : t_{t} {}
+  bool operator<(const StripeSeqNumber& other) const { return t_ < other.t_; }
+
+  bool operator<=(const StripeSeqNumber& other) const { return t_ <= other.t_; }
+
+ private:
+  std::uint64_t t_{};
+};
+
+using Message = std::vector<std::uint8_t>;
+
+constexpr int32_t kJpegMaxQualityEncoding = -23;
+constexpr int32_t kJpegMinQualityEncoding = -32;
+
+enum class ScreenOrientation { Portrait, Landscape };
+constexpr int kNumOrientations = 2;
+
+struct Stripe {
+  int index = -1;
+  std::uint64_t frame_id{};
+  std::uint16_t x{};
+  std::uint16_t y{};
+  std::uint16_t width{};
+  std::uint16_t height{};
+  Message raw_data{};
+  Message jpeg_data{};
+  StripeSeqNumber seq_number{};
+  ScreenOrientation orientation{};
+};
+
+vsoc::framebuffer::FBBroadcastRegionView* GetFBBroadcastRegionView();
+
+inline int BytesPerPixel() {
+  return GetFBBroadcastRegionView()->bytes_per_pixel();
+}
+
+// The width of the screen regardless of orientation. Does not change.
+inline int ActualScreenWidth() {
+  return GetFBBroadcastRegionView()->x_res();
+}
+
+// The height of the screen regardless of orientation. Does not change.
+inline int ActualScreenHeight() {
+  return GetFBBroadcastRegionView()->y_res();
+}
+
+inline int ScreenSizeInBytes() {
+  return ActualScreenWidth() * ActualScreenHeight() * BytesPerPixel();
+}
+
+}  // namespace vnc
+}  // namespace cvd
diff --git a/host/libs/Android.bp b/host/libs/Android.bp
new file mode 100644
index 0000000..c7aa2ec
--- /dev/null
+++ b/host/libs/Android.bp
@@ -0,0 +1,22 @@
+//
+// 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.
+
+subdirs = [
+    "config",
+    "monitor",
+    "ivserver",
+    "vadb",
+    "usbip",
+]
diff --git a/host/libs/config/Android.bp b/host/libs/config/Android.bp
new file mode 100644
index 0000000..4a02ec1
--- /dev/null
+++ b/host/libs/config/Android.bp
@@ -0,0 +1,21 @@
+cc_library_host_static {
+    name: "libcuttlefish_host_config",
+    srcs: [
+        "file_partition.cpp",
+        "guest_config.cpp",
+        "host_config.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libxml2",
+        "libgflags",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/libs/config/README.md b/host/libs/config/README.md
new file mode 100644
index 0000000..f2a8138
--- /dev/null
+++ b/host/libs/config/README.md
@@ -0,0 +1,18 @@
+# VM Configuration libraries
+
+This package supplies library that supports VM configuration by:
+
+* creating, initializing and verifying images used to create a VM,
+* creating configuration file that can be used with `libvirt` directly.
+
+## Details
+
+### `FilePartition`
+
+`FilePartition` class offers means to create (both persistent and ephemeral),
+initialize and access image files.
+
+### `GuestConfig`
+
+`GuestConfig` class builds XML configuration string based on supplied details
+that is used to initialize `libvirt` domain.
diff --git a/host/libs/config/file_partition.cpp b/host/libs/config/file_partition.cpp
new file mode 100644
index 0000000..ede4870
--- /dev/null
+++ b/host/libs/config/file_partition.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <limits.h>
+
+#include <sstream>
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/config/file_partition.h"
+
+namespace config {
+namespace {
+constexpr char kTempFileSuffix[] = ".img";
+
+void Initialize(const std::string& path) {
+  std::string command = "/sbin/mkfs.ext4 -F '" + path + "' &>/dev/null";
+  CHECK(system(command.c_str()) == 0)
+      << "Could not initialize filesystem on partition image " << path << ": "
+      << strerror(errno);
+}
+}  // namespace
+
+FilePartition::~FilePartition() {
+  if (should_delete_) {
+    LOG(INFO) << "Deleting partition image file " << name_;
+    errno = 0;
+    unlink(name_.c_str());
+    if (errno != 0) {
+      LOG(WARNING) << "Could not delete partition image file: "
+                   << strerror(errno);
+    }
+  }
+}
+
+std::unique_ptr<FilePartition> FilePartition::ReuseExistingFile(
+    const std::string& path) {
+  return std::unique_ptr<FilePartition>(new FilePartition(path, false));
+}
+
+std::unique_ptr<FilePartition> FilePartition::CreateNewFile(
+    const std::string& path, int size_mb) {
+  {
+    cvd::SharedFD fd(cvd::SharedFD::Open(path.c_str(), O_CREAT | O_RDWR, 0600));
+    CHECK(fd->IsOpen()) << "Could not open file: " << path << ": "
+                        << fd->StrError();
+    CHECK(fd->Truncate(size_mb << 20) == 0)
+        << "Could not truncate file " << path << ": " << fd->StrError();
+  }
+
+  Initialize(path);
+  return std::unique_ptr<FilePartition>(new FilePartition(path, false));
+}
+
+// Create temporary FilePartition object using supplied prefix.
+// Newly created file will be deleted after this instance is destroyed.
+std::unique_ptr<FilePartition> FilePartition::CreateTemporaryFile(
+    const std::string& prefix, int size_mb) {
+  std::stringstream ss;
+  ss << prefix << "-XXXXXX" << kTempFileSuffix;
+  char path[PATH_MAX];
+  strncpy(&path[0], ss.str().c_str(), sizeof(path));
+
+  {
+    int raw_fd = mkostemps(&path[0], strlen(kTempFileSuffix), O_RDWR | O_CREAT);
+    CHECK(raw_fd > 0) << "Could not create temporary file: " << strerror(errno);
+    CHECK(ftruncate(raw_fd, size_mb << 20) == 0)
+        << "Could not truncate file " << path << ": " << strerror(errno);
+    close(raw_fd);
+  }
+
+  Initialize(path);
+  return std::unique_ptr<FilePartition>(new FilePartition(path, true));
+}
+
+}  // namespace config
diff --git a/host/libs/config/file_partition.h b/host/libs/config/file_partition.h
new file mode 100644
index 0000000..3c8e55b
--- /dev/null
+++ b/host/libs/config/file_partition.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+#include <string>
+
+namespace config {
+// FilePartition class manages partition image files.
+// Partition image files can be reused or created on demand. Temporary images
+// are deleted when corresponding instances of FilePartition object are
+// destroyed.
+class FilePartition {
+ public:
+  ~FilePartition();
+
+  // Create FilePartition object from existing file.
+  // Specified file will not be disposed of after this instance is destroyed.
+  static std::unique_ptr<FilePartition> ReuseExistingFile(
+      const std::string& path);
+
+  // Create FilePartition object at specified location and initialize content.
+  // Specified file will not be disposed of after this instance is destroyed.
+  static std::unique_ptr<FilePartition> CreateNewFile(const std::string& path,
+                                                      int size_mb);
+
+  // Create temporary FilePartition object using supplied prefix.
+  // Newly created file will be deleted after this instance is destroyed.
+  static std::unique_ptr<FilePartition> CreateTemporaryFile(
+      const std::string& prefix, int size_mb);
+
+  const std::string& GetName() const { return name_; }
+
+ private:
+  std::string name_;
+  bool should_delete_ = false;
+
+  FilePartition(const std::string& name, bool should_delete)
+      : name_(name), should_delete_(should_delete) {}
+
+  FilePartition(const FilePartition&) = delete;
+  FilePartition& operator=(const FilePartition&) = delete;
+};
+
+}  // namespace config
diff --git a/host/libs/config/guest_config.cpp b/host/libs/config/guest_config.cpp
new file mode 100644
index 0000000..359cb24
--- /dev/null
+++ b/host/libs/config/guest_config.cpp
@@ -0,0 +1,312 @@
+/*
+ * 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/config/guest_config.h"
+#include <iomanip>
+#include <sstream>
+
+#include <glog/logging.h>
+#include <gflags/gflags.h>
+#include "host/libs/config/host_config.h"
+
+std::string g_default_libvirt_domain{vsoc::GetPerInstanceDefault("cvd-")};
+DEFINE_string(libvirt_domain, g_default_libvirt_domain.c_str(),
+              "Domain name to use with libvirt");
+
+// This class represents libvirt guest configuration.
+// 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 config {
+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) {
+  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()));
+  xmlNewChild(os, nullptr, xc("initrd"), xc(initrd.c_str()));
+  xmlNewChild(os, nullptr, xc("cmdline"), xc(args.c_str()));
+}
+
+// Configure QEmu specific arguments.
+// This section adds the <qemu:commandline> node.
+void ConfigureQEmuSpecificOptions(
+    xmlNode* root, std::initializer_list<std::string> qemu_args) {
+  xmlNs* qemu_ns{xmlNewNs(
+      root, xc("http://libvirt.org/schemas/domain/qemu/1.0"), xc("qemu"))};
+
+  auto 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()));
+  }
+}
+
+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);
+
+  if (type == DeviceSourceType::kFile) {
+    LOG(INFO) << "Non-interactive serial port will send output to " << path;
+  } else {
+    LOG(INFO) << "Interactive serial port set up. To access the console run:";
+    LOG(INFO) << "$ sudo socat file:$(tty),raw,echo=0 " << 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) {
+  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"));
+}
+
+}  // namespace
+
+std::string GuestConfig::GetInstanceName() const {
+  return FLAGS_libvirt_domain;
+}
+
+std::string GuestConfig::Build() const {
+  std::string instance_name = GetInstanceName();
+
+  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, vcpus_, memory_mb_, uuid_);
+  ConfigureVMFeatures(root, {"acpi", "apic", "hap"});
+  ConfigureOperatingSystem(root, kernel_name_, initrd_name_, kernel_args_);
+  ConfigureQEmuSpecificOptions(
+      root,
+      {"-chardev", concat("socket,path=", ivshmem_socket_path_, ",id=ivsocket"),
+       "-device",
+       concat("ivshmem-doorbell,chardev=ivsocket,vectors=",
+              ivshmem_vector_count_),
+       "-cpu", "host"});
+
+  if (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 (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,
+                      GetKernelLogSocketName());
+  ConfigureSerialPort(devices, 1, DeviceSourceType::kUnixSocketServer,
+                      vsoc::GetDefaultPerInstancePath("console"));
+  ConfigureVirtioChannel(devices, 1, "cf-logcat", DeviceSourceType::kFile,
+                         vsoc::GetDefaultPerInstancePath("logcat"));
+  ConfigureVirtioChannel(devices, 2, "cf-gadget-usb-v1",
+                         DeviceSourceType::kUnixSocketClient,
+                         GetUSBV1SocketName());
+
+  ConfigureDisk(devices, "vda", system_partition_path_);
+  ConfigureDisk(devices, "vdb", data_partition_path_);
+  ConfigureDisk(devices, "vdc", cache_partition_path_);
+  ConfigureDisk(devices, "vdd", vendor_partition_path_);
+
+  ConfigureNIC(devices, concat("amobile", id_), mobile_bridge_name_, id_, 1);
+  ConfigureHWRNG(devices, 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 config
diff --git a/host/libs/config/guest_config.h b/host/libs/config/guest_config.h
new file mode 100644
index 0000000..5d4c566
--- /dev/null
+++ b/host/libs/config/guest_config.h
@@ -0,0 +1,188 @@
+/*
+ * 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 <libxml/tree.h>
+#include <memory>
+
+namespace config {
+// GuestConfig builds XML document describing target VM.
+// Documents built by GuestConfig can be directly used by libvirt to instantiate
+// new virtual machine.
+class GuestConfig {
+ public:
+  GuestConfig() = default;
+  ~GuestConfig() = default;
+
+  // Set instance ID.
+  GuestConfig& SetID(int id) {
+    id_ = id;
+    return *this;
+  }
+
+  // Set number of virtual CPUs.
+  GuestConfig& SetVCPUs(int vcpus) {
+    vcpus_ = vcpus;
+    return *this;
+  }
+
+  // Set total memory amount it MB.
+  GuestConfig& SetMemoryMB(int mem_mb) {
+    memory_mb_ = mem_mb;
+    return *this;
+  }
+
+  // Set kernel path.
+  GuestConfig& SetKernelName(const std::string& kernel) {
+    kernel_name_ = kernel;
+    return *this;
+  }
+
+  // Set kernel cmdline arguments.
+  GuestConfig& SetKernelArgs(const std::string& args) {
+    kernel_args_ = args;
+    return *this;
+  }
+
+  // Set initrd path.
+  GuestConfig& SetInitRDName(const std::string& initrd) {
+    initrd_name_ = initrd;
+    return *this;
+  }
+
+  // Set Android system partition image path.
+  GuestConfig& SetSystemPartitionPath(const std::string& path) {
+    system_partition_path_ = path;
+    return *this;
+  }
+
+  // Set Android data partition image path.
+  GuestConfig& SetCachePartitionPath(const std::string& path) {
+    cache_partition_path_ = path;
+    return *this;
+  }
+
+  // Set Android data partition image path.
+  GuestConfig& SetDataPartitionPath(const std::string& path) {
+    data_partition_path_ = path;
+    return *this;
+  }
+
+  // Set Android vendor partition image path.
+  GuestConfig& SetVendorPartitionPath(const std::string& path) {
+    vendor_partition_path_ = path;
+    return *this;
+  }
+
+  // Set ivshmem server socket path.
+  GuestConfig& SetIVShMemSocketPath(const std::string& path) {
+    ivshmem_socket_path_ = path;
+    return *this;
+  }
+
+  // Set number of vectors supplied by ivserver.
+  GuestConfig& SetIVShMemVectorCount(int count) {
+    ivshmem_vector_count_ = count;
+    return *this;
+  }
+
+  // Set name of the mobile bridge, eg. br0
+  GuestConfig& SetMobileBridgeName(const std::string& name) {
+    mobile_bridge_name_ = name;
+    return *this;
+  }
+
+  // Set source of entropy, eg. /dev/urandom.
+  GuestConfig& SetEntropySource(const std::string& source) {
+    entropy_source_ = source;
+    return *this;
+  }
+
+  // Flags to disable the AppArmor security features of libvirt
+  GuestConfig& SetDisableAppArmorSecurity(bool value) {
+    disable_app_armor_security_ = value;
+    return *this;
+  }
+
+  // Flags to disable the DAC security features of libvirt
+  GuestConfig& SetDisableDACSecurity(bool value) {
+    disable_dac_security_ = value;
+    return *this;
+  }
+
+  // The UUID that libvirt uses to identify the instance
+  GuestConfig& SetUUID(const std::string& uuid) {
+    uuid_ = uuid;
+    return *this;
+  }
+
+  // GetInstanceName returns name of this newly created instance.
+  std::string GetInstanceName() const;
+
+  // GetUSBSocketName returns name of the USB socket that will be used to
+  // forward access to USB gadget. This is for V1 of the USB bus.
+  std::string GetUSBV1SocketName() const {
+    return usb_v1_socket_name_;
+  }
+
+  GuestConfig& SetUSBV1SocketName(const std::string& source) {
+    usb_v1_socket_name_ = source;
+    return *this;
+  }
+
+  std::string GetKernelLogSocketName() const {
+    return kernel_log_socket_name_;
+  }
+
+  GuestConfig& SetKernelLogSocketName( const std::string& source) {
+    kernel_log_socket_name_ = source;
+    return *this;
+  }
+
+  // Build document as formatted XML string.
+  std::string Build() const;
+
+ private:
+  int id_;
+  int vcpus_;
+  int memory_mb_;
+
+  std::string kernel_name_;
+  std::string kernel_args_;
+  std::string initrd_name_;
+
+  std::string system_partition_path_;
+  std::string cache_partition_path_;
+  std::string data_partition_path_;
+  std::string vendor_partition_path_;
+  std::string usb_v1_socket_name_;
+  std::string kernel_log_socket_name_;
+
+  std::string ivshmem_socket_path_;
+  int ivshmem_vector_count_;
+
+  std::string mobile_bridge_name_;
+  std::string entropy_source_;
+
+  std::string uuid_;
+  bool disable_dac_security_;
+  bool disable_app_armor_security_;
+
+  GuestConfig(const GuestConfig&) = delete;
+  GuestConfig& operator=(const GuestConfig&) = delete;
+};
+
+}  // namespace config
diff --git a/host/libs/config/host_config.cpp b/host/libs/config/host_config.cpp
new file mode 100644
index 0000000..5b74c67
--- /dev/null
+++ b/host/libs/config/host_config.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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/config/host_config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <iomanip>
+#include <sstream>
+
+#include <gflags/gflags.h>
+
+const char vsoc_user_prefix[] = "vsoc-";
+
+int GetDefaultInstance() {
+  char* user = getenv("USER");
+  if (user && !memcmp(user, vsoc_user_prefix, sizeof(vsoc_user_prefix) - 1)) {
+    int temp = atoi(user + sizeof(vsoc_user_prefix) - 1);
+    if (temp > 0) {
+      return temp;
+    }
+  }
+  return 1;
+}
+
+DEFINE_string(domain, vsoc::GetDefaultShmClientSocketPath(),
+              "Path to the ivshmem client socket");
+DEFINE_int32(instance, GetDefaultInstance(),
+             "Instance number. Must be unique.");
+
+std::string vsoc::GetPerInstanceDefault(const char* prefix) {
+  std::ostringstream stream;
+  stream << prefix << std::setfill('0') << std::setw(2)
+         << GetDefaultInstance();
+  return stream.str();
+}
+
+std::string vsoc::GetDefaultPerInstanceDir() {
+  return vsoc::GetPerInstanceDefault("/var/run/cvd-");
+}
+
+std::string vsoc::GetDefaultPerInstancePath(const std::string& basename) {
+  std::ostringstream stream;
+  stream << GetDefaultPerInstanceDir() << "/" << basename;
+  return stream.str();
+}
+
+std::string vsoc::GetDefaultShmClientSocketPath() {
+  return vsoc::GetDefaultPerInstancePath("ivshmem_socket_client");
+}
+
+std::string vsoc::GetDomain() {
+  return FLAGS_domain;
+}
diff --git a/host/libs/config/host_config.h b/host/libs/config/host_config.h
new file mode 100644
index 0000000..8456270
--- /dev/null
+++ b/host/libs/config/host_config.h
@@ -0,0 +1,29 @@
+#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.
+ */
+
+/* Host-specific values associated with RegionControl. */
+
+#include <string>
+
+namespace vsoc {
+std::string GetPerInstanceDefault(const char* prefix);
+std::string GetDefaultPerInstanceDir();
+std::string GetDefaultPerInstancePath(const std::string& basename);
+std::string GetDefaultShmClientSocketPath();
+std::string GetDomain();
+}  // namespace vsoc
diff --git a/host/libs/ivserver/Android.bp b/host/libs/ivserver/Android.bp
new file mode 100644
index 0000000..c15c4b1
--- /dev/null
+++ b/host/libs/ivserver/Android.bp
@@ -0,0 +1,41 @@
+cc_library_host_static {
+    name: "libivserver",
+    srcs: [
+        "hald_client.cc",
+        "ivserver.cc",
+        "options.cc",
+        "qemu_client.cc",
+        "vsocsharedmem.cc",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    static_libs: [
+        "libjsoncpp",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
+
+cc_test_host {
+    name: "hald_client_test",
+    srcs: [
+        "hald_client_test.cc",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libivserver",
+        "libjsoncpp",
+        "libgmock",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/libs/ivserver/README.md b/host/libs/ivserver/README.md
new file mode 100644
index 0000000..ea1d2c2
--- /dev/null
+++ b/host/libs/ivserver/README.md
@@ -0,0 +1,72 @@
+## Overview
+
+This is the native ivshmem-server implementation catering to the GCE CVD
+vSoC project.
+
+We are breaking from the general philosophy of ivshmem-server inter-vm
+communication.
+
+There is no concept of inter-vm communication. The server itself is meant to
+run on the L1 Guest (or L0 even). We will call this domain as 'host-side'. The
+following functions are envisoned:
+
+* Create the shared-memory window, listen for VM connection (and subsequent
+  disconnection). Note that the server can only accomodate one VM connection at
+  a time. This may need to be enforced as there may be mulitple VMs (but each
+  of them need a dedicated shared_memory and VM <--> server UNIX Domain
+  sockets).
+
+* Parse a JSON file describing memory layout and other information. Use
+  this information to initialize the shared memory.
+
+* Create two UNIX Domain sockets. One to communicate with QEMU and the other
+  to communicate with host clients.
+
+* For QEMU, speak the ivshmem protocol, i.e. pass a vmid, pass the shm fd
+  to the qemu VM along with event fds. One for host to guest signalling and the
+  other for guest to host signalling. Please see:
+  QEMU_SRC/docs/specs/ivshmem-spec.txt. The only twist is that the server
+  pretends to be another peer VM to comply to the ivshmem protocol.
+
+* For the client, speak the ad-hoc client protocol:
+
+   ivshmem_client <--> ivshmem_server handshake.
+
+   Client -> 'GET PROTOCOL_VER'
+   Server -> 'PROTOCOL_VER 0'
+   Client -> INFORM REGION_NAME_LEN: 0x0000000a
+   Client -> GET REGION: HW_COMPOSER
+   Server -> 0xffffffff(If region name not found)
+   Server -> 0xAABBC000 (region start offset)
+   Server -> 0xAABBC0000 (region end offset)
+   Server -> <Send cmsg with guest_to_host eventfd>
+   Server -> <Send cmsg with host_to_guest eventfd>
+   Server -> <Send cmsg with shmfd>
+
+ * This also launches QEMU with the appropriate parameters
+
+Building:
+  From this directory issue the following in the command line
+  `bazel build src:ivserver`
+
+Running:
+  Once the binary is built using bazel. Just run it from this directory
+  as the default options expect the JSON files under conf directory.
+  e.g.
+  `./bazel-bin/src/ivserver`
+
+  If you encounter either
+  * Error in creating shared_memory file: File exists
+  * Bind failed: Address already in use
+
+  Please run the following commands and retry launching.
+  `rm /dev/shm/ivshmem`
+  `rm /tmp/ivsh*`
+
+TODO:
+ * Refactor.
+ * Separate the JSON configuration into QEMU and mem-layout specific files.
+ * Conform to Google coding standards.
+ * Add some documentation on the default options.
+ * Fault Tolerance.
+
diff --git a/host/libs/ivserver/hald_client.cc b/host/libs/ivserver/hald_client.cc
new file mode 100644
index 0000000..6f2f6b8
--- /dev/null
+++ b/host/libs/ivserver/hald_client.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "host/libs/ivserver/hald_client.h"
+
+#include <string>
+
+#include <glog/logging.h>
+
+namespace ivserver {
+namespace {
+// The protocol between host-clients and the ivserver could change.
+// Clients should verify what version they are talking to during the handshake.
+const uint32_t kHaldClientProtocolVersion = 0;
+}  // anonymous namespace
+
+std::unique_ptr<HaldClient> HaldClient::New(const VSoCSharedMemory& shmem,
+                                            const cvd::SharedFD& clientfd) {
+  std::unique_ptr<HaldClient> res;
+
+  if (!clientfd->IsOpen()) {
+    LOG(WARNING) << "Invalid socket passed to HaldClient: "
+                 << clientfd->StrError();
+    return res;
+  }
+
+  res.reset(new HaldClient(clientfd));
+  if (!res->PerformHandshake(shmem)) {
+    LOG(ERROR) << "HalD handshake failed. Dropping connection.";
+    res.reset();
+  }
+
+  return res;
+}
+
+HaldClient::HaldClient(const cvd::SharedFD& client_socket)
+    : client_socket_(client_socket) {}
+
+bool HaldClient::PerformHandshake(const VSoCSharedMemory& shared_mem) {
+  int rval =
+      client_socket_->Send(&kHaldClientProtocolVersion,
+                           sizeof(kHaldClientProtocolVersion), MSG_NOSIGNAL);
+  if (rval != sizeof(kHaldClientProtocolVersion)) {
+    LOG(ERROR) << "failed to send protocol version: "
+               << client_socket_->StrError();
+    return false;
+  }
+
+  int16_t region_name_len;
+  if (client_socket_->Recv(&region_name_len, sizeof(region_name_len),
+                           MSG_NOSIGNAL) != sizeof(region_name_len)) {
+    LOG(ERROR) << "Error receiving region name length: "
+               << client_socket_->StrError();
+    return false;
+  }
+
+  if (region_name_len <= 0 ||
+      region_name_len > VSoCSharedMemory::kMaxRegionNameLength) {
+    LOG(ERROR) << "Invalid region length received: " << region_name_len;
+    return false;
+  }
+
+  std::vector<char> region_name_data(region_name_len);
+  rval = client_socket_->Recv(region_name_data.data(), region_name_len,
+                              MSG_NOSIGNAL);
+  if (rval != region_name_len) {
+    LOG(ERROR) << "Incomplete region name length received. Want: "
+               << region_name_len << ", got: " << rval;
+    return false;
+  }
+
+  std::string region_name(region_name_data.begin(), region_name_data.end());
+  LOG(INFO) << "New HALD requesting region: " << region_name;
+
+  // Send Host, Guest and SharedMemory FDs associated with this region.
+  cvd::SharedFD guest_to_host_efd;
+  cvd::SharedFD host_to_guest_efd;
+
+  if (!shared_mem.GetEventFdPairForRegion(region_name, &guest_to_host_efd,
+                                          &host_to_guest_efd)) {
+    LOG(ERROR) << "Region " << region_name << " was not found.";
+    return false;
+  }
+
+  if (!guest_to_host_efd->IsOpen()) {
+    LOG(ERROR) << "Host channel is not open; last known error: "
+               << guest_to_host_efd->StrError();
+    return false;
+  }
+
+  if (!host_to_guest_efd->IsOpen()) {
+    LOG(ERROR) << "Guest channel is not open; last known error: "
+               << host_to_guest_efd->StrError();
+    return false;
+  }
+
+  // TODO(ender): delete this once no longer necessary. Currently, absence of
+  // payload makes RecvMsgAndFDs hang forever.
+  uint64_t control_data = 0;
+  struct iovec vec {
+    &control_data, sizeof(control_data)
+  };
+  cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+  cvd::SharedFD fds[3] = {guest_to_host_efd, host_to_guest_efd,
+                          shared_mem.SharedMemFD()};
+  rval = client_socket_->SendMsgAndFDs<3>(hdr, MSG_NOSIGNAL, fds);
+  if (rval == -1) {
+    LOG(ERROR) << "failed to send Host FD: " << client_socket_->StrError();
+    return false;
+  }
+
+  LOG(INFO) << "HALD managing region: " << region_name << " connected.";
+  return true;
+}
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/hald_client.h b/host/libs/ivserver/hald_client.h
new file mode 100644
index 0000000..99b5fee
--- /dev/null
+++ b/host/libs/ivserver/hald_client.h
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <unistd.h>
+#include <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+
+// Handles a HAL deamon client connection & handshake.
+class HaldClient final {
+ public:
+  static std::unique_ptr<HaldClient> New(const VSoCSharedMemory &shared_mem,
+                                         const cvd::SharedFD &client_fd);
+
+ private:
+  cvd::SharedFD client_socket_;
+
+  // Initialize new instance of HAL Client.
+  HaldClient(const cvd::SharedFD &client_fd);
+
+  // Perform handshake with HAL Client.
+  // If the region is not found approprate status (-1) is sent.
+  // Note that for every new client connected a unique ClientConnection object
+  // will be created and after the handshake it will be destroyed.
+  bool PerformHandshake(const VSoCSharedMemory &shared_mem);
+
+  HaldClient(const HaldClient &) = delete;
+  HaldClient &operator=(const HaldClient &) = delete;
+};
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/hald_client_test.cc b/host/libs/ivserver/hald_client_test.cc
new file mode 100644
index 0000000..c566079
--- /dev/null
+++ b/host/libs/ivserver/hald_client_test.cc
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <memory>
+#include <string>
+#include <thread>
+
+#include <glog/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/ivserver/hald_client.h"
+#include "host/libs/ivserver/vsocsharedmem_mock.h"
+
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SetArgPointee;
+using ::testing::_;
+
+namespace ivserver {
+namespace test {
+
+class HaldClientTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    std::string socket_location;
+    ASSERT_TRUE(GetTempLocation(&socket_location))
+        << "Could not create temp file";
+    LOG(INFO) << "Temp file location: " << socket_location;
+
+    hald_server_socket_ = cvd::SharedFD::SocketLocalServer(
+        socket_location.c_str(), false, SOCK_STREAM, 0666);
+    test_socket_ = cvd::SharedFD::SocketLocalClient(socket_location.c_str(),
+                                                    false, SOCK_STREAM);
+    hald_socket_ =
+        cvd::SharedFD::Accept(*hald_server_socket_, nullptr, nullptr);
+
+    EXPECT_TRUE(hald_server_socket_->IsOpen());
+    EXPECT_TRUE(test_socket_->IsOpen());
+    EXPECT_TRUE(hald_socket_->IsOpen());
+  }
+
+  bool GetTempLocation(std::string* target) {
+    char temp_location[] = "/tmp/hald-client-test-XXXXXX";
+    mktemp(temp_location);
+    *target = temp_location;
+    if (target->size()) {
+      cleanup_files_.emplace_back(*target);
+      return true;
+    }
+    return false;
+  }
+
+  void TearDown() override {
+    hald_socket_->Close();
+    test_socket_->Close();
+    hald_server_socket_->Close();
+
+    for (const std::string& file : cleanup_files_) {
+      unlink(file.c_str());
+    }
+  }
+
+ protected:
+  ::testing::NiceMock<VSoCSharedMemoryMock> vsoc_;
+
+  cvd::SharedFD hald_server_socket_;
+  cvd::SharedFD hald_socket_;
+  cvd::SharedFD test_socket_;
+
+ private:
+  std::vector<std::string> cleanup_files_;
+};
+
+TEST_F(HaldClientTest, HandshakeTerminatedByHald) {
+  std::thread thread(
+      [this]() {
+        auto client(HaldClient::New(vsoc_, hald_socket_));
+        EXPECT_FALSE(client)
+            << "Handshake should not complete when client terminates early.";
+      });
+
+  test_socket_->Close();
+  thread.join();
+}
+
+TEST_F(HaldClientTest, HandshakeTerminatedByInvalidRegionSize) {
+  uint16_t sizes[] = {0, VSoCSharedMemory::kMaxRegionNameLength + 1, 0xffff};
+
+  for (uint16_t size : sizes) {
+    std::thread thread([this, size]() {
+      auto client(HaldClient::New(vsoc_, hald_socket_));
+      EXPECT_FALSE(client) << "Handle should not be created when size is "
+                           << size;
+    });
+
+    int32_t proto_version;
+    EXPECT_EQ(sizeof(proto_version),
+              test_socket_->Recv(&proto_version, sizeof(proto_version),
+                                 MSG_NOSIGNAL));
+
+    test_socket_->Send(&size, sizeof(size), MSG_NOSIGNAL);
+    thread.join();
+  }
+}
+
+TEST_F(HaldClientTest, FullSaneHandshake) {
+  std::string temp;
+  ASSERT_TRUE(GetTempLocation(&temp));
+  cvd::SharedFD host_fd(
+      cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+  EXPECT_TRUE(host_fd->IsOpen());
+
+  ASSERT_TRUE(GetTempLocation(&temp));
+  cvd::SharedFD guest_fd(
+      cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+  EXPECT_TRUE(guest_fd->IsOpen());
+
+  ASSERT_TRUE(GetTempLocation(&temp));
+  cvd::SharedFD shmem_fd(
+      cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+  EXPECT_TRUE(shmem_fd->IsOpen());
+
+  const std::string test_location("testing");
+  EXPECT_CALL(vsoc_, GetEventFdPairForRegion(test_location, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(host_fd), SetArgPointee<2>(guest_fd),
+                      Return(true)));
+  EXPECT_CALL(vsoc_, SharedMemFD()).WillOnce(ReturnRef(shmem_fd));
+
+  std::thread thread([this]() {
+    auto client(HaldClient::New(vsoc_, hald_socket_));
+    EXPECT_TRUE(client);
+  });
+
+  int32_t proto_version;
+  EXPECT_EQ(
+      sizeof(proto_version),
+      test_socket_->Recv(&proto_version, sizeof(proto_version), MSG_NOSIGNAL));
+
+  uint16_t size = test_location.size();
+  EXPECT_EQ(sizeof(size),
+            test_socket_->Send(&size, sizeof(size), MSG_NOSIGNAL));
+  EXPECT_EQ(size, test_socket_->Send(test_location.data(), size, MSG_NOSIGNAL));
+
+  // TODO(ender): delete this once no longer necessary. Currently, absence of
+  // payload makes RecvMsgAndFDs hang forever.
+  uint64_t control_data;
+  struct iovec vec {
+    &control_data, sizeof(control_data)
+  };
+  cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+  cvd::SharedFD fds[3];
+
+  EXPECT_GT(test_socket_->RecvMsgAndFDs<3>(hdr, MSG_NOSIGNAL, &fds), 0);
+  EXPECT_TRUE(fds[0]->IsOpen());
+  EXPECT_TRUE(fds[1]->IsOpen());
+  EXPECT_TRUE(fds[2]->IsOpen());
+
+  thread.join();
+}
+
+}  // namespace test
+}  // namespace ivserver
diff --git a/host/libs/ivserver/ivserver.cc b/host/libs/ivserver/ivserver.cc
new file mode 100644
index 0000000..d966764
--- /dev/null
+++ b/host/libs/ivserver/ivserver.cc
@@ -0,0 +1,85 @@
+/*
+ * 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/ivserver/ivserver.h"
+
+#include <sys/select.h>
+#include <algorithm>
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_select.h"
+#include "host/libs/ivserver/hald_client.h"
+#include "host/libs/ivserver/qemu_client.h"
+
+namespace ivserver {
+
+IVServer::IVServer(const IVServerOptions &options, const Json::Value &json_root)
+    : json_root_{json_root},
+      vsoc_shmem_(VSoCSharedMemory::New(options.shm_file_path, json_root_)) {
+  LOG_IF(WARNING, unlink(options.qemu_socket_path.c_str()) == 0)
+      << "Removed existing unix socket: " << options.qemu_socket_path
+      << ". We can't confirm yet whether another instance is running.";
+  qemu_channel_ = cvd::SharedFD::SocketLocalServer(
+      options.qemu_socket_path.c_str(), false, SOCK_STREAM, 0666);
+  LOG_IF(FATAL, !qemu_channel_->IsOpen())
+      << "Could not create QEmu channel: " << qemu_channel_->StrError();
+
+  LOG_IF(WARNING, unlink(options.client_socket_path.c_str()) == 0)
+      << "Removed existing unix socket: " << options.client_socket_path
+      << ". We can't confirm yet whether another instance is running.";
+  client_channel_ = cvd::SharedFD::SocketLocalServer(
+      options.client_socket_path.c_str(), false, SOCK_STREAM, 0666);
+  LOG_IF(FATAL, !client_channel_->IsOpen())
+      << "Could not create Client channel: " << client_channel_->StrError();
+}
+
+void IVServer::Serve() {
+  while (true) {
+    cvd::SharedFDSet rset;
+    rset.Set(qemu_channel_);
+    rset.Set(client_channel_);
+    cvd::Select(&rset, nullptr, nullptr, nullptr);
+
+    if (rset.IsSet(qemu_channel_)) {
+      HandleNewQemuConnection();
+    }
+
+    if (rset.IsSet(client_channel_)) {
+      HandleNewClientConnection();
+    }
+  }
+
+  LOG(FATAL) << "Control reached out of event loop";
+}
+
+void IVServer::HandleNewClientConnection() {
+  std::unique_ptr<HaldClient> res = HaldClient::New(
+      *vsoc_shmem_, cvd::SharedFD::Accept(*client_channel_, nullptr, nullptr));
+  if (!res) {
+    LOG(WARNING) << "Rejecting unsuccessful HALD connection.";
+  }
+}
+
+void IVServer::HandleNewQemuConnection() {
+  std::unique_ptr<QemuClient> res = QemuClient::New(
+      *vsoc_shmem_, cvd::SharedFD::Accept(*qemu_channel_, nullptr, nullptr));
+
+  if (!res) {
+    LOG(WARNING) << "Could not accept new QEmu client.";
+  }
+}
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/ivserver.h b/host/libs/ivserver/ivserver.h
new file mode 100644
index 0000000..ad32d81
--- /dev/null
+++ b/host/libs/ivserver/ivserver.h
@@ -0,0 +1,48 @@
+#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 <json/json.h>
+#include <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/ivserver/options.h"
+#include "host/libs/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+
+// This class is responsible for orchestrating the setup and then serving
+// new connections.
+class IVServer final {
+ public:
+  IVServer(const IVServerOptions &options, const Json::Value &json_root);
+  IVServer(const IVServer &) = delete;
+
+  // Serves incoming client and qemu connection.
+  // This method should never return.
+  void Serve();
+
+ private:
+  void HandleNewClientConnection();
+  void HandleNewQemuConnection();
+
+  const Json::Value &json_root_;
+  std::unique_ptr<VSoCSharedMemory> vsoc_shmem_;
+  cvd::SharedFD qemu_channel_;
+  cvd::SharedFD client_channel_;
+};
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/options.cc b/host/libs/ivserver/options.cc
new file mode 100644
index 0000000..25f88dd
--- /dev/null
+++ b/host/libs/ivserver/options.cc
@@ -0,0 +1,38 @@
+/*
+ * 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/ivserver/options.h"
+
+namespace ivserver {
+
+IVServerOptions::IVServerOptions(const std::string &mem_layout_conf,
+                                 const std::string &shm_file_path,
+                                 const std::string &qemu_socket_path,
+                                 const std::string &client_socket_path)
+    : memory_layout_conf_path(mem_layout_conf),
+      shm_file_path(shm_file_path),
+      qemu_socket_path(qemu_socket_path),
+      client_socket_path(client_socket_path) {}
+
+std::ostream &operator<<(std::ostream &out, const IVServerOptions &options) {
+  out << "\nmem_layout_conf_path: " << options.memory_layout_conf_path
+      << "\nshm_file: " << options.shm_file_path
+      << "\nqemu_socket_path: " << options.qemu_socket_path
+      << "\nclient_socket_path: " << options.client_socket_path << std::endl;
+
+  return out;
+}
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/options.h b/host/libs/ivserver/options.h
new file mode 100644
index 0000000..53a51e2
--- /dev/null
+++ b/host/libs/ivserver/options.h
@@ -0,0 +1,50 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <iostream>
+#include <string>
+
+namespace ivserver {
+
+const uint16_t kIVServerMajorVersion = 1;
+const uint16_t kIVServerMinorVersion = 0;
+const uint32_t kIVServerDefaultShmSizeInMiB = 4;
+const std::string kIVServerDefaultLayoutFile = "vsoc_mem.json";
+
+//
+// structure that contains the various options to start the server.
+//
+struct IVServerOptions final {
+  IVServerOptions(const std::string &mem_layout_conf,
+                  const std::string &shm_file_path,
+                  const std::string &qemu_socket_path,
+                  const std::string &client_socket_path);
+
+  //
+  // We still need a friend here
+  //
+  friend std::ostream &operator<<(std::ostream &out,
+                                  const IVServerOptions &opts);
+
+  const std::string memory_layout_conf_path;
+  const std::string shm_file_path;
+  const std::string qemu_socket_path;
+  const std::string client_socket_path;
+};
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/qemu_client.cc b/host/libs/ivserver/qemu_client.cc
new file mode 100644
index 0000000..2771e06
--- /dev/null
+++ b/host/libs/ivserver/qemu_client.cc
@@ -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 "host/libs/ivserver/qemu_client.h"
+
+#include <glog/logging.h>
+
+namespace ivserver {
+
+std::unique_ptr<QemuClient> QemuClient::New(const VSoCSharedMemory& shmem,
+                                            const cvd::SharedFD& socket) {
+  std::unique_ptr<QemuClient> res;
+  if (!socket->IsOpen()) {
+    LOG(WARNING) << "Invalid socket passed to QemuClient: "
+                 << socket->StrError();
+    return res;
+  }
+
+  res.reset(new QemuClient(std::move(socket)));
+  if (!res->PerformHandshake(shmem)) {
+    LOG(ERROR) << "Qemu handshake failed. Dropping connection.";
+    res.reset();
+  }
+
+  return res;
+}
+
+QemuClient::QemuClient(cvd::SharedFD socket) : client_socket_(socket) {}
+
+// Once the QemuClient object is constructed, invoking the following
+// method will perform the actual handshake with a QEMU instance.
+bool QemuClient::PerformHandshake(const VSoCSharedMemory& shmem) {
+  LOG(INFO) << "New QEmu client connected.";
+  // 1. The protocol version number, currently zero.  The client should
+  //    close the connection on receipt of versions it can't handle.
+  int64_t msg = QemuConstants::kIvshMemProtocolVersion;
+  int rval = client_socket_->Send(&msg, sizeof(msg), MSG_NOSIGNAL);
+  if (rval != sizeof(msg)) {
+    LOG(ERROR) << "Failed to send protocol version: "
+               << client_socket_->StrError();
+    return false;
+  }
+
+  // 2. The client's ID.  This is unique among all clients of this server.
+  //    IDs must be between 0 and 65535, because the Doorbell register
+  //    provides only 16 bits for them.
+  msg = QemuConstants::kGuestID;
+  rval = client_socket_->Send(&msg, sizeof(msg), MSG_NOSIGNAL);
+  if (rval != sizeof(msg)) {
+    LOG(ERROR) << "Failed to send VM Id: " << client_socket_->StrError();
+    return false;
+  }
+
+  // 3. The number -1, accompanied by the file descriptor for the shared
+  //    memory.
+  if (!SendSocketInfo(QemuFDMsg::kSharedMem, shmem.SharedMemFD())) {
+    LOG(ERROR) << "Failed to send Shared Memory socket: "
+               << client_socket_->StrError();
+    return false;
+  }
+
+  // 4. Connect notifications for existing other clients, if any.  This is
+  //    a peer ID (number between 0 and 65535 other than the client's ID),
+  //    repeated N times.  Each repetition is accompanied by one file
+  //    descriptor.  These are for interrupting the peer with that ID using
+  //    vector 0,..,N-1, in order.  If the client is configured for fewer
+  //    vectors, it closes the extra file descriptors.  If it is configured
+  //    for more, the extra vectors remain unconnected.
+  for (const auto region_data : shmem.Regions()) {
+    if (!SendSocketInfo(QemuFDMsg::kHostSideHald, region_data.host_fd)) {
+      LOG(ERROR) << "Failed to send Host Side FD for region "
+                 << region_data.values.device_name << ": " << client_socket_->StrError();
+      return false;
+    }
+  }
+
+  // 5. Interrupt setup.  This is the client's own ID, repeated N times.
+  //    Each repetition is accompanied by one file descriptor.  These are
+  //    for receiving interrupts from peers using vector 0,..,N-1, in
+  //    order.  If the client is configured for fewer vectors, it closes
+  //    the extra file descriptors.  If it is configured for more, the
+  //    extra vectors remain unconnected.
+  for (const auto region_data : shmem.Regions()) {
+    if (!SendSocketInfo(QemuFDMsg::kGuestSideHal, region_data.guest_fd)) {
+      LOG(ERROR) << "Failed to send Guest Side FD for region "
+                 << region_data.values.device_name << ": " << client_socket_->StrError();
+      return false;
+    }
+  }
+
+  LOG(INFO) << "QEmu handshake completed.";
+  return true;
+}
+
+bool QemuClient::SendSocketInfo(QemuFDMsg message,
+                                const cvd::SharedFD& socket) {
+  struct iovec vec {
+    &message, sizeof(message)
+  };
+  cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+  cvd::SharedFD fds[] = {socket};
+  int rval = client_socket_->SendMsgAndFDs(hdr, 0, fds);
+  if (rval == -1) {
+    LOG(ERROR) << "failed to send shared_mem_fd: "
+               << client_socket_->StrError();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/qemu_client.h b/host/libs/ivserver/qemu_client.h
new file mode 100644
index 0000000..658b42c
--- /dev/null
+++ b/host/libs/ivserver/qemu_client.h
@@ -0,0 +1,79 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+
+// QemuClient manages individual QEmu connections using protocol specified
+// in documentation file distributed as part of QEmu 2.8 package under:
+// docs/specs/ivshmem-spec.txt
+// Alternatively, please point your browser to the following URL:
+// https://github.com/qemu/qemu/blob/stable-2.8/docs/specs/ivshmem-spec.txt
+class QemuClient final {
+ public:
+  static std::unique_ptr<QemuClient> New(const VSoCSharedMemory &shmem,
+                                         const cvd::SharedFD &connection);
+
+  cvd::SharedFD client_socket() const { return client_socket_; }
+
+ private:
+  enum QemuConstants : int64_t {
+    kIvshMemProtocolVersion = 0,
+    // HostID is in fact a Peer ID and can take multiple values, depending on
+    // how many subsystems we would like Guest to talk to.
+    kHostBaseID = 0,
+    // GuestID is a unique form of Peer ID (see above), that identifies newly
+    // created quest in IvSharedMem world.
+    kGuestID = 1024
+  };
+
+  static_assert(QemuConstants::kHostBaseID < QemuConstants::kGuestID,
+                "Guest and host should have different IDs");
+  // Type of QEmu FD messages.
+  // QEmu uses these messages to identify purpose of socket it is
+  // receiving.
+  enum class QemuFDMsg : int64_t {
+    // Represents SharedMemory FD.
+    kSharedMem = -1,
+    // Represents primary (and currently only) FD that is owned and managed by
+    // Host side.
+    kHostSideHald = QemuConstants::kHostBaseID,
+    // Represents FDs that are owned by Guest.
+    kGuestSideHal = QemuConstants::kGuestID,
+  };
+
+  cvd::SharedFD client_socket_;
+
+  // Initialize new instance of QemuClient.
+  QemuClient(cvd::SharedFD qemu_listener_socket);
+
+  // Once the QemuClient object is constructed, invoking the following
+  // method will perform the actual handshake with a QEMU instance.
+  bool PerformHandshake(const VSoCSharedMemory &shmem_fd);
+
+  // Send socket data to Qemu.
+  bool SendSocketInfo(QemuFDMsg message, const cvd::SharedFD &socket);
+
+  QemuClient(const QemuClient &) = delete;
+  QemuClient &operator=(const QemuClient &) = delete;
+};
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/shm_layout.txt b/host/libs/ivserver/shm_layout.txt
new file mode 100644
index 0000000..39d66a7
--- /dev/null
+++ b/host/libs/ivserver/shm_layout.txt
@@ -0,0 +1,88 @@
+(Please      use      a      smaller        font        and/or       appropriate     terminal   width  to      see    this     line without wrapping)
+
+
+                                              REGION DESCRIPTORS
+                                              (starts at page 0)
+                                       +------------------------------+
+                                       |     major_version: u16       |
+                                       +------------------------------+
+                                       |     minor_version: u16       |
+                                       +------------------------------+
+        (size of shared memory)        |         size: u32            |
+                                       +------------------------------+
+          (number of regions)          |     region_count: u32        |
+                                       +------------------------------+
+  (offset of region descriptor data)   | vsoc_region_desc_offset: u32 |o-----+
+                                       +------------------------------+      |
+                                       |                              |      |
+                                       | ..          ..            .. |      |
+                                       |                              |      |
+      (region 0 descriptor start)      +------------------------------+ <----+
+                                       |    current_version: u16      |
+                                       +------------------------------+
+                                       |  min_compatible_version: u16 |
+                                       +------------------------------+
+       (start offset of region 0)      |  region_begin_offset: u32    |o--------------------+
+                                       +------------------------------+                     |
+       (end offset   of region 0)      |  region_end_offset: u32      |o----------------------------------------------------------------------+
+                                       +------------------------------+                     |                                                 |
+                                       |  offset_of_region_data: u32  |o-----+              |                                                 |
+                                       +------------------------------+      |              |                                                 |
+                                       | guest_to_host_signal_table   |      |              |                                                 |
+                                       |______________________________|      |              |                                                 |
+                                        +----------------------------+       |              |                                                 |
+                                        |    num_nodes_lg2: u32      |       |              |                                                 |
+                                        +----------------------------+       |              |                                                 |
+                                        |futex_uaddr_table_offset:u32|o------------------+  |                                                 |
+                                        +----------------------------+       |           |  |                                                 |
+                                        | intr_signaled_offset: u32  |o----------------+ |  |                                                 |
+                                        +----------------------------+       |         | |  |                                                 |
+                                       +------------------------------+      |         | |  |                                                 |
+                                       | host_to_guest_signal_table   |      |         | |  |                                                 |
+                                       |______________________________|      |         | |  |                                                 |
+                                        +----------------------------+       |         | |  |                                                 |
+                                        |    num_nodes_lg2: u32      |       |         | |  |                                                 |
+                                        +----------------------------+       |         | |  |                                                 |
+                                        |futex_uaddr_table_offset:u32|o--------------+ | |  |                                                 |
+                                        +----------------------------+       |       | | |  \/      REGION AREA (page aligned)                |
+                                        | intr_signaled_offset: u32  |o------|-----+ | | |  +---------------------------------------+         |
+                                        +----------------------------+       |     | | | |  |  region area before start of guest to |         |
+                                       +------------------------------+      |     | | | |  |  host signal table.                   |         |
+                                       |    device_name: char[16]     |      |     | | | |  |     (currently does not exist)        |         |
+         (region 0 descriptor end)     +------------------------------+      |     | | | +->+---------------------------------------+         |
+       (region 1 descriptor start)     |                              |      |     | | |    |        uaddr_offset_0 : u32           |         |
+                                       |  current_version: u16        |      |     | | |    +---------------------------------------+         |
+                                       +------------------------------+      |     | | |    |        uaddr_offset_1 : u32           |         |
+                                       |  min_compatible_version: u16 |      |     | | |    +---------------------------------------+         |
+                                       +------------------------------+      |     | | |    |     ...      ..       ...             |         |
+                                       |  region_begin_offset: u32    |      |     | | |    +---------------------------------------+         |
+                                       +------------------------------+      |     | | |    | uaddr_offset_(2^num_nodes_lg2 - 1):u32|         |
+                                       |  region_end_offset: u32      |      |     | | +--->+---------------------------------------+         |
+                                       +------------------------------+      |     | |      |     (interrupt_signaled_area) : u32   |         |
+                                       |  offset_of_region_data: u32  |      |     | +----->+---------------------------------------+         |
+                                       +------------------------------+      |     |        |        uaddr_offset_0 : u32           |         |
+                                       | guest_to_host_signal_table   |      |     |        +---------------------------------------+         |
+                                       +------------------------------+      |     |        |     ...      ..       ...             |         |
+                                       | host_to_guest_signal_table   |      |     |        +---------------------------------------+         |
+                                       +------------------------------+      |     |        | uaddr_offset_(2^num_nodes_lg2 - 1):u32|         |
+                                       |    device_name: char[16]     |      |     +------->+---------------------------------------+         |
+          (region 1 descriptor end)    +------------------------------+      |              |     (interrupt_signaled_area) : u32   |         |
+                                       ...           ...            ...      +------------->+---------------------------------------+         |
+                                       +------------------------------+                     |                                       |         |
+                                       |    current_version: u16      |                     |                                       |         |
+                                       +------------------------------+                     |                                       |         |
+                                       |  min_compatible_version: u16 |                     |         region specific data          |         |
+                                       +------------------------------+                     | (defined by region specific agreement |         |
+                                       |  region_begin_offset: u32    |                     |  between HAL and host-side process)   |         |
+                                       +------------------------------+                     |                                       |         |
+                                       |  region_end_offset: u32      |                     |                                       |         |
+                                       +------------------------------+                     |                                       |         |
+                                       |  offset_of_region_data: u32  |                     +---------------------------------------+ <-------+
+                                       +------------------------------+
+                                       | guest_to_host_signal_table   |
+                                       +------------------------------+
+                                       | host_to_guest_signal_table   |
+                                       +------------------------------+
+                                       |    device_name: char[16]     |
+                                       +------------------------------+
+                                          END OF REGION DESCRIPTORS
diff --git a/host/libs/ivserver/vsocsharedmem.cc b/host/libs/ivserver/vsocsharedmem.cc
new file mode 100644
index 0000000..9714d23
--- /dev/null
+++ b/host/libs/ivserver/vsocsharedmem.cc
@@ -0,0 +1,354 @@
+/*
+ * 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/ivserver/vsocsharedmem.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/eventfd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <algorithm>
+#include <tuple>
+
+#include <glog/logging.h>
+
+#include "uapi/vsoc_shm.h"
+
+namespace ivserver {
+namespace {
+
+static_assert(CURRENT_VSOC_LAYOUT_MAJOR_VERSION == 2,
+              "Region layout code must be updated");
+
+// Field names from the json file. These are declared so the compiler will
+// catch typos.
+const char g_vsoc_device_regions[] = "vsoc_device_regions";
+const char g_device_name_tag[] = "device_name";
+const char g_managed_by_tag[] = "managed_by";
+
+class RegionAllocator {
+ public:
+  static uint32_t PageSize() {
+    static long page_size = sysconf(_SC_PAGESIZE);
+    return page_size;
+  }
+
+  explicit RegionAllocator(const std::string &name, uint32_t max_size,
+                           uint32_t offset = 0)
+      : name_{name}, max_size_{max_size}, offset_{offset} {}
+
+  uint32_t Allocate(uint32_t size, const char *usage, bool *error) {
+    if (size > (max_size_ - offset_)) {
+      *error = true;
+      LOG(ERROR) << name_ << ":"
+                 << "allocation of " << size << "bytes for " << usage
+                 << " will overflow memory region";
+    }
+
+    offset_ += size;
+    return (offset_ - size);
+  }
+
+  void PadTo(uint32_t size, bool *error) {
+    uint32_t padding = ((offset_ + (size - 1)) / size) * size - offset_;
+    Allocate(padding, "padding", error);
+  }
+
+  uint32_t AllocateRest(bool *error) {
+    return Allocate(max_size_ - offset_, "rest of region", error);
+  }
+
+  uint32_t GetOffset() const { return offset_; }
+
+ private:
+  std::string name_;
+  uint32_t max_size_;
+  uint32_t offset_;
+};
+
+class VSoCSharedMemoryImpl : public VSoCSharedMemory {
+ public:
+  VSoCSharedMemoryImpl(const vsoc_shm_layout_descriptor &header,
+                       const std::map<std::string, size_t> &name_to_region_idx,
+                       const std::vector<Region> &regions,
+                       const std::string &path);
+
+  bool GetEventFdPairForRegion(const std::string &region_name,
+                               cvd::SharedFD *guest_to_host,
+                               cvd::SharedFD *host_to_guest) const override;
+
+  const cvd::SharedFD &SharedMemFD() const override;
+
+  const std::vector<Region> &Regions() const override;
+
+ private:
+  void CreateLayout();
+
+  const vsoc_shm_layout_descriptor &header_;
+  cvd::SharedFD shared_mem_fd_;
+  const std::map<std::string, size_t> region_name_to_index_;
+  const std::vector<Region> region_data_;
+
+  VSoCSharedMemoryImpl(const VSoCSharedMemoryImpl &) = delete;
+  VSoCSharedMemoryImpl &operator=(const VSoCSharedMemoryImpl &other) = delete;
+};
+
+VSoCSharedMemoryImpl::VSoCSharedMemoryImpl(
+    const vsoc_shm_layout_descriptor &header,
+    const std::map<std::string, size_t> &name_to_region_idx,
+    const std::vector<Region> &regions, const std::string &path)
+    : header_{header},
+      region_name_to_index_{name_to_region_idx},
+      region_data_{regions} {
+  // TODO(ender): Lock the file after creation and check lock status upon second
+  // execution attempt instead of throwing an error.
+  LOG_IF(WARNING, unlink(path.c_str()) == 0)
+      << "Removed existing instance of " << path
+      << ". We currently don't know if another instance of daemon is running";
+  shared_mem_fd_ = cvd::SharedFD::Open(path.c_str(), O_RDWR | O_CREAT | O_EXCL,
+                                       S_IRUSR | S_IWUSR);
+  LOG_IF(FATAL, !shared_mem_fd_->IsOpen())
+      << "Error in creating shared_memory file: " << shared_mem_fd_->StrError();
+
+  int truncate_res = shared_mem_fd_->Truncate(header_.size);
+  LOG_IF(FATAL, truncate_res == -1)
+      << "Error in sizing up the shared memory file: "
+      << shared_mem_fd_->StrError();
+  CreateLayout();
+}
+
+const cvd::SharedFD &VSoCSharedMemoryImpl::SharedMemFD() const {
+  return shared_mem_fd_;
+}
+
+const std::vector<VSoCSharedMemory::Region> &VSoCSharedMemoryImpl::Regions()
+    const {
+  return region_data_;
+}
+
+void VSoCSharedMemoryImpl::CreateLayout() {
+  void *mmap_addr = shared_mem_fd_->Mmap(0, header_.size,
+                                         PROT_READ | PROT_WRITE, MAP_SHARED, 0);
+  LOG_IF(FATAL, mmap_addr == MAP_FAILED)
+      << "Error mmaping file: " << strerror(errno);
+  *reinterpret_cast<vsoc_shm_layout_descriptor *>(mmap_addr) = header_;
+  auto region_dest = reinterpret_cast<vsoc_device_region *>(
+      reinterpret_cast<uintptr_t>(mmap_addr) + header_.vsoc_region_desc_offset);
+
+  for (const auto &region : region_data_) {
+    *region_dest++ = region.values;
+  }
+  munmap(mmap_addr, header_.size);
+}
+
+bool VSoCSharedMemoryImpl::GetEventFdPairForRegion(
+    const std::string &region_name, cvd::SharedFD *guest_to_host,
+    cvd::SharedFD *host_to_guest) const {
+  auto it = region_name_to_index_.find(region_name);
+  if (it == region_name_to_index_.end()) return false;
+
+  *guest_to_host = region_data_[it->second].host_fd;
+  *host_to_guest = region_data_[it->second].guest_fd;
+  return true;
+}
+
+template <typename T>
+void GetMandatoryUInt(const std::string &region_name,
+                      const Json::Value &json_region, const char *field_name,
+                      bool *error, T *dest) {
+  if (!json_region.isMember(field_name)) {
+    LOG(ERROR) << region_name << " missing " << field_name << " field";
+    *error = true;
+    return;
+  }
+  if (!json_region[field_name].isNumeric()) {
+    LOG(ERROR) << region_name << " " << field_name << " is not numeric";
+    *error = true;
+    return;
+  }
+  *dest = json_region[field_name].asUInt();
+}
+
+void JSONToSignalTable(const std::string &region_name,
+                       const Json::Value &json_region, const char *table_name,
+                       RegionAllocator *allocator, bool *error,
+                       vsoc_signal_table_layout *dest) {
+  if (!json_region.isMember(table_name)) {
+    LOG(ERROR) << region_name << " has no " << table_name << " section";
+    *error = true;
+    return;
+  }
+  GetMandatoryUInt(region_name, json_region[table_name], "num_nodes_lg2", error,
+                   &dest->num_nodes_lg2);
+  dest->futex_uaddr_table_offset = allocator->Allocate(
+      (1 << dest->num_nodes_lg2) * sizeof(uint32_t), "node table", error);
+  dest->interrupt_signalled_offset =
+      allocator->Allocate(sizeof(uint32_t), "signal word", error);
+}
+
+std::shared_ptr<VSoCSharedMemory::Region> JSONToRegion(
+    const std::string &region_name, const Json::Value &json_region,
+    bool *failed) {
+  std::shared_ptr<VSoCSharedMemory::Region> region(
+      new VSoCSharedMemory::Region);
+  GetMandatoryUInt(region_name, json_region, "current_version", failed,
+                   &region->values.current_version);
+  GetMandatoryUInt(region_name, json_region, "min_compatible_version", failed,
+                   &region->values.min_compatible_version);
+  GetMandatoryUInt(region_name, json_region, "region_size", failed,
+                   &region->values.region_end_offset);
+  RegionAllocator allocator(region_name, region->values.region_end_offset);
+  JSONToSignalTable(region_name, json_region, "guest_to_host_signal_table",
+                    &allocator, failed,
+                    &region->values.guest_to_host_signal_table);
+  JSONToSignalTable(region_name, json_region, "host_to_guest_signal_table",
+                    &allocator, failed,
+                    &region->values.host_to_guest_signal_table);
+
+  region->values.offset_of_region_data = allocator.AllocateRest(failed);
+  return region;
+}
+
+}  // anonymous namespace
+
+std::unique_ptr<VSoCSharedMemory> VSoCSharedMemory::New(
+    const std::string &path, const Json::Value &root) {
+  // This is so catastrophic that there isn't anything else to check.
+  if (!root.isMember(g_vsoc_device_regions)) {
+    LOG(ERROR) << "vsoc_device_regions section is absent";
+    return nullptr;
+  }
+  auto device_regions = root[g_vsoc_device_regions];
+  bool failed = false;
+  RegionAllocator shm_file("shared_memory_file", UINT32_MAX);
+  vsoc_shm_layout_descriptor header{};
+  header.major_version = CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
+  header.minor_version = CURRENT_VSOC_LAYOUT_MINOR_VERSION;
+  // size is handled at N1
+  header.region_count = device_regions.size();
+  shm_file.Allocate(sizeof(header), "header", &failed);
+  header.vsoc_region_desc_offset =
+      shm_file.Allocate(sizeof(vsoc_device_region) * header.region_count,
+                        "region descriptors", &failed);
+  // Align to a page boundary for the first region
+  shm_file.PadTo(RegionAllocator::PageSize(), &failed);
+
+  std::map<std::string, size_t> name_to_region_idx;
+  std::vector<Region> regions;
+  regions.reserve(header.region_count);
+  std::map<std::string, std::string> managed_by_references;
+
+  // Pass 1: Parse individual region structures validating all of the
+  // fields that can be validated without help.
+  for (const auto &json_region : device_regions) {
+    if (!json_region.isMember(g_device_name_tag)) {
+      LOG(ERROR) << g_device_name_tag << " is missing from region";
+      failed = true;
+      continue;
+    }
+    const std::string device_name = json_region[g_device_name_tag].asString();
+    if (name_to_region_idx.count(device_name)) {
+      LOG(ERROR) << device_name << " used for more than one region";
+      failed = true;
+      continue;
+    }
+    std::shared_ptr<Region> region =
+        JSONToRegion(device_name, json_region, &failed);
+    // Create one pair of eventfds for this region. Note that the guest to host
+    // eventfd is non-blocking, whereas the host to guest eventfd is blocking.
+    // This is in anticipation of blocking semantics for the host side locks.
+    region->host_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
+    if (!region->host_fd->IsOpen()) {
+      failed = true;
+      LOG(ERROR) << "Failed to create host eventfd for " << device_name << ": "
+                 << region->host_fd->StrError();
+    }
+    region->guest_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
+    if (!region->guest_fd->IsOpen()) {
+      failed = true;
+      LOG(ERROR) << "Failed to create guest eventfd for " << device_name << ": "
+                 << region->guest_fd->StrError();
+    }
+    region->values.region_begin_offset = shm_file.Allocate(
+        region->values.region_end_offset, device_name.c_str(), &failed);
+    shm_file.PadTo(RegionAllocator::PageSize(), &failed);
+    region->values.region_end_offset += region->values.region_begin_offset;
+    if (sizeof(region->values.device_name) - device_name.size() < 1) {
+      LOG(ERROR) << device_name << " is too long for a region name";
+      failed = true;
+    } else {
+      strcpy(region->values.device_name, device_name.c_str());
+    }
+
+    name_to_region_idx[device_name] = regions.size();
+    regions.push_back(*region);
+    // We will attempt to resolve this link in Pass 2
+    if (json_region.isMember(g_managed_by_tag)) {
+      managed_by_references[device_name] =
+          json_region[g_managed_by_tag].asString();
+    }
+  }
+
+  // Pass 2: Resolve the managed_by_references
+  for (const auto &it : managed_by_references) {
+    if (!name_to_region_idx.count(it.second)) {
+      LOG(ERROR) << it.first << " managed by missing region " << it.second;
+      failed = true;
+      continue;
+    }
+    regions[name_to_region_idx[it.first]].values.managed_by =
+        name_to_region_idx[it.second];
+    if (regions[name_to_region_idx[it.first]].values.managed_by ==
+        VSOC_REGION_WHOLE) {
+      LOG(ERROR)
+          << "Region '" << it.first << "' has owner " << it.second
+          << " with index "
+          << regions[name_to_region_idx[it.first]].values.managed_by
+          << " which is the default value for regions without an owner. Choose"
+             "a different region to be at index "
+          << regions[name_to_region_idx[it.first]].values.managed_by
+          << ", make sure the chosen region is NOT the owner of any other "
+             "region";
+    }
+  }
+  if (failed) {
+    return nullptr;
+  }
+  // Handles size (marker N1)
+  // The size must be a power of 2
+  size_t temp = shm_file.GetOffset();
+  size_t allocated_size = 0;
+  // Find the highest set bit
+  while(temp) {
+    allocated_size = temp;
+    // Clear a bit
+    temp = temp & (temp -1);
+  }
+  // If the size is already a power of 2 just use it. Otherswise use the
+  // next higher power of 2
+  if (allocated_size < shm_file.GetOffset()) {
+    allocated_size *= 2;
+  }
+  header.size = allocated_size;
+  return std::unique_ptr<VSoCSharedMemory>(
+      new VSoCSharedMemoryImpl(header, name_to_region_idx, regions, path));
+}
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/vsocsharedmem.h b/host/libs/ivserver/vsocsharedmem.h
new file mode 100644
index 0000000..1edab05
--- /dev/null
+++ b/host/libs/ivserver/vsocsharedmem.h
@@ -0,0 +1,59 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <json/json.h>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "common/libs/fs/shared_fd.h"
+#include "uapi/vsoc_shm.h"
+
+namespace ivserver {
+
+class VSoCSharedMemory {
+ public:
+  // Region describes a VSoCSharedMem region.
+  struct Region {
+    vsoc_device_region values{};
+    cvd::SharedFD host_fd;
+    cvd::SharedFD guest_fd;
+  };
+
+  // Max name length of a memory region.
+  static constexpr int32_t kMaxRegionNameLength = sizeof(vsoc_device_name);
+
+  VSoCSharedMemory() = default;
+  virtual ~VSoCSharedMemory() = default;
+
+  static std::unique_ptr<VSoCSharedMemory> New(const std::string &name,
+                                               const Json::Value &json_root);
+
+  virtual bool GetEventFdPairForRegion(const std::string &region_name,
+                                       cvd::SharedFD *guest_to_host,
+                                       cvd::SharedFD *host_to_guest) const = 0;
+
+  virtual const cvd::SharedFD &SharedMemFD() const = 0;
+  virtual const std::vector<Region> &Regions() const = 0;
+
+ private:
+  VSoCSharedMemory(const VSoCSharedMemory &) = delete;
+  VSoCSharedMemory &operator=(const VSoCSharedMemory &other) = delete;
+};
+
+}  // namespace ivserver
diff --git a/host/libs/ivserver/vsocsharedmem_mock.h b/host/libs/ivserver/vsocsharedmem_mock.h
new file mode 100644
index 0000000..5ce64ae
--- /dev/null
+++ b/host/libs/ivserver/vsocsharedmem_mock.h
@@ -0,0 +1,33 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+#include "host/libs/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+namespace test {
+class VSoCSharedMemoryMock : public VSoCSharedMemory {
+ public:
+  MOCK_CONST_METHOD3(GetEventFdPairForRegion,
+                     bool(const std::string&, cvd::SharedFD*, cvd::SharedFD*));
+  MOCK_CONST_METHOD0(SharedMemFD, const cvd::SharedFD&());
+  MOCK_CONST_METHOD0(Regions,
+                     const std::vector<VSoCSharedMemory::Region>&());
+};
+}  // namespace test
+}  // namespace ivserver
diff --git a/host/libs/monitor/Android.bp b/host/libs/monitor/Android.bp
new file mode 100644
index 0000000..b062ed5
--- /dev/null
+++ b/host/libs/monitor/Android.bp
@@ -0,0 +1,18 @@
+cc_library_host_static {
+    name: "libmonitor",
+    srcs: [
+        "kernel_log_server.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libgflags",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/libs/monitor/kernel_log_server.cpp b/host/libs/monitor/kernel_log_server.cpp
new file mode 100644
index 0000000..e0f3ee2
--- /dev/null
+++ b/host/libs/monitor/kernel_log_server.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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/monitor/kernel_log_server.h"
+
+#include <glog/logging.h>
+#include <netinet/in.h>
+#include "common/libs/fs/shared_select.h"
+
+using cvd::SharedFD;
+
+namespace {
+static const std::string kVirtualDeviceStages[] = {
+  "VIRTUAL_DEVICE_BOOT_STARTED",
+  "VIRTUAL_DEVICE_BOOT_COMPLETED",
+  "VIRTUAL_DEVICE_BOOT_FAILED",
+};
+}  // namespace
+
+namespace monitor {
+KernelLogServer::KernelLogServer(const std::string& socket_name,
+                                 const std::string& log_name,
+                                 bool deprecated_boot_completed)
+    : name_(socket_name),
+      log_fd_(cvd::SharedFD::Open(
+          log_name.c_str(), O_CREAT | O_RDWR, 0666)),
+      deprecated_boot_completed_(deprecated_boot_completed) {}
+
+bool KernelLogServer::Init() { return CreateServerSocket(); }
+
+// Open new listening server socket.
+// Returns false, if listening socket could not be created.
+bool KernelLogServer::CreateServerSocket() {
+  LOG(INFO) << "Starting server socket: " << name_;
+
+  server_fd_ =
+      cvd::SharedFD::SocketLocalServer(name_.c_str(), false, SOCK_STREAM, 0666);
+  if (!server_fd_->IsOpen()) {
+    LOG(ERROR) << "Could not create socket: " << server_fd_->StrError();
+    return false;
+  }
+  return true;
+}
+
+void KernelLogServer::BeforeSelect(cvd::SharedFDSet* fd_read) const {
+  if (!client_fd_->IsOpen()) fd_read->Set(server_fd_);
+  if (client_fd_->IsOpen()) fd_read->Set(client_fd_);
+}
+
+void KernelLogServer::AfterSelect(const cvd::SharedFDSet& fd_read) {
+  if (fd_read.IsSet(server_fd_)) HandleIncomingConnection();
+
+  if (client_fd_->IsOpen() && fd_read.IsSet(client_fd_)) {
+    if (!HandleIncomingMessage()) {
+      client_fd_->Close();
+    }
+  }
+}
+
+// Accept new kernel log connection.
+void KernelLogServer::HandleIncomingConnection() {
+  if (client_fd_->IsOpen()) {
+    LOG(ERROR) << "Client already connected. No longer accepting connection.";
+    return;
+  }
+
+  client_fd_ = SharedFD::Accept(*server_fd_, nullptr, nullptr);
+  if (!client_fd_->IsOpen()) {
+    LOG(ERROR) << "Client connection failed: " << client_fd_->StrError();
+    return;
+  }
+}
+
+bool KernelLogServer::HandleIncomingMessage() {
+  const size_t buf_len = 256;
+  char buf[buf_len];
+  ssize_t ret = client_fd_->Read(buf, buf_len);
+  if (ret < 0) {
+    LOG(ERROR) << "Could not read from QEmu serial port: " << client_fd_->StrError();
+    return false;
+  }
+  if (ret == 0) return false;
+
+  // Detect VIRTUAL_DEVICE_BOOT_*
+  for (ssize_t i=0; i<ret; i++) {
+    if ('\n' == buf[i]) {
+      for (auto stage : kVirtualDeviceStages) {
+        if (std::string::npos != line_.find(stage)) {
+          LOG(INFO) << stage;
+          //TODO(b/69417553) Remove this when our clients have transitioned to the
+          // new boot completed
+          if (deprecated_boot_completed_) {
+            // Write to host kernel log
+            FILE* log = popen("/usr/bin/sudo /usr/bin/tee /dev/kmsg", "w");
+            fprintf(log, "%s\n", stage.c_str());
+            fclose(log);
+          }
+        }
+      }
+      line_.clear();
+    }
+    line_.append(1, buf[i]);
+  }
+
+  // Write the log to a file
+  if (log_fd_->Write(buf, ret) < 0) {
+    LOG(ERROR) << "Could not write kernel log to file: " << log_fd_->StrError();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace monitor
diff --git a/host/libs/monitor/kernel_log_server.h b/host/libs/monitor/kernel_log_server.h
new file mode 100644
index 0000000..d952d73
--- /dev/null
+++ b/host/libs/monitor/kernel_log_server.h
@@ -0,0 +1,70 @@
+/*
+ * 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 <string>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+//#include "host/libs/monitor/kernel_log_client.h"
+
+namespace monitor {
+// KernelLogServer manages incoming kernel log connection from QEmu. Only accept
+// one connection.
+class KernelLogServer {
+ public:
+  KernelLogServer(const std::string& socket_name,
+                  const std::string& log_name,
+                  bool deprecated_boot_completed);
+
+  ~KernelLogServer() = default;
+
+  // Initialize this instance of Server.
+  // Returns true, if initialization was successful.
+  bool Init();
+
+  // BeforeSelect is Called right before Select() to populate interesting
+  // SharedFDs.
+  void BeforeSelect(cvd::SharedFDSet* fd_read) const;
+
+  // AfterSelect is Called right after Select() to detect and respond to changes
+  // on affected SharedFDs.
+  void AfterSelect(const cvd::SharedFDSet& fd_read);
+
+ private:
+  // Create kernel log server socket.
+  // Returns true, if socket was successfully created.
+  bool CreateServerSocket();
+
+  // Handle new client connection. Only accept one connection.
+  void HandleIncomingConnection();
+
+  // Respond to message from remote client.
+  // Returns false, if client disconnected.
+  bool HandleIncomingMessage();
+
+  std::string name_;
+  cvd::SharedFD server_fd_;
+  cvd::SharedFD client_fd_;
+  cvd::SharedFD log_fd_;
+  std::string line_;
+  bool deprecated_boot_completed_;
+
+  KernelLogServer(const KernelLogServer&) = delete;
+  KernelLogServer& operator=(const KernelLogServer&) = delete;
+};
+
+}  // namespace monitor
diff --git a/host/libs/usbip/Android.bp b/host/libs/usbip/Android.bp
new file mode 100644
index 0000000..29f3778
--- /dev/null
+++ b/host/libs/usbip/Android.bp
@@ -0,0 +1,22 @@
+cc_library_host_static {
+    name: "libusbip",
+    srcs: [
+        "client.cpp",
+        "device_pool.cpp",
+        "messages.cpp",
+        "server.cpp",
+        "vhci_instrument.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libgflags",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/libs/usbip/README.md b/host/libs/usbip/README.md
new file mode 100644
index 0000000..e652352
--- /dev/null
+++ b/host/libs/usbip/README.md
@@ -0,0 +1,36 @@
+# USB/IP server library
+
+This folder contains set of classes and structures that constitute basic USB/IP 
+server.
+
+Protocol used in this library is defined as part of
+[Linux kernel documentation](https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt).
+
+## Structure
+
+### [`vadb::usbip::Device`](./device.h)[](#Device)
+
+Structure describing individual device accessible over USB/IP protocol.
+
+### [`vadb::usbip::DevicePool`](./device_pool.h)[](#DevicePool)
+
+DevicePool holds a set of [Devices](#Device) that can be enumerated and
+accessed by clients of this Server.
+
+### [`vadb::usbip::Server`](./server.h)
+
+Purpose of this class is to start a new listening socket and accept incoming
+USB/IP connections & requests.
+
+### [`vadb::usbip::Client`](./client.h)
+
+Client class represents individual USB/IP connection. Client enables remote
+USB/IP client to enumerate and access devices registered in
+[DevicePool](#DevicePool).
+
+### [`USB/IP Messages`](./messages.h)
+
+This file contains structures and enum values defined by the USB/IP protocol.
+All definitions found there have been collected from
+[Linux kernel documentation](https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt)
+.
diff --git a/host/libs/usbip/client.cpp b/host/libs/usbip/client.cpp
new file mode 100644
index 0000000..7860f24
--- /dev/null
+++ b/host/libs/usbip/client.cpp
@@ -0,0 +1,211 @@
+/*
+ * 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/usbip/client.h"
+
+#include <glog/logging.h>
+#include <iostream>
+
+#include "host/libs/usbip/device.h"
+#include "host/libs/usbip/messages.h"
+
+namespace vadb {
+namespace usbip {
+
+void Client::BeforeSelect(cvd::SharedFDSet* fd_read) const {
+  fd_read->Set(fd_);
+}
+
+bool Client::AfterSelect(const cvd::SharedFDSet& fd_read) {
+  if (fd_read.IsSet(fd_)) return HandleIncomingMessage();
+  return true;
+}
+
+// Handle incoming COMMAND.
+//
+// Read next CMD from client channel.
+// Returns false, if connection should be dropped.
+bool Client::HandleIncomingMessage() {
+  CmdHeader hdr;
+  if (!RecvUSBIPMsg(fd_, &hdr)) {
+    LOG(ERROR) << "Could not read command header: " << fd_->StrError();
+    return false;
+  }
+
+  // And the protocol, again.
+  switch (hdr.command) {
+    case kUsbIpCmdReqSubmit:
+      return HandleSubmitCmd(hdr);
+
+    case kUsbIpCmdReqUnlink:
+      return HandleUnlinkCmd(hdr);
+
+    default:
+      LOG(ERROR) << "Unsupported command requested: " << hdr.command;
+      return false;
+  }
+}
+
+// Handle incoming SUBMIT COMMAND.
+//
+// Execute command on specified USB device.
+// Returns false, if connection should be dropped.
+bool Client::HandleSubmitCmd(const CmdHeader& cmd) {
+  CmdReqSubmit req;
+  if (!RecvUSBIPMsg(fd_, &req)) {
+    LOG(ERROR) << "Could not read submit command: " << fd_->StrError();
+    return false;
+  }
+
+  uint32_t seq_num = cmd.seq_num;
+
+  // Reserve buffer for data in or out.
+  std::vector<uint8_t> payload;
+  int payload_length = req.transfer_buffer_length;
+  payload.resize(payload_length);
+
+  bool is_host_to_device = cmd.direction == kUsbIpDirectionOut;
+  // Control requests are quite easy to detect; if setup is all '0's, then we're
+  // doing a data transfer, otherwise it's a control transfer.
+  // We only check for cmd and type fields here, as combination 0/0 of these
+  // fields is already invalid (cmd == GET_STATUS, type = WRITE).
+  bool is_control_request = !(req.setup.cmd == 0 && req.setup.type == 0);
+
+  // Find requested device and execute command.
+  auto device = pool_.GetDevice({cmd.bus_num, cmd.dev_num});
+  if (device) {
+    // Read data to be sent to device, if specified.
+    if (is_host_to_device && payload_length) {
+      int32_t got = 0;
+      // Make sure we read everything.
+      while (got < payload.size()) {
+        auto read =
+            fd_->Recv(&payload[got], payload.size() - got, MSG_NOSIGNAL);
+        if (fd_->GetErrno() != 0) {
+          LOG(ERROR) << "Client disconnected: " << fd_->StrError();
+          return false;
+        } else if (!read) {
+          LOG(ERROR) << "Short read; client likely disconnected.";
+          return false;
+        }
+        got += read;
+      }
+    }
+
+    // If setup structure of request is initialized then we need to execute
+    // control transfer. Otherwise, this is a plain data exchange.
+    bool send_success = false;
+    if (is_control_request) {
+      send_success = device->handle_control_transfer(
+          req.setup, req.deadline_interval, std::move(payload),
+          [this, seq_num, is_host_to_device](bool is_success,
+                                             std::vector<uint8_t> data) {
+            HandleAsyncDataReady(seq_num, is_success, is_host_to_device,
+                                 std::move(data));
+          });
+    } else {
+      send_success = device->handle_data_transfer(
+          cmd.endpoint, is_host_to_device, req.deadline_interval,
+          std::move(payload),
+          [this, seq_num, is_host_to_device](bool is_success,
+                                             std::vector<uint8_t> data) {
+            HandleAsyncDataReady(seq_num, is_success, is_host_to_device,
+                                 std::move(data));
+          });
+    }
+
+    // Simply fail if couldn't execute command.
+    if (!send_success) {
+      HandleAsyncDataReady(seq_num, false, is_host_to_device,
+                           std::vector<uint8_t>());
+    }
+  }
+  return true;
+}
+
+void Client::HandleAsyncDataReady(uint32_t seq_num, bool is_success,
+                                  bool is_host_to_device,
+                                  std::vector<uint8_t> data) {
+  // Response template.
+  // - in header, host doesn't care about anything else except for command type
+  //   and sequence number.
+  // - in body, report status == !OK unless we completed everything
+  //   successfully.
+  CmdHeader rephdr{};
+  rephdr.command = kUsbIpCmdRepSubmit;
+  rephdr.seq_num = seq_num;
+
+  CmdRepSubmit rep{};
+  rep.status = is_success ? 0 : 1;
+  rep.actual_length = data.size();
+
+  // Data out.
+  if (!SendUSBIPMsg(fd_, rephdr)) {
+    LOG(ERROR) << "Failed to send response header: " << fd_->StrError();
+    return;
+  }
+
+  if (!SendUSBIPMsg(fd_, rep)) {
+    LOG(ERROR) << "Failed to send response body: " << fd_->StrError();
+    return;
+  }
+
+  if (!is_host_to_device && data.size() > 0) {
+    if (fd_->Send(data.data(), data.size(), MSG_NOSIGNAL) != data.size()) {
+      LOG(ERROR) << "Failed to send response payload: " << fd_->StrError();
+      return;
+    }
+  }
+}
+
+// Handle incoming UNLINK COMMAND.
+//
+// Unlink removes command specified via seq_num from a list of commands to be
+// executed.
+// We don't schedule commands for execution, so technically every UNLINK will
+// come in late.
+// Returns false, if connection should be dropped.
+bool Client::HandleUnlinkCmd(const CmdHeader& cmd) {
+  CmdReqUnlink req;
+  if (!RecvUSBIPMsg(fd_, &req)) {
+    LOG(ERROR) << "Could not read unlink command: " << fd_->StrError();
+    return false;
+  }
+  LOG(INFO) << "Client requested to unlink previously submitted command: "
+            << req.seq_num;
+
+  CmdHeader rephdr{};
+  rephdr.command = kUsbIpCmdRepUnlink;
+  rephdr.seq_num = cmd.seq_num;
+
+  // Technically we do not schedule commands for execution, so we cannot
+  // de-queue commands, either. Indicate this by sending status != ok.
+  CmdRepUnlink rep;
+  rep.status = 1;
+
+  if (!SendUSBIPMsg(fd_, rephdr)) {
+    LOG(ERROR) << "Could not send unlink command header: " << fd_->StrError();
+    return false;
+  }
+
+  if (!SendUSBIPMsg(fd_, rep)) {
+    LOG(ERROR) << "Could not send unlink command data: " << fd_->StrError();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/client.h b/host/libs/usbip/client.h
new file mode 100644
index 0000000..ae2cff2
--- /dev/null
+++ b/host/libs/usbip/client.h
@@ -0,0 +1,72 @@
+/*
+ * 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/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+#include "host/libs/usbip/device_pool.h"
+#include "host/libs/usbip/messages.h"
+
+namespace vadb {
+namespace usbip {
+
+// Represents USB/IP client, or individual connection to our USB/IP server.
+// Multiple clients are allowed, even if practically we anticipate only one
+// connection at the time.
+class Client final {
+ public:
+  Client(const DevicePool& pool, const cvd::SharedFD& fd)
+      : pool_(pool), fd_(fd) {}
+
+  ~Client() {}
+
+  // BeforeSelect is Called right before Select() to populate interesting
+  // SharedFDs.
+  void BeforeSelect(cvd::SharedFDSet* fd_read) const;
+
+  // AfterSelect is Called right after Select() to detect and respond to changes
+  // on affected SharedFDs.
+  // Return value indicates whether this client is still valid.
+  bool AfterSelect(const cvd::SharedFDSet& fd_read);
+
+ private:
+  // Respond to message from remote client.
+  // Returns false, if client violated protocol or disconnected, indicating,
+  // that this instance should no longer be used.
+  bool HandleIncomingMessage();
+
+  // Execute command on USB device.
+  // Returns false, if connection should be dropped.
+  bool HandleSubmitCmd(const CmdHeader& hdr);
+
+  // HandleAsyncDataReady is called asynchronously once previously submitted
+  // data transfer (control or bulk) has completed (or failed).
+  void HandleAsyncDataReady(uint32_t seq_num, bool is_success,
+                            bool is_host_to_device, std::vector<uint8_t> data);
+
+  // Unlink previously submitted message from device queue.
+  // Returns false, if connection should be dropped.
+  bool HandleUnlinkCmd(const CmdHeader& hdr);
+
+  const DevicePool& pool_;
+  cvd::SharedFD fd_;
+
+  Client(const Client&) = delete;
+  Client& operator=(const Client&) = delete;
+};
+
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/device.h b/host/libs/usbip/device.h
new file mode 100644
index 0000000..f310c8e
--- /dev/null
+++ b/host/libs/usbip/device.h
@@ -0,0 +1,85 @@
+/*
+ * 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 <cstdint>
+#include <functional>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "host/libs/usbip/messages.h"
+
+namespace vadb {
+namespace usbip {
+
+// The device descriptor of a USB device represents a USB device that is
+// available for import.
+class Device {
+ public:
+  // AsyncTransferReadyCB specifies a signature of a function that will be
+  // called upon transfer completion (whether successful or failed). Parameters
+  // supplied to the function are:
+  // - operation status, indicated by boolean flag (true = success),
+  // - vector containing transferred data (and actual size).
+  using AsyncTransferReadyCB = std::function<void(bool, std::vector<uint8_t>)>;
+
+  // Interface provides minimal description of device's interface.
+  struct Interface {
+    uint8_t iface_class;
+    uint8_t iface_subclass;
+    uint8_t iface_protocol;
+  };
+
+  // vendor_id and product_id identify device manufacturer and type.
+  // dev_version describes device version (as BCD).
+  uint16_t vendor_id;
+  uint16_t product_id;
+  uint16_t dev_version;
+
+  // Class, Subclass and Protocol define device type.
+  uint8_t dev_class;
+  uint8_t dev_subclass;
+  uint8_t dev_protocol;
+
+  // Speed indicates device speed (see libusb_speed).
+  uint8_t speed;
+
+  // ConfigurationsCount and ConfigurationNumber describe total number of device
+  // configurations and currently activated device configuration.
+  size_t configurations_count;
+  size_t configuration_number;
+
+  // Interfaces returns a collection of device interfaces.
+  std::vector<Interface> interfaces;
+
+  // Attach request handler.
+  std::function<bool()> handle_attach;
+
+  // Device control request dispatcher.
+  std::function<bool(const CmdRequest& request, uint32_t deadline,
+                     std::vector<uint8_t> data, AsyncTransferReadyCB callback)>
+      handle_control_transfer;
+
+  // Device  data request dispatcher.
+  std::function<bool(uint8_t endpoint, bool is_host_to_device,
+                     uint32_t deadline, std::vector<uint8_t> data,
+                     AsyncTransferReadyCB callback)>
+      handle_data_transfer;
+};
+
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/device_pool.cpp b/host/libs/usbip/device_pool.cpp
new file mode 100644
index 0000000..22f334b
--- /dev/null
+++ b/host/libs/usbip/device_pool.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/libs/usbip/device_pool.h"
+
+#include <glog/logging.h>
+
+namespace vadb {
+namespace usbip {
+
+void DevicePool::AddDevice(BusDevNumber bdn, std::unique_ptr<Device> device) {
+  devices_[bdn] = std::move(device);
+}
+
+Device* DevicePool::GetDevice(BusDevNumber bus_id) const {
+  auto iter = devices_.find(bus_id);
+  if (iter == devices_.end()) return nullptr;
+  return iter->second.get();
+}
+
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/device_pool.h b/host/libs/usbip/device_pool.h
new file mode 100644
index 0000000..1ba8527
--- /dev/null
+++ b/host/libs/usbip/device_pool.h
@@ -0,0 +1,66 @@
+/*
+ * 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 <map>
+#include <string>
+
+#include "host/libs/usbip/device.h"
+
+namespace vadb {
+namespace usbip {
+// Container for all virtual USB/IP devices.
+// Stores devices by virtual BUS ID.
+class DevicePool {
+ public:
+  // BusDevNumber is a pair uniquely identifying bus and device.
+  struct BusDevNumber {
+    uint16_t bus_number;
+    uint16_t dev_number;
+
+    bool operator<(BusDevNumber other) const {
+      return (bus_number << 16 | dev_number) <
+             (other.bus_number << 16 | other.dev_number);
+    }
+  };
+
+  // Internal container type.
+  using MapType = std::map<BusDevNumber, std::unique_ptr<Device>>;
+
+  DevicePool() = default;
+  virtual ~DevicePool() = default;
+
+  // Add new device associated with virtual BUS ID.
+  void AddDevice(BusDevNumber bus_id, std::unique_ptr<Device> device);
+
+  // Get device associated with supplied virtual bus/device number.
+  Device* GetDevice(BusDevNumber bus_dev_num) const;
+
+  // Get total number of USB/IP devices.
+  size_t Size() const { return devices_.size(); }
+
+  MapType::const_iterator begin() const { return devices_.cbegin(); }
+  MapType::const_iterator end() const { return devices_.cend(); }
+
+ private:
+  MapType devices_;
+
+  DevicePool(const DevicePool&) = delete;
+  DevicePool& operator=(const DevicePool&) = delete;
+};
+
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/messages.cpp b/host/libs/usbip/messages.cpp
new file mode 100644
index 0000000..27af986
--- /dev/null
+++ b/host/libs/usbip/messages.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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/usbip/messages.h"
+
+#include <netinet/in.h>
+#include <iostream>
+
+#include <glog/logging.h>
+
+namespace vadb {
+namespace usbip {
+namespace {
+// Basic sanity checking.
+// We're using CmdHeader + CmdReq/Rep in case any of the fields is moved between
+// structures.
+constexpr int kUsbIpCmdLength = 48;
+
+static_assert(sizeof(CmdHeader) + sizeof(CmdReqSubmit) == kUsbIpCmdLength,
+              "USB/IP command + header must be exactly 48 bytes.");
+static_assert(sizeof(CmdHeader) + sizeof(CmdRepSubmit) == kUsbIpCmdLength,
+              "USB/IP command + header must be exactly 48 bytes.");
+static_assert(sizeof(CmdHeader) + sizeof(CmdReqUnlink) == kUsbIpCmdLength,
+              "USB/IP command + header must be exactly 48 bytes.");
+static_assert(sizeof(CmdHeader) + sizeof(CmdRepUnlink) == kUsbIpCmdLength,
+              "USB/IP command + header must be exactly 48 bytes.");
+}  // namespace
+
+// NetToHost and HostToNet are used to reduce risk of copy/paste errors and to
+// provide uniform method of converting messages between different endian types.
+namespace internal {
+
+template <>
+void NetToHost(uint32_t* t) {
+  *t = ntohl(*t);
+}
+
+template <>
+void NetToHost(Command* t) {
+  *t = static_cast<Command>(ntohl(*t));
+}
+
+template <>
+void NetToHost(Direction* t) {
+  *t = static_cast<Direction>(ntohl(*t));
+}
+
+template <>
+void NetToHost(uint16_t* t) {
+  *t = ntohs(*t);
+}
+
+template <>
+void NetToHost(CmdHeader* t) {
+  NetToHost(&t->command);
+  NetToHost(&t->seq_num);
+  NetToHost(&t->bus_num);
+  NetToHost(&t->dev_num);
+  NetToHost(&t->direction);
+  NetToHost(&t->endpoint);
+}
+
+template <>
+void NetToHost(CmdReqSubmit* t) {
+  NetToHost(&t->transfer_flags);
+  NetToHost(&t->transfer_buffer_length);
+  NetToHost(&t->start_frame);
+  NetToHost(&t->number_of_packets);
+  NetToHost(&t->deadline_interval);
+}
+
+template <>
+void NetToHost(CmdReqUnlink* t) {
+  NetToHost(&t->seq_num);
+}
+
+template <>
+void HostToNet(uint32_t* t) {
+  *t = htonl(*t);
+}
+
+template <>
+void HostToNet(Command* t) {
+  *t = static_cast<Command>(htonl(*t));
+}
+
+template <>
+void HostToNet(Direction* t) {
+  *t = static_cast<Direction>(htonl(*t));
+}
+
+template <>
+void HostToNet(uint16_t* t) {
+  *t = htons(*t);
+}
+
+template <>
+void HostToNet(CmdHeader* t) {
+  HostToNet(&t->command);
+  HostToNet(&t->seq_num);
+  HostToNet(&t->bus_num);
+  HostToNet(&t->dev_num);
+  HostToNet(&t->direction);
+  HostToNet(&t->endpoint);
+}
+
+template <>
+void HostToNet(CmdRepSubmit* t) {
+  HostToNet(&t->status);
+  HostToNet(&t->actual_length);
+  HostToNet(&t->start_frame);
+  HostToNet(&t->number_of_packets);
+  HostToNet(&t->error_count);
+}
+
+template <>
+void HostToNet(CmdRepUnlink* t) {
+  HostToNet(&t->status);
+}
+
+}  // namespace internal
+
+std::ostream& operator<<(std::ostream& out, const CmdHeader& header) {
+  out << "CmdHeader\n";
+  out << "\t\tcmd:\t" << header.command << '\n';
+  out << "\t\tseq#:\t" << header.seq_num << '\n';
+  out << "\t\tbus#:\t0x" << header.bus_num << '\n';
+  out << "\t\tdev#:\t0x" << header.dev_num << '\n';
+  out << "\t\tdir:\t" << (header.direction ? "in" : "out") << '\n';
+  out << "\t\tendpt:\t" << header.endpoint << "\n";
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdRequest& setup) {
+  out << "Request\n";
+  out << "\t\t\ttype:\t" << std::hex << int(setup.type) << '\n';
+  out << "\t\t\treq:\t" << int(setup.cmd) << std::dec << '\n';
+  out << "\t\t\tval:\t" << setup.value << '\n';
+  out << "\t\t\tidx:\t" << setup.index << '\n';
+  out << "\t\t\tlen:\t" << setup.length << '\n';
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdReqSubmit& submit) {
+  out << "CmdReqSubmit\n";
+  out << "\t\ttr_flg:\t" << std::hex << submit.transfer_flags << std::dec
+      << '\n';
+  out << "\t\ttr_len:\t" << submit.transfer_buffer_length << '\n';
+  out << "\t\tstart:\t" << submit.start_frame << '\n';
+  out << "\t\tpktcnt:\t" << submit.number_of_packets << '\n';
+  out << "\t\tttl:\t" << submit.deadline_interval << '\n';
+  out << "\t\tsetup:\t" << submit.setup << '\n';
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdRepSubmit& submit) {
+  out << "CmdRepSubmit\n";
+  out << "\t\tstatus:\t" << submit.status << '\n';
+  out << "\t\tlen:\t" << submit.actual_length << '\n';
+  out << "\t\tstart:\t" << submit.start_frame << '\n';
+  out << "\t\tpktcnt:\t" << submit.number_of_packets << '\n';
+  out << "\t\terrors:\t" << submit.error_count << '\n';
+  out << "\t\tsetup:\t" << submit.setup << '\n';
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdReqUnlink& unlink) {
+  out << "CmdReqUnlink\n";
+  out << "\t\tseq#:\t" << unlink.seq_num << '\n';
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CmdRepUnlink& unlink) {
+  out << "CmdRepUnlink\n";
+  out << "\t\tstatus:\t" << unlink.status << '\n';
+  return out;
+}
+
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/messages.h b/host/libs/usbip/messages.h
new file mode 100644
index 0000000..8941df1
--- /dev/null
+++ b/host/libs/usbip/messages.h
@@ -0,0 +1,135 @@
+/*
+ * 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 <glog/logging.h>
+#include <stdint.h>
+
+#include "common/libs/fs/shared_fd.h"
+
+// Requests and constants below are defined in kernel documentation file:
+// https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt
+namespace vadb {
+namespace usbip {
+namespace internal {
+// Rotate endianness of the data to match protocol.
+template <typename T>
+void HostToNet(T* data);
+template <typename T>
+void NetToHost(T* data);
+}  // namespace internal
+
+// Send message to USB/IP client.
+// Accept data by value and modify it to match net endian locally.
+// Returns true, if message was sent successfully.
+template <typename T>
+bool SendUSBIPMsg(const cvd::SharedFD& fd, T data) {
+  internal::HostToNet(&data);
+  return fd->Send(&data, sizeof(T), MSG_NOSIGNAL) == sizeof(T);
+}
+
+// Receive message from USB/IP client.
+// After message is received, it's updated to match host endian.
+// Returns true, if message was received successfully.
+template <typename T>
+bool RecvUSBIPMsg(const cvd::SharedFD& fd, T* data) {
+  bool res = fd->Recv(data, sizeof(T), MSG_NOSIGNAL) == sizeof(T);
+  if (res) {
+    internal::NetToHost(data);
+  }
+  return res;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// COMMANDS
+////////////////////////////////////////////////////////////////////////////////
+
+// Command numbers. Commands are valid only once USB device is attached.
+enum Command : uint32_t {
+  kUsbIpCmdReqSubmit = 1,  // Submit request
+  kUsbIpCmdReqUnlink = 2,  // Unlink request
+  kUsbIpCmdRepSubmit = 3,  // Submit response
+  kUsbIpCmdRepUnlink = 4,  // Unlink response
+};
+
+// Direction of data flow.
+enum Direction : uint32_t {
+  kUsbIpDirectionOut = 0,
+  kUsbIpDirectionIn = 1,
+};
+
+// Setup structure is explained in great detail here:
+// - http://www.beyondlogic.org/usbnutshell/usb6.shtml
+// - http://www.usbmadesimple.co.uk/ums_4.htm
+struct CmdRequest {
+  uint8_t type;
+  uint8_t cmd;
+  uint16_t value;
+  uint16_t index;
+  uint16_t length;
+} __attribute__((packed));
+
+// CmdHeader precedes any command request or response body.
+struct CmdHeader {
+  Command command;
+  uint32_t seq_num;
+  uint16_t bus_num;
+  uint16_t dev_num;
+  Direction direction;
+  uint32_t endpoint;  // valid values: 0-15
+} __attribute__((packed));
+
+// Command data for submitting an USB request.
+struct CmdReqSubmit {
+  uint32_t transfer_flags;
+  uint32_t transfer_buffer_length;
+  uint32_t start_frame;
+  uint32_t number_of_packets;
+  uint32_t deadline_interval;
+  CmdRequest setup;
+} __attribute__((packed));
+
+// Command response for submitting an USB request.
+struct CmdRepSubmit {
+  uint32_t status;  // 0 = success.
+  uint32_t actual_length;
+  uint32_t start_frame;
+  uint32_t number_of_packets;
+  uint32_t error_count;
+  CmdRequest setup;
+} __attribute__((packed));
+
+// Unlink USB request.
+struct CmdReqUnlink {
+  uint32_t seq_num;
+  uint32_t reserved[6];
+} __attribute__((packed));
+
+// Unlink USB response.
+struct CmdRepUnlink {
+  uint32_t status;
+  uint32_t reserved[6];
+} __attribute__((packed));
+
+// Diagnostics.
+std::ostream& operator<<(std::ostream& out, const CmdHeader& header);
+std::ostream& operator<<(std::ostream& out, const CmdReqSubmit& data);
+std::ostream& operator<<(std::ostream& out, const CmdRepSubmit& data);
+std::ostream& operator<<(std::ostream& out, const CmdReqUnlink& data);
+std::ostream& operator<<(std::ostream& out, const CmdRepUnlink& data);
+
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/server.cpp b/host/libs/usbip/server.cpp
new file mode 100644
index 0000000..bada417
--- /dev/null
+++ b/host/libs/usbip/server.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/libs/usbip/server.h"
+
+#include <glog/logging.h>
+#include <netinet/in.h>
+#include "common/libs/fs/shared_select.h"
+
+using cvd::SharedFD;
+
+namespace vadb {
+namespace usbip {
+Server::Server(const std::string& name, const DevicePool& devices)
+    : name_{name}, device_pool_{devices} {}
+
+bool Server::Init() { return CreateServerSocket(); }
+
+// Open new listening server socket.
+// Returns false, if listening socket could not be created.
+bool Server::CreateServerSocket() {
+  LOG(INFO) << "Starting server socket: " << name_;
+
+  server_ = SharedFD::SocketLocalServer(name_.c_str(), true, SOCK_STREAM, 0700);
+  if (!server_->IsOpen()) {
+    LOG(ERROR) << "Could not create socket: " << server_->StrError();
+    return false;
+  }
+  return true;
+}
+
+void Server::BeforeSelect(cvd::SharedFDSet* fd_read) const {
+  fd_read->Set(server_);
+  for (const auto& client : clients_) client.BeforeSelect(fd_read);
+}
+
+void Server::AfterSelect(const cvd::SharedFDSet& fd_read) {
+  if (fd_read.IsSet(server_)) HandleIncomingConnection();
+
+  for (auto iter = clients_.begin(); iter != clients_.end();) {
+    if (!iter->AfterSelect(fd_read)) {
+      // If client conversation failed, hang up.
+      iter = clients_.erase(iter);
+      continue;
+    }
+    ++iter;
+  }
+}
+
+// Accept new USB/IP connection. Add it to client pool.
+void Server::HandleIncomingConnection() {
+  SharedFD client = SharedFD::Accept(*server_, nullptr, nullptr);
+  if (!client->IsOpen()) {
+    LOG(ERROR) << "Client connection failed: " << client->StrError();
+    return;
+  }
+
+  clients_.emplace_back(device_pool_, client);
+}
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/server.h b/host/libs/usbip/server.h
new file mode 100644
index 0000000..ec47e43
--- /dev/null
+++ b/host/libs/usbip/server.h
@@ -0,0 +1,65 @@
+/*
+ * 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 <list>
+#include <string>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/usbip/client.h"
+#include "host/libs/usbip/device_pool.h"
+
+namespace vadb {
+namespace usbip {
+
+class Server final {
+ public:
+  Server(const std::string& name, const DevicePool& device_pool);
+  ~Server() = default;
+
+  // Initialize this instance of Server.
+  // Returns true, if initialization was successful.
+  bool Init();
+
+  // BeforeSelect is Called right before Select() to populate interesting
+  // SharedFDs.
+  void BeforeSelect(cvd::SharedFDSet* fd_read) const;
+
+  // AfterSelect is Called right after Select() to detect and respond to changes
+  // on affected SharedFDs.
+  void AfterSelect(const cvd::SharedFDSet& fd_read);
+
+ private:
+  // Create USBIP server socket.
+  // Returns true, if socket was successfully created.
+  bool CreateServerSocket();
+
+  // Handle new client connection.
+  // New clients will be appended to clients_ list.
+  void HandleIncomingConnection();
+
+  std::string name_;
+  cvd::SharedFD server_;
+  std::list<Client> clients_;
+
+  const DevicePool& device_pool_;
+
+  Server(const Server&) = delete;
+  Server& operator=(const Server&) = delete;
+};
+
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/vhci_instrument.cpp b/host/libs/usbip/vhci_instrument.cpp
new file mode 100644
index 0000000..179d6c5
--- /dev/null
+++ b/host/libs/usbip/vhci_instrument.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include <glog/logging.h>
+#include <fstream>
+#include <sstream>
+#include "common/libs/fs/shared_select.h"
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/usbip/vhci_instrument.h"
+
+namespace vadb {
+namespace usbip {
+namespace {
+// Device ID is specified as a concatenated pair of BUS and DEVICE id.
+// Since we only export one device and our server doesn't care much about
+// its number, we use the default value of BUS=1 and DEVICE=1.
+// This can be set to something else and should still work, as long as
+// numbers are valid in USB sense.
+constexpr uint32_t kDefaultDeviceID = (1 << 16) | 1;
+
+// Request Highspeed configuration. Superspeed isn't supported by vhci.
+// Supported configurations are:
+//  4 -> wireless
+//  3 -> highspeed
+//  2 -> full speed
+//  1 -> low speed
+//  Please refer to the Kernel source tree in the following locations:
+//     include/uapi/linux/usb/ch9.h
+//     drivers/usb/usbip/vhci_sysfs.c
+constexpr uint32_t kDefaultDeviceSpeed = 3;
+
+// Subsystem and device type where VHCI driver is located.
+const char* const kVHCIPlatformPaths[] = {
+  "/sys/devices/platform/vhci_hcd",
+  "/sys/devices/platform/vhci_hcd.1",
+};
+
+// Control messages.
+// Attach tells thread to attach remote device.
+// Detach tells thread to detach remote device.
+using ControlMsgType = uint8_t;
+constexpr ControlMsgType kControlAttach = 'A';
+constexpr ControlMsgType kControlDetach = 'D';
+constexpr ControlMsgType kControlExit = 'E';
+
+// Used with EPOLL as epoll_data to determine event type.
+enum EpollEventType {
+  kControlEvent,
+  kVHCIEvent,
+};
+
+// Port status values deducted from /sys/devices/platform/vhci_hcd/status
+enum {
+  // kVHCIPortFree indicates the port is not currently in use.
+  kVHCIStatusPortFree = 4
+};
+}  // anonymous namespace
+
+VHCIInstrument::VHCIInstrument(const std::string& name)
+    : name_(name) {}
+
+VHCIInstrument::~VHCIInstrument() {
+  control_write_end_->Write(&kControlExit, sizeof(kControlExit));
+  attach_thread_->join();
+}
+
+bool VHCIInstrument::Init() {
+  cvd::SharedFD::Pipe(&control_read_end_, &control_write_end_);
+
+  struct stat buf;
+  for (const auto* path : kVHCIPlatformPaths) {
+    if (stat(path, &buf) == 0) {
+      syspath_ = path;
+      break;
+    }
+  }
+
+  if (syspath_.empty()) {
+    LOG(ERROR) << "VHCI not available. Is the driver loaded?";
+    LOG(ERROR) << "Try: sudo modprobe vhci_hcd";
+    LOG(ERROR) << "The driver is part of linux-image-extra-`uname -r` package";
+    return false;
+  }
+
+  if (!FindFreePort()) {
+    LOG(ERROR) << "It appears all your VHCI ports are currently occupied.";
+    LOG(ERROR) << "New VHCI device cannot be registered unless one of the "
+               << "ports is freed.";
+    return false;
+  }
+
+  attach_thread_.reset(new std::thread([this]() { AttachThread(); }));
+  return true;
+}
+
+bool VHCIInstrument::FindFreePort() {
+  std::ifstream stat(syspath_ + "/status");
+  int port;
+  int status;
+  std::string everything_else;
+
+  if (!stat.is_open()) {
+    LOG(ERROR) << "Could not open usb-ip status file.";
+    return false;
+  }
+
+  // Skip past the header line.
+  std::getline(stat, everything_else);
+
+  while (stat.rdstate() == std::ios_base::goodbit) {
+    stat >> port >> status;
+    std::getline(stat, everything_else);
+    if (status == kVHCIStatusPortFree) {
+      port_ = port;
+      LOG(INFO) << "Using VHCI port " << port_;
+      return true;
+    }
+  }
+  return false;
+}
+
+void VHCIInstrument::TriggerAttach() {
+  control_write_end_->Write(&kControlAttach, sizeof(kControlAttach));
+}
+
+void VHCIInstrument::TriggerDetach() {
+  control_write_end_->Write(&kControlDetach, sizeof(kControlDetach));
+}
+
+void VHCIInstrument::AttachThread() {
+  cvd::SharedFD epoll = cvd::SharedFD::Epoll();
+  // Trigger attach upon start.
+  bool want_attach = true;
+  // Operation is pending on read.
+  bool is_pending = false;
+
+  epoll_event control_event;
+  control_event.events = EPOLLIN;
+  control_event.data.u64 = kControlEvent;
+  epoll_event vhci_event;
+  vhci_event.events = EPOLLRDHUP | EPOLLONESHOT;
+  vhci_event.data.u64 = kVHCIEvent;
+
+  epoll->EpollCtl(EPOLL_CTL_ADD, control_read_end_, &control_event);
+  while (true) {
+    if (vhci_socket_->IsOpen()) {
+      epoll->EpollCtl(EPOLL_CTL_ADD, vhci_socket_, &vhci_event);
+    }
+
+    epoll_event found_event{};
+    ControlMsgType request_type;
+
+    if (epoll->EpollWait(&found_event, 1, 1000)) {
+      switch (found_event.data.u64) {
+        case kControlEvent:
+          control_read_end_->Read(&request_type, sizeof(request_type));
+          is_pending = true;
+          want_attach = request_type == kControlAttach;
+          LOG(INFO) << (want_attach ? "Attach" : "Detach") << " triggered.";
+          break;
+        case kVHCIEvent:
+          vhci_socket_ = cvd::SharedFD();
+          // Only re-establish VHCI if it was already established before.
+          is_pending = want_attach;
+          // Do not immediately fall into attach cycle. It will likely complete
+          // before VHCI finishes deregistering this callback.
+          continue;
+      }
+    }
+
+    // Make an attempt to re-attach. If successful, clear pending attach flag.
+    if (is_pending) {
+      if (want_attach && Attach()) {
+        is_pending = false;
+      } else if (!want_attach && Detach()) {
+        is_pending = false;
+      } else {
+        LOG(INFO) << (want_attach ? "Attach" : "Detach") << " unsuccessful. "
+                  << "Will re-try.";
+        sleep(1);
+      }
+    }
+  }
+}
+
+bool VHCIInstrument::Detach() {
+  std::stringstream result;
+  result << port_;
+  std::ofstream detach(syspath_ + "/detach");
+
+  if (!detach.is_open()) {
+    LOG(WARNING) << "Could not open VHCI detach file.";
+    return false;
+  }
+  detach << result.str();
+  return detach.rdstate() == std::ios_base::goodbit;
+}
+
+bool VHCIInstrument::Attach() {
+  if (!vhci_socket_->IsOpen()) {
+    vhci_socket_ =
+        cvd::SharedFD::SocketLocalClient(name_.c_str(), true, SOCK_STREAM);
+    if (!vhci_socket_->IsOpen()) return false;
+  }
+
+  int sys_fd = vhci_socket_->UNMANAGED_Dup();
+  bool success = false;
+
+  {
+    std::stringstream result;
+    result << port_ << ' ' << sys_fd << ' ' << kDefaultDeviceID << ' '
+           << kDefaultDeviceSpeed;
+    std::string path = syspath_ + "/attach";
+    std::ofstream attach(path);
+
+    if (!attach.is_open()) {
+      LOG(WARNING) << "Could not open VHCI attach file " << path << " ("
+                   << strerror(errno) << ")";
+      close(sys_fd);
+      return false;
+    }
+    attach << result.str();
+
+    // It is unclear whether duplicate FD should remain open or not. There are
+    // cases supporting both assumptions, likely related to kernel version.
+    // Kernel 4.10 is having problems communicating with USB/IP server if the
+    // socket is closed after it's passed to kernel. It is a clear indication that
+    // the kernel requires the socket to be kept open.
+    success = attach.rdstate() == std::ios_base::goodbit;
+    // Make sure everything was written and flushed. This happens when we close
+    // the ofstream attach.
+  }
+
+  close(sys_fd);
+  return success;
+}
+
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/usbip/vhci_instrument.h b/host/libs/usbip/vhci_instrument.h
new file mode 100644
index 0000000..ccb641e
--- /dev/null
+++ b/host/libs/usbip/vhci_instrument.h
@@ -0,0 +1,72 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <thread>
+
+#include "common/libs/fs/shared_fd.h"
+
+namespace vadb {
+namespace usbip {
+// VHCIInstrument class configures VHCI-HCD on local kernel.
+class VHCIInstrument {
+ public:
+  VHCIInstrument(const std::string& name);
+  virtual ~VHCIInstrument();
+
+  // Init opens vhci-hcd driver and allocates port to which remote USB device
+  // will be attached.
+  // Returns false, if vhci-hcd driver could not be opened, or if no free port
+  // was found.
+  bool Init();
+
+  // TriggerAttach tells underlying thread to make attempt to re-attach USB
+  // device.
+  void TriggerAttach();
+
+  // TriggerDetach tells underlying thread to disconnect remote USB device.
+  void TriggerDetach();
+
+ private:
+  // Attach makes an attempt to configure VHCI to enable virtual USB device.
+  // Returns true, if configuration attempt was successful.
+  bool Attach();
+
+  // Detach disconnects virtual USB device.
+  // Returns true, if attempt was successful.
+  bool Detach();
+
+  // AttachThread is a background thread that responds to configuration
+  // requests.
+  void AttachThread();
+  bool FindFreePort();
+
+ private:
+  std::string name_;
+  std::unique_ptr<std::thread> attach_thread_;
+  std::string syspath_;
+  cvd::SharedFD control_write_end_;
+  cvd::SharedFD control_read_end_;
+  cvd::SharedFD vhci_socket_;
+  int port_;
+
+  VHCIInstrument(const VHCIInstrument& other) = delete;
+  VHCIInstrument& operator=(const VHCIInstrument& other) = delete;
+};
+}  // namespace usbip
+}  // namespace vadb
diff --git a/host/libs/vadb/Android.bp b/host/libs/vadb/Android.bp
new file mode 100644
index 0000000..48971df
--- /dev/null
+++ b/host/libs/vadb/Android.bp
@@ -0,0 +1,24 @@
+cc_library_host_static {
+    name: "libvadb",
+    srcs: [
+        "usb_cmd_attach.cpp",
+        "usb_cmd_control_transfer.cpp",
+        "usb_cmd_data_transfer.cpp",
+        "usb_cmd_device_list.cpp",
+        "usb_cmd_heartbeat.cpp",
+        "virtual_adb_client.cpp",
+        "virtual_adb_server.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "cuttlefish_auto_resources",
+        "libbase",
+    ],
+    static_libs: [
+        "libgflags",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/libs/vadb/README.md b/host/libs/vadb/README.md
new file mode 100644
index 0000000..432fa2e
--- /dev/null
+++ b/host/libs/vadb/README.md
@@ -0,0 +1,35 @@
+# Virtual ADB
+
+VirtualADB serves the purpose of making Cuttlefish device available locally as a
+USB device. VirtualADB uses USB/IP protocol to forward USB gadget from
+Cuttlefish to localhost.
+
+## Requirements
+
+To compile the VirtualADB package you need to install:
+
+```
+sudo apt-get install libudev-dev
+```
+
+VirtualADB requires `vhci-hcd` kernel module to be loaded. Module is part of
+kernel extra modules package:
+
+```
+sudo apt-get install linux-image-extra-`uname -r`
+```
+
+## Usage
+
+VirtualADB uses currently `virtio channel` to communicate with usb forwarder on
+`cuttlefish`. The tool instruments kernel to attach remote USB device directly.
+To do that, it requires super-user privileges - primarily because it's adding a
+new device to your system.
+
+To start VirtualADB simply execute:
+
+```
+sudo vadb /path/to/usb_forwarder_socket
+```
+
+where `usb_forwarder_socket` is the socket used by usb forwarder to communicate.
diff --git a/host/libs/vadb/usb_cmd.h b/host/libs/vadb/usb_cmd.h
new file mode 100644
index 0000000..7039fbe
--- /dev/null
+++ b/host/libs/vadb/usb_cmd.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/usbforward/protocol.h"
+
+namespace vadb {
+// USBCommand is an abstraction of a proxied USB command.
+// Instances of this object all share the following life cycle:
+// 1) A specific instance (COMMAND) is being created.
+// 2) Instance owner (OWNER) sends RequestHeader.
+// 3) OWNER calls COMMAND.OnRequest() to send any relevant, additional
+//    information.
+// 4) OWNER queues COMMAND until response arrives.
+//
+// At this point instance owner can process next command in queue. Then,
+// eventually:
+//
+// 5) OWNER receives matching ResponseHeader.
+// 6) OWNER calls COMMAND.OnResponse(), supplying FD that carries additional
+//    data.
+// 7) OWNER dequeues and deletes COMMAND.
+class USBCommand {
+ public:
+  USBCommand() = default;
+  virtual ~USBCommand() = default;
+
+  // Command returns a specific usbforward command ID associated with this
+  // request.
+  virtual usb_forward::Command Command() = 0;
+
+  // OnRequest is called whenever additional data relevant to this command
+  // (other than RequestHeader) should be sent.
+  // Returns false, if communication with remote host failed (and should be
+  // terminated).
+  virtual bool OnRequest(const cvd::SharedFD& data) = 0;
+
+  // OnResponse is called whenever additional data relevant to this command
+  // (other than ResponseHeader) should be received.
+  // Returns false, if communication with remote host failed (and should be
+  // terminated).
+  virtual bool OnResponse(bool is_success, const cvd::SharedFD& data) = 0;
+
+ private:
+  USBCommand(const USBCommand& other) = delete;
+  USBCommand& operator=(const USBCommand& other) = delete;
+};
+
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_attach.cpp b/host/libs/vadb/usb_cmd_attach.cpp
new file mode 100644
index 0000000..02ade80
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_attach.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <glog/logging.h>
+
+#include "common/libs/usbforward/protocol.h"
+#include "host/libs/vadb/usb_cmd_attach.h"
+
+namespace vadb {
+bool USBCmdAttach::OnRequest(const cvd::SharedFD& fd) {
+  if (fd->Write(&req_, sizeof(req_)) != sizeof(req_)) {
+    LOG(ERROR) << "Short write: " << fd->StrError();
+    return false;
+  }
+  return true;
+}
+
+bool USBCmdAttach::OnResponse(bool is_success, const cvd::SharedFD& data) {
+  if (!is_success) return false;
+  LOG(INFO) << "Attach successful.";
+  return true;
+}
+
+USBCmdAttach::USBCmdAttach(uint8_t bus_id, uint8_t dev_id) {
+  req_.bus_id = bus_id;
+  req_.dev_id = dev_id;
+}
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_attach.h b/host/libs/vadb/usb_cmd_attach.h
new file mode 100644
index 0000000..9cdd89e
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_attach.h
@@ -0,0 +1,44 @@
+/*
+ * 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 "host/libs/vadb/usb_cmd.h"
+
+namespace vadb {
+// Request remote device attach (~open).
+class USBCmdAttach : public USBCommand {
+ public:
+  USBCmdAttach(uint8_t bus_id, uint8_t dev_id);
+  ~USBCmdAttach() override = default;
+
+  // Return usbforward command this instance is executing.
+  usb_forward::Command Command() override { return usb_forward::CmdAttach; }
+
+  // Send request body to the server.
+  // Return false, if communication failed.
+  bool OnRequest(const cvd::SharedFD& data) override;
+
+  // Receive response data from the server.
+  // Return false, if communication failed.
+  bool OnResponse(bool is_success, const cvd::SharedFD& data) override;
+
+ private:
+  usb_forward::AttachRequest req_;
+
+  USBCmdAttach(const USBCmdAttach& other) = delete;
+  USBCmdAttach& operator=(const USBCmdAttach& other) = delete;
+};
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_control_transfer.cpp b/host/libs/vadb/usb_cmd_control_transfer.cpp
new file mode 100644
index 0000000..643d416
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_control_transfer.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <glog/logging.h>
+
+#include "host/libs/vadb/usb_cmd_control_transfer.h"
+
+namespace vadb {
+USBCmdControlTransfer::USBCmdControlTransfer(
+    uint8_t bus_id, uint8_t dev_id, uint8_t type, uint8_t request,
+    uint16_t value, uint16_t index, uint32_t timeout, std::vector<uint8_t> data,
+    usbip::Device::AsyncTransferReadyCB callback)
+    : data_(std::move(data)), callback_(std::move(callback)) {
+  req_.bus_id = bus_id;
+  req_.dev_id = dev_id;
+  req_.type = type;
+  req_.cmd = request;
+  req_.value = value;
+  req_.index = index;
+  req_.length = data_.size();
+  req_.timeout = timeout;
+}
+
+bool USBCmdControlTransfer::OnRequest(const cvd::SharedFD& fd) {
+  if (fd->Write(&req_, sizeof(req_)) != sizeof(req_)) {
+    LOG(ERROR) << "Short write: " << fd->StrError();
+    return false;
+  }
+
+  if ((req_.type & 0x80) == 0) {
+    if (data_.size() > 0) {
+      if (fd->Write(data_.data(), data_.size()) != data_.size()) {
+        LOG(ERROR) << "Short write: " << fd->StrError();
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool USBCmdControlTransfer::OnResponse(bool is_success,
+                                       const cvd::SharedFD& fd) {
+  if (!is_success) {
+    callback_(false, std::move(data_));
+    return true;
+  }
+
+  if (req_.type & 0x80) {
+    int32_t len;
+    if (fd->Read(&len, sizeof(len)) != sizeof(len)) {
+      LOG(ERROR) << "Short read: " << fd->StrError();
+      callback_(false, std::move(data_));
+      return false;
+    }
+
+    if (len > 0) {
+      data_.resize(len);
+      if (fd->Read(data_.data(), len) != len) {
+        LOG(ERROR) << "Short read: " << fd->StrError();
+        callback_(false, std::move(data_));
+        return false;
+      }
+    }
+  }
+
+  callback_(true, std::move(data_));
+  return true;
+}
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_control_transfer.h b/host/libs/vadb/usb_cmd_control_transfer.h
new file mode 100644
index 0000000..74607d0
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_control_transfer.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include <stdint.h>
+
+#include "common/libs/usbforward/protocol.h"
+#include "host/libs/vadb/usb_cmd.h"
+#include "host/libs/usbip/device.h"
+
+namespace vadb {
+// Execute control transfer.
+class USBCmdControlTransfer : public USBCommand {
+ public:
+  USBCmdControlTransfer(uint8_t bus_id, uint8_t dev_id, uint8_t type,
+                        uint8_t request, uint16_t value, uint16_t index,
+                        uint32_t timeout, std::vector<uint8_t> data,
+                        usbip::Device::AsyncTransferReadyCB callback);
+
+  ~USBCmdControlTransfer() override = default;
+
+  // Return usbforward command this instance is executing.
+  usb_forward::Command Command() override {
+    return usb_forward::CmdControlTransfer;
+  }
+
+  // Send request body to the server.
+  // Return false, if communication failed.
+  bool OnRequest(const cvd::SharedFD& data) override;
+
+  // Receive response data from the server.
+  // Return false, if communication failed.
+  bool OnResponse(bool is_success, const cvd::SharedFD& data) override;
+
+ private:
+  usb_forward::ControlTransfer req_;
+  std::vector<uint8_t> data_;
+  usbip::Device::AsyncTransferReadyCB callback_;
+
+  USBCmdControlTransfer(const USBCmdControlTransfer& other) = delete;
+  USBCmdControlTransfer& operator=(const USBCmdControlTransfer& other) = delete;
+};
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_data_transfer.cpp b/host/libs/vadb/usb_cmd_data_transfer.cpp
new file mode 100644
index 0000000..7503374
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_data_transfer.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <glog/logging.h>
+
+#include "host/libs/vadb/usb_cmd_data_transfer.h"
+
+namespace vadb {
+USBCmdDataTransfer::USBCmdDataTransfer(
+    uint8_t bus_id, uint8_t dev_id, uint8_t endpoint, bool is_host_to_device,
+    uint32_t deadline, std::vector<uint8_t> data,
+    usbip::Device::AsyncTransferReadyCB callback)
+    : data_(std::move(data)), callback_(std::move(callback)) {
+  req_.bus_id = bus_id;
+  req_.dev_id = dev_id;
+  req_.endpoint_id = endpoint;
+  req_.is_host_to_device = is_host_to_device;
+  req_.length = data_.size();
+  req_.timeout = deadline;
+}
+
+bool USBCmdDataTransfer::OnRequest(const cvd::SharedFD& fd) {
+  if (fd->Write(&req_, sizeof(req_)) != sizeof(req_)) {
+    LOG(ERROR) << "Short write: " << fd->StrError();
+    return false;
+  }
+
+  if (req_.is_host_to_device && data_.size() > 0) {
+    if (fd->Write(data_.data(), data_.size()) != data_.size()) {
+      LOG(ERROR) << "Short write: " << fd->StrError();
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool USBCmdDataTransfer::OnResponse(bool is_success, const cvd::SharedFD& fd) {
+  if (!is_success) {
+    callback_(false, std::move(data_));
+    return true;
+  }
+
+  if (!req_.is_host_to_device) {
+    int32_t len;
+    if (fd->Read(&len, sizeof(len)) != sizeof(len)) {
+      LOG(ERROR) << "Short read: " << fd->StrError();
+      callback_(false, std::move(data_));
+      return false;
+    }
+
+    if (len > 0) {
+      data_.resize(len);
+      int32_t got = 0;
+      // Virtio sends data in 32k packets. We may have to do a few reads.
+      while (got < len) {
+        auto packetsize = fd->Read(&data_[got], len - got);
+        got += packetsize;
+
+        if (fd->GetErrno() != 0) {
+          // This could, technically, also be a disconnect.
+          LOG(ERROR) << "Read failed: " << fd->StrError();
+          return false;
+        } else if (packetsize == 0) {
+          LOG(ERROR) << "Short read; remote end disconnected.";
+          return false;
+        }
+      }
+    }
+  }
+
+  callback_(true, std::move(data_));
+  return true;
+}
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_data_transfer.h b/host/libs/vadb/usb_cmd_data_transfer.h
new file mode 100644
index 0000000..8cba30f
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_data_transfer.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <memory>
+#include <stdint.h>
+
+#include "common/libs/usbforward/protocol.h"
+#include "host/libs/vadb/usb_cmd.h"
+#include "host/libs/usbip/device.h"
+
+namespace vadb {
+// Execute control transfer.
+class USBCmdDataTransfer : public USBCommand {
+ public:
+  USBCmdDataTransfer(uint8_t bus_id, uint8_t dev_id, uint8_t endpoint,
+                     bool is_host_to_device, uint32_t timeout,
+                     std::vector<uint8_t> data,
+                     usbip::Device::AsyncTransferReadyCB callback);
+
+  ~USBCmdDataTransfer() override = default;
+
+  // Return usbforward command this instance is executing.
+  usb_forward::Command Command() override {
+    return usb_forward::CmdDataTransfer;
+  }
+
+  // Send request body to the server.
+  // Return false, if communication failed.
+  bool OnRequest(const cvd::SharedFD& data) override;
+
+  // Receive response data from the server.
+  // Return false, if communication failed.
+  bool OnResponse(bool is_success, const cvd::SharedFD& data) override;
+
+ private:
+  usb_forward::DataTransfer req_;
+  std::vector<uint8_t> data_;
+  usbip::Device::AsyncTransferReadyCB callback_;
+
+  USBCmdDataTransfer(const USBCmdDataTransfer& other) = delete;
+  USBCmdDataTransfer& operator=(const USBCmdDataTransfer& other) = delete;
+};
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_device_list.cpp b/host/libs/vadb/usb_cmd_device_list.cpp
new file mode 100644
index 0000000..ce561de
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_device_list.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <glog/logging.h>
+
+#include "host/libs/vadb/usb_cmd_device_list.h"
+
+namespace vadb {
+bool USBCmdDeviceList::OnRequest(const cvd::SharedFD& data) {
+  LOG(INFO) << "Requesting device list from Cuttlefish...";
+  // No action required.
+  return true;
+}
+
+bool USBCmdDeviceList::OnResponse(bool is_success, const cvd::SharedFD& fd) {
+  // This should never happen. If this command fails, something is very wrong.
+  if (!is_success) return false;
+
+  int32_t count;
+  if (fd->Read(&count, sizeof(count)) != sizeof(count)) {
+    LOG(ERROR) << "Short read: " << fd->StrError();
+    return false;
+  }
+
+  LOG(INFO) << "Device list completed with " << count << " devices.";
+
+  while (count-- > 0) {
+    usb_forward::DeviceInfo dev;
+    std::vector<usb_forward::InterfaceInfo> ifaces;
+
+    if (fd->Read(&dev, sizeof(dev)) != sizeof(dev)) {
+      LOG(ERROR) << "Short read: " << fd->StrError();
+      return false;
+    }
+
+    ifaces.resize(dev.num_interfaces);
+    if (fd->Read(ifaces.data(),
+                 ifaces.size() * sizeof(usb_forward::InterfaceInfo)) !=
+        ifaces.size() * sizeof(usb_forward::InterfaceInfo)) {
+      LOG(ERROR) << "Short read: " << fd->StrError();
+      return false;
+    }
+
+    LOG(INFO) << "Found remote device 0x" << std::hex << dev.vendor_id << ":"
+              << dev.product_id;
+
+    on_device_discovered_(dev, ifaces);
+  }
+
+  return true;
+}
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_device_list.h b/host/libs/vadb/usb_cmd_device_list.h
new file mode 100644
index 0000000..2972ed2
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_device_list.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <functional>
+#include <vector>
+#include "common/libs/usbforward/protocol.h"
+#include "host/libs/vadb/usb_cmd.h"
+
+namespace vadb {
+// Request device list from remote host.
+class USBCmdDeviceList : public USBCommand {
+ public:
+  // DeviceDiscoveredCB is a callback function invoked for every new discovered
+  // device.
+  using DeviceDiscoveredCB =
+      std::function<void(const usb_forward::DeviceInfo&,
+                         const std::vector<usb_forward::InterfaceInfo>&)>;
+
+  USBCmdDeviceList(DeviceDiscoveredCB cb)
+      : on_device_discovered_(std::move(cb)) {}
+
+  ~USBCmdDeviceList() override = default;
+
+  // Return usbforward command this instance is executing.
+  usb_forward::Command Command() override { return usb_forward::CmdDeviceList; }
+
+  // Send request body to the server.
+  // Return false, if communication failed.
+  bool OnRequest(const cvd::SharedFD& data) override;
+
+  // Receive response data from the server.
+  // Return false, if communication failed.
+  bool OnResponse(bool is_success, const cvd::SharedFD& data) override;
+
+ private:
+  DeviceDiscoveredCB on_device_discovered_;
+  USBCmdDeviceList(const USBCmdDeviceList& other) = delete;
+  USBCmdDeviceList& operator=(const USBCmdDeviceList& other) = delete;
+};
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_heartbeat.cpp b/host/libs/vadb/usb_cmd_heartbeat.cpp
new file mode 100644
index 0000000..59138bc
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_heartbeat.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <glog/logging.h>
+
+#include "common/libs/usbforward/protocol.h"
+#include "host/libs/vadb/usb_cmd_heartbeat.h"
+
+namespace vadb {
+bool USBCmdHeartbeat::OnRequest(const cvd::SharedFD& fd) { return true; }
+
+bool USBCmdHeartbeat::OnResponse(bool is_success, const cvd::SharedFD& data) {
+  callback_(is_success);
+  return true;
+}
+
+USBCmdHeartbeat::USBCmdHeartbeat(USBCmdHeartbeat::HeartbeatResultCB callback)
+    : callback_(callback) {}
+}  // namespace vadb
diff --git a/host/libs/vadb/usb_cmd_heartbeat.h b/host/libs/vadb/usb_cmd_heartbeat.h
new file mode 100644
index 0000000..e0bc20e
--- /dev/null
+++ b/host/libs/vadb/usb_cmd_heartbeat.h
@@ -0,0 +1,48 @@
+/*
+ * 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 "host/libs/vadb/usb_cmd.h"
+
+namespace vadb {
+// Request remote device attach (~open).
+class USBCmdHeartbeat : public USBCommand {
+ public:
+  // Heartbeat result callback receives a boolean argument indicating whether
+  // remote device is ready to be attached.
+  using HeartbeatResultCB = std::function<void(bool)>;
+
+  USBCmdHeartbeat(HeartbeatResultCB callback);
+  ~USBCmdHeartbeat() override = default;
+
+  // Return usbforward command this instance is executing.
+  usb_forward::Command Command() override { return usb_forward::CmdHeartbeat; }
+
+  // Send request body to the server.
+  // Return false, if communication failed.
+  bool OnRequest(const cvd::SharedFD& data) override;
+
+  // Receive response data from the server.
+  // Return false, if communication failed.
+  bool OnResponse(bool is_success, const cvd::SharedFD& data) override;
+
+ private:
+  HeartbeatResultCB callback_;
+
+  USBCmdHeartbeat(const USBCmdHeartbeat& other) = delete;
+  USBCmdHeartbeat& operator=(const USBCmdHeartbeat& other) = delete;
+};
+}  // namespace vadb
diff --git a/host/libs/vadb/virtual_adb_client.cpp b/host/libs/vadb/virtual_adb_client.cpp
new file mode 100644
index 0000000..71a5c73
--- /dev/null
+++ b/host/libs/vadb/virtual_adb_client.cpp
@@ -0,0 +1,234 @@
+/*
+ * 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 <algorithm>
+#include <memory>
+#include <gflags/gflags.h>
+
+#include "common/libs/fs/shared_select.h"
+#include "host/libs/vadb/usb_cmd_attach.h"
+#include "host/libs/vadb/usb_cmd_control_transfer.h"
+#include "host/libs/vadb/usb_cmd_data_transfer.h"
+#include "host/libs/vadb/usb_cmd_device_list.h"
+#include "host/libs/vadb/usb_cmd_heartbeat.h"
+#include "host/libs/vadb/virtual_adb_client.h"
+
+DEFINE_bool(debug_adb_client, false, "Turn on verbose logging in the virtual_adb_client.cpp");
+
+#define VLOG(X) if (FLAGS_debug_adb_client) LOG(VERBOSE)
+
+namespace vadb {
+namespace {
+constexpr int kHeartbeatTimeoutSeconds = 3;
+}  // namespace
+
+VirtualADBClient::VirtualADBClient(usbip::DevicePool* pool, cvd::SharedFD fd,
+                                   const std::string& usbip_socket_name)
+    : pool_{pool}, fd_{fd}, vhci_{usbip_socket_name} {
+  CHECK(vhci_.Init());
+  timer_ = cvd::SharedFD::TimerFD(CLOCK_MONOTONIC, 0);
+  SendHeartbeat();
+}
+
+void VirtualADBClient::RegisterDevice(
+    const usb_forward::DeviceInfo& dev,
+    const std::vector<usb_forward::InterfaceInfo>& ifaces) {
+  auto d = std::unique_ptr<usbip::Device>(new usbip::Device);
+  d->vendor_id = dev.vendor_id;
+  d->product_id = dev.product_id;
+  d->dev_version = dev.dev_version;
+  d->dev_class = dev.dev_class;
+  d->dev_subclass = dev.dev_subclass;
+  d->dev_protocol = dev.dev_protocol;
+  d->speed = dev.speed;
+  d->configurations_count = dev.num_configurations;
+  d->configuration_number = dev.cur_configuration;
+
+  for (const auto& iface : ifaces) {
+    d->interfaces.push_back(usbip::Device::Interface{
+        iface.if_class, iface.if_subclass, iface.if_protocol});
+  }
+
+  uint8_t bus_id = dev.bus_id;
+  uint8_t dev_id = dev.dev_id;
+
+  d->handle_attach = [this, bus_id, dev_id]() -> bool {
+    return HandleAttach(bus_id, dev_id);
+  };
+
+  d->handle_control_transfer =
+      [this, bus_id, dev_id](
+          const usbip::CmdRequest& r, uint32_t deadline,
+          std::vector<uint8_t> data,
+          usbip::Device::AsyncTransferReadyCB callback) -> bool {
+    return HandleDeviceControlRequest(bus_id, dev_id, r, deadline,
+                                      std::move(data), std::move(callback));
+  };
+
+  d->handle_data_transfer =
+      [this, bus_id, dev_id](
+          uint8_t endpoint, bool is_host_to_device, uint32_t deadline,
+          std::vector<uint8_t> data,
+          usbip::Device::AsyncTransferReadyCB callback) -> bool {
+    return HandleDeviceDataRequest(bus_id, dev_id, endpoint, is_host_to_device,
+                                   deadline, std::move(data),
+                                   std::move(callback));
+  };
+
+  pool_->AddDevice(usbip::DevicePool::BusDevNumber{bus_id, dev_id},
+                   std::move(d));
+
+  // Attach this device.
+  HandleAttach(bus_id, dev_id);
+}
+
+bool VirtualADBClient::PopulateRemoteDevices() {
+  return ExecuteCommand(std::unique_ptr<USBCommand>(new USBCmdDeviceList(
+      [this](const usb_forward::DeviceInfo& info,
+             const std::vector<usb_forward::InterfaceInfo>& ifaces) {
+        RegisterDevice(info, ifaces);
+      })));
+}
+
+bool VirtualADBClient::HandleDeviceControlRequest(
+    uint8_t bus_id, uint8_t dev_id, const usbip::CmdRequest& r,
+    uint32_t timeout, std::vector<uint8_t> data,
+    usbip::Device::AsyncTransferReadyCB callback) {
+  return ExecuteCommand(std::unique_ptr<USBCommand>(new USBCmdControlTransfer(
+      bus_id, dev_id, r.type, r.cmd, r.value, r.index, timeout, std::move(data),
+      std::move(callback))));
+}
+
+bool VirtualADBClient::HandleDeviceDataRequest(
+    uint8_t bus_id, uint8_t dev_id, uint8_t endpoint, bool is_host_to_device,
+    uint32_t deadline, std::vector<uint8_t> data,
+    usbip::Device::AsyncTransferReadyCB callback) {
+  return ExecuteCommand(std::unique_ptr<USBCommand>(
+      new USBCmdDataTransfer(bus_id, dev_id, endpoint, is_host_to_device,
+                             deadline, std::move(data), std::move(callback))));
+}
+
+bool VirtualADBClient::HandleAttach(uint8_t bus_id, uint8_t dev_id) {
+  return ExecuteCommand(
+      std::unique_ptr<USBCommand>(new USBCmdAttach(bus_id, dev_id)));
+}
+
+bool VirtualADBClient::SendHeartbeat() {
+  VLOG(1) << "Sending heartbeat...";
+  struct itimerspec spec {};
+  spec.it_value.tv_sec = kHeartbeatTimeoutSeconds;
+  timer_->TimerSet(0, &spec, nullptr);
+
+  heartbeat_tag_ = tag_;
+
+  return ExecuteCommand(std::unique_ptr<USBCommand>(
+      new USBCmdHeartbeat([this](bool success) { HandleHeartbeat(success); })));
+}
+
+void VirtualADBClient::HandleHeartbeat(bool is_ready) {
+  VLOG(1) << "Remote server status: " << is_ready;
+  if (is_ready && !is_remote_server_ready_) {
+    LOG(INFO) << "Remote server is now ready.";
+    PopulateRemoteDevices();
+    vhci_.TriggerAttach();
+  } else if (is_remote_server_ready_ && !is_ready) {
+    vhci_.TriggerDetach();
+    LOG(WARNING) << "Remote server connection lost.";
+    // It makes perfect sense to cancel all outstanding USB requests, as device
+    // is not going to answer any of these anyway.
+    for (const auto& pair : commands_) {
+      pair.second->OnResponse(false, fd_);
+    }
+    commands_.clear();
+  }
+  is_remote_server_ready_ = is_ready;
+}
+
+bool VirtualADBClient::HandleHeartbeatTimeout() {
+  uint64_t timer_result;
+  timer_->Read(&timer_result, sizeof(timer_result));
+
+  auto iter = commands_.find(heartbeat_tag_);
+  if (iter != commands_.end()) {
+    // Make sure to erase the value from list of commands prior to running
+    // callback. Particularly important for heartbeat, which cancels all
+    // outstanding USB commands (including self, if found), if device goes
+    // away (eg. reboots).
+    auto command = std::move(iter->second);
+    commands_.erase(iter);
+    command->OnResponse(false, fd_);
+  }
+
+  return SendHeartbeat();
+}
+
+bool VirtualADBClient::ExecuteCommand(std::unique_ptr<USBCommand> cmd) {
+  uint32_t this_tag = tag_;
+  tag_++;
+  usb_forward::RequestHeader hdr{cmd->Command(), this_tag};
+  if (fd_->Write(&hdr, sizeof(hdr)) != sizeof(hdr)) {
+    LOG(ERROR) << "Could not contact USB Forwarder: " << fd_->StrError();
+    return false;
+  }
+
+  if (!cmd->OnRequest(fd_)) return false;
+
+  commands_[this_tag] = std::move(cmd);
+  return true;
+}
+
+// BeforeSelect is Called right before Select() to populate interesting
+// SharedFDs.
+void VirtualADBClient::BeforeSelect(cvd::SharedFDSet* fd_read) const {
+  fd_read->Set(fd_);
+  fd_read->Set(timer_);
+}
+
+// AfterSelect is Called right after Select() to detect and respond to changes
+// on affected SharedFDs.
+// Return value indicates whether this client is still valid.
+bool VirtualADBClient::AfterSelect(const cvd::SharedFDSet& fd_read) {
+  if (fd_read.IsSet(timer_)) {
+    HandleHeartbeatTimeout();
+  }
+  if (fd_read.IsSet(fd_)) {
+    usb_forward::ResponseHeader rhdr;
+    if (fd_->Read(&rhdr, sizeof(rhdr)) != sizeof(rhdr)) {
+      LOG(ERROR) << "Could not read from USB Forwarder: " << fd_->StrError();
+      // TODO(ender): it is very likely the connection has been dropped by QEmu.
+      // Should we cancel all pending commands now?
+      return false;
+    }
+
+    auto iter = commands_.find(rhdr.tag);
+    if (iter == commands_.end()) {
+      // This is likely a late heartbeat response, but could very well be any of
+      // the remaining commands.
+      LOG(INFO) << "Received response for discarded tag " << rhdr.tag;
+    } else {
+      // Make sure to erase the value from list of commands prior to running
+      // callback. Particularly important for heartbeat, which cancels all
+      // outstanding USB commands (including self, if found), if device goes
+      // away (eg. reboots).
+      auto command = std::move(iter->second);
+      commands_.erase(iter);
+      command->OnResponse(rhdr.status == usb_forward::StatusSuccess, fd_);
+    }
+  }
+
+  return true;
+}
+
+}  // namespace vadb
diff --git a/host/libs/vadb/virtual_adb_client.h b/host/libs/vadb/virtual_adb_client.h
new file mode 100644
index 0000000..d521c74
--- /dev/null
+++ b/host/libs/vadb/virtual_adb_client.h
@@ -0,0 +1,108 @@
+/*
+ * 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 <string>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+#include "common/libs/usbforward/protocol.h"
+#include "host/libs/vadb/usb_cmd.h"
+#include "host/libs/usbip/device.h"
+#include "host/libs/usbip/device_pool.h"
+#include "host/libs/usbip/messages.h"
+#include "host/libs/usbip/vhci_instrument.h"
+
+namespace vadb {
+// VirtualADBClient is a companion class for USBForwarder, running on
+// Cuttlefish. VirtualADBClient collects list of available USB devices from
+// Cuttlefish and makes them available to USB/IP.
+//
+// Purpose of this class is to connect to USBForwarder and make access to
+// remote USB devices possible with help of USB/IP protocol.
+class VirtualADBClient {
+ public:
+  VirtualADBClient(usbip::DevicePool* pool, cvd::SharedFD fd,
+                   const std::string& usbip_socket_name);
+
+  virtual ~VirtualADBClient() = default;
+
+  // Query remote server; populate available USB devices.
+  bool PopulateRemoteDevices();
+
+  // BeforeSelect is Called right before Select() to populate interesting
+  // SharedFDs.
+  void BeforeSelect(cvd::SharedFDSet* fd_read) const;
+
+  // AfterSelect is Called right after Select() to detect and respond to changes
+  // on affected SharedFDs.
+  // Return value indicates whether this client is still valid.
+  bool AfterSelect(const cvd::SharedFDSet& fd_read);
+
+ private:
+  // Register new device in a device pool.
+  void RegisterDevice(const usb_forward::DeviceInfo& dev,
+                      const std::vector<usb_forward::InterfaceInfo>& ifaces);
+
+  // Request attach remote USB device.
+  bool HandleAttach(uint8_t bus_id, uint8_t dev_id);
+
+  // Execute control request on remote device.
+  bool HandleDeviceControlRequest(uint8_t bus_id, uint8_t dev_id,
+                                  const usbip::CmdRequest& r, uint32_t deadline,
+                                  std::vector<uint8_t> data,
+                                  usbip::Device::AsyncTransferReadyCB callback);
+
+  // Execute data request on remote device.
+  bool HandleDeviceDataRequest(uint8_t bus_id, uint8_t dev_id, uint8_t endpoint,
+                               bool is_host_to_device, uint32_t deadline,
+                               std::vector<uint8_t> data,
+                               usbip::Device::AsyncTransferReadyCB callback);
+
+  // Send new heartbeat request and arm the heartbeat timer.
+  bool SendHeartbeat();
+
+  // Heartbeat handler receives response to heartbeat request.
+  // Supplied argument indicates, whether remote server is ready to export USB
+  // gadget.
+  void HandleHeartbeat(bool is_ready);
+
+  // Heartbeat timeout detects situation where heartbeat did not receive
+  // matching response. This could be a direct result of device reset.
+  bool HandleHeartbeatTimeout();
+
+  // ExecuteCommand creates command header and executes supplied USBCommand.
+  // If execution was successful, command will be stored internally until
+  // response arrives.
+  bool ExecuteCommand(std::unique_ptr<USBCommand> cmd);
+
+  usbip::DevicePool* pool_;
+  cvd::SharedFD fd_;
+  cvd::SharedFD timer_;
+  usbip::VHCIInstrument vhci_;
+  bool is_remote_server_ready_ = false;
+
+  uint32_t tag_ = 0;
+  // Assign an 'invalid' tag as previously sent heartbeat command. This will
+  // prevent heartbeat timeout handler from finding a command if none was sent.
+  uint32_t heartbeat_tag_ = ~0;
+  std::map<uint32_t, std::unique_ptr<USBCommand>> commands_;
+
+  VirtualADBClient(const VirtualADBClient& other) = delete;
+  VirtualADBClient& operator=(const VirtualADBClient& other) = delete;
+};
+
+}  // namespace vadb
diff --git a/host/libs/vadb/virtual_adb_server.cpp b/host/libs/vadb/virtual_adb_server.cpp
new file mode 100644
index 0000000..63283e1
--- /dev/null
+++ b/host/libs/vadb/virtual_adb_server.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/libs/vadb/virtual_adb_server.h"
+
+namespace vadb {
+
+bool VirtualADBServer::Init() {
+  LOG(INFO) << "Starting server socket: " << name_;
+
+  server_ =
+      cvd::SharedFD::SocketLocalServer(name_.c_str(), false, SOCK_STREAM, 0666);
+  if (!server_->IsOpen()) {
+    LOG(ERROR) << "Could not create socket: " << server_->StrError();
+    return false;
+  }
+  return true;
+}
+
+void VirtualADBServer::BeforeSelect(cvd::SharedFDSet* fd_read) const {
+  fd_read->Set(server_);
+  for (const auto& client : clients_) client.BeforeSelect(fd_read);
+}
+
+void VirtualADBServer::AfterSelect(const cvd::SharedFDSet& fd_read) {
+  if (fd_read.IsSet(server_)) HandleIncomingConnection();
+
+  for (auto iter = clients_.begin(); iter != clients_.end();) {
+    if (!iter->AfterSelect(fd_read)) {
+      // If client conversation failed, hang up.
+      iter = clients_.erase(iter);
+      continue;
+    }
+    ++iter;
+  }
+}
+
+// Accept new QEmu connection. Add it to client pool.
+// Typically we will have no more than one QEmu connection, but the nature
+// of server requires proper handling nonetheless.
+void VirtualADBServer::HandleIncomingConnection() {
+  cvd::SharedFD client = cvd::SharedFD::Accept(*server_, nullptr, nullptr);
+  if (!client->IsOpen()) {
+    LOG(ERROR) << "Client connection failed: " << client->StrError();
+    return;
+  }
+
+  clients_.emplace_back(&pool_, client, usbip_name_);
+}
+
+}  // namespace vadb
diff --git a/host/libs/vadb/virtual_adb_server.h b/host/libs/vadb/virtual_adb_server.h
new file mode 100644
index 0000000..d679a5d
--- /dev/null
+++ b/host/libs/vadb/virtual_adb_server.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/usbip/device_pool.h"
+#include "host/libs/vadb/virtual_adb_client.h"
+
+namespace vadb {
+// VirtualADBServer manages incoming VirtualUSB/ADB connections from QEmu.
+class VirtualADBServer {
+ public:
+  VirtualADBServer(const std::string& usb_socket_name,
+                   const std::string& usbip_socket_name)
+      : name_(usb_socket_name), usbip_name_(usbip_socket_name) {}
+
+  ~VirtualADBServer() = default;
+
+  // Initialize this instance of Server.
+  // Returns true, if initialization was successful.
+  bool Init();
+
+  // Pool of USB devices available to export.
+  const usbip::DevicePool& Pool() const { return pool_; };
+
+  // BeforeSelect is Called right before Select() to populate interesting
+  // SharedFDs.
+  void BeforeSelect(cvd::SharedFDSet* fd_read) const;
+
+  // AfterSelect is Called right after Select() to detect and respond to changes
+  // on affected SharedFDs.
+  void AfterSelect(const cvd::SharedFDSet& fd_read);
+
+ private:
+  void HandleIncomingConnection();
+
+  usbip::DevicePool pool_;
+  std::string name_;
+  std::string usbip_name_;
+  cvd::SharedFD server_;
+  std::list<VirtualADBClient> clients_;
+
+  VirtualADBServer(const VirtualADBServer&) = delete;
+  VirtualADBServer& operator=(const VirtualADBServer&) = delete;
+};
+
+}  // namespace vadb
diff --git a/host/vsoc/lib/gralloc_buffer_region_view.cpp b/host/vsoc/lib/gralloc_buffer_region_view.cpp
new file mode 100644
index 0000000..c2a8d87
--- /dev/null
+++ b/host/vsoc/lib/gralloc_buffer_region_view.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/vsoc/lib/gralloc_buffer_region_view.h"
+
+#include <mutex>
+#include "glog/logging.h"
+
+using vsoc::gralloc::GrallocBufferRegionView;
+
+// static
+GrallocBufferRegionView* GrallocBufferRegionView::GetInstance(const char* domain) {
+  static std::mutex mutex;
+  static std::map<std::string, GrallocBufferRegionView*> gralloc_regions;
+
+  std::lock_guard<std::mutex> guard(mutex);
+  if (!gralloc_regions.count(domain)) {
+    gralloc_regions[domain] = new GrallocBufferRegionView(domain);
+  }
+  return gralloc_regions[domain];
+}
+
+uint8_t* GrallocBufferRegionView::OffsetToBufferPtr(vsoc_reg_off_t offset) {
+  if (offset <= control_->region_desc().offset_of_region_data ||
+      offset >= control_->region_size()) {
+    LOG(FATAL)
+        << "Attempted to access a gralloc buffer outside region data, offset: "
+        << offset;
+    return nullptr;
+  }
+  return region_offset_to_pointer<uint8_t>(offset);
+}
+
+GrallocBufferRegionView::GrallocBufferRegionView(const char* domain) {
+  is_open_ = Open(domain);
+}
diff --git a/host/vsoc/lib/gralloc_buffer_region_view.h b/host/vsoc/lib/gralloc_buffer_region_view.h
new file mode 100644
index 0000000..f2d10b4
--- /dev/null
+++ b/host/vsoc/lib/gralloc_buffer_region_view.h
@@ -0,0 +1,46 @@
+#pragma once
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/gralloc_layout.h"
+
+#include <string>
+
+namespace vsoc {
+namespace gralloc {
+
+// Allows access to the gralloc buffer region from host side. It needs to be a
+// different class than the one on guest side because of the required
+// interactions with the kernel on the guest.
+// Initially this class only returns a pointer to a buffer in memory given a
+// region offset, which is enough for now since it's only used by the hwcomposer
+// (which gets all other information from the guest side hwcomposer) and by the
+// VNC server (which uses only the frame buffer and gets the information it
+// needs from the framebuffer region).
+class GrallocBufferRegionView
+    : vsoc::TypedRegionView<vsoc::layout::gralloc::GrallocBufferLayout> {
+ public:
+  static GrallocBufferRegionView* GetInstance(const char* domain);
+
+  uint8_t* OffsetToBufferPtr(vsoc_reg_off_t offset);
+ protected:
+  GrallocBufferRegionView(const char* domain);
+  bool is_open_{};
+};
+
+}
+}
diff --git a/host/vsoc/lib/host_lock.cpp b/host/vsoc/lib/host_lock.cpp
new file mode 100644
index 0000000..44c20b5
--- /dev/null
+++ b/host/vsoc/lib/host_lock.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/shm/lock.h"
+
+#include "sys/types.h"
+
+#include "common/vsoc/lib/compat.h"
+#include "common/vsoc/lib/single_sided_signal.h"
+
+namespace vsoc {
+namespace layout {
+
+void HostLock::Lock() {
+  uint32_t tid = gettid();
+  uint32_t expected_value;
+  uint32_t* uaddr = reinterpret_cast<uint32_t*>(&lock_uint32_);
+
+  while (1) {
+    if (TryLock(tid, &expected_value)) {
+      return;
+    }
+    SingleSidedSignal::AwaitSignal(expected_value, uaddr);
+  }
+}
+
+void HostLock::Unlock() {
+  Sides sides_to_signal = UnlockCommon(gettid());
+  if (sides_to_signal.value_ != Sides::NoSides) {
+    SingleSidedSignal::Signal(&lock_uint32_);
+  }
+}
+
+}  // namespace layout
+}  // namespace vsoc
diff --git a/host/vsoc/lib/host_region_e2e_test.cpp b/host/vsoc/lib/host_region_e2e_test.cpp
new file mode 100644
index 0000000..d4f9e29
--- /dev/null
+++ b/host/vsoc/lib/host_region_e2e_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * End-to-end test to ensure that mapping of vsoc regions works on the host.
+ */
+
+#include <gtest/gtest.h>
+#include "common/vsoc/lib/e2e_test_region_view.h"
+#include "host/libs/config/host_config.h"
+
+// Here is a summary of the two regions interrupt and write test:
+// 1. Write our strings to the first region
+// 2. Ensure that our peer hasn't signalled the second region. That would
+//    indicate that it didn't wait for our interrupt.
+// 3. Send the interrupt on the first region
+// 4. Wait for our peer's interrupt on the first region
+// 5. Confirm that we can see our peer's writes in the first region
+// 6. Initialize our strings in the second region
+// 7. Send an interrupt on the second region to our peer
+// 8. Wait for our peer's interrupt on the second region
+// 9. Confirm that we can see our peer's writes in the second region
+// 10. Repeat the process for signaling.
+// 11. Confirm that no interrupt is pending in the first region
+// 12. Confirm that no interrupt is pending in the second region
+
+template <typename View>
+void SetHostStrings(View* in) {
+  size_t num_data = in->string_size();
+  EXPECT_LE(2, num_data);
+  for (size_t i = 0; i < num_data; ++i) {
+    EXPECT_TRUE(!in->host_string(i)[0] ||
+                !strcmp(in->host_string(i), View::Layout::host_pattern));
+    in->set_host_string(i, View::Layout::host_pattern);
+    EXPECT_STREQ(in->host_string(i), View::Layout::host_pattern);
+  }
+}
+
+template <typename View>
+void CheckPeerStrings(View* in) {
+  size_t num_data = in->string_size();
+  EXPECT_LE(2, num_data);
+  for (size_t i = 0; i < num_data; ++i) {
+    EXPECT_STREQ(View::Layout::guest_pattern, in->guest_string(i));
+  }
+}
+
+TEST(RegionTest, PeerTests) {
+  vsoc::E2EPrimaryRegionView primary;
+  ASSERT_TRUE(primary.Open(vsoc::GetDomain().c_str()));
+  vsoc::E2ESecondaryRegionView secondary;
+  ASSERT_TRUE(secondary.Open(vsoc::GetDomain().c_str()));
+  LOG(INFO) << "Regions are open";
+  SetHostStrings(&primary);
+  EXPECT_FALSE(secondary.HasIncomingInterrupt());
+  EXPECT_TRUE(primary.MaybeInterruptPeer());
+  LOG(INFO) << "Waiting for first interrupt from peer";
+  primary.WaitForInterrupt();
+  LOG(INFO) << "First interrupt received";
+  CheckPeerStrings(&primary);
+  SetHostStrings(&secondary);
+  EXPECT_TRUE(secondary.MaybeInterruptPeer());
+  LOG(INFO) << "Waiting for second interrupt from peer";
+  secondary.WaitForInterrupt();
+  LOG(INFO) << "Second interrupt received";
+  CheckPeerStrings(&secondary);
+
+  // Test signals
+  EXPECT_FALSE(secondary.HasIncomingInterrupt());
+  LOG(INFO) << "Verified no early second signal";
+  vsoc::layout::Sides side;
+  side.value_ = vsoc::layout::Sides::Peer;
+  primary.SendSignal(side, &primary.data()->host_to_guest_signal);
+  LOG(INFO) << "Signal sent. Waiting for first signal from peer";
+  primary.WaitForInterrupt();
+  int count = 0; // counts the number of signals received.
+  primary.ProcessSignalsFromPeer([&primary, &count](uint32_t* uaddr){
+      ++count;
+      EXPECT_TRUE(uaddr == &primary.data()->guest_to_host_signal);
+    });
+  EXPECT_TRUE(count == 1);
+  LOG(INFO) << "Signal received on primary region";
+  secondary.SendSignal(side, &secondary.data()->host_to_guest_signal);
+  LOG(INFO) << "Signal sent. Waiting for second signal from peer";
+  secondary.WaitForInterrupt();
+  count = 0;
+  secondary.ProcessSignalsFromPeer([&secondary, &count](uint32_t* uaddr){
+      ++count;
+      EXPECT_TRUE(uaddr == &secondary.data()->guest_to_host_signal);
+    });
+  EXPECT_TRUE(count == 1);
+  LOG(INFO) << "Signal received on secondary region";
+
+  EXPECT_FALSE(primary.HasIncomingInterrupt());
+  EXPECT_FALSE(secondary.HasIncomingInterrupt());
+}
+
+TEST(RegionTest, MissingRegionCausesDeath) {
+  vsoc::E2EUnfindableRegionView test;
+  EXPECT_DEATH(test.Open(vsoc::GetDomain().c_str()), ".*");
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  int rval = RUN_ALL_TESTS();
+  if (!rval) {
+    vsoc::E2EPrimaryRegionView region;
+    region.Open(vsoc::GetDomain().c_str());
+    region.host_status(vsoc::layout::e2e_test::E2E_MEMORY_FILLED);
+  }
+  return rval;
+}
diff --git a/host/vsoc/lib/region_control.cpp b/host/vsoc/lib/region_control.cpp
new file mode 100644
index 0000000..1ad94a5
--- /dev/null
+++ b/host/vsoc/lib/region_control.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/lib/region_view.h"
+
+#define LOG_TAG "vsoc: region_host"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+
+using cvd::SharedFD;
+
+namespace {
+
+class HostRegionControl : public vsoc::RegionControl {
+ public:
+  HostRegionControl(const char* region_name,
+                    const SharedFD& incoming_interrupt_fd,
+                    const SharedFD& outgoing_interrupt_fd,
+                    const SharedFD& shared_memory_fd)
+      : region_name_{region_name},
+        incoming_interrupt_fd_{incoming_interrupt_fd},
+        outgoing_interrupt_fd_{outgoing_interrupt_fd},
+        shared_memory_fd_{shared_memory_fd} {}
+
+  int CreateFdScopedPermission(const char* managed_region_name,
+                               vsoc_reg_off_t owner_offset, uint32_t owned_val,
+                               vsoc_reg_off_t begin_offset,
+                               vsoc_reg_off_t end_offset) override {
+    return -1;
+  }
+
+  bool InitializeRegion();
+
+  virtual bool InterruptPeer() override {
+    uint64_t one = 1;
+    ssize_t rval = outgoing_interrupt_fd_->Write(&one, sizeof(one));
+    if (rval != sizeof(one)) {
+      LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))";
+      return false;
+    }
+    return true;
+  }
+
+  // Wake the local signal table scanner. Primarily used during shutdown
+  virtual void InterruptSelf() override {
+    uint64_t one = 1;
+    ssize_t rval = incoming_interrupt_fd_->Write(&one, sizeof(one));
+    if (rval != sizeof(one)) {
+      LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))";
+    }
+  }
+
+  virtual void WaitForInterrupt() override {
+    // Check then act isn't a problem here: the other side does
+    // the following things in exactly this order:
+    //   1. exchanges 1 with interrupt_signalled
+    //   2. if interrupt_signalled was 0 it increments the eventfd
+    // eventfd increments are persistent, so if interrupt_signalled was set
+    // back to 1 while we are going to sleep the sleep will return
+    // immediately.
+    uint64_t missed{};
+    cvd::SharedFDSet readset;
+    readset.Set(incoming_interrupt_fd_);
+    cvd::Select(&readset, NULL, NULL, NULL);
+    ssize_t rval = incoming_interrupt_fd_->Read(&missed, sizeof(missed));
+    if (rval != sizeof(missed)) {
+      LOG(FATAL) << __FUNCTION__ << ": rval (" << rval
+                 << ") != sizeof(missed))";
+    }
+    if (!missed) {
+      LOG(FATAL) << __FUNCTION__ << ": woke with 0 interrupts";
+    }
+  }
+
+  virtual void* Map() override {
+    if (region_base_) {
+      return region_base_;
+    }
+    // Now actually map the region
+    region_base_ =
+        shared_memory_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE,
+                                MAP_SHARED, region_desc_.region_begin_offset);
+    if (region_base_ == MAP_FAILED) {
+      LOG(FATAL) << "mmap failed for offset "
+                 << region_desc_.region_begin_offset << " ("
+                 << shared_memory_fd_->StrError() << ")";
+      region_base_ = nullptr;
+    }
+    return region_base_;
+  }
+
+ protected:
+  const char* region_name_{};
+  cvd::SharedFD incoming_interrupt_fd_;
+  cvd::SharedFD outgoing_interrupt_fd_;
+  cvd::SharedFD shared_memory_fd_;
+};
+
+// Default path to the ivshmem_server socket. This can vary when we're
+// launching multiple CVDs.
+constexpr int kMaxSupportedProtocolVersion = 0;
+
+bool HostRegionControl::InitializeRegion() {
+  size_t region_name_len = strlen(region_name_);
+  if (region_name_len >= sizeof(vsoc_device_name)) {
+    LOG(FATAL) << "Region name length (" << region_name_len << ") not < "
+               << sizeof(vsoc_device_name);
+    return false;
+  }
+  vsoc_shm_layout_descriptor layout;
+  ssize_t rval = shared_memory_fd_->Pread(&layout, sizeof(layout), 0);
+  if (rval != sizeof(layout)) {
+    LOG(FATAL) << "Unable to read layout, rval=" << rval << " ("
+               << shared_memory_fd_->StrError() << ")";
+    return false;
+  }
+  if (layout.major_version != CURRENT_VSOC_LAYOUT_MAJOR_VERSION) {
+    LOG(FATAL) << "Incompatible major version: saw " << layout.major_version
+               << " wanted " << CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
+  }
+  std::vector<vsoc_device_region> descriptors;
+  descriptors.resize(layout.region_count);
+  ssize_t wanted = sizeof(vsoc_device_region) * layout.region_count;
+  rval = shared_memory_fd_->Pread(descriptors.data(), wanted,
+                                  layout.vsoc_region_desc_offset);
+  if (rval != wanted) {
+    LOG(FATAL) << "Unable to read region descriptors, rval=" << rval << " ("
+               << shared_memory_fd_->StrError() << ")";
+    return false;
+  }
+  for (const auto& desc : descriptors) {
+    if (!strcmp(region_name_, desc.device_name)) {
+      region_desc_ = desc;
+      return true;
+    }
+  }
+
+  std::ostringstream buf;
+  for (const auto& desc : descriptors) {
+    buf << " " << desc.device_name;
+  }
+  LOG(FATAL) << "Region name of " << region_name_
+             << " not found among:" << buf.str();
+  return false;
+}
+}  // namespace
+
+std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open(
+    const char* region_name, const char* domain) {
+  AutoFreeBuffer msg;
+
+  SharedFD region_server =
+      SharedFD::SocketLocalClient(domain, false, SOCK_STREAM);
+  if (!region_server->IsOpen()) {
+    LOG(FATAL) << "Could not contact ivshmem_server ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+
+  // Check server protocol version.
+  uint32_t protocol_version;
+  ssize_t bytes = region_server->Recv(&protocol_version,
+                                      sizeof(protocol_version), MSG_NOSIGNAL);
+  if (bytes != sizeof(protocol_version)) {
+    LOG(FATAL) << "Failed to recv protocol version; res=" << bytes << " ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+
+  if (protocol_version > kMaxSupportedProtocolVersion) {
+    LOG(FATAL) << "Unsupported protocol version " << protocol_version
+               << "; max supported version is " << kMaxSupportedProtocolVersion;
+    return nullptr;
+  }
+
+  // Send requested region.
+  int16_t size = strlen(region_name);
+  bytes = region_server->Send(&size, sizeof(size), MSG_NOSIGNAL);
+  if (bytes != sizeof(size)) {
+    LOG(FATAL) << "Failed to send region name length; res=" << bytes << " ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+
+  bytes = region_server->Send(region_name, size, MSG_NOSIGNAL);
+  if (bytes != size) {
+    LOG(FATAL) << "Failed to send region name; res=" << bytes << " ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+
+  // Receive control sockets.
+  uint64_t control_data;
+  struct iovec iov {
+    &control_data, sizeof(control_data)
+  };
+  cvd::InbandMessageHeader hdr{};
+  hdr.msg_iov = &iov;
+  hdr.msg_iovlen = 1;
+  SharedFD fds[3];
+  bytes = region_server->RecvMsgAndFDs(hdr, 0, &fds);
+  if (bytes != sizeof(control_data)) {
+    LOG(FATAL) << "Failed to complete handshake; res=" << bytes << " ("
+               << region_server->StrError() << ")";
+    return nullptr;
+  }
+  HostRegionControl* rval =
+      new HostRegionControl(region_name, fds[0], fds[1], fds[2]);
+  if (!rval) {
+    return nullptr;
+  }
+  // Search for the region header
+  if (!rval->InitializeRegion()) {
+    // We already logged, so we can just bail out.
+    return nullptr;
+  }
+  return std::shared_ptr<RegionControl>(rval);
+}
diff --git a/host/vsoc/lib/region_view.cpp b/host/vsoc/lib/region_view.cpp
new file mode 100644
index 0000000..975b7cd
--- /dev/null
+++ b/host/vsoc/lib/region_view.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/vsoc/lib/region_view.h"
+#include "common/vsoc/lib/region_control.h"
+
+const vsoc_signal_table_layout& vsoc::RegionView::incoming_signal_table() {
+  return control_->region_desc().guest_to_host_signal_table;
+}
+
+// Returns a pointer to the table that will be used to post signals
+const vsoc_signal_table_layout& vsoc::RegionView::outgoing_signal_table() {
+  return control_->region_desc().host_to_guest_signal_table;
+}
diff --git a/tools/bazel.rc b/tools/bazel.rc
new file mode 100644
index 0000000..6b32331
--- /dev/null
+++ b/tools/bazel.rc
@@ -0,0 +1,2 @@
+test --client_env=CC=/usr/bin/clang-3.8 --color=yes --test_output all
+build --client_env=CC=/usr/bin/clang-3.8 --color=yes