Merge "Build shared library for batteryservice"
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index dd8104d..c380598 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -15,6 +15,7 @@
 cc_binary {
     name: "lshal",
     shared_libs: [
+        "libbase",
         "libutils",
         "libhidlbase",
         "android.hidl.manager@1.0",
diff --git a/cmds/lshal/lshal.cpp b/cmds/lshal/lshal.cpp
index bc8bf39..bc5eaf2 100644
--- a/cmds/lshal/lshal.cpp
+++ b/cmds/lshal/lshal.cpp
@@ -18,25 +18,69 @@
 #include <getopt.h>
 
 #include <map>
+#include <fstream>
 #include <iomanip>
 #include <iostream>
 #include <sstream>
+#include <regex>
 
+#include <android-base/parseint.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
 
-template <typename A, typename B, typename C, typename D>
+template <typename A, typename B, typename C, typename D, typename E>
 void printColumn(std::stringstream &stream,
-        const A &a, const B &b, const C &c, const D &d) {
+        const A &a, const B &b, const C &c, const D &d, const E &e) {
     using namespace ::std;
     stream << left
            << setw(70) << a << "\t"
            << setw(20) << b << "\t"
            << setw(10) << c << "\t"
            << setw(5)  << d << "\t"
+           << setw(0)  << e
            << endl;
 }
 
+std::string toHexString(uint64_t t) {
+    std::ostringstream os;
+    os << std::hex << std::setfill('0') << std::setw(16) << t;
+    return os.str();
+}
+
+::android::status_t getReferencedPids(
+        pid_t serverPid, std::map<uint64_t, std::string> *objects) {
+
+    std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
+    if (!ifs.is_open()) {
+        return ::android::PERMISSION_DENIED;
+    }
+
+    static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
+
+    std::string line;
+    std::smatch match;
+    while(getline(ifs, line)) {
+        if (!std::regex_search(line, match, prefix)) {
+            // the line doesn't start with the correct prefix
+            continue;
+        }
+        std::string ptrString = "0x" + match.str(2); // use number after c
+        uint64_t ptr;
+        if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
+            // Should not reach here, but just be tolerant.
+            std::cerr << "Could not parse number " << ptrString << std::endl;
+            continue;
+        }
+        const std::string proc = " proc ";
+        auto pos = line.rfind(proc);
+        if (pos != std::string::npos) {
+            (*objects)[ptr] += line.substr(pos + proc.size());
+        }
+    }
+    return ::android::OK;
+}
+
+
 int dump() {
     using namespace ::std;
     using namespace ::android::hardware;
@@ -51,7 +95,7 @@
 
     stream << "All services:" << endl;
     stream << left;
-    printColumn(stream, "Interface", "Instance", "Transport", "Ref");
+    printColumn(stream, "Interface", "Instance", "Transport", "Server", "Clients");
 
     for (const auto &pair : mapping) {
         const std::string &mode = pair.first;
@@ -63,12 +107,29 @@
         }
 
         auto ret = manager->debugDump([&](const auto &registered) {
+            // server pid, .ptr value of binder object, child pids
+            std::map<pid_t, std::map<uint64_t, std::string>> allPids;
+            for (const auto &info : registered) {
+                if (info.pid < 0) {
+                    continue;
+                }
+                pid_t serverPid = info.pid;
+                allPids[serverPid].clear();
+            }
+            for (auto &pair : allPids) {
+                pid_t serverPid = pair.first;
+                if (getReferencedPids(serverPid, &allPids[serverPid]) != ::android::OK) {
+                    std::cerr << "Warning: no information for PID " << serverPid
+                              << ", are you root?" << std::endl;
+                }
+            }
             for (const auto &info : registered) {
                 printColumn(stream,
                     info.interfaceName,
                     info.instanceName.empty() ? "N/A" : info.instanceName,
                     mode,
-                    info.refCount == 0 ? "N/A" : std::to_string(info.refCount - 1));
+                    info.pid < 0 ? "N/A" : std::to_string(info.pid),
+                    info.pid < 0 || info.ptr == 0 ? "" : allPids[info.pid][info.ptr]);
             }
         });
         if (!ret.isOk()) {
diff --git a/libs/vr/libbufferhub/Android.mk b/libs/vr/libbufferhub/Android.mk
index 467f69f..0877b0b 100644
--- a/libs/vr/libbufferhub/Android.mk
+++ b/libs/vr/libbufferhub/Android.mk
@@ -23,7 +23,6 @@
 	$(LOCAL_PATH)/include
 
 staticLibraries := \
-	libchrome \
 	libdvrcommon \
 	libpdx_default_transport \
 
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
index 146780e..e2413bd 100644
--- a/libs/vr/libbufferhub/buffer_hub_client.cpp
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -1,6 +1,6 @@
 #include <private/dvr/buffer_hub_client.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <poll.h>
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #include <utils/Trace.h>
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
index cb45dbe..0b9e0cc 100644
--- a/libs/vr/libbufferhub/bufferhub_tests.cpp
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -1,11 +1,19 @@
 #include <android/native_window.h>
-#include <base/posix/eintr_wrapper.h>
 #include <gtest/gtest.h>
 #include <private/dvr/buffer_hub_client.h>
 
 #include <mutex>
 #include <thread>
 
+#define RETRY_EINTR(fnc_call)                 \
+  ([&]() -> decltype(fnc_call) {              \
+    decltype(fnc_call) result;                \
+    do {                                      \
+      result = (fnc_call);                    \
+    } while (result == -1 && errno == EINTR); \
+    return result;                            \
+  })()
+
 using android::dvr::BufferProducer;
 using android::dvr::BufferConsumer;
 using android::pdx::LocalHandle;
@@ -32,27 +40,27 @@
 
   EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
   // Both consumers should be triggered.
-  EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
-  EXPECT_LT(0, HANDLE_EINTR(c->Poll(10)));
-  EXPECT_LT(0, HANDLE_EINTR(c2->Poll(10)));
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+  EXPECT_LT(0, RETRY_EINTR(c2->Poll(10)));
 
   uint64_t context;
   LocalHandle fence;
   EXPECT_LE(0, c->Acquire(&fence, &context));
   EXPECT_EQ(kContext, context);
-  EXPECT_GE(0, HANDLE_EINTR(c->Poll(0)));
+  EXPECT_GE(0, RETRY_EINTR(c->Poll(0)));
 
   EXPECT_LE(0, c2->Acquire(&fence, &context));
   EXPECT_EQ(kContext, context);
-  EXPECT_GE(0, HANDLE_EINTR(c2->Poll(0)));
+  EXPECT_GE(0, RETRY_EINTR(c2->Poll(0)));
 
   EXPECT_EQ(0, c->Release(LocalHandle()));
-  EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
   EXPECT_EQ(0, c2->Discard());
 
-  EXPECT_LE(0, HANDLE_EINTR(p->Poll(0)));
+  EXPECT_LE(0, RETRY_EINTR(p->Poll(0)));
   EXPECT_EQ(0, p->Gain(&fence));
-  EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
 }
 
 TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
@@ -69,7 +77,7 @@
 
   Metadata m = {1, 3};
   EXPECT_EQ(0, p->Post(LocalHandle(), m));
-  EXPECT_LE(0, HANDLE_EINTR(c->Poll(10)));
+  EXPECT_LE(0, RETRY_EINTR(c->Poll(10)));
 
   LocalHandle fence;
   Metadata m2 = {};
@@ -78,7 +86,7 @@
   EXPECT_EQ(m.field2, m2.field2);
 
   EXPECT_EQ(0, c->Release(LocalHandle()));
-  EXPECT_LT(0, HANDLE_EINTR(p->Poll(0)));
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(0)));
 }
 
 TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
@@ -95,7 +103,7 @@
 
   int64_t sequence = 3;
   EXPECT_NE(0, p->Post(LocalHandle(), sequence));
-  EXPECT_GE(0, HANDLE_EINTR(c->Poll(10)));
+  EXPECT_GE(0, RETRY_EINTR(c->Poll(10)));
 }
 
 TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_buffer.h b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
index f6c24d9..afed052 100644
--- a/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
@@ -4,8 +4,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <android/native_window.h>
-#include <base/logging.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <system/window.h>
 #include <ui/ANativeObjectBase.h>
 #include <utils/RefBase.h>
@@ -181,7 +180,7 @@
     ANativeWindowBuffer::stride = buffer_->stride();
     ANativeWindowBuffer::format = buffer_->format();
     ANativeWindowBuffer::usage = buffer_->usage();
-    CHECK(buffer_->slice_count() > index);
+    LOG_ALWAYS_FATAL_IF(buffer_->slice_count() <= index);
     handle = buffer_->slice(index)->handle();
   }
 
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
index 7d20049..4db2164 100644
--- a/libs/vr/libbufferhub/ion_buffer.cpp
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -1,6 +1,6 @@
 #include <private/dvr/ion_buffer.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #include <utils/Trace.h>
 
diff --git a/libs/vr/libbufferhubqueue/Android.mk b/libs/vr/libbufferhubqueue/Android.mk
index 46b83e7..3ed7ff2 100644
--- a/libs/vr/libbufferhubqueue/Android.mk
+++ b/libs/vr/libbufferhubqueue/Android.mk
@@ -25,7 +25,6 @@
 
 staticLibraries := \
 	libbufferhub \
-	libchrome \
 	libdvrcommon \
 	libpdx_default_transport \
 
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index 4fbfcf6..0576b21 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -1,6 +1,7 @@
 #include "include/private/dvr/buffer_hub_queue_client.h"
 
-#include <base/logging.h>
+#include <inttypes.h>
+#include <log/log.h>
 #include <sys/epoll.h>
 
 #include <array>
@@ -43,8 +44,8 @@
 void BufferHubQueue::Initialize() {
   int ret = epoll_fd_.Create();
   if (ret < 0) {
-    LOG(ERROR) << "BufferHubQueue::BufferHubQueue: Failed to create epoll fd:"
-               << strerror(-ret);
+    ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s",
+          strerror(-ret));
     return;
   }
 
@@ -53,8 +54,8 @@
                                     BufferHubQueue::kEpollQueueEventIndex)}};
   ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
   if (ret < 0) {
-    LOG(ERROR) << "Failed to register ConsumerQueue into epoll event: "
-               << strerror(-ret);
+    ALOGE("Failed to register ConsumerQueue into epoll event: %s",
+          strerror(-ret));
   }
 }
 
@@ -63,13 +64,13 @@
       InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>();
 
   if (!status) {
-    LOG(ERROR) << "Cannot create ConsumerQueue: " << status.GetErrorMessage();
+    ALOGE("Cannot create ConsumerQueue: %s", status.GetErrorMessage().c_str());
     return nullptr;
   }
 
   auto return_value = status.take();
 
-  VLOG(1) << "CreateConsumerQueue: meta_size_bytes=" << return_value.second;
+  ALOGD("CreateConsumerQueue: meta_size_bytes=%zu", return_value.second);
   return ConsumerQueue::Create(std::move(return_value.first),
                                return_value.second);
 }
@@ -81,12 +82,12 @@
     int ret = epoll_fd_.Wait(events.data(), events.size(), timeout);
 
     if (ret == 0) {
-      VLOG(1) << "Wait on epoll returns nothing before timeout.";
+      ALOGD("Wait on epoll returns nothing before timeout.");
       return false;
     }
 
     if (ret < 0 && ret != -EINTR) {
-      LOG(ERROR) << "Failed to wait for buffers:" << strerror(-ret);
+      ALOGE("Failed to wait for buffers: %s", strerror(-ret));
       return false;
     }
 
@@ -98,13 +99,13 @@
     for (int i = 0; i < num_events; i++) {
       int64_t index = static_cast<int64_t>(events[i].data.u64);
 
-      VLOG(1) << "New BufferHubQueue event " << i << ": index=" << index;
+      ALOGD("New BufferHubQueue event %d: index=%" PRId64, i, index);
 
       if (is_buffer_event_index(index) && (events[i].events & EPOLLIN)) {
         auto buffer = buffers_[index];
         ret = OnBufferReady(buffer);
         if (ret < 0) {
-          LOG(ERROR) << "Failed to set buffer ready:" << strerror(-ret);
+          ALOGE("Failed to set buffer ready: %s", strerror(-ret));
           continue;
         }
         Enqueue(buffer, index);
@@ -113,18 +114,18 @@
         // This maybe caused by producer replacing an exising buffer slot.
         // Currently the epoll FD is cleaned up when the replacement consumer
         // client is imported.
-        LOG(WARNING) << "Receives EPOLLHUP at slot: " << index;
+        ALOGW("Receives EPOLLHUP at slot: %" PRId64, index);
       } else if (is_queue_event_index(index) && (events[i].events & EPOLLIN)) {
         // Note that after buffer imports, if |count()| still returns 0, epoll
         // wait will be tried again to acquire the newly imported buffer.
         ret = OnBufferAllocated();
         if (ret < 0) {
-          LOG(ERROR) << "Failed to import buffer:" << strerror(-ret);
+          ALOGE("Failed to import buffer: %s", strerror(-ret));
           continue;
         }
       } else {
-        LOG(WARNING) << "Unknown event " << i << ": u64=" << index
-                     << ": events=" << events[i].events;
+        ALOGW("Unknown event %d: u64=%" PRId64 ": events=%" PRIu32, i, index,
+              events[i].events);
       }
     }
   }
@@ -137,8 +138,8 @@
   if (is_full()) {
     // TODO(jwcai) Move the check into Producer's AllocateBuffer and consumer's
     // import buffer.
-    LOG(ERROR) << "BufferHubQueue::AddBuffer queue is at maximum capacity: "
-               << capacity_;
+    ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu",
+          capacity_);
     return -E2BIG;
   }
 
@@ -152,9 +153,8 @@
   epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u64 = slot}};
   const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, buf->event_fd(), &event);
   if (ret < 0) {
-    LOG(ERROR)
-        << "BufferHubQueue::AddBuffer: Failed to add buffer to epoll set:"
-        << strerror(-ret);
+    ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s",
+          strerror(-ret));
     return ret;
   }
 
@@ -166,15 +166,16 @@
 int BufferHubQueue::DetachBuffer(size_t slot) {
   auto& buf = buffers_[slot];
   if (buf == nullptr) {
-    LOG(ERROR) << "BufferHubQueue::DetachBuffer: Invalid slot: " << slot;
+    ALOGE("BufferHubQueue::DetachBuffer: Invalid slot: %zu", slot);
     return -EINVAL;
   }
 
   const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, buf->event_fd(), nullptr);
   if (ret < 0) {
-    LOG(ERROR) << "BufferHubQueue::DetachBuffer: Failed to detach buffer from  "
-                  "epoll set:"
-               << strerror(-ret);
+    ALOGE(
+        "BufferHubQueue::DetachBuffer: Failed to detach buffer from epoll set: "
+        "%s",
+        strerror(-ret));
     return ret;
   }
 
@@ -186,7 +187,7 @@
 void BufferHubQueue::Enqueue(std::shared_ptr<BufferHubBuffer> buf,
                              size_t slot) {
   if (count() == capacity_) {
-    LOG(ERROR) << "Buffer queue is full!";
+    ALOGE("Buffer queue is full!");
     return;
   }
 
@@ -206,7 +207,7 @@
 std::shared_ptr<BufferHubBuffer> BufferHubQueue::Dequeue(int timeout,
                                                          size_t* slot,
                                                          void* meta) {
-  VLOG(1) << "Dequeue: count=" << count() << ", timeout=" << timeout;
+  ALOGD("Dequeue: count=%zu, timeout=%d", count(), timeout);
 
   if (count() == 0 && !WaitForBuffers(timeout))
     return nullptr;
@@ -224,7 +225,7 @@
   available_buffers_.PopFront();
 
   if (!buf) {
-    LOG(ERROR) << "Dequeue: Buffer to be dequeued is nullptr";
+    ALOGE("Dequeue: Buffer to be dequeued is nullptr");
     return nullptr;
   }
 
@@ -250,9 +251,8 @@
       meta_size_, usage_set_mask, usage_clear_mask, usage_deny_set_mask,
       usage_deny_clear_mask);
   if (!status) {
-    LOG(ERROR)
-        << "ProducerQueue::ProducerQueue: Failed to create producer queue: %s"
-        << status.GetErrorMessage();
+    ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s",
+          status.GetErrorMessage().c_str());
     Close(-status.error());
     return;
   }
@@ -261,13 +261,13 @@
 int ProducerQueue::AllocateBuffer(int width, int height, int format, int usage,
                                   size_t slice_count, size_t* out_slot) {
   if (out_slot == nullptr) {
-    LOG(ERROR) << "Parameter out_slot cannot be null.";
+    ALOGE("Parameter out_slot cannot be null.");
     return -EINVAL;
   }
 
   if (is_full()) {
-    LOG(ERROR) << "ProducerQueue::AllocateBuffer queue is at maximum capacity: "
-               << capacity();
+    ALOGE("ProducerQueue::AllocateBuffer queue is at maximum capacity: %zu",
+          capacity());
     return -E2BIG;
   }
 
@@ -277,21 +277,22 @@
       InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
           width, height, format, usage, slice_count, kBufferCount);
   if (!status) {
-    LOG(ERROR) << "ProducerQueue::AllocateBuffer failed to create producer "
-                  "buffer through BufferHub.";
+    ALOGE(
+        "ProducerQueue::AllocateBuffer failed to create producer buffer "
+        "through BufferHub.");
     return -status.error();
   }
 
   auto buffer_handle_slots = status.take();
-  CHECK_EQ(buffer_handle_slots.size(), kBufferCount)
-      << "BufferHubRPC::ProducerQueueAllocateBuffers should return one and "
-         "only one buffer handle.";
+  LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != kBufferCount,
+                      "BufferHubRPC::ProducerQueueAllocateBuffers should "
+                      "return one and only one buffer handle.");
 
   // We only allocate one buffer at a time.
   auto& buffer_handle = buffer_handle_slots[0].first;
   size_t buffer_slot = buffer_handle_slots[0].second;
-  VLOG(1) << "ProducerQueue::AllocateBuffer, new buffer, channel_handle: "
-          << buffer_handle.value();
+  ALOGD("ProducerQueue::AllocateBuffer, new buffer, channel_handle: %d",
+        buffer_handle.value());
 
   *out_slot = buffer_slot;
   return AddBuffer(BufferProducer::Import(std::move(buffer_handle)),
@@ -314,9 +315,10 @@
   Status<int> status =
       InvokeRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(slot);
   if (!status) {
-    LOG(ERROR) << "ProducerQueue::DetachBuffer failed to detach producer "
-                  "buffer through BufferHub, error: "
-               << status.GetErrorMessage();
+    ALOGE(
+        "ProducerQueue::DetachBuffer failed to detach producer buffer through "
+        "BufferHub, error: %s",
+        status.GetErrorMessage().c_str());
     return -status.error();
   }
 
@@ -344,9 +346,10 @@
   Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
       InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
   if (!status) {
-    LOG(ERROR) << "ConsumerQueue::ImportBuffers failed to import consumer "
-                  "buffer through BufferBub, error: "
-               << status.GetErrorMessage();
+    ALOGE(
+        "ConsumerQueue::ImportBuffers failed to import consumer buffer through "
+        "BufferBub, error: %s",
+        status.GetErrorMessage().c_str());
     return -status.error();
   }
 
@@ -355,15 +358,15 @@
 
   auto buffer_handle_slots = status.take();
   for (auto& buffer_handle_slot : buffer_handle_slots) {
-    VLOG(1) << "ConsumerQueue::ImportBuffers, new buffer, buffer_handle: "
-            << buffer_handle_slot.first.value();
+    ALOGD("ConsumerQueue::ImportBuffers, new buffer, buffer_handle: %d",
+          buffer_handle_slot.first.value());
 
     std::unique_ptr<BufferConsumer> buffer_consumer =
         BufferConsumer::Import(std::move(buffer_handle_slot.first));
     int ret = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second);
     if (ret < 0) {
-      LOG(ERROR) << "ConsumerQueue::ImportBuffers failed to add buffer, ret: "
-                 << strerror(-ret);
+      ALOGE("ConsumerQueue::ImportBuffers failed to add buffer, ret: %s",
+            strerror(-ret));
       last_error = ret;
       continue;
     } else {
@@ -384,9 +387,10 @@
                                                        size_t* slot, void* meta,
                                                        size_t meta_size) {
   if (meta_size != meta_size_) {
-    LOG(ERROR) << "metadata size (" << meta_size
-               << ") for the dequeuing buffer does not match metadata size ("
-               << meta_size_ << ") for the queue.";
+    ALOGE(
+        "metadata size (%zu) for the dequeuing buffer does not match metadata "
+        "size (%zu) for the queue.",
+        meta_size, meta_size_);
     return nullptr;
   }
   auto buf = BufferHubQueue::Dequeue(timeout, slot, meta);
@@ -402,11 +406,11 @@
 int ConsumerQueue::OnBufferAllocated() {
   const int ret = ImportBuffers();
   if (ret == 0) {
-    LOG(WARNING) << "No new buffer can be imported on buffer allocated event.";
+    ALOGW("No new buffer can be imported on buffer allocated event.");
   } else if (ret < 0) {
-    LOG(ERROR) << "Failed to import buffers on buffer allocated event.";
+    ALOGE("Failed to import buffers on buffer allocated event.");
   }
-  VLOG(1) << "Imported " << ret << " consumer buffers.";
+  ALOGD("Imported %d consumer buffers.", ret);
   return ret;
 }
 
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
index 3fc0600..a108042 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
@@ -1,5 +1,7 @@
 #include "include/private/dvr/buffer_hub_queue_core.h"
 
+#include <log/log.h>
+
 namespace android {
 namespace dvr {
 
@@ -14,9 +16,9 @@
 std::shared_ptr<BufferHubQueueCore> BufferHubQueueCore::Create(
     const std::shared_ptr<ProducerQueue>& producer) {
   if (producer->metadata_size() != sizeof(BufferMetadata)) {
-    LOG(ERROR)
-        << "BufferHubQueueCore::Create producer's metadata size is "
-        << "different than the size of BufferHubQueueCore::BufferMetadata";
+    ALOGE(
+        "BufferHubQueueCore::Create producer's metadata size is different than "
+        "the size of BufferHubQueueCore::BufferMetadata");
     return nullptr;
   }
 
@@ -39,18 +41,18 @@
   // bookkeeping.
   if (producer_->AllocateBuffer(width, height, format, usage, slice_count,
                                 &slot) < 0) {
-    LOG(ERROR) << "Failed to allocate new buffer in BufferHub.";
+    ALOGE("Failed to allocate new buffer in BufferHub.");
     return NO_MEMORY;
   }
 
   auto buffer_producer = producer_->GetBuffer(slot);
 
-  CHECK(buffer_producer != nullptr) << "Failed to get buffer producer at slot: "
-                                    << slot;
+  LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr,
+                      "Failed to get buffer producer at slot: %zu", slot);
 
   // Allocating a new buffer, |buffers_[slot]| should be in initial state.
-  CHECK(buffers_[slot].mGraphicBuffer == nullptr) << "AllocateBuffer: slot "
-                                                  << slot << " is not empty.";
+  LOG_ALWAYS_FATAL_IF(buffers_[slot].mGraphicBuffer != nullptr,
+                      "AllocateBuffer: slot %zu is not empty.", slot);
 
   // Create new GraphicBuffer based on the newly created |buffer_producer|. Here
   // we have to cast |buffer_handle_t| to |native_handle_t|, it's OK because
@@ -65,8 +67,8 @@
       const_cast<native_handle_t*>(buffer_producer->buffer()->handle()),
       false));
 
-  CHECK_EQ(NO_ERROR, graphic_buffer->initCheck())
-      << "Failed to init GraphicBuffer.";
+  LOG_ALWAYS_FATAL_IF(NO_ERROR != graphic_buffer->initCheck(),
+                      "Failed to init GraphicBuffer.");
   buffers_[slot].mBufferProducer = buffer_producer;
   buffers_[slot].mGraphicBuffer = graphic_buffer;
 
@@ -77,8 +79,8 @@
   // Detach the buffer producer via BufferHubRPC.
   int ret = producer_->DetachBuffer(slot);
   if (ret < 0) {
-    LOG(ERROR) << "BufferHubQueueCore::DetachBuffer failed through RPC, ret="
-               << strerror(-ret);
+    ALOGE("BufferHubQueueCore::DetachBuffer failed through RPC, ret=%s",
+          strerror(-ret));
     return ret;
   }
 
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
index 93d7307..752e8c4 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -1,5 +1,8 @@
 #include "include/private/dvr/buffer_hub_queue_producer.h"
 
+#include <inttypes.h>
+#include <log/log.h>
+
 namespace android {
 namespace dvr {
 
@@ -9,18 +12,17 @@
 
 status_t BufferHubQueueProducer::requestBuffer(int slot,
                                                sp<GraphicBuffer>* buf) {
-  VLOG(1) << "requestBuffer: slot=" << slot;;
+  ALOGD("requestBuffer: slot=%d", slot);
 
   std::unique_lock<std::mutex> lock(core_->mutex_);
 
   if (slot < 0 || slot >= req_buffer_count_) {
-    LOG(ERROR) << "requestBuffer: slot index " << slot << " out of range [0, "
-               << req_buffer_count_ << ")";
+    ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot,
+          req_buffer_count_);
     return BAD_VALUE;
   } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
-    LOG(ERROR) << "requestBuffer: slot " << slot
-               << " is not owned by the producer (state = "
-               << core_->buffers_[slot].mBufferState.string() << " )";
+    ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, core_->buffers_[slot].mBufferState.string());
     return BAD_VALUE;
   }
 
@@ -31,17 +33,16 @@
 
 status_t BufferHubQueueProducer::setMaxDequeuedBufferCount(
     int max_dequeued_buffers) {
-  VLOG(1) << "setMaxDequeuedBufferCount: max_dequeued_buffers="
-          << max_dequeued_buffers;
+  ALOGD("setMaxDequeuedBufferCount: max_dequeued_buffers=%d",
+        max_dequeued_buffers);
 
   std::unique_lock<std::mutex> lock(core_->mutex_);
 
   if (max_dequeued_buffers <= 0 ||
       max_dequeued_buffers >
           static_cast<int>(BufferHubQueue::kMaxQueueCapacity)) {
-    LOG(ERROR) << "setMaxDequeuedBufferCount: " << max_dequeued_buffers
-               << " out of range (0, " << BufferHubQueue::kMaxQueueCapacity
-               << "]";
+    ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]",
+          max_dequeued_buffers, BufferHubQueue::kMaxQueueCapacity);
     return BAD_VALUE;
   }
 
@@ -50,7 +51,7 @@
 }
 
 status_t BufferHubQueueProducer::setAsyncMode(bool /* async */) {
-  LOG(ERROR) << "BufferHubQueueProducer::setAsyncMode not implemented.";
+  ALOGE("BufferHubQueueProducer::setAsyncMode not implemented.");
   return INVALID_OPERATION;
 }
 
@@ -60,8 +61,8 @@
                                                PixelFormat format,
                                                uint32_t usage,
                                                FrameEventHistoryDelta* /* outTimestamps */) {
-  VLOG(1) << "dequeueBuffer: w=" << width << ", h=" << height
-          << " format=" << format << ", usage=" << usage;
+  ALOGD("dequeueBuffer: w=%u, h=%u, format=%d, usage=%u", width, height, format,
+        usage);
 
   status_t ret;
   std::unique_lock<std::mutex> lock(core_->mutex_);
@@ -94,13 +95,12 @@
 
     // Needs reallocation.
     // TODO(jwcai) Consider use VLOG instead if we find this log is not useful.
-    LOG(INFO) << "dequeueBuffer,: requested buffer (w=" << width
-              << ", h=" << height << ", format=" << format
-              << ") is different from the buffer returned at slot: " << slot
-              << " (w=" << buffer_producer->width()
-              << ", h=" << buffer_producer->height()
-              << ", format=" << buffer_producer->format()
-              << "). Need re-allocattion.";
+    ALOGI(
+        "dequeueBuffer: requested buffer (w=%u, h=%u, format=%d) is different "
+        "from the buffer returned at slot: %zu (w=%d, h=%d, format=%d). Need "
+        "re-allocattion.",
+        width, height, format, slot, buffer_producer->width(),
+        buffer_producer->height(), buffer_producer->format());
     // Mark the slot as reallocating, so that later we can set
     // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
     core_->buffers_[slot].mIsReallocating = true;
@@ -125,13 +125,13 @@
   // BufferHubQueue).
   // TODO(jwcai) Clean this up, make mBufferState compatible with BufferHub's
   // model.
-  CHECK(core_->buffers_[slot].mBufferState.isFree() ||
-        core_->buffers_[slot].mBufferState.isQueued())
-      << "dequeueBuffer: slot " << slot << " is not free or queued.";
+  LOG_ALWAYS_FATAL_IF(!core_->buffers_[slot].mBufferState.isFree() &&
+                          !core_->buffers_[slot].mBufferState.isQueued(),
+                      "dequeueBuffer: slot %zu is not free or queued.", slot);
 
   core_->buffers_[slot].mBufferState.freeQueued();
   core_->buffers_[slot].mBufferState.dequeue();
-  VLOG(1) << "dequeueBuffer: slot=" << slot;
+  ALOGD("dequeueBuffer: slot=%zu", slot);
 
   // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
   // just need to exopose that through |BufferHubQueue| once we need fence.
@@ -148,13 +148,13 @@
 }
 
 status_t BufferHubQueueProducer::detachBuffer(int /* slot */) {
-  LOG(ERROR) << "BufferHubQueueProducer::detachBuffer not implemented.";
+  ALOGE("BufferHubQueueProducer::detachBuffer not implemented.");
   return INVALID_OPERATION;
 }
 
 status_t BufferHubQueueProducer::detachNextBuffer(
     sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */) {
-  LOG(ERROR) << "BufferHubQueueProducer::detachNextBuffer not implemented.";
+  ALOGE("BufferHubQueueProducer::detachNextBuffer not implemented.");
   return INVALID_OPERATION;
 }
 
@@ -163,14 +163,14 @@
   // With this BufferHub backed implementation, we assume (for now) all buffers
   // are allocated and owned by the BufferHub. Thus the attempt of transfering
   // ownership of a buffer to the buffer queue is intentionally unsupported.
-  LOG(FATAL) << "BufferHubQueueProducer::attachBuffer not supported.";
+  LOG_ALWAYS_FATAL("BufferHubQueueProducer::attachBuffer not supported.");
   return INVALID_OPERATION;
 }
 
 status_t BufferHubQueueProducer::queueBuffer(int slot,
                                              const QueueBufferInput& input,
                                              QueueBufferOutput* /* output */) {
-  VLOG(1) << "queueBuffer: slot " << slot;
+  ALOGD("queueBuffer: slot %d", slot);
 
   int64_t timestamp;
   sp<Fence> fence;
@@ -186,7 +186,7 @@
                 &scaling_mode, &transform, &fence);
 
   if (fence == nullptr) {
-    LOG(ERROR) << "queueBuffer: fence is NULL";
+    ALOGE("queueBuffer: fence is NULL");
     return BAD_VALUE;
   }
 
@@ -194,13 +194,12 @@
   std::unique_lock<std::mutex> lock(core_->mutex_);
 
   if (slot < 0 || slot >= req_buffer_count_) {
-    LOG(ERROR) << "queueBuffer: slot index " << slot << " out of range [0, "
-               << req_buffer_count_ << ")";
+    ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot,
+          req_buffer_count_);
     return BAD_VALUE;
   } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
-    LOG(ERROR) << "queueBuffer: slot " << slot
-               << " is not owned by the producer (state = "
-               << core_->buffers_[slot].mBufferState.string() << " )";
+    ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, core_->buffers_[slot].mBufferState.string());
     return BAD_VALUE;
   }
 
@@ -218,21 +217,20 @@
 
 status_t BufferHubQueueProducer::cancelBuffer(int slot,
                                               const sp<Fence>& fence) {
-  VLOG(1) << (__FUNCTION__);
+  ALOGD(__FUNCTION__);
 
   std::unique_lock<std::mutex> lock(core_->mutex_);
 
   if (slot < 0 || slot >= req_buffer_count_) {
-    LOG(ERROR) << "cancelBuffer: slot index " << slot << " out of range [0, "
-               << req_buffer_count_ << ")";
+    ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
+          req_buffer_count_);
     return BAD_VALUE;
   } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
-    LOG(ERROR) << "cancelBuffer: slot " << slot
-               << " is not owned by the producer (state = "
-               << core_->buffers_[slot].mBufferState.string() << " )";
+    ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, core_->buffers_[slot].mBufferState.string());
     return BAD_VALUE;
   } else if (fence == NULL) {
-    LOG(ERROR) << "cancelBuffer: fence is NULL";
+    ALOGE("cancelBuffer: fence is NULL");
     return BAD_VALUE;
   }
 
@@ -240,18 +238,18 @@
   core_->producer_->Enqueue(buffer_producer, slot);
   core_->buffers_[slot].mBufferState.cancel();
   core_->buffers_[slot].mFence = fence;
-  VLOG(1) << "cancelBuffer: slot " << slot;
+  ALOGD("cancelBuffer: slot %d", slot);
 
   return NO_ERROR;
 }
 
 status_t BufferHubQueueProducer::query(int what, int* out_value) {
-  VLOG(1) << (__FUNCTION__);
+  ALOGD(__FUNCTION__);
 
   std::unique_lock<std::mutex> lock(core_->mutex_);
 
   if (out_value == NULL) {
-    LOG(ERROR) << "query: out_value was NULL";
+    ALOGE("query: out_value was NULL");
     return BAD_VALUE;
   }
 
@@ -277,7 +275,7 @@
       return BAD_VALUE;
   }
 
-  VLOG(1) << "query: key=" << what << ", v=" << value;
+  ALOGD("query: key=%d, v=%d", what, value);
   *out_value = value;
   return NO_ERROR;
 }
@@ -287,14 +285,14 @@
     bool /* producer_controlled_by_app */, QueueBufferOutput* /* output */) {
   // Consumer interaction are actually handled by buffer hub, and we need
   // to maintain consumer operations here. Hence |connect| is a NO-OP.
-  VLOG(1) << (__FUNCTION__);
+  ALOGD(__FUNCTION__);
   return NO_ERROR;
 }
 
 status_t BufferHubQueueProducer::disconnect(int /* api */, DisconnectMode /* mode */) {
   // Consumer interaction are actually handled by buffer hub, and we need
   // to maintain consumer operations here. Hence |disconnect| is a NO-OP.
-  VLOG(1) << (__FUNCTION__);
+  ALOGD(__FUNCTION__);
   return NO_ERROR;
 }
 
@@ -303,7 +301,7 @@
   if (stream != NULL) {
     // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
     // metadata.
-    LOG(ERROR) << "SidebandStream is not currently supported.";
+    ALOGE("SidebandStream is not currently supported.");
     return INVALID_OPERATION;
   }
   return NO_ERROR;
@@ -316,17 +314,17 @@
   // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
   // of buffers permitted by the current BufferQueue configuration (aka
   // |req_buffer_count_|).
-  LOG(ERROR) << "BufferHubQueueProducer::allocateBuffers not implemented.";
+  ALOGE("BufferHubQueueProducer::allocateBuffers not implemented.");
 }
 
 status_t BufferHubQueueProducer::allowAllocation(bool /* allow */) {
-  LOG(ERROR) << "BufferHubQueueProducer::allowAllocation not implemented.";
+  ALOGE("BufferHubQueueProducer::allowAllocation not implemented.");
   return INVALID_OPERATION;
 }
 
 status_t BufferHubQueueProducer::setGenerationNumber(
     uint32_t generation_number) {
-  VLOG(1) << (__FUNCTION__);
+  ALOGD(__FUNCTION__);
 
   std::unique_lock<std::mutex> lock(core_->mutex_);
   core_->generation_number_ = generation_number;
@@ -337,23 +335,23 @@
   // BufferHub based implementation could have one to many producer/consumer
   // relationship, thus |getConsumerName| from the producer side does not
   // make any sense.
-  LOG(ERROR) << "BufferHubQueueProducer::getConsumerName not supported.";
+  ALOGE("BufferHubQueueProducer::getConsumerName not supported.");
   return String8("BufferHubQueue::DummyConsumer");
 }
 
 status_t BufferHubQueueProducer::setSharedBufferMode(
     bool /* shared_buffer_mode */) {
-  LOG(ERROR) << "BufferHubQueueProducer::setSharedBufferMode not implemented.";
+  ALOGE("BufferHubQueueProducer::setSharedBufferMode not implemented.");
   return INVALID_OPERATION;
 }
 
 status_t BufferHubQueueProducer::setAutoRefresh(bool /* auto_refresh */) {
-  LOG(ERROR) << "BufferHubQueueProducer::setAutoRefresh not implemented.";
+  ALOGE("BufferHubQueueProducer::setAutoRefresh not implemented.");
   return INVALID_OPERATION;
 }
 
 status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) {
-  VLOG(1) << (__FUNCTION__);
+  ALOGD(__FUNCTION__);
 
   std::unique_lock<std::mutex> lock(core_->mutex_);
   core_->dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000));
@@ -363,17 +361,17 @@
 status_t BufferHubQueueProducer::getLastQueuedBuffer(
     sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */,
     float /*out_transform_matrix*/[16]) {
-  LOG(ERROR) << "BufferHubQueueProducer::getLastQueuedBuffer not implemented.";
+  ALOGE("BufferHubQueueProducer::getLastQueuedBuffer not implemented.");
   return INVALID_OPERATION;
 }
 
 void BufferHubQueueProducer::getFrameTimestamps(
     FrameEventHistoryDelta* /*outDelta*/) {
-  LOG(ERROR) << "BufferHubQueueProducer::getFrameTimestamps not implemented.";
+  ALOGE("BufferHubQueueProducer::getFrameTimestamps not implemented.");
 }
 
 status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const {
-  VLOG(1) << (__FUNCTION__);
+  ALOGD(__FUNCTION__);
 
   *out_id = core_->unique_id_;
   return NO_ERROR;
@@ -382,7 +380,7 @@
 IBinder* BufferHubQueueProducer::onAsBinder() {
   // BufferHubQueueProducer is a non-binder implementation of
   // IGraphicBufferProducer.
-  LOG(WARNING) << "BufferHubQueueProducer::onAsBinder is not supported.";
+  ALOGW("BufferHubQueueProducer::onAsBinder is not supported.");
   return nullptr;
 }
 
diff --git a/libs/vr/libdisplay/Android.mk b/libs/vr/libdisplay/Android.mk
index 670bdcd..f0e62df 100644
--- a/libs/vr/libdisplay/Android.mk
+++ b/libs/vr/libdisplay/Android.mk
@@ -49,7 +49,6 @@
 	libsync
 
 staticLibraries := \
-	libchrome \
 	libbufferhub \
 	libbufferhubqueue \
 	libdvrcommon \
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
index cfb346d..54098e8 100644
--- a/libs/vr/libdisplay/display_client.cpp
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -1,7 +1,7 @@
 #include "include/private/dvr/display_client.h"
 
-#include <cutils/log.h>
 #include <cutils/native_handle.h>
+#include <log/log.h>
 #include <pdx/default_transport/client_channel.h>
 #include <pdx/default_transport/client_channel_factory.h>
 #include <pdx/status.h>
diff --git a/libs/vr/libdisplay/frame_history.cpp b/libs/vr/libdisplay/frame_history.cpp
index 67e4a09..154afbe 100644
--- a/libs/vr/libdisplay/frame_history.cpp
+++ b/libs/vr/libdisplay/frame_history.cpp
@@ -1,7 +1,7 @@
 #include <private/dvr/frame_history.h>
 
-#include <cutils/log.h>
 #include <errno.h>
+#include <log/log.h>
 #include <sync/sync.h>
 
 #include <pdx/file_handle.h>
diff --git a/libs/vr/libdisplay/gl_fenced_flush.cpp b/libs/vr/libdisplay/gl_fenced_flush.cpp
index 64b2e99..c70d554 100644
--- a/libs/vr/libdisplay/gl_fenced_flush.cpp
+++ b/libs/vr/libdisplay/gl_fenced_flush.cpp
@@ -6,7 +6,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #include <utils/Trace.h>
 
-#include <base/logging.h>
+#include <log/log.h>
 
 using android::pdx::LocalHandle;
 
@@ -22,14 +22,14 @@
       eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
   glFlush();
   if (sync_point == EGL_NO_SYNC_KHR) {
-    LOG(ERROR) << "sync_point == EGL_NO_SYNC_KHR";
+    ALOGE("sync_point == EGL_NO_SYNC_KHR");
     return LocalHandle();
   }
   EGLint fence_fd = eglDupNativeFenceFDANDROID(display, sync_point);
   eglDestroySyncKHR(display, sync_point);
 
   if (fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
-    LOG(ERROR) << "fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID";
+    ALOGE("fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID");
     return LocalHandle();
   }
   return LocalHandle(fence_fd);
diff --git a/libs/vr/libdisplay/graphics.cpp b/libs/vr/libdisplay/graphics.cpp
index d599616..d0557a9 100644
--- a/libs/vr/libdisplay/graphics.cpp
+++ b/libs/vr/libdisplay/graphics.cpp
@@ -1,10 +1,11 @@
 #include <dvr/graphics.h>
 
+#include <inttypes.h>
 #include <sys/timerfd.h>
 #include <array>
 #include <vector>
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <utils/Trace.h>
 
 #ifndef VK_USE_PLATFORM_ANDROID_KHR
@@ -372,8 +373,8 @@
       case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_FORMAT_OUT:
         break;
       default:
-        ALOGE("Invalid display surface parameter: key=%d value=%ld", p->key,
-              p->value);
+        ALOGE("Invalid display surface parameter: key=%d value=%" PRId64,
+              p->key, p->value);
         return nullptr;
     }
   }
@@ -583,7 +584,8 @@
   static int LockBuffer_DEPRECATED(ANativeWindow* window,
                                    ANativeWindowBuffer* buffer);
 
-  DISALLOW_COPY_AND_ASSIGN(DvrGraphicsContext);
+  DvrGraphicsContext(const DvrGraphicsContext&) = delete;
+  void operator=(const DvrGraphicsContext&) = delete;
 };
 
 DvrGraphicsContext::DvrGraphicsContext()
@@ -743,8 +745,8 @@
     // so that anyone who tries to bind an FBO to context->texture_id
     // will not get an incomplete buffer.
     context->current_buffer = context->buffer_queue->Dequeue();
-    CHECK(context->gl.texture_count ==
-          context->current_buffer->buffer()->slice_count());
+    LOG_ALWAYS_FATAL_IF(context->gl.texture_count !=
+                        context->current_buffer->buffer()->slice_count());
     for (int i = 0; i < context->gl.texture_count; ++i) {
       glBindTexture(context->gl.texture_target_type, context->gl.texture_id[i]);
       glEGLImageTargetTexture2DOES(context->gl.texture_target_type,
@@ -794,12 +796,12 @@
     result = vkCreateAndroidSurfaceKHR(
         context->vk.instance, &android_surface_ci,
         context->vk.allocation_callbacks, &context->vk.surface);
-    CHECK_EQ(result, VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
     VkBool32 surface_supports_present = VK_FALSE;
     result = vkGetPhysicalDeviceSurfaceSupportKHR(
         context->vk.physical_device, context->vk.present_queue_family,
         context->vk.surface, &surface_supports_present);
-    CHECK_EQ(result, VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
     if (!surface_supports_present) {
       ALOGE("Error: provided queue family (%u) does not support presentation",
             context->vk.present_queue_family);
@@ -809,21 +811,22 @@
     result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
         context->vk.physical_device, context->vk.surface,
         &surface_capabilities);
-    CHECK_EQ(result, VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
     // Determine the swapchain image format.
     uint32_t device_surface_format_count = 0;
     result = vkGetPhysicalDeviceSurfaceFormatsKHR(
         context->vk.physical_device, context->vk.surface,
         &device_surface_format_count, nullptr);
-    CHECK_EQ(result, VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
     std::vector<VkSurfaceFormatKHR> device_surface_formats(
         device_surface_format_count);
     result = vkGetPhysicalDeviceSurfaceFormatsKHR(
         context->vk.physical_device, context->vk.surface,
         &device_surface_format_count, device_surface_formats.data());
-    CHECK_EQ(result, VK_SUCCESS);
-    CHECK_GT(device_surface_format_count, 0U);
-    CHECK_NE(device_surface_formats[0].format, VK_FORMAT_UNDEFINED);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(device_surface_format_count == 0U);
+    LOG_ALWAYS_FATAL_IF(device_surface_formats[0].format ==
+                        VK_FORMAT_UNDEFINED);
     VkSurfaceFormatKHR present_surface_format = device_surface_formats[0];
     // Determine the swapchain present mode.
     // TODO(cort): query device_present_modes to make sure MAILBOX is supported.
@@ -832,19 +835,19 @@
     result = vkGetPhysicalDeviceSurfacePresentModesKHR(
         context->vk.physical_device, context->vk.surface,
         &device_present_mode_count, nullptr);
-    CHECK_EQ(result, VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
     std::vector<VkPresentModeKHR> device_present_modes(
         device_present_mode_count);
     result = vkGetPhysicalDeviceSurfacePresentModesKHR(
         context->vk.physical_device, context->vk.surface,
         &device_present_mode_count, device_present_modes.data());
-    CHECK_EQ(result, VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
     VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
     // Extract presentation surface extents, image count, transform, usages,
     // etc.
-    LOG_ASSERT(
-        static_cast<int>(surface_capabilities.currentExtent.width) != -1 &&
-        static_cast<int>(surface_capabilities.currentExtent.height) != -1);
+    LOG_ALWAYS_FATAL_IF(
+        static_cast<int>(surface_capabilities.currentExtent.width) == -1 ||
+        static_cast<int>(surface_capabilities.currentExtent.height) == -1);
     VkExtent2D swapchain_extent = surface_capabilities.currentExtent;
 
     uint32_t desired_image_count = surface_capabilities.minImageCount;
@@ -856,8 +859,8 @@
         surface_capabilities.currentTransform;
     VkImageUsageFlags image_usage_flags =
         surface_capabilities.supportedUsageFlags;
-    CHECK_NE(surface_capabilities.supportedCompositeAlpha,
-             static_cast<VkFlags>(0));
+    LOG_ALWAYS_FATAL_IF(surface_capabilities.supportedCompositeAlpha ==
+                        static_cast<VkFlags>(0));
     VkCompositeAlphaFlagBitsKHR composite_alpha =
         VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
     if (!(surface_capabilities.supportedCompositeAlpha &
@@ -889,18 +892,18 @@
     result = vkCreateSwapchainKHR(context->vk.device, &swapchain_ci,
                                   context->vk.allocation_callbacks,
                                   &context->vk.swapchain);
-    CHECK_EQ(result, VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
     // Create swapchain image views
     uint32_t image_count = 0;
     result = vkGetSwapchainImagesKHR(context->vk.device, context->vk.swapchain,
                                      &image_count, nullptr);
-    CHECK_EQ(result, VK_SUCCESS);
-    CHECK_GT(image_count, 0U);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(image_count == 0U);
     context->vk.swapchain_images.resize(image_count);
     result = vkGetSwapchainImagesKHR(context->vk.device, context->vk.swapchain,
                                      &image_count,
                                      context->vk.swapchain_images.data());
-    CHECK_EQ(result, VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
     context->vk.swapchain_image_views.resize(image_count);
     VkImageViewCreateInfo image_view_ci = {};
     image_view_ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -923,7 +926,7 @@
       result = vkCreateImageView(context->vk.device, &image_view_ci,
                                  context->vk.allocation_callbacks,
                                  &context->vk.swapchain_image_views[i]);
-      CHECK_EQ(result, VK_SUCCESS);
+      LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
     }
     // Fill in any requested output parameters.
     for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
@@ -950,7 +953,7 @@
 // by the Vulkan path.
 int DvrGraphicsContext::Post(android::dvr::NativeBufferProducer* buffer,
                              int fence_fd) {
-  LOG_ASSERT(graphics_api == DVR_GRAPHICS_API_VULKAN);
+  LOG_ALWAYS_FATAL_IF(graphics_api != DVR_GRAPHICS_API_VULKAN);
   ATRACE_NAME(__PRETTY_FUNCTION__);
   ALOGI_IF(TRACE, "DvrGraphicsContext::Post: buffer_id=%d, fence_fd=%d",
            buffer->buffer()->id(), fence_fd);
@@ -967,7 +970,7 @@
   ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval);
   DvrGraphicsContext* self = getSelf(window);
   (void)self;
-  LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
   return android::NO_ERROR;
 }
 
@@ -977,7 +980,7 @@
   ATRACE_NAME(__PRETTY_FUNCTION__);
 
   DvrGraphicsContext* self = getSelf(window);
-  LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
   std::lock_guard<std::mutex> autolock(self->lock_);
 
   if (!self->current_buffer) {
@@ -997,7 +1000,7 @@
   ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd);
 
   DvrGraphicsContext* self = getSelf(window);
-  LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
   std::lock_guard<std::mutex> autolock(self->lock_);
 
   android::dvr::NativeBufferProducer* native_buffer =
@@ -1007,7 +1010,7 @@
   if (self->buffer_already_posted) {
     // Check that the buffer is the one we expect, but handle it if this happens
     // in production by allowing this buffer to post on top of the previous one.
-    DCHECK(native_buffer == self->current_buffer);
+    LOG_FATAL_IF(native_buffer != self->current_buffer);
     if (native_buffer == self->current_buffer) {
       do_post = false;
       if (fence_fd >= 0)
@@ -1031,7 +1034,7 @@
   ALOGI_IF(TRACE, "DvrGraphicsContext::CancelBuffer: fence_fd: %d", fence_fd);
 
   DvrGraphicsContext* self = getSelf(window);
-  LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
   std::lock_guard<std::mutex> autolock(self->lock_);
 
   android::dvr::NativeBufferProducer* native_buffer =
@@ -1042,7 +1045,7 @@
   if (self->buffer_already_posted) {
     // Check that the buffer is the one we expect, but handle it if this happens
     // in production by returning this buffer to the buffer queue.
-    DCHECK(native_buffer == self->current_buffer);
+    LOG_FATAL_IF(native_buffer != self->current_buffer);
     if (native_buffer == self->current_buffer) {
       do_enqueue = false;
     }
@@ -1061,7 +1064,7 @@
 int DvrGraphicsContext::Query(const ANativeWindow* window, int what,
                               int* value) {
   DvrGraphicsContext* self = getSelf(const_cast<ANativeWindow*>(window));
-  LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
   std::lock_guard<std::mutex> autolock(self->lock_);
 
   switch (what) {
@@ -1100,7 +1103,7 @@
 
 int DvrGraphicsContext::Perform(ANativeWindow* window, int operation, ...) {
   DvrGraphicsContext* self = getSelf(window);
-  LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
   std::lock_guard<std::mutex> autolock(self->lock_);
 
   va_list args;
@@ -1231,7 +1234,7 @@
                            float32x4_t render_pose_orientation,
                            float32x4_t render_pose_translation) {
   ATRACE_NAME("dvrBeginRenderFrameEds");
-  LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_GLES);
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api != DVR_GRAPHICS_API_GLES);
   CHECK_GL();
   // Grab a buffer from the queue and set its pose.
   if (!graphics_context->current_buffer) {
@@ -1270,7 +1273,8 @@
                              uint32_t* swapchain_image_index,
                              VkImageView* swapchain_image_view) {
   ATRACE_NAME("dvrBeginRenderFrameEds");
-  LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_VULKAN);
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api !=
+                      DVR_GRAPHICS_API_VULKAN);
 
   // Acquire a swapchain image. This calls Dequeue() internally.
   VkResult result = vkAcquireNextImageKHR(
@@ -1313,7 +1317,7 @@
     return -EPERM;
   }
   if (num_views > DVR_GRAPHICS_SURFACE_MAX_VIEWS) {
-    LOG(ERROR) << "dvrBeginRenderFrameLateLatch called with too many views.";
+    ALOGE("dvrBeginRenderFrameLateLatch called with too many views.");
     return -EINVAL;
   }
   dvrBeginRenderFrameEds(graphics_context, DVR_POSE_LATE_LATCH,
@@ -1424,7 +1428,7 @@
   ATRACE_NAME("dvrGraphicsPostEarly");
   ALOGI_IF(TRACE, "dvrGraphicsPostEarly");
 
-  LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_GLES);
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api != DVR_GRAPHICS_API_GLES);
 
   // Note that this function can be called before or after
   // dvrBeginRenderFrame.
@@ -1445,7 +1449,7 @@
 }
 
 int dvrPresent(DvrGraphicsContext* graphics_context) {
-  LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_GLES);
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api != DVR_GRAPHICS_API_GLES);
 
   std::array<char, 128> buf;
   snprintf(buf.data(), buf.size(), "dvrPresent|vsync=%d|",
@@ -1482,7 +1486,8 @@
 
 int dvrPresentVk(DvrGraphicsContext* graphics_context,
                  VkSemaphore submit_semaphore, uint32_t swapchain_image_index) {
-  LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_VULKAN);
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api !=
+                      DVR_GRAPHICS_API_VULKAN);
 
   std::array<char, 128> buf;
   snprintf(buf.data(), buf.size(), "dvrPresent|vsync=%d|",
@@ -1549,7 +1554,7 @@
   auto display_surface = graphics_context->display_surface;
   // A DisplaySurface must be created prior to the creation of a
   // VideoMeshSurface.
-  LOG_ASSERT(display_surface != nullptr);
+  LOG_ALWAYS_FATAL_IF(display_surface == nullptr);
 
   LocalChannelHandle surface_handle = display_surface->CreateVideoMeshSurface();
   if (!surface_handle.valid()) {
diff --git a/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h b/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h
index a2659a6..e52d0b9 100644
--- a/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h
+++ b/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h
@@ -1,7 +1,6 @@
 #ifndef ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
 #define ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
 
-#include <base/macros.h>
 #include <private/dvr/buffer_hub_queue_client.h>
 #include <private/dvr/display_client.h>
 
diff --git a/libs/vr/libdisplay/late_latch.cpp b/libs/vr/libdisplay/late_latch.cpp
index 3681e10..b1a1589 100644
--- a/libs/vr/libdisplay/late_latch.cpp
+++ b/libs/vr/libdisplay/late_latch.cpp
@@ -6,7 +6,7 @@
 #include <iostream>
 #include <string>
 
-#include <base/logging.h>
+#include <log/log.h>
 #include <private/dvr/clock_ns.h>
 #include <private/dvr/debug.h>
 #include <private/dvr/graphics/gpu_profiler.h>
@@ -20,7 +20,6 @@
 #ifndef LOG_TAG
 #define LOG_TAG "latelatch"
 #endif
-#include <cutils/log.h>
 
 #define PE(str, ...)                                                  \
   fprintf(stderr, "[%s:%d] " str, __FILE__, __LINE__, ##__VA_ARGS__); \
@@ -268,18 +267,18 @@
   LocalHandle pose_buffer_fd;
   pose_client_ = dvrPoseCreate();
   if (!pose_client_) {
-    LOG(ERROR) << "LateLatch Error: failed to create pose client";
+    ALOGE("LateLatch Error: failed to create pose client");
   } else {
     int ret = privateDvrPoseGetRingBufferFd(pose_client_, &pose_buffer_fd);
     if (ret < 0) {
-      LOG(ERROR) << "LateLatch Error: failed to get pose ring buffer";
+      ALOGE("LateLatch Error: failed to get pose ring buffer");
     }
   }
 
   glGenBuffers(1, &pose_buffer_object_);
   glGenBuffers(1, &metadata_buffer_id_);
   if (!glBindSharedBufferQCOM) {
-    LOG(ERROR) << "Error: Missing gralloc buffer extension, no pose data";
+    ALOGE("Error: Missing gralloc buffer extension, no pose data");
   } else {
     if (pose_buffer_fd) {
       glBindBuffer(GL_SHADER_STORAGE_BUFFER, pose_buffer_object_);
@@ -346,7 +345,7 @@
 }
 
 void LateLatch::AddLateLatch(const LateLatchInput& data) const {
-  CHECK(is_app_late_latch_);
+  LOG_ALWAYS_FATAL_IF(!is_app_late_latch_);
   CHECK_GL();
   late_latch_program_.Use();
 
@@ -361,7 +360,7 @@
   if (adata)
     *adata = data;
   else
-    LOG(ERROR) << "Error: LateLatchInput gl mapping is null";
+    ALOGE("Error: LateLatchInput gl mapping is null");
   glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
   glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, input_buffer_id_);
   glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
@@ -410,7 +409,7 @@
 
 void LateLatch::AddEdsLateLatch(const LateLatchInput& data,
                                 GLuint render_pose_buffer_object) const {
-  CHECK(!is_app_late_latch_);
+  LOG_ALWAYS_FATAL_IF(is_app_late_latch_);
   late_latch_program_.Use();
 
   // Fall back on internal buffer when none is provided.
diff --git a/libs/vr/libdisplay/native_buffer_queue.cpp b/libs/vr/libdisplay/native_buffer_queue.cpp
index 2d1e23d..8dd0ee0 100644
--- a/libs/vr/libdisplay/native_buffer_queue.cpp
+++ b/libs/vr/libdisplay/native_buffer_queue.cpp
@@ -1,7 +1,6 @@
 #include "include/private/dvr/native_buffer_queue.h"
 
-#include <base/logging.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <sys/epoll.h>
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #include <utils/Trace.h>
@@ -23,7 +22,7 @@
     : surface_(surface),
       buffers_(capacity),
       buffer_queue_(capacity) {
-  CHECK(surface);
+  LOG_ALWAYS_FATAL_IF(!surface);
 
   epoll_fd_ = epoll_create(64);
   if (epoll_fd_ < 0) {
@@ -34,7 +33,7 @@
 
   // The kSurfaceBufferMaxCount must be >= the capacity so that shader code
   // can bind surface buffer array data.
-  CHECK(kSurfaceBufferMaxCount >= capacity);
+  LOG_ALWAYS_FATAL_IF(kSurfaceBufferMaxCount < capacity);
 
   for (size_t i = 0; i < capacity; i++) {
     uint32_t buffer_index = 0;
diff --git a/libs/vr/libdisplay/native_window.cpp b/libs/vr/libdisplay/native_window.cpp
index 63c81ed..24ecd8a 100644
--- a/libs/vr/libdisplay/native_window.cpp
+++ b/libs/vr/libdisplay/native_window.cpp
@@ -1,7 +1,6 @@
 #include <EGL/egl.h>
 
 #include <android/native_window.h>
-#include <base/logging.h>
 #include <cutils/native_handle.h>
 #include <errno.h>
 #include <pthread.h>
@@ -17,7 +16,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #include <utils/Trace.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <memory>
 #include <mutex>
@@ -177,7 +176,7 @@
   if (self->next_buffer_already_posted_) {
     // Check that the buffer is the one we expect, but handle it if this happens
     // in production by allowing this buffer to post on top of the previous one.
-    DCHECK(native_buffer == self->next_post_buffer_.get());
+    LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get());
     if (native_buffer == self->next_post_buffer_.get()) {
       do_post = false;
       if (fence_fd >= 0)
@@ -210,7 +209,7 @@
   if (self->next_buffer_already_posted_) {
     // Check that the buffer is the one we expect, but handle it if this happens
     // in production by returning this buffer to the buffer queue.
-    DCHECK(native_buffer == self->next_post_buffer_.get());
+    LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get());
     if (native_buffer == self->next_post_buffer_.get()) {
       do_enqueue = false;
     }
diff --git a/libs/vr/libdisplay/screenshot_client.cpp b/libs/vr/libdisplay/screenshot_client.cpp
index 78f5e0f..3ad0c68 100644
--- a/libs/vr/libdisplay/screenshot_client.cpp
+++ b/libs/vr/libdisplay/screenshot_client.cpp
@@ -1,6 +1,6 @@
 #include "include/private/dvr/screenshot_client.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <mutex>
 
diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp
index c4cad50..a0fb313 100644
--- a/libs/vr/libdisplay/vsync_client.cpp
+++ b/libs/vr/libdisplay/vsync_client.cpp
@@ -1,6 +1,6 @@
 #include "include/private/dvr/vsync_client.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <pdx/default_transport/client_channel_factory.h>
 #include <private/dvr/display_rpc.h>
diff --git a/libs/vr/libdvrcommon/Android.mk b/libs/vr/libdvrcommon/Android.mk
index 72afab2..80eb3a6 100644
--- a/libs/vr/libdvrcommon/Android.mk
+++ b/libs/vr/libdvrcommon/Android.mk
@@ -36,7 +36,6 @@
 	libhardware
 
 staticLibraries := \
-	libchrome \
 	libpdx_default_transport \
 
 include $(CLEAR_VARS)
diff --git a/libs/vr/libdvrcommon/frame_time_history.cpp b/libs/vr/libdvrcommon/frame_time_history.cpp
index 796498c..d4718a9 100644
--- a/libs/vr/libdvrcommon/frame_time_history.cpp
+++ b/libs/vr/libdvrcommon/frame_time_history.cpp
@@ -1,6 +1,6 @@
 #include <private/dvr/frame_time_history.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 namespace android {
 namespace dvr {
diff --git a/libs/vr/libdvrcommon/include/private/dvr/debug.h b/libs/vr/libdvrcommon/include/private/dvr/debug.h
index 7db681a..c31a385 100644
--- a/libs/vr/libdvrcommon/include/private/dvr/debug.h
+++ b/libs/vr/libdvrcommon/include/private/dvr/debug.h
@@ -4,15 +4,15 @@
 #include <GLES3/gl3.h>
 #include <math.h>
 
-#include <base/logging.h>
+#include <log/log.h>
 
 #ifndef NDEBUG
-#define CHECK_GL()                          \
-  do {                                      \
-    const GLenum err = glGetError();        \
-    if (err != GL_NO_ERROR) {               \
-      LOG(ERROR) << "OpenGL error " << err; \
-    }                                       \
+#define CHECK_GL()                   \
+  do {                               \
+    const GLenum err = glGetError(); \
+    if (err != GL_NO_ERROR) {        \
+      ALOGE("OpenGL error %d", err); \
+    }                                \
   } while (0)
 
 #define CHECK_GL_FBO()                                        \
@@ -22,10 +22,10 @@
       case GL_FRAMEBUFFER_COMPLETE:                           \
         break;                                                \
       case GL_FRAMEBUFFER_UNSUPPORTED:                        \
-        LOG(ERROR) << "GL_FRAMEBUFFER_UNSUPPORTED";           \
+        ALOGE("GL_FRAMEBUFFER_UNSUPPORTED");                  \
         break;                                                \
       default:                                                \
-        LOG(ERROR) << "FBO user error: " << status;           \
+        ALOGE("FBO user error: %d", status);                  \
         break;                                                \
     }                                                         \
   } while (0)
diff --git a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
index 8df741c..91e12c5 100644
--- a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
+++ b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
@@ -2,7 +2,7 @@
 #define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
 
 #include <android-base/unique_fd.h>
-#include <base/logging.h>
+#include <log/log.h>
 #include <sys/epoll.h>
 
 namespace android {
@@ -24,7 +24,7 @@
 
   int Create() {
     if (IsValid()) {
-      LOG(WARNING) << "epoll fd has already been created.";
+      ALOGW("epoll fd has already been created.");
       return -EALREADY;
     }
 
diff --git a/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
index c9f7f8e..12ef622 100644
--- a/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
+++ b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
@@ -2,8 +2,8 @@
 #define ANDROID_DVR_LOG_HELPERS_H_
 
 #include <iomanip>
+#include <ostream>
 
-#include <base/logging.h>
 #include <private/dvr/eigen.h>
 #include <private/dvr/field_of_view.h>
 
@@ -32,7 +32,8 @@
 template <typename T>
 inline std::ostream& operator<<(std::ostream& out,
                                 const Eigen::AffineMatrix<T, 4>& mat) {
-  out << std::setfill(' ') << std::setprecision(4) << std::fixed << std::showpos;
+  out << std::setfill(' ') << std::setprecision(4) << std::fixed
+      << std::showpos;
   out << "\nmat4[";
   out << std::setw(10) << mat(0, 0) << " " << std::setw(10) << mat(0, 1) << " "
       << std::setw(10) << mat(0, 2) << " " << std::setw(10) << mat(0, 3);
diff --git a/libs/vr/libdvrcommon/revision.cpp b/libs/vr/libdvrcommon/revision.cpp
index ae8603f..7925f65 100644
--- a/libs/vr/libdvrcommon/revision.cpp
+++ b/libs/vr/libdvrcommon/revision.cpp
@@ -9,7 +9,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <base/logging.h>
+#include <log/log.h>
 
 #include "revision_path.h"
 
@@ -74,16 +74,16 @@
 
   fd = open(dvr_product_revision_file_path(), O_RDONLY);
   if (fd < 0) {
-    PLOG(ERROR) << "Could not open '" << dvr_product_revision_file_path()
-                << "' to get product revision";
+    ALOGE("Could not open '%s' to get product revision: %s",
+          dvr_product_revision_file_path(), strerror(errno));
     global_product_revision_processed = true;
     return;
   }
 
   read_rc = read(fd, global_product_revision_str, kProductRevisionStringSize);
   if (read_rc <= 0) {
-    PLOG(ERROR) << "Could not read from '" << dvr_product_revision_file_path()
-                << "'";
+    ALOGE("Could not read from '%s': %s", dvr_product_revision_file_path(),
+          strerror(errno));
     global_product_revision_processed = true;
     return;
   }
@@ -102,8 +102,8 @@
     global_product = product_revision->product;
     global_revision = product_revision->revision;
   } else {
-    LOG(ERROR) << "Unable to match '" << global_product_revision_str
-               << "' to a product/revision.";
+    ALOGE("Unable to match '%s' to a product/revision.",
+          global_product_revision_str);
   }
 
   global_product_revision_processed = true;
diff --git a/libs/vr/libdvrgraphics/Android.mk b/libs/vr/libdvrgraphics/Android.mk
index 3d84319..b95b18e 100644
--- a/libs/vr/libdvrgraphics/Android.mk
+++ b/libs/vr/libdvrgraphics/Android.mk
@@ -13,7 +13,6 @@
 	$(LOCAL_PATH)/include
 
 staticLibraries := \
-	libchrome \
 	libbufferhub \
 	libdvrcommon \
 	libpdx_default_transport \
diff --git a/libs/vr/libdvrgraphics/blur.cpp b/libs/vr/libdvrgraphics/blur.cpp
index 7365b0e..90e271e 100644
--- a/libs/vr/libdvrgraphics/blur.cpp
+++ b/libs/vr/libdvrgraphics/blur.cpp
@@ -11,8 +11,7 @@
 
 #include <string>
 
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
+#include <log/log.h>
 #include <private/dvr/debug.h>
 #include <private/dvr/graphics/egl_image.h>
 #include <private/dvr/graphics/shader_program.h>
@@ -78,7 +77,7 @@
       width_(w),
       height_(h),
       fbo_q_free_(1 + num_blur_outputs) {
-  CHECK(num_blur_outputs > 0);
+  LOG_ALWAYS_FATAL_IF(num_blur_outputs <= 0);
   source_fbo_ =
       CreateFbo(w, h, source_texture, source_texture_target, is_external);
   fbo_half_ = CreateFbo(w / 2, h / 2, 0, target_texture_target, is_external);
@@ -113,7 +112,7 @@
 }
 
 GLuint Blur::DrawBlur(GLuint source_texture) {
-  CHECK(fbo_q_free_.GetSize() >= 2);
+  LOG_ALWAYS_FATAL_IF(fbo_q_free_.GetSize() < 2);
 
   // Downsample to half w x half h.
   glBindFramebuffer(GL_READ_FRAMEBUFFER, source_fbo_.fbo);
diff --git a/libs/vr/libdvrgraphics/gpu_profiler.cpp b/libs/vr/libdvrgraphics/gpu_profiler.cpp
index d252a34..49c515f 100644
--- a/libs/vr/libdvrgraphics/gpu_profiler.cpp
+++ b/libs/vr/libdvrgraphics/gpu_profiler.cpp
@@ -1,6 +1,6 @@
 #include "include/private/dvr/graphics/gpu_profiler.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <private/dvr/clock_ns.h>
 
diff --git a/libs/vr/libdvrgraphics/shader_program.cpp b/libs/vr/libdvrgraphics/shader_program.cpp
index bf36eff..2d36600 100644
--- a/libs/vr/libdvrgraphics/shader_program.cpp
+++ b/libs/vr/libdvrgraphics/shader_program.cpp
@@ -3,15 +3,14 @@
 #include <regex>
 #include <sstream>
 
-#include <base/logging.h>
-#include <base/strings/string_util.h>
+#include <log/log.h>
 
 namespace {
 
 static bool CompileShader(GLuint shader, const std::string& shader_string) {
-  std::string prefix = "";
-  if (!base::StartsWith(shader_string, "#version",
-                        base::CompareCase::SENSITIVE)) {
+  std::string prefix;
+  const std::string kVersion = "#version";
+  if (shader_string.substr(0, kVersion.size()) != kVersion) {
     prefix = "#version 310 es\n";
   }
   std::string string_with_prefix = prefix + shader_string;
@@ -24,8 +23,7 @@
   if (!success) {
     GLchar infoLog[512];
     glGetShaderInfoLog(shader, 512, nullptr, infoLog);
-    LOG(ERROR) << "Shader Failed to compile: " << *shader_str << " -- "
-               << infoLog;
+    ALOGE("Shader Failed to compile: %s -- %s", *shader_str, infoLog);
     return false;
   }
   return true;
@@ -43,7 +41,7 @@
   if (!success) {
     GLchar infoLog[512];
     glGetProgramInfoLog(program, 512, nullptr, infoLog);
-    LOG(ERROR) << "Shader failed to link: " << infoLog;
+    ALOGE("Shader failed to link: %s", infoLog);
     return false;
   }
 
@@ -60,7 +58,7 @@
   if (!success) {
     GLchar infoLog[512];
     glGetProgramInfoLog(program, 512, nullptr, infoLog);
-    LOG(ERROR) << "Shader failed to link: " << infoLog;
+    ALOGE("Shader failed to link: %s", infoLog);
     return false;
   }
 
diff --git a/libs/vr/libdvrgraphics/timer_query.cpp b/libs/vr/libdvrgraphics/timer_query.cpp
index dcc6216..23d2b7c 100644
--- a/libs/vr/libdvrgraphics/timer_query.cpp
+++ b/libs/vr/libdvrgraphics/timer_query.cpp
@@ -1,7 +1,7 @@
 #include "include/private/dvr/graphics/timer_query.h"
 
 #include <GLES2/gl2ext.h>
-#include <base/logging.h>
+#include <log/log.h>
 
 namespace android {
 namespace dvr {
@@ -38,7 +38,7 @@
 
 double SyncTimerQuery::FlushAndGetTimeInMS() {
   if (timer_.query_ == 0) {
-    LOG(ERROR) << "Error: Only call FlushAndGetTimeInMS() once.";
+    ALOGE("Error: Only call FlushAndGetTimeInMS() once.");
     return 0.0;
   }
   timer_.End();
@@ -51,7 +51,7 @@
   GLint disjoint_occurred = 0;
   glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_occurred);
   if (disjoint_occurred) {
-    LOG(ERROR) << "Disjoint occurred.";
+    ALOGE("Disjoint occurred.");
     timer_.Delete();
     return 0.0;
   }
diff --git a/libs/vr/libeds/Android.mk b/libs/vr/libeds/Android.mk
index 0345f6d..373e68e 100644
--- a/libs/vr/libeds/Android.mk
+++ b/libs/vr/libeds/Android.mk
@@ -39,7 +39,6 @@
 	libvulkan
 
 staticLibraries := \
-	libchrome \
 	libdisplay \
 	libdvrcommon \
 	libdvrgraphics \
diff --git a/libs/vr/libeds/composite_hmd.cpp b/libs/vr/libeds/composite_hmd.cpp
index d29cd65..d6bf164 100644
--- a/libs/vr/libeds/composite_hmd.cpp
+++ b/libs/vr/libeds/composite_hmd.cpp
@@ -1,6 +1,7 @@
 #include "include/private/dvr/composite_hmd.h"
 
-#include <base/logging.h>
+#include <log/log.h>
+
 #include <private/dvr/numeric.h>
 
 namespace android {
@@ -113,9 +114,9 @@
   float meters_per_tan_angle = virtual_eye_to_screen_dist;
   vec2 pixels_per_tan_angle = pixels_per_meter * meters_per_tan_angle;
 
-  CHECK_NE(0.0f, display_width_meters);
-  CHECK_NE(0.0f, display_height_meters);
-  CHECK_NE(0.0f, virtual_eye_to_screen_dist);
+  LOG_ALWAYS_FATAL_IF(0.0f == display_width_meters);
+  LOG_ALWAYS_FATAL_IF(0.0f == display_height_meters);
+  LOG_ALWAYS_FATAL_IF(0.0f == virtual_eye_to_screen_dist);
 
   // Height of lenses from the bottom of the screen.
   float lens_y_center = 0;
diff --git a/libs/vr/libeds/distortion_renderer.cpp b/libs/vr/libeds/distortion_renderer.cpp
index a19843f..59029d8 100644
--- a/libs/vr/libeds/distortion_renderer.cpp
+++ b/libs/vr/libeds/distortion_renderer.cpp
@@ -8,7 +8,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #include <utils/Trace.h>
 
-#include <base/logging.h>
+#include <log/log.h>
 #include <private/dvr/clock_ns.h>
 #include <private/dvr/composite_hmd.h>
 #include <private/dvr/debug.h>
@@ -303,12 +303,12 @@
     vert_builder += "#define COMPOSITE_LAYER_2\n";
     frag_builder += "#define COMPOSITE_LAYER_2\n";
   } else {
-    CHECK_EQ(num_layers, 1);
+    LOG_ALWAYS_FATAL_IF(num_layers != 1);
   }
   if (blend_with_previous_layer) {
     // Check for unsupported shader combinations:
-    CHECK_EQ(num_layers, 1);
-    CHECK_EQ(use_alpha_vignette, false);
+    LOG_ALWAYS_FATAL_IF(num_layers != 1);
+    LOG_ALWAYS_FATAL_IF(!use_alpha_vignette);
     if (kUseFramebufferReadback)
       frag_builder += "#define BLEND_WITH_PREVIOUS_LAYER\n";
   }
@@ -320,7 +320,7 @@
   vert_builder += vertex;
   frag_builder += fragment;
   pgm.Link(vert_builder, frag_builder);
-  CHECK(pgm.IsUsable());
+  LOG_ALWAYS_FATAL_IF(!pgm.IsUsable());
 
   pgm.Use();
 
@@ -343,7 +343,7 @@
   projectionMatrix =
       projectionMatrix * Eigen::AngleAxisf(rotation, vec3::UnitZ());
 
-  CHECK(sizeof(mat4) == 4 * 4 * 4);
+  LOG_ALWAYS_FATAL_IF(sizeof(mat4) != 4 * 4 * 4);
   glUniformMatrix4fv(uProjectionMatrix, 1, false, projectionMatrix.data());
 }
 
@@ -367,8 +367,7 @@
   if (eds_enabled_) {
     // Late latch must be on if eds_enabled_ is true.
     if (!late_latch_enabled) {
-      LOG(ERROR) << "Cannot enable EDS without late latch. "
-                 << "Force enabling late latch.";
+      ALOGE("Cannot enable EDS without late latch. Force enabling late latch.");
       late_latch_enabled = true;
     }
   }
@@ -633,11 +632,11 @@
     PrepGlState(eye);
 
   if (num_textures > kMaxLayers) {
-    LOG(ERROR) << "Too many textures for DistortionRenderer";
+    ALOGE("Too many textures for DistortionRenderer");
     num_textures = kMaxLayers;
   }
 
-  CHECK(num_textures == 1 || num_textures == 2);
+  LOG_ALWAYS_FATAL_IF(num_textures != 1 && num_textures != 2);
 
   if (num_textures == 2) {
     if (chromatic_aberration_correction_enabled_) {
@@ -776,7 +775,7 @@
 
 bool DistortionRenderer::GetLastEdsPose(LateLatchOutput* out_data, int layer_id) const {
   if (layer_id >= kMaxLayers) {
-    LOG(ERROR) << "Accessing invalid layer " << layer_id << std::endl;
+    ALOGE("Accessing invalid layer %d", layer_id);
     return false;
   }
 
@@ -784,7 +783,7 @@
     late_latch_[layer_id]->CaptureOutputData(out_data);
     return true;
   } else {
-    LOG(ERROR) << "Late latch shader not enabled." << std::endl;
+    ALOGE("Late latch shader not enabled.");
     return false;
   }
 }
diff --git a/libs/vr/libeds/eds_mesh.cpp b/libs/vr/libeds/eds_mesh.cpp
index 2c7dc2f..01a90cf 100644
--- a/libs/vr/libeds/eds_mesh.cpp
+++ b/libs/vr/libeds/eds_mesh.cpp
@@ -1,8 +1,8 @@
 #include "include/private/dvr/eds_mesh.h"
 
+#include <log/log.h>
 #include <math.h>
 
-#include <base/logging.h>
 #include <private/dvr/types.h>
 
 namespace {
@@ -105,7 +105,7 @@
 // provided by |hmd| for |eye|.
 EdsMesh BuildDistortionMesh(EyeType eye, int resolution,
                             const DistortionFunction& distortion_function) {
-  CHECK_GT(resolution, 2);
+  LOG_ALWAYS_FATAL_IF(resolution <= 2);
 
   // Number of indices produced by the strip method
   // (see comment in ComputeDistortionMeshIndices):
diff --git a/libs/vr/libeds/lucid_pose_tracker.cpp b/libs/vr/libeds/lucid_pose_tracker.cpp
index c321bb0..5247020 100644
--- a/libs/vr/libeds/lucid_pose_tracker.cpp
+++ b/libs/vr/libeds/lucid_pose_tracker.cpp
@@ -1,7 +1,7 @@
 #include "include/private/dvr/lucid_pose_tracker.h"
 
 #define LOG_TAG "LucidPoseTracker"
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <private/dvr/clock_ns.h>
 
diff --git a/libs/vr/libeds/tests/eds_app_tests.cpp b/libs/vr/libeds/tests/eds_app_tests.cpp
index 1742736..549d864 100644
--- a/libs/vr/libeds/tests/eds_app_tests.cpp
+++ b/libs/vr/libeds/tests/eds_app_tests.cpp
@@ -1,7 +1,6 @@
 #include <EGL/egl.h>
 #include <GLES2/gl2.h>
 
-#include <base/logging.h>
 #include <dvr/graphics.h>
 #include <dvr/pose_client.h>
 #include <gtest/gtest.h>
diff --git a/libs/vr/libgvr/Android.mk b/libs/vr/libgvr/Android.mk
index 0fcf94b..be78605 100644
--- a/libs/vr/libgvr/Android.mk
+++ b/libs/vr/libgvr/Android.mk
@@ -68,14 +68,12 @@
     libhardware \
     liblog \
     libsync \
-    libevent \
     libprotobuf-cpp-full
 
 LOCAL_STATIC_LIBRARIES := \
     libdisplay \
     libbufferhub \
     libbufferhubqueue \
-    libchrome \
     libdvrcommon \
     libeds \
     libdvrgraphics \
@@ -125,7 +123,6 @@
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_SRC_FILES := dummy_gvr_ext.cpp
-LOCAL_STATIC_LIBRARIES := libchrome
 LOCAL_LDLIBS := -llog
 LOCAL_MODULE_TAGS := optional
 LOCAL_SHARED_LIBRARIES += libgvr
diff --git a/libs/vr/libgvr/dummy_gvr_ext.cpp b/libs/vr/libgvr/dummy_gvr_ext.cpp
index c507038..f73838d 100644
--- a/libs/vr/libgvr/dummy_gvr_ext.cpp
+++ b/libs/vr/libgvr/dummy_gvr_ext.cpp
@@ -1,4 +1,4 @@
-#include <base/logging.h>
+#include <log/log.h>
 #include <vr/gvr/capi/include/gvr.h>
 #include <vr/gvr/capi/include/gvr_ext.h>
 #include <vr/gvr/capi/include/gvr_types.h>
@@ -14,8 +14,8 @@
 
 gvr_mat4f gvr_get_6dof_head_pose_in_start_space(gvr_context* gvr,
                                                 uint32_t /* vsync_count */) {
-  LOG(FATAL) << "gvr_get_6dof_head_pose_in_start_space is not implemented. "
-             << "Use gvr_get_head_space_from_start_space_pose instead.";
+  LOG_ALWAYS_FATAL("gvr_get_6dof_head_pose_in_start_space is not implemented. "
+                   "Use gvr_get_head_space_from_start_space_pose instead.");
   return gvr_mat4f({{{1.0f, 0.0f, 0.0f, 0.0f},
                      {0.0f, 1.0f, 0.0f, 0.0f},
                      {0.0f, 0.0f, 1.0f, 0.0f},
@@ -25,5 +25,5 @@
 void gvr_wait_next_frame(gvr_swap_chain* /* swap_chain */,
                          int64_t /* sched_offset_nanos */,
                          gvr_frame_schedule* /* out_next_frame_schedule */) {
-  LOG(FATAL) << "gvr_wait_next_frame is not implemented.";
+  LOG_ALWAYS_FATAL("gvr_wait_next_frame is not implemented.");
 }
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h
index 5054ebd..c459eca 100644
--- a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h
@@ -338,6 +338,15 @@
                            const gvr_buffer_viewport_list* viewport_list,
                            gvr_mat4f head_space_from_start_space,
                            gvr_clock_time_point target_presentation_time);
+
+/// Queries whether a particular GVR feature is supported by the underlying
+/// platform.
+///
+/// @param gvr The context to query against.
+/// @param feature The gvr_feature type being queried.
+/// @return true if feature is supported, false otherwise.
+bool gvr_is_feature_supported(const gvr_context* gvr, int32_t feature);
+
 /// @}
 
 /////////////////////////////////////////////////////////////////////////////
@@ -743,7 +752,7 @@
 /// scenarios, e.g., when tracking is non-biological.
 ///
 /// @param gvr Pointer to the context instance from which the pose was obtained.
-/// @param head_rotation_in_start_space The head rotation as returned by
+/// @param head_space_from_start_space_rotation The head rotation as returned by
 ///     gvr_get_head_space_from_start_space_rotation().
 /// @param factor A scaling factor for the neck model offset, clamped from 0 to
 ///     1. This should be 1 for most scenarios, while 0 will effectively disable
@@ -1589,6 +1598,11 @@
                           texture_presentation_time);
   }
 
+  /// For more information, see gvr_is_feature_supported().
+  bool IsFeatureSupported(int32_t feature) {
+    return gvr_is_feature_supported(context_, feature);
+  }
+
   /// For more information, see gvr_buffer_spec_create().
   BufferSpec CreateBufferSpec() {
     return BufferSpec(context_);
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h
index eee3d0c..d34c84d 100644
--- a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h
@@ -441,7 +441,7 @@
 /// @return Id of new sound object. Returns kInvalidId if the sound file has not
 ///     been preloaded or if the number of input channels is > 1.
 gvr_audio_source_id gvr_audio_create_sound_object(gvr_audio_context* api,
-                                                 const char* filename);
+                                                  const char* filename);
 
 /// Returns a new ambisonic sound field. Note that the sample needs to be
 /// preloaded and must have 4 separate audio channels. The handle automatically
@@ -453,7 +453,7 @@
 ///     been preloaded or if the number of input channels does not match that
 ///     required.
 gvr_audio_source_id gvr_audio_create_soundfield(gvr_audio_context* api,
-                                               const char* filename);
+                                                const char* filename);
 
 /// Returns a new stereo non-spatialized source, which directly plays back mono
 /// or stereo audio. Note the sample needs to be preloaded and may contain only
@@ -465,7 +465,7 @@
 ///     sound file has not been preloaded or if the number of input channels is
 ///     > 2;
 gvr_audio_source_id gvr_audio_create_stereo_sound(gvr_audio_context* api,
-                                                 const char* filename);
+                                                  const char* filename);
 
 /// Starts the playback of a sound.
 ///
@@ -549,7 +549,6 @@
     gvr_audio_context* api, gvr_audio_source_id sound_object_id,
     int32_t rolloff_model, float min_distance, float max_distance);
 
-
 /// Changes the master volume.
 ///
 /// @param api Pointer to a gvr_audio_context.
@@ -707,7 +706,7 @@
     return gvr_audio_create_soundfield(context_, filename.c_str());
   }
 
-  /// Returns a new stereo soound.
+  /// Returns a new stereo sound.
   /// For more information, see gvr_audio_create_stereo_sound().
   AudioSourceId CreateStereoSound(const std::string& filename) {
     return gvr_audio_create_stereo_sound(context_, filename.c_str());
@@ -824,8 +823,7 @@
   /// @name Wrapper manipulation
   /// @{
   /// Creates a C++ wrapper for a C object and takes ownership.
-  explicit AudioApi(gvr_audio_context* context)
-      : context_(context) {}
+  explicit AudioApi(gvr_audio_context* context) : context_(context) {}
 
   /// Returns the wrapped C object. Does not affect ownership.
   gvr_audio_context* cobj() { return context_; }
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h
index a67502f..8347393 100644
--- a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h
@@ -414,6 +414,27 @@
 int64_t gvr_controller_state_get_last_position_timestamp(
     const gvr_controller_state* state);
 
+/// Returns whether the controller battery is currently charging.
+/// This may not be real time information and may be slow to be updated.
+bool gvr_controller_state_get_battery_charging(
+    const gvr_controller_state* state);
+
+/// Returns the bucketed controller battery level.
+/// Note this is a gvr_controller_battery_level and not a percent.
+int32_t gvr_controller_state_get_battery_level(
+    const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last battery event was received.
+int64_t gvr_controller_state_get_last_battery_timestamp(
+    const gvr_controller_state* state);
+
+/// Convenience to convert a battery level to string. The returned pointer
+/// is static and valid throughout the lifetime of the application.
+///
+/// @param level The gvr_controller_battery_level to convert to string.
+/// @return A pointer to a string that describes the value.
+const char* gvr_controller_battery_level_to_string(int32_t level);
+
 /// @}
 
 #ifdef __cplusplus
@@ -572,6 +593,10 @@
     return gvr_controller_button_to_string(button);
   }
 
+  static const char* ToString(ControllerBatteryLevel level) {
+    return gvr_controller_battery_level_to_string(level);
+  }
+
   /// @name Wrapper manipulation
   /// @{
   /// Creates a C++ wrapper for a C object and takes ownership.
@@ -724,6 +749,22 @@
     return gvr_controller_state_get_last_position_timestamp(state_);
   }
 
+  /// For more information, see gvr_controller_state_get_battery_charging
+  bool GetBatteryCharging() const {
+    return gvr_controller_state_get_battery_charging(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_battery_level
+  ControllerBatteryLevel GetBatteryLevel() const {
+    return static_cast<ControllerBatteryLevel>(
+        gvr_controller_state_get_battery_level(state_));
+  }
+
+  /// For more information, see gvr_controller_state_get_last_battery_timestamp
+  int64_t GetLastBatteryTimestamp() const {
+    return gvr_controller_state_get_last_battery_timestamp(state_);
+  }
+
   /// @name Wrapper manipulation
   /// @{
   /// Creates a C++ wrapper for a C object and takes ownership.
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h
index ff00863..cf81b51 100644
--- a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h
@@ -50,6 +50,14 @@
   GVR_VIEWER_TYPE_DAYDREAM = 1,
 } gvr_viewer_type;
 
+// Types of VR-specific features which may or may not be supported on the
+// underlying platform.
+typedef enum {
+  // Asynchronous reprojection warps the app's rendered frame using the most
+  // recent head pose just before pushing the frame to the display.
+  GVR_FEATURE_ASYNC_REPROJECTION = 0,
+} gvr_feature;
+
 /// @}
 
 /// Version information for the Google VR API.
@@ -185,6 +193,8 @@
   GVR_CONTROLLER_ENABLE_POSE_PREDICTION = 1 << 5,
   /// Indicates that controller position data should be reported.
   GVR_CONTROLLER_ENABLE_POSITION = 1 << 6,
+  /// Indicates that controller battery data should be reported.
+  GVR_CONTROLLER_ENABLE_BATTERY = 1 << 7,
 };
 
 /// Constants that represent the status of the controller API.
@@ -239,6 +249,21 @@
   GVR_CONTROLLER_BUTTON_COUNT = 6,
 } gvr_controller_button;
 
+/// Controller battery states.
+typedef enum {
+  GVR_CONTROLLER_BATTERY_LEVEL_UNKNOWN = 0,
+  GVR_CONTROLLER_BATTERY_LEVEL_CRITICAL_LOW = 1,
+  GVR_CONTROLLER_BATTERY_LEVEL_LOW = 2,
+  GVR_CONTROLLER_BATTERY_LEVEL_MEDIUM = 3,
+  GVR_CONTROLLER_BATTERY_LEVEL_ALMOST_FULL = 4,
+  GVR_CONTROLLER_BATTERY_LEVEL_FULL = 5,
+
+  /// Note: there are 5 distinct levels, but there are 6 due to the inclusion
+  /// of an UNKNOWN state before any battery information is collected, etc.
+  GVR_CONTROLLER_BATTERY_LEVEL_COUNT = 6,
+} gvr_controller_battery_level;
+
+
 /// @}
 
 /// Opaque handle to controller state.
@@ -322,6 +347,41 @@
 /// Sound object and sound field identifier.
 typedef int32_t gvr_audio_source_id;
 
+/// Supported surround sound formats.
+typedef enum {
+  // Enables to initialize a yet undefined rendering mode.
+  GVR_AUDIO_SURROUND_FORMAT_INVALID = 0,
+
+  // Virtual stereo speakers at -30 degrees and +30 degrees.
+  GVR_AUDIO_SURROUND_FORMAT_SURROUND_STEREO = 1,
+
+  // 5.1 surround sound according to the ITU-R BS 775 speaker configuration
+  // recommendation:
+  //   - Front left (FL) at 30 degrees.
+  //   - Front right (FR) at -30 degrees.
+  //   - Front center (FC) at 0 degrees.
+  //   - Low frequency effects (LFE) at front center at 0 degrees.
+  //   - Left side (LS) at 110 degrees.
+  //   - Right side (RS) at -110 degrees.
+  //
+  // The 5.1 channel input layout must matches AAC: FL, FR, FC, LFE, LS, RS.
+  // Note that this differs from the Vorbis/Opus 5.1 channel layout, which
+  // is: FL, FC, FR, LS, RS, LFE.
+  GVR_AUDIO_SURROUND_FORMAT_SURROUND_FIVE_DOT_ONE = 2,
+
+  // First-order ambisonics (AmbiX format: 4 channels, ACN channel ordering,
+  // SN3D normalization).
+  GVR_AUDIO_SURROUND_FORMAT_FIRST_ORDER_AMBISONICS = 3,
+
+  // Second-order ambisonics (AmbiX format: 9 channels, ACN channel ordering,
+  // SN3D normalization).
+  GVR_AUDIO_SURROUND_FORMAT_SECOND_ORDER_AMBISONICS = 4,
+
+  // Third-order ambisonics (AmbiX format: 16 channels, ACN channel ordering,
+  // SN3D normalization).
+  GVR_AUDIO_SURROUND_FORMAT_THIRD_ORDER_AMBISONICS = 5,
+} gvr_audio_surround_format_type;
+
 /// Valid color formats for swap chain buffers.
 typedef enum {
   /// Equivalent to GL_RGBA8
@@ -400,6 +460,8 @@
     static_cast<int32_t>(GVR_CONTROLLER_ENABLE_POSE_PREDICTION);
 const int32_t kControllerEnablePosition =
     static_cast<int32_t>(GVR_CONTROLLER_ENABLE_POSITION);
+const int32_t kControllerEnableBattery =
+    static_cast<int32_t>(GVR_CONTROLLER_ENABLE_BATTERY);
 
 typedef gvr_controller_api_status ControllerApiStatus;
 const ControllerApiStatus kControllerApiOk =
@@ -443,6 +505,26 @@
 const ControllerButton kControllerButtonCount =
     static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_COUNT);
 
+typedef gvr_controller_battery_level ControllerBatteryLevel;
+const ControllerBatteryLevel kControllerBatteryLevelUnknown =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_UNKNOWN);
+const ControllerBatteryLevel kControllerBatteryLevelCriticalLow =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_CRITICAL_LOW);
+const ControllerBatteryLevel kControllerBatteryLevelLow =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_LOW);
+const ControllerBatteryLevel kControllerBatteryLevelMedium =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_MEDIUM);
+const ControllerBatteryLevel kControllerBatteryLevelAlmostFull =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_ALMOST_FULL);
+const ControllerBatteryLevel kControllerBatteryLevelFull =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_FULL);
+
 /// An uninitialized external surface ID.
 const int32_t kUninitializedExternalSurface = GVR_BUFFER_INDEX_EXTERNAL_SURFACE;
 /// The default source buffer index for viewports.
@@ -474,6 +556,7 @@
 typedef gvr_audio_material_type AudioMaterialName;
 typedef gvr_audio_distance_rolloff_type AudioRolloffMethod;
 typedef gvr_audio_source_id AudioSourceId;
+typedef gvr_audio_surround_format_type AudioSurroundFormat;
 
 typedef gvr_color_format_type ColorFormat;
 const ColorFormat kColorFormatRgba8888 =
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h
index 6bc2b4e..5bd6174 100644
--- a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h
@@ -86,15 +86,6 @@
 int32_t gvr_external_surface_get_surface_id(
     const gvr_external_surface* surface);
 
-/// Queries whether a particular GVR feature is supported by the underlying
-/// platform.
-///
-/// @param gvr The context to query against.
-/// @param feature The gvr_feature type being queried.
-/// @return true if feature is supported, false otherwise.
-bool gvr_experimental_is_feature_supported(const gvr_context* gvr,
-                                           int32_t feature);
-
 /// Sets the z order of the layer to be created.
 /// Note that this API is a short-term workaround for SysUI work and is never
 /// meant to graduate as is to either gvr.h or gvr_private.h. The proper
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h
index 1df2443..f7ae6a5 100644
--- a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h
@@ -43,15 +43,13 @@
   GVR_NUM_PERF_EVENT_CALLBACK_TYPES = 3,
 } gvr_perf_event_callback_type;
 
-// Types of VR-specific features which may or may not be supported on the
-// underlying platform.
+// Experimental VR-specific features which may or may not be supported on the
+// underlying platform.  These values should not overlap with current or future
+// gvr_feature values, so we're starting with 1000000 and increasing upward.
 typedef enum {
-  // Asynchronous reprojection warps the app's rendered frame using the most
-  // recent head pose just before pushing the frame to the display.
-  GVR_ASYNC_REPROJECTION = 0,
   // Head tracking with 6 degrees of freedom (position & rotation)
-  GVR_6DOF_HEAD_POSE = 1,
-} gvr_feature;
+  GVR_FEATURE_HEAD_POSE_6DOF = 1000000,
+} gvr_experimental_feature;
 
 // ************************************************************************** //
 // *                    GVR Analytics experimental APIs                     * //
diff --git a/libs/vr/libgvr/shim_gvr.cpp b/libs/vr/libgvr/shim_gvr.cpp
index 4b074e7..fa8a655 100644
--- a/libs/vr/libgvr/shim_gvr.cpp
+++ b/libs/vr/libgvr/shim_gvr.cpp
@@ -19,10 +19,10 @@
 #endif
 #endif
 
-#include <cutils/log.h>
 #include <dvr/graphics.h>
 #include <dvr/performance_client_api.h>
 #include <dvr/pose_client.h>
+#include <log/log.h>
 #include <private/dvr/buffer_hub_queue_core.h>
 #include <private/dvr/buffer_hub_queue_producer.h>
 #include <private/dvr/clock_ns.h>
@@ -511,6 +511,10 @@
   gvr_set_error(gvr, GVR_ERROR_INTERNAL);
 }
 
+bool gvr_is_feature_supported(const gvr_context* /*gvr*/, int32_t feature) {
+  return feature == GVR_FEATURE_ASYNC_REPROJECTION;
+}
+
 /////////////////////////////////////////////////////////////////////////////
 // Viewports and viewport lists
 /////////////////////////////////////////////////////////////////////////////
@@ -1200,17 +1204,6 @@
   dvrGraphicsSurfaceSetZOrder(swap_chain->graphics_context_, z_order);
 }
 
-bool gvr_experimental_is_feature_supported(const gvr_context* /* gvr */,
-                                           int32_t feature) {
-  switch (feature) {
-    case GVR_ASYNC_REPROJECTION:
-    case GVR_6DOF_HEAD_POSE:
-      return true;
-    default:
-      return false;
-  }
-}
-
 bool gvr_experimental_register_perf_event_callback(
     gvr_context* gvr, int* /* out_handle */, void* /* user_data */,
     void (* /* event_callback */)(void*, int, float)) {
@@ -1333,14 +1326,14 @@
 }
 
 void* gvr_external_surface_get_surface(const gvr_external_surface* surface) {
-  CHECK(surface->swap_chain != nullptr &&
-        surface->swap_chain->context != nullptr &&
-        surface->swap_chain->context->jni_env_ != nullptr)
-      << "gvr_external_surface_get_surface: Surface must be constructed within "
-      << "a JNIEnv. Check |gvr_create| call.";
+  LOG_ALWAYS_FATAL_IF(surface->swap_chain == nullptr ||
+                          surface->swap_chain->context == nullptr ||
+                          surface->swap_chain->context->jni_env_ == nullptr,
+                      "gvr_external_surface_get_surface: Surface must be "
+                      "constructed within a JNIEnv. Check |gvr_create| call.");
 
-  CHECK(surface->video_surface != nullptr)
-      << "gvr_external_surface_get_surface: Invalid surface.";
+  LOG_ALWAYS_FATAL_IF(surface->video_surface == nullptr,
+                      "gvr_external_surface_get_surface: Invalid surface.");
 
   std::shared_ptr<android::dvr::ProducerQueue> producer_queue =
       surface->video_surface->client->GetProducerQueue();
diff --git a/libs/vr/libgvr/shim_gvr_controller.cpp b/libs/vr/libgvr/shim_gvr_controller.cpp
index 54bc270..0f55903 100644
--- a/libs/vr/libgvr/shim_gvr_controller.cpp
+++ b/libs/vr/libgvr/shim_gvr_controller.cpp
@@ -1,6 +1,6 @@
 #define LOG_TAG "libgvr_controller_shim"
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <vr/gvr/capi/include/gvr_controller.h>
 #include <vr/gvr/capi/include/gvr_types.h>
 
diff --git a/libs/vr/libgvr/shim_gvr_private.cpp b/libs/vr/libgvr/shim_gvr_private.cpp
index 6ab6971..25a5110 100644
--- a/libs/vr/libgvr/shim_gvr_private.cpp
+++ b/libs/vr/libgvr/shim_gvr_private.cpp
@@ -1,6 +1,6 @@
 #define LOG_TAG "libgvr_shim_private"
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <private/dvr/display_rpc.h>
 #include <private/dvr/internal_types.h>
 #include <vr/gvr/capi/include/gvr.h>
@@ -42,7 +42,7 @@
       serialized_viewer_params_size_bytes);
   std::unique_ptr<proto::DeviceParams> device_params(new proto::DeviceParams);
   if (!device_params->ParseFromString(serialized_device_params_string)) {
-    LOG(ERROR) << "Invalid serialized Cardboard DeviceParams";
+    ALOGE("Invalid serialized Cardboard DeviceParams");
     return false;
   }
 
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_logging.h b/libs/vr/libimageio/include/private/dvr/image_io_logging.h
index 3c0f2a5..ac78179 100644
--- a/libs/vr/libimageio/include/private/dvr/image_io_logging.h
+++ b/libs/vr/libimageio/include/private/dvr/image_io_logging.h
@@ -1,7 +1,7 @@
 #ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
 #define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
 
-// This header acts as cutils/log.h if LOG_TO_STDERR is not defined.
+// This header acts as log/log.h if LOG_TO_STDERR is not defined.
 // If LOG_TO_STDERR is defined, then android logging macros (such as ALOGE)
 // would log to stderr. This is useful if the code is also being used/tested on
 // a desktop.
@@ -33,7 +33,7 @@
 #define ALOGV(fmt, ...) LogToStderr("VERBOSE", fmt, ##__VA_ARGS__)
 
 #else  // LOG_TO_STDERR
-#include <cutils/log.h>
+#include <log/log.h>
 #endif  // LOG_TO_STDERR
 
 #endif  // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
diff --git a/libs/vr/libpdx/private/pdx/client.h b/libs/vr/libpdx/private/pdx/client.h
index 4eafe76..a590087 100644
--- a/libs/vr/libpdx/private/pdx/client.h
+++ b/libs/vr/libpdx/private/pdx/client.h
@@ -108,8 +108,22 @@
    */
   void DisableAutoReconnect();
 
+  /*
+   * Returns an fd that the client may use to check/wait for asynchronous
+   * notifications to the channel. It is implementation dependent how the
+   * transport backend handles this feature, however all implementations must
+   * support at least POLLIN/EPOLLIN/readable.
+   *
+   * For uses that require more than one type of event, use
+   * ClientChannel::GetEventMask() to distinguish between events.
+   */
   int event_fd() const;
+
+  /*
+   * Returns the underlying ClientChannel object.
+   */
   ClientChannel* GetChannel() const { return channel_.get(); }
+  std::unique_ptr<ClientChannel>&& TakeChannel() { return std::move(channel_); }
 
  private:
   Client(const Client&) = delete;
diff --git a/libs/vr/libpdx/private/pdx/client_channel.h b/libs/vr/libpdx/private/pdx/client_channel.h
index e7ea475..dbfd626 100644
--- a/libs/vr/libpdx/private/pdx/client_channel.h
+++ b/libs/vr/libpdx/private/pdx/client_channel.h
@@ -18,6 +18,8 @@
   virtual uint32_t GetIpcTag() const = 0;
 
   virtual int event_fd() const = 0;
+  virtual Status<int> GetEventMask(int events) = 0;
+
   virtual LocalChannelHandle& GetChannelHandle() = 0;
   virtual void* AllocateTransactionState() = 0;
   virtual void FreeTransactionState(void* state) = 0;
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel.h b/libs/vr/libpdx/private/pdx/mock_client_channel.h
index 7780ee3..561c939 100644
--- a/libs/vr/libpdx/private/pdx/mock_client_channel.h
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel.h
@@ -11,6 +11,7 @@
  public:
   MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
   MOCK_CONST_METHOD0(event_fd, int());
+  MOCK_METHOD1(GetEventMask, Status<int>(int));
   MOCK_METHOD0(GetChannelHandle, LocalChannelHandle&());
   MOCK_METHOD0(AllocateTransactionState, void*());
   MOCK_METHOD1(FreeTransactionState, void(void* state));
diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h
index 029e6bf..175cedf 100644
--- a/libs/vr/libpdx/private/pdx/service.h
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -1,8 +1,8 @@
 #ifndef ANDROID_PDX_SERVICE_H_
 #define ANDROID_PDX_SERVICE_H_
 
-#include <cutils/log.h>
 #include <errno.h>
+#include <log/log.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
index 0053af8..daf9af8 100644
--- a/libs/vr/libpdx/service.cpp
+++ b/libs/vr/libpdx/service.cpp
@@ -1,8 +1,8 @@
 #define LOG_TAG "ServiceFramework"
 #include "pdx/service.h"
 
-#include <cutils/log.h>
 #include <fcntl.h>
+#include <log/log.h>
 #include <utils/misc.h>
 
 #include <algorithm>
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
index 9c4a3b0..09eeaa0 100644
--- a/libs/vr/libpdx_uds/Android.bp
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -5,10 +5,13 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-DLOG_TAG=\"libpdx_uds\"",
+        "-DTRACE=0",
     ],
     export_include_dirs: ["private"],
     local_include_dirs: ["private"],
     srcs: [
+        "channel_event_set.cpp",
         "channel_manager.cpp",
         "client_channel_factory.cpp",
         "client_channel.cpp",
diff --git a/libs/vr/libpdx_uds/channel_event_set.cpp b/libs/vr/libpdx_uds/channel_event_set.cpp
new file mode 100644
index 0000000..f8baeab
--- /dev/null
+++ b/libs/vr/libpdx_uds/channel_event_set.cpp
@@ -0,0 +1,115 @@
+#include "private/uds/channel_event_set.h"
+
+#include <log/log.h>
+
+#include <uds/ipc_helper.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+ChannelEventSet::ChannelEventSet() {
+  const int flags = EFD_CLOEXEC | EFD_NONBLOCK;
+  LocalHandle epoll_fd, event_fd;
+
+  if (!SetupHandle(epoll_create(1), &epoll_fd, "epoll") ||
+      !SetupHandle(eventfd(0, flags), &event_fd, "event")) {
+    return;
+  }
+
+  epoll_event event;
+  event.events = 0;
+  event.data.u32 = 0;
+  if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_fd.Get(), &event) < 0) {
+    const int error = errno;
+    ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s",
+          strerror(error));
+    return;
+  }
+
+  epoll_fd_ = std::move(epoll_fd);
+  event_fd_ = std::move(event_fd);
+}
+
+Status<void> ChannelEventSet::AddDataFd(const LocalHandle& data_fd) {
+  epoll_event event;
+  event.events = EPOLLHUP | EPOLLRDHUP;
+  event.data.u32 = event.events;
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, data_fd.Get(), &event) < 0) {
+    const int error = errno;
+    ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s",
+          strerror(error));
+    return ErrorStatus{error};
+  } else {
+    return {};
+  }
+}
+
+int ChannelEventSet::ModifyEvents(int clear_mask, int set_mask) {
+  ALOGD_IF(TRACE, "ChannelEventSet::ModifyEvents: clear_mask=%x set_mask=%x",
+           clear_mask, set_mask);
+  const int old_bits = event_bits_;
+  const int new_bits = (event_bits_ & ~clear_mask) | set_mask;
+  event_bits_ = new_bits;
+
+  // If anything changed clear the event and update the event mask.
+  if (old_bits != new_bits) {
+    eventfd_t value;
+    eventfd_read(event_fd_.Get(), &value);
+
+    epoll_event event;
+    event.events = POLLIN;
+    event.data.u32 = event_bits_;
+    if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, event_fd_.Get(), &event) <
+        0) {
+      const int error = errno;
+      ALOGE("ChannelEventSet::AddEventHandle: Failed to update event: %s",
+            strerror(error));
+      return -error;
+    }
+  }
+
+  // If there are any bits set, re-trigger the eventfd.
+  if (new_bits)
+    eventfd_write(event_fd_.Get(), 1);
+
+  return 0;
+}
+
+Status<void> ChannelEventSet::SetupHandle(int fd, LocalHandle* handle,
+                                          const char* error_name) {
+  const int error = errno;
+  handle->Reset(fd);
+  if (!*handle) {
+    ALOGE("ChannelEventSet::SetupHandle: Failed to setup %s handle: %s",
+          error_name, strerror(error));
+    return ErrorStatus{error};
+  }
+  return {};
+}
+
+Status<int> ChannelEventReceiver::GetPendingEvents() const {
+  constexpr long kTimeoutMs = 0;
+  epoll_event event;
+  const int count =
+      RETRY_EINTR(epoll_wait(epoll_fd_.Get(), &event, 1, kTimeoutMs));
+
+  Status<int> status;
+  if (count < 0) {
+    status.SetError(errno);
+    ALOGE("ChannelEventReceiver::GetPendingEvents: Failed to get events: %s",
+          status.GetErrorMessage().c_str());
+    return status;
+  }
+
+  const int mask_out = event.data.u32;
+  ALOGD_IF(TRACE, "ChannelEventReceiver::GetPendingEvents: mask_out=%x",
+           mask_out);
+
+  status.SetValue(mask_out);
+  return status;
+}
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/channel_manager.cpp b/libs/vr/libpdx_uds/channel_manager.cpp
index 387625c..afc0a4f 100644
--- a/libs/vr/libpdx_uds/channel_manager.cpp
+++ b/libs/vr/libpdx_uds/channel_manager.cpp
@@ -33,11 +33,11 @@
   return LocalChannelHandle(nullptr, -1);
 }
 
-int ChannelManager::GetEventFd(int32_t handle) {
+ChannelManager::ChannelData* ChannelManager::GetChannelData(int32_t handle) {
   std::lock_guard<std::mutex> autolock(mutex_);
   auto channel = channels_.find(handle);
-  return channel != channels_.end() ? channel->second.event_fd.Get() : -1;
-};
+  return channel != channels_.end() ? &channel->second : nullptr;
+}
 
 }  // namespace uds
 }  // namespace pdx
diff --git a/libs/vr/libpdx_uds/client_channel.cpp b/libs/vr/libpdx_uds/client_channel.cpp
index 3394759..4cbdb94 100644
--- a/libs/vr/libpdx_uds/client_channel.cpp
+++ b/libs/vr/libpdx_uds/client_channel.cpp
@@ -50,12 +50,17 @@
   ChannelReference PushChannelHandle(BorrowedChannelHandle handle) {
     if (!handle)
       return handle.value();
-    ChannelInfo<BorrowedHandle> channel_info;
-    channel_info.data_fd.Reset(handle.value());
-    channel_info.event_fd.Reset(
-        ChannelManager::Get().GetEventFd(handle.value()));
-    request.channels.push_back(std::move(channel_info));
-    return request.channels.size() - 1;
+
+    if (auto* channel_data =
+            ChannelManager::Get().GetChannelData(handle.value())) {
+      ChannelInfo<BorrowedHandle> channel_info;
+      channel_info.data_fd.Reset(handle.value());
+      channel_info.event_fd = channel_data->event_receiver.event_fd();
+      request.channels.push_back(std::move(channel_info));
+      return request.channels.size() - 1;
+    } else {
+      return -1;
+    }
   }
 
   RequestHeader<BorrowedHandle> request;
@@ -127,27 +132,7 @@
 
 ClientChannel::ClientChannel(LocalChannelHandle channel_handle)
     : channel_handle_{std::move(channel_handle)} {
-  int data_fd = channel_handle_.value();
-  int event_fd =
-      channel_handle_ ? ChannelManager::Get().GetEventFd(data_fd) : -1;
-
-  if (event_fd >= 0) {
-    epoll_fd_.Reset(epoll_create(1));
-    if (epoll_fd_) {
-      epoll_event data_ev;
-      data_ev.events = EPOLLHUP;
-      data_ev.data.fd = data_fd;
-
-      epoll_event event_ev;
-      event_ev.events = EPOLLIN;
-      event_ev.data.fd = event_fd;
-      if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, data_fd, &data_ev) < 0 ||
-          epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd, &event_ev) < 0) {
-        ALOGE("Failed to add fd to epoll fd because: %s\n", strerror(errno));
-        epoll_fd_.Close();
-      }
-    }
-  }
+  channel_data_ = ChannelManager::Get().GetChannelData(channel_handle_.value());
 }
 
 std::unique_ptr<pdx::ClientChannel> ClientChannel::Create(
diff --git a/libs/vr/libpdx_uds/ipc_helper.cpp b/libs/vr/libpdx_uds/ipc_helper.cpp
index dca23ef..ee7299e 100644
--- a/libs/vr/libpdx_uds/ipc_helper.cpp
+++ b/libs/vr/libpdx_uds/ipc_helper.cpp
@@ -134,7 +134,9 @@
       RETRY_EINTR(recv(socket_fd, &preamble, sizeof(preamble), MSG_WAITALL));
   if (ret < 0)
     return ErrorStatus(errno);
-  if (ret != sizeof(preamble) || preamble.magic != kMagicPreamble)
+  else if (ret == 0)
+    return ErrorStatus(ESHUTDOWN);
+  else if (ret != sizeof(preamble) || preamble.magic != kMagicPreamble)
     return ErrorStatus(EIO);
 
   buffer_.resize(preamble.data_size);
@@ -157,7 +159,9 @@
   ret = RETRY_EINTR(recvmsg(socket_fd, &msg, MSG_WAITALL));
   if (ret < 0)
     return ErrorStatus(errno);
-  if (static_cast<uint32_t>(ret) != preamble.data_size)
+  else if (ret == 0)
+    return ErrorStatus(ESHUTDOWN);
+  else if (static_cast<uint32_t>(ret) != preamble.data_size)
     return ErrorStatus(EIO);
 
   bool cred_available = false;
@@ -239,7 +243,9 @@
   ssize_t size_read = RETRY_EINTR(recv(socket_fd, data, size, MSG_WAITALL));
   if (size_read < 0)
     return ErrorStatus(errno);
-  if (static_cast<size_t>(size_read) != size)
+  else if (size_read == 0)
+    return ErrorStatus(ESHUTDOWN);
+  else if (static_cast<size_t>(size_read) != size)
     return ErrorStatus(EIO);
   return {};
 }
@@ -251,7 +257,9 @@
   ssize_t size_read = RETRY_EINTR(recvmsg(socket_fd, &msg, MSG_WAITALL));
   if (size_read < 0)
     return ErrorStatus(errno);
-  if (static_cast<size_t>(size_read) != CountVectorSize(data, count))
+  else if (size_read == 0)
+    return ErrorStatus(ESHUTDOWN);
+  else if (static_cast<size_t>(size_read) != CountVectorSize(data, count))
     return ErrorStatus(EIO);
   return {};
 }
diff --git a/libs/vr/libpdx_uds/private/uds/channel_event_set.h b/libs/vr/libpdx_uds/private/uds/channel_event_set.h
new file mode 100644
index 0000000..1f464d5
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/channel_event_set.h
@@ -0,0 +1,62 @@
+#ifndef ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
+#define ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
+
+#include <errno.h>
+#include <poll.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ChannelEventSet {
+ public:
+  ChannelEventSet();
+  ChannelEventSet(ChannelEventSet&&) = default;
+  ChannelEventSet& operator=(ChannelEventSet&&) = default;
+
+  BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); }
+
+  explicit operator bool() const { return !!epoll_fd_ && !!event_fd_; }
+
+  Status<void> AddDataFd(const LocalHandle& data_fd);
+  int ModifyEvents(int clear_mask, int set_mask);
+
+ private:
+  LocalHandle epoll_fd_;
+  LocalHandle event_fd_;
+  uint32_t event_bits_ = 0;
+
+  static Status<void> SetupHandle(int fd, LocalHandle* handle,
+                                  const char* error_name);
+
+  ChannelEventSet(const ChannelEventSet&) = delete;
+  void operator=(const ChannelEventSet&) = delete;
+};
+
+class ChannelEventReceiver {
+ public:
+  ChannelEventReceiver() = default;
+  ChannelEventReceiver(LocalHandle epoll_fd) : epoll_fd_{std::move(epoll_fd)} {}
+  ChannelEventReceiver(ChannelEventReceiver&&) = default;
+  ChannelEventReceiver& operator=(ChannelEventReceiver&&) = default;
+
+  BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); }
+  Status<int> GetPendingEvents() const;
+
+ private:
+  LocalHandle epoll_fd_;
+
+  ChannelEventReceiver(const ChannelEventReceiver&) = delete;
+  void operator=(const ChannelEventReceiver&) = delete;
+};
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
diff --git a/libs/vr/libpdx_uds/private/uds/channel_manager.h b/libs/vr/libpdx_uds/private/uds/channel_manager.h
index e8d3f6e..2aca414 100644
--- a/libs/vr/libpdx_uds/private/uds/channel_manager.h
+++ b/libs/vr/libpdx_uds/private/uds/channel_manager.h
@@ -6,6 +6,7 @@
 
 #include <pdx/channel_handle.h>
 #include <pdx/file_handle.h>
+#include <uds/channel_event_set.h>
 
 namespace android {
 namespace pdx {
@@ -16,13 +17,14 @@
   static ChannelManager& Get();
 
   LocalChannelHandle CreateHandle(LocalHandle data_fd, LocalHandle event_fd);
-  int GetEventFd(int32_t handle);
-
- private:
   struct ChannelData {
     LocalHandle data_fd;
-    LocalHandle event_fd;
+    ChannelEventReceiver event_receiver;
   };
+
+  ChannelData* GetChannelData(int32_t handle);
+
+ private:
   ChannelManager() = default;
 
   void CloseHandle(int32_t handle) override;
diff --git a/libs/vr/libpdx_uds/private/uds/client_channel.h b/libs/vr/libpdx_uds/private/uds/client_channel.h
index 12a40e7..45f6473 100644
--- a/libs/vr/libpdx_uds/private/uds/client_channel.h
+++ b/libs/vr/libpdx_uds/private/uds/client_channel.h
@@ -3,6 +3,7 @@
 
 #include <pdx/client_channel.h>
 
+#include <uds/channel_event_set.h>
 #include <uds/channel_manager.h>
 #include <uds/service_endpoint.h>
 
@@ -18,7 +19,17 @@
       LocalChannelHandle channel_handle);
 
   uint32_t GetIpcTag() const override { return Endpoint::kIpcTag; }
-  int event_fd() const override { return epoll_fd_.Get(); }
+
+  int event_fd() const override {
+    return channel_data_ ? channel_data_->event_receiver.event_fd().Get() : -1;
+  }
+  Status<int> GetEventMask(int /*events*/) override {
+    if (channel_data_)
+      return channel_data_->event_receiver.GetPendingEvents();
+    else
+      return ErrorStatus(EINVAL);
+  }
+
   LocalChannelHandle& GetChannelHandle() override { return channel_handle_; }
   void* AllocateTransactionState() override;
   void FreeTransactionState(void* state) override;
@@ -61,7 +72,7 @@
                              const iovec* receive_vector, size_t receive_count);
 
   LocalChannelHandle channel_handle_;
-  LocalHandle epoll_fd_;
+  ChannelManager::ChannelData* channel_data_;
 };
 
 }  // namespace uds
diff --git a/libs/vr/libpdx_uds/private/uds/service_endpoint.h b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
index 0f69400..3ec8519 100644
--- a/libs/vr/libpdx_uds/private/uds/service_endpoint.h
+++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
@@ -11,6 +11,7 @@
 
 #include <pdx/service.h>
 #include <pdx/service_endpoint.h>
+#include <uds/channel_event_set.h>
 #include <uds/service_dispatcher.h>
 
 namespace android {
@@ -95,8 +96,7 @@
  private:
   struct ChannelData {
     LocalHandle data_fd;
-    LocalHandle event_fd;
-    uint32_t event_mask{0};
+    ChannelEventSet event_set;
     Channel* channel_state{nullptr};
   };
 
@@ -110,6 +110,8 @@
     return next_message_id_.fetch_add(1, std::memory_order_relaxed);
   }
 
+  void BuildCloseMessage(int channel_id, Message* message);
+
   Status<void> AcceptConnection(Message* message);
   Status<void> ReceiveMessageForChannel(int channel_id, Message* message);
   Status<void> OnNewChannel(LocalHandle channel_fd);
@@ -130,9 +132,6 @@
   mutable std::mutex channel_mutex_;
   std::map<int, ChannelData> channels_;
 
-  mutable std::mutex service_mutex_;
-  std::condition_variable condition_;
-
   Service* service_{nullptr};
   std::atomic<uint32_t> next_message_id_;
 };
diff --git a/libs/vr/libpdx_uds/service_dispatcher.cpp b/libs/vr/libpdx_uds/service_dispatcher.cpp
index 8a40158..fa98f26 100644
--- a/libs/vr/libpdx_uds/service_dispatcher.cpp
+++ b/libs/vr/libpdx_uds/service_dispatcher.cpp
@@ -1,4 +1,3 @@
-#define LOG_TAG "ServiceDispatcher"
 #include "uds/service_dispatcher.h"
 
 #include <errno.h>
@@ -9,8 +8,6 @@
 #include "pdx/service.h"
 #include "uds/service_endpoint.h"
 
-#define TRACE 0
-
 static const int kMaxEventsPerLoop = 128;
 
 namespace android {
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
index b6021e8..7bf753d 100644
--- a/libs/vr/libpdx_uds/service_endpoint.cpp
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -61,12 +61,17 @@
   ChannelReference PushChannelHandle(BorrowedChannelHandle handle) {
     if (!handle)
       return handle.value();
-    ChannelInfo<BorrowedHandle> channel_info;
-    channel_info.data_fd.Reset(handle.value());
-    channel_info.event_fd.Reset(
-        ChannelManager::Get().GetEventFd(handle.value()));
-    response.channels.push_back(std::move(channel_info));
-    return response.channels.size() - 1;
+
+    if (auto* channel_data =
+            ChannelManager::Get().GetChannelData(handle.value())) {
+      ChannelInfo<BorrowedHandle> channel_info;
+      channel_info.data_fd.Reset(handle.value());
+      channel_info.event_fd = channel_data->event_receiver.event_fd();
+      response.channels.push_back(std::move(channel_info));
+      return response.channels.size() - 1;
+    } else {
+      return -1;
+    }
   }
 
   ChannelReference PushChannelHandle(BorrowedHandle data_fd,
@@ -156,8 +161,6 @@
     return;
   }
 
-  // Use "this" as a unique pointer to distinguish the event fd from all
-  // the other entries that point to instances of Service.
   epoll_event socket_event;
   socket_event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
   socket_event.data.fd = fd.Get();
@@ -244,9 +247,9 @@
     return ErrorStatus(errno);
   }
   ChannelData channel_data;
-  int channel_id = channel_fd.Get();
+  const int channel_id = channel_fd.Get();
+  channel_data.event_set.AddDataFd(channel_fd);
   channel_data.data_fd = std::move(channel_fd);
-  channel_data.event_fd.Reset(eventfd(0, 0));
   channel_data.channel_state = channel_state;
   auto pair = channels_.emplace(channel_id, std::move(channel_data));
   return &pair.first->second;
@@ -272,6 +275,8 @@
 }
 
 int Endpoint::CloseChannelLocked(int channel_id) {
+  ALOGD_IF(TRACE, "Endpoint::CloseChannelLocked: channel_id=%d", channel_id);
+
   auto channel_data = channels_.find(channel_id);
   if (channel_data == channels_.end())
     return -EINVAL;
@@ -293,31 +298,15 @@
 int Endpoint::ModifyChannelEvents(int channel_id, int clear_mask,
                                   int set_mask) {
   std::lock_guard<std::mutex> autolock(channel_mutex_);
-  auto channel_data = channels_.find(channel_id);
-  if (channel_data == channels_.end())
-    return -EINVAL;
-  int old_mask = channel_data->second.event_mask;
-  int new_mask = (old_mask & ~clear_mask) | set_mask;
 
-  // EPOLLHUP shares the same bitmask with POLLHUP.
-  if ((new_mask & POLLHUP) && !(old_mask & POLLHUP)) {
-    CloseChannelLocked(channel_id);
+  auto search = channels_.find(channel_id);
+  if (search != channels_.end()) {
+    auto& channel_data = search->second;
+    channel_data.event_set.ModifyEvents(clear_mask, set_mask);
     return 0;
   }
 
-  // EPOLLIN shares the same bitmask with POLLIN and EPOLLPRI shares the same
-  // bitmask with POLLPRI
-  eventfd_t value = 1;
-  if (((new_mask & POLLIN) && !(old_mask & POLLIN)) ||
-      ((new_mask & POLLPRI) && !(old_mask & POLLPRI))) {
-    eventfd_write(channel_data->second.event_fd.Get(), value);
-  } else if ((!(new_mask & POLLIN) && (old_mask & POLLIN)) ||
-             (!(new_mask & POLLPRI) && (old_mask & POLLPRI))) {
-    eventfd_read(channel_data->second.event_fd.Get(), &value);
-  }
-
-  channel_data->second.event_mask = new_mask;
-  return 0;
+  return -EINVAL;
 }
 
 Status<RemoteChannelHandle> Endpoint::PushChannel(Message* message,
@@ -355,7 +344,8 @@
 
   auto* state = static_cast<MessageState*>(message->GetState());
   ChannelReference ref = state->PushChannelHandle(
-      remote_socket.Borrow(), channel_data.get()->event_fd.Borrow());
+      remote_socket.Borrow(),
+      channel_data.get()->event_set.event_fd().Borrow());
   state->sockets_to_close.push_back(std::move(remote_socket));
   return RemoteChannelHandle{ref};
 }
@@ -391,8 +381,9 @@
 int Endpoint::GetChannelEventFd(int channel_id) {
   std::lock_guard<std::mutex> autolock(channel_mutex_);
   auto channel_data = channels_.find(channel_id);
-  return (channel_data != channels_.end()) ? channel_data->second.event_fd.Get()
-                                           : -1;
+  return (channel_data != channels_.end())
+             ? channel_data->second.event_set.event_fd().Get()
+             : -1;
 }
 
 Status<void> Endpoint::ReceiveMessageForChannel(int channel_id,
@@ -400,9 +391,15 @@
   RequestHeader<LocalHandle> request;
   auto status = ReceiveData(channel_id, &request);
   if (!status) {
-    CloseChannel(channel_id);
-    return status;
+    if (status.error() == ESHUTDOWN) {
+      BuildCloseMessage(channel_id, message);
+      return {};
+    } else {
+      CloseChannel(channel_id);
+      return status;
+    }
   }
+
   MessageInfo info;
   info.pid = request.cred.pid;
   info.tid = -1;
@@ -435,19 +432,42 @@
   if (status && request.is_impulse)
     status = ReenableEpollEvent(channel_id);
 
-  if (!status)
-    CloseChannel(channel_id);
+  if (!status) {
+    if (status.error() == ESHUTDOWN) {
+      BuildCloseMessage(channel_id, message);
+      return {};
+    } else {
+      CloseChannel(channel_id);
+      return status;
+    }
+  }
 
   return status;
 }
 
-int Endpoint::MessageReceive(Message* message) {
-  {
-    std::unique_lock<std::mutex> lock(service_mutex_);
-    condition_.wait(lock, [this] { return service_ != nullptr; });
-  }
+void Endpoint::BuildCloseMessage(int channel_id, Message* message) {
+  ALOGD_IF(TRACE, "Endpoint::BuildCloseMessage: channel_id=%d", channel_id);
+  MessageInfo info;
+  info.pid = -1;
+  info.tid = -1;
+  info.cid = channel_id;
+  info.mid = GetNextAvailableMessageId();
+  info.euid = -1;
+  info.egid = -1;
+  info.op = opcodes::CHANNEL_CLOSE;
+  info.flags = 0;
+  info.service = service_;
+  info.channel = GetChannelState(channel_id);
+  info.send_len = 0;
+  info.recv_len = 0;
+  info.fd_count = 0;
+  *message = Message{info};
+}
 
-  // One event at a time.
+int Endpoint::MessageReceive(Message* message) {
+  // Receive at most one event from the epoll set. This should prevent multiple
+  // dispatch threads from attempting to handle messages on the same socket at
+  // the same time.
   epoll_event event;
   int count = RETRY_EINTR(
       epoll_wait(epoll_fd_.Get(), &event, 1, is_blocking_ ? -1 : 0));
@@ -472,22 +492,8 @@
   }
 
   int channel_id = event.data.fd;
-  if (event.events & EPOLLRDHUP) {
-    MessageInfo info;
-    info.pid = -1;
-    info.tid = -1;
-    info.cid = channel_id;
-    info.mid = GetNextAvailableMessageId();
-    info.euid = -1;
-    info.egid = -1;
-    info.op = opcodes::CHANNEL_CLOSE;
-    info.flags = 0;
-    info.service = service_;
-    info.channel = GetChannelState(channel_id);
-    info.send_len = 0;
-    info.recv_len = 0;
-    info.fd_count = 0;
-    *message = Message{info};
+  if (event.events & (EPOLLRDHUP | EPOLLHUP)) {
+    BuildCloseMessage(channel_id, message);
     return 0;
   }
 
@@ -498,18 +504,19 @@
 }
 
 int Endpoint::MessageReply(Message* message, int return_code) {
-  int channel_socket = GetChannelSocketFd(message->GetChannelId());
+  const int channel_id = message->GetChannelId();
+  const int channel_socket = GetChannelSocketFd(channel_id);
   if (channel_socket < 0)
     return -EBADF;
 
   auto* state = static_cast<MessageState*>(message->GetState());
   switch (message->GetOp()) {
     case opcodes::CHANNEL_CLOSE:
-      return CloseChannel(channel_socket);
+      return CloseChannel(channel_id);
 
     case opcodes::CHANNEL_OPEN:
       if (return_code < 0)
-        return CloseChannel(channel_socket);
+        return CloseChannel(channel_id);
       // Reply with the event fd.
       return_code = state->PushFileHandle(
           BorrowedHandle{GetChannelEventFd(channel_socket)});
diff --git a/libs/vr/libposepredictor/linear_pose_predictor.cpp b/libs/vr/libposepredictor/linear_pose_predictor.cpp
index a2ce2ca..de1b951 100644
--- a/libs/vr/libposepredictor/linear_pose_predictor.cpp
+++ b/libs/vr/libposepredictor/linear_pose_predictor.cpp
@@ -1,4 +1,4 @@
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <private/dvr/linear_pose_predictor.h>
 
diff --git a/libs/vr/libsensor/Android.mk b/libs/vr/libsensor/Android.mk
index db1514d..8c7ad43 100644
--- a/libs/vr/libsensor/Android.mk
+++ b/libs/vr/libsensor/Android.mk
@@ -23,7 +23,6 @@
 
 staticLibraries := \
 	libbufferhub \
-	libchrome \
 	libdvrcommon \
 	libpdx_default_transport \
 
@@ -33,7 +32,6 @@
 	libhardware \
 	liblog \
 	libutils \
-	libevent
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(sourceFiles)
diff --git a/libs/vr/libsensor/pose_client.cpp b/libs/vr/libsensor/pose_client.cpp
index 13e127d..9eae3aa 100644
--- a/libs/vr/libsensor/pose_client.cpp
+++ b/libs/vr/libsensor/pose_client.cpp
@@ -3,8 +3,7 @@
 
 #include <stdint.h>
 
-#include <base/logging.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <pdx/client.h>
 #include <pdx/default_transport/client_channel_factory.h>
 #include <pdx/file_handle.h>
@@ -18,6 +17,8 @@
 using android::pdx::Status;
 using android::pdx::Transaction;
 
+#define arraysize(x) (static_cast<int32_t>(std::extent<decltype(x)>::value))
+
 namespace android {
 namespace dvr {
 
@@ -64,8 +65,7 @@
 
   int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
                         DvrPoseAsync* out_pose) {
-    if (controller_id < 0 ||
-        controller_id >= static_cast<int32_t>(arraysize(controllers_))) {
+    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
       return -EINVAL;
     }
     if (!controllers_[controller_id].mapped_pose_buffer) {
@@ -154,14 +154,14 @@
     }
     pose_buffer_.swap(buffer);
     mapped_pose_buffer_ = static_cast<const DvrPoseRingBuffer*>(addr);
-    LOG(INFO) << "Mapped pose data translation "
-              << mapped_pose_buffer_->ring[0].translation[0] << ','
-              << mapped_pose_buffer_->ring[0].translation[1] << ','
-              << mapped_pose_buffer_->ring[0].translation[2] << ", quat "
-              << mapped_pose_buffer_->ring[0].orientation[0] << ','
-              << mapped_pose_buffer_->ring[0].orientation[1] << ','
-              << mapped_pose_buffer_->ring[0].orientation[2] << ','
-              << mapped_pose_buffer_->ring[0].orientation[3];
+    ALOGI("Mapped pose data translation %f,%f,%f quat %f,%f,%f,%f",
+          mapped_pose_buffer_->ring[0].translation[0],
+          mapped_pose_buffer_->ring[0].translation[1],
+          mapped_pose_buffer_->ring[0].translation[2],
+          mapped_pose_buffer_->ring[0].orientation[0],
+          mapped_pose_buffer_->ring[0].orientation[1],
+          mapped_pose_buffer_->ring[0].orientation[2],
+          mapped_pose_buffer_->ring[0].orientation[3]);
     if (out_info) {
       GetPoseRingBufferInfo(out_info);
     }
@@ -169,8 +169,7 @@
   }
 
   int GetControllerRingBuffer(int32_t controller_id) {
-    if (controller_id < 0 ||
-        controller_id >= static_cast<int32_t>(arraysize(controllers_))) {
+    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
       return -EINVAL;
     }
     ControllerClientState& client_state = controllers_[controller_id];
@@ -183,8 +182,6 @@
         DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
         sizeof(controller_id), nullptr, 0);
     if (!status) {
-      ALOGE("Pose GetControllerRingBuffer() failed because: %s",
-            status.GetErrorMessage().c_str());
       return -status.error();
     }
 
@@ -202,15 +199,15 @@
     }
     client_state.pose_buffer.swap(buffer);
     client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
-    LOG(INFO) << "Mapped controller " << controller_id
-              << " pose data translation "
-              << client_state.mapped_pose_buffer[0].translation[0] << ','
-              << client_state.mapped_pose_buffer[0].translation[1] << ','
-              << client_state.mapped_pose_buffer[0].translation[2] << ", quat "
-              << client_state.mapped_pose_buffer[0].orientation[0] << ','
-              << client_state.mapped_pose_buffer[0].orientation[1] << ','
-              << client_state.mapped_pose_buffer[0].orientation[2] << ','
-              << client_state.mapped_pose_buffer[0].orientation[3];
+    ALOGI(
+        "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
+        controller_id, client_state.mapped_pose_buffer[0].translation[0],
+        client_state.mapped_pose_buffer[0].translation[1],
+        client_state.mapped_pose_buffer[0].translation[2],
+        client_state.mapped_pose_buffer[0].orientation[0],
+        client_state.mapped_pose_buffer[0].orientation[1],
+        client_state.mapped_pose_buffer[0].orientation[2],
+        client_state.mapped_pose_buffer[0].orientation[3]);
     return 0;
   }
 
diff --git a/libs/vr/libsensor/sensor_client.cpp b/libs/vr/libsensor/sensor_client.cpp
index 1c240f5..04e88cc 100644
--- a/libs/vr/libsensor/sensor_client.cpp
+++ b/libs/vr/libsensor/sensor_client.cpp
@@ -1,7 +1,7 @@
 #define LOG_TAG "SensorClient"
 #include <private/dvr/sensor_client.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <poll.h>
 
 #include <pdx/default_transport/client_channel_factory.h>
diff --git a/libs/vr/libsensor/tests/sensor_app_tests.cpp b/libs/vr/libsensor/tests/sensor_app_tests.cpp
index 8cd6f79..0f5bf00 100644
--- a/libs/vr/libsensor/tests/sensor_app_tests.cpp
+++ b/libs/vr/libsensor/tests/sensor_app_tests.cpp
@@ -2,10 +2,10 @@
 #include <GLES2/gl2.h>
 #include <math.h>
 
-#include <base/logging.h>
 #include <dvr/graphics.h>
 #include <dvr/pose_client.h>
 #include <gtest/gtest.h>
+#include <log/log.h>
 #include <private/dvr/types.h>
 
 using android::dvr::vec4;
@@ -63,9 +63,9 @@
     // startup anomalies.
     if (i > 0) {
       if (last_vsync_count == schedule.vsync_count)
-        LOG(ERROR) << "vsync did not increment: " << schedule.vsync_count;
+        ALOGE("vsync did not increment: %u", schedule.vsync_count);
       if (pose.timestamp_ns == last_pose.timestamp_ns)
-        LOG(ERROR) << "timestamp did not change: " << pose.timestamp_ns;
+        ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
       // TODO(jbates) figure out why the bots are not passing this check.
       // EXPECT_NE(last_vsync_count, schedule.vsync_count);
       // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
@@ -112,9 +112,9 @@
     // startup anomalies.
     if (i > 0) {
       if (last_vsync_count == schedule.vsync_count)
-        LOG(ERROR) << "vsync did not increment: " << schedule.vsync_count;
+        ALOGE("vsync did not increment: %u", schedule.vsync_count);
       if (pose.timestamp_ns == last_pose.timestamp_ns)
-        LOG(ERROR) << "timestamp did not change: " << pose.timestamp_ns;
+        ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
       // TODO(jbates) figure out why the bots are not passing this check.
       // EXPECT_NE(last_vsync_count, schedule.vsync_count);
       // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 6f5947a..0e05d54 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -9,6 +9,7 @@
     DisplayDevice.cpp \
     DispSync.cpp \
     EventControlThread.cpp \
+    StartBootAnimThread.cpp \
     EventThread.cpp \
     FrameTracker.cpp \
     GpuService.cpp \
diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/EventControlThread.h
index 9368db6..1b1ef75 100644
--- a/services/surfaceflinger/EventControlThread.h
+++ b/services/surfaceflinger/EventControlThread.h
@@ -45,4 +45,4 @@
 
 }
 
-#endif // ANDROID_DISPSYNC_H
+#endif // ANDROID_EVENTCONTROLTHREAD_H
diff --git a/services/surfaceflinger/StartBootAnimThread.cpp b/services/surfaceflinger/StartBootAnimThread.cpp
new file mode 100644
index 0000000..c3f7296
--- /dev/null
+++ b/services/surfaceflinger/StartBootAnimThread.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 <cutils/properties.h>
+#include "StartBootAnimThread.h"
+
+namespace android {
+
+StartBootAnimThread::StartBootAnimThread():
+        Thread(false) {
+}
+
+status_t StartBootAnimThread::Start() {
+    return run("SurfaceFlinger::StartBootAnimThread", PRIORITY_NORMAL);
+}
+
+bool StartBootAnimThread::threadLoop() {
+    property_set("service.bootanim.exit", "0");
+    property_set("ctl.start", "bootanim");
+    // Exit immediately
+    return false;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/StartBootAnimThread.h b/services/surfaceflinger/StartBootAnimThread.h
new file mode 100644
index 0000000..dba2bee
--- /dev/null
+++ b/services/surfaceflinger/StartBootAnimThread.h
@@ -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.
+ */
+
+#ifndef ANDROID_STARTBOOTANIMTHREAD_H
+#define ANDROID_STARTBOOTANIMTHREAD_H
+
+#include <stddef.h>
+
+#include <utils/Mutex.h>
+#include <utils/Thread.h>
+
+namespace android {
+
+class StartBootAnimThread : public Thread {
+// Boot animation is triggered via calls to "property_set()" which can block
+// if init's executing slow operation such as 'mount_all --late' (currently
+// happening 1/10th with fsck)  concurrently. Running in a separate thread
+// allows to pursue the SurfaceFlinger's init process without blocking.
+// see b/34499826.
+public:
+    StartBootAnimThread();
+    status_t Start();
+private:
+    virtual bool threadLoop();
+};
+
+}
+
+#endif // ANDROID_STARTBOOTANIMTHREAD_H
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bd3836e..9e25e07 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -338,6 +338,9 @@
 
 void SurfaceFlinger::bootFinished()
 {
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -579,16 +582,22 @@
 
     mRenderEngine->primeCache();
 
-    // start boot animation
-    startBootAnim();
+    mStartBootAnimThread = new StartBootAnimThread();
+    if (mStartBootAnimThread->Start() != NO_ERROR) {
+        ALOGE("Run StartBootAnimThread failed!");
+    }
 
     ALOGV("Done initializing");
 }
 
 void SurfaceFlinger::startBootAnim() {
-    // start boot animation
-    property_set("service.bootanim.exit", "0");
-    property_set("ctl.start", "bootanim");
+    // Start boot animation service by setting a property mailbox
+    // if property setting thread is already running, Start() will be just a NOP
+    mStartBootAnimThread->Start();
+    // Wait until property was set
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
 }
 
 size_t SurfaceFlinger::getMaxTextureSize() const {
@@ -2429,8 +2438,7 @@
         if (s.client != NULL) {
             sp<IBinder> binder = IInterface::asBinder(s.client);
             if (binder != NULL) {
-                String16 desc(binder->getInterfaceDescriptor());
-                if (desc == ISurfaceComposerClient::descriptor) {
+                if (binder->queryLocalInterface(ISurfaceComposerClient::descriptor) != NULL) {
                     sp<Client> client( static_cast<Client *>(s.client.get()) );
                     transactionFlags |= setClientStateLocked(client, s.state);
                 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 75c1920..55735b1 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -58,6 +58,7 @@
 #include "LayerVector.h"
 #include "MessageQueue.h"
 #include "SurfaceInterceptor.h"
+#include "StartBootAnimThread.h"
 
 #include "DisplayHardware/HWComposer.h"
 #include "Effects/Daltonizer.h"
@@ -345,6 +346,8 @@
             bool useIdentityTransform, Transform::orientation_flags rotation,
             bool isLocalScreenshot);
 
+    sp<StartBootAnimThread> mStartBootAnimThread = nullptr;
+
     /* ------------------------------------------------------------------------
      * EGL
      */
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index f8a1f34..fe9ba96 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -317,6 +317,9 @@
 
 void SurfaceFlinger::bootFinished()
 {
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -589,8 +592,12 @@
 
     mRenderEngine->primeCache();
 
-    // start boot animation
-    startBootAnim();
+    mStartBootAnimThread = new StartBootAnimThread();
+    if (mStartBootAnimThread->Start() != NO_ERROR) {
+        ALOGE("Run StartBootAnimThread failed!");
+    }
+
+    ALOGV("Done initializing");
 }
 
 int32_t SurfaceFlinger::allocateHwcDisplayId(DisplayDevice::DisplayType type) {
@@ -599,9 +606,13 @@
 }
 
 void SurfaceFlinger::startBootAnim() {
-    // start boot animation
-    property_set("service.bootanim.exit", "0");
-    property_set("ctl.start", "bootanim");
+    // Start boot animation service by setting a property mailbox
+    // if property setting thread is already running, Start() will be just a NOP
+    mStartBootAnimThread->Start();
+    // Wait until property was set
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
 }
 
 size_t SurfaceFlinger::getMaxTextureSize() const {
@@ -2316,8 +2327,7 @@
         if (s.client != NULL) {
             sp<IBinder> binder = IInterface::asBinder(s.client);
             if (binder != NULL) {
-                String16 desc(binder->getInterfaceDescriptor());
-                if (desc == ISurfaceComposerClient::descriptor) {
+                if (binder->queryLocalInterface(ISurfaceComposerClient::descriptor) != NULL) {
                     sp<Client> client( static_cast<Client *>(s.client.get()) );
                     transactionFlags |= setClientStateLocked(client, s.state);
                 }
diff --git a/services/vr/bufferhubd/Android.mk b/services/vr/bufferhubd/Android.mk
index 492acb2..4ba2373 100644
--- a/services/vr/bufferhubd/Android.mk
+++ b/services/vr/bufferhubd/Android.mk
@@ -23,7 +23,6 @@
     producer_queue_channel.cpp \
 
 staticLibraries := \
-	libchrome \
 	libperformance \
 	libpdx_default_transport \
 	libbufferhub
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index a0c7439..12243dc 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -1,6 +1,6 @@
 #include "buffer_hub.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <poll.h>
 #include <utils/Trace.h>
 
diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp
index a8e2ddf..d4fc540 100644
--- a/services/vr/bufferhubd/bufferhubd.cpp
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -1,7 +1,7 @@
 #include <sched.h>
 #include <unistd.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <dvr/performance_client_api.h>
 #include <pdx/default_transport/service_dispatcher.h>
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
index 8db92a3..4f0ca4e 100644
--- a/services/vr/bufferhubd/consumer_channel.cpp
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -1,6 +1,6 @@
 #include "consumer_channel.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <utils/Trace.h>
 
 #include <thread>
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index b87b709..c1ef22c 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -1,6 +1,6 @@
 #include "producer_channel.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <sync/sync.h>
 #include <sys/poll.h>
 #include <utils/Trace.h>
@@ -9,7 +9,6 @@
 #include <atomic>
 #include <thread>
 
-#include <base/logging.h>
 #include <private/dvr/bufferhub_rpc.h>
 #include "consumer_channel.h"
 
diff --git a/services/vr/performanced/cpu_set.cpp b/services/vr/performanced/cpu_set.cpp
index 916226e..1a3723f 100644
--- a/services/vr/performanced/cpu_set.cpp
+++ b/services/vr/performanced/cpu_set.cpp
@@ -1,6 +1,6 @@
 #include "cpu_set.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <algorithm>
 #include <iomanip>
diff --git a/services/vr/performanced/main.cpp b/services/vr/performanced/main.cpp
index 114413d..ca66c71 100644
--- a/services/vr/performanced/main.cpp
+++ b/services/vr/performanced/main.cpp
@@ -3,9 +3,9 @@
 #include <sys/prctl.h>
 #include <sys/stat.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
+#include <log/log.h>
 #include <sys/resource.h>
 #include <utils/threads.h>
 
diff --git a/services/vr/performanced/task.cpp b/services/vr/performanced/task.cpp
index ad12858..1175a7b 100644
--- a/services/vr/performanced/task.cpp
+++ b/services/vr/performanced/task.cpp
@@ -1,8 +1,8 @@
 #include "task.h"
 
-#include <cutils/log.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <log/log.h>
 #include <stdio.h>
 
 #include <cctype>
diff --git a/services/vr/sensord/Android.mk b/services/vr/sensord/Android.mk
index 907c3d6..36d8400 100644
--- a/services/vr/sensord/Android.mk
+++ b/services/vr/sensord/Android.mk
@@ -32,7 +32,6 @@
 	libperformance \
 	libbufferhub \
 	libpdx_default_transport \
-	libchrome \
 	libposepredictor \
 
 sharedLibraries := \
diff --git a/services/vr/sensord/pose_service.cpp b/services/vr/sensord/pose_service.cpp
index c2863ee..8e4dbba 100644
--- a/services/vr/sensord/pose_service.cpp
+++ b/services/vr/sensord/pose_service.cpp
@@ -11,12 +11,12 @@
 #include <sstream>
 #include <type_traits>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
 #include <cutils/trace.h>
 #include <dvr/performance_client_api.h>
 #include <dvr/pose_client.h>
 #include <hardware/sensors.h>
+#include <log/log.h>
 #include <pdx/default_transport/service_endpoint.h>
 #include <private/dvr/benchmark.h>
 #include <private/dvr/clock_ns.h>
@@ -57,8 +57,7 @@
 
 // Device type property for controlling classes of behavior that differ
 // between devices. If unset, defaults to kOrientationTypeSmartphone.
-static constexpr char kOrientationTypeProp[] = "dvr.orientation_type";
-
+static constexpr char kOrientationTypeProp[] = "ro.dvr.orientation_type";
 static constexpr char kEnableSensorRecordProp[] = "dvr.enable_6dof_recording";
 static constexpr char kEnableSensorPlayProp[] = "dvr.enable_6dof_playback";
 static constexpr char kEnableSensorPlayIdProp[] = "dvr.6dof_playback_id";
diff --git a/services/vr/sensord/sensor_hal_thread.cpp b/services/vr/sensord/sensor_hal_thread.cpp
index 59b433f..c321d4f 100644
--- a/services/vr/sensord/sensor_hal_thread.cpp
+++ b/services/vr/sensord/sensor_hal_thread.cpp
@@ -1,7 +1,7 @@
 #include "sensor_hal_thread.h"
 
-#include <cutils/log.h>
 #include <dvr/performance_client_api.h>
+#include <log/log.h>
 
 namespace android {
 namespace dvr {
diff --git a/services/vr/sensord/sensor_ndk_thread.cpp b/services/vr/sensord/sensor_ndk_thread.cpp
index b5e16e7..815453b 100644
--- a/services/vr/sensord/sensor_ndk_thread.cpp
+++ b/services/vr/sensord/sensor_ndk_thread.cpp
@@ -1,7 +1,7 @@
 #include "sensor_ndk_thread.h"
 
-#include <cutils/log.h>
 #include <dvr/performance_client_api.h>
+#include <log/log.h>
 
 namespace android {
 namespace dvr {
diff --git a/services/vr/sensord/sensor_service.cpp b/services/vr/sensord/sensor_service.cpp
index 4396851..1b809b0 100644
--- a/services/vr/sensord/sensor_service.cpp
+++ b/services/vr/sensord/sensor_service.cpp
@@ -1,9 +1,9 @@
 #include "sensor_service.h"
 
-#include <cutils/log.h>
 #include <hardware/sensors.h>
-#include <poll.h>
+#include <log/log.h>
 #include <pdx/default_transport/service_endpoint.h>
+#include <poll.h>
 #include <private/dvr/sensor-ipc.h>
 #include <time.h>
 
diff --git a/services/vr/virtual_touchpad/EvdevInjector.cpp b/services/vr/virtual_touchpad/EvdevInjector.cpp
index be20c6c..d8a1dfa 100644
--- a/services/vr/virtual_touchpad/EvdevInjector.cpp
+++ b/services/vr/virtual_touchpad/EvdevInjector.cpp
@@ -1,9 +1,9 @@
 #include "EvdevInjector.h"
 
-#include <cutils/log.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <linux/input.h>
+#include <log/log.h>
 #include <string.h>
 #include <sys/fcntl.h>
 #include <unistd.h>
diff --git a/services/vr/virtual_touchpad/VirtualTouchpad.cpp b/services/vr/virtual_touchpad/VirtualTouchpad.cpp
index b137dd7..4793058 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpad.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpad.cpp
@@ -1,20 +1,29 @@
 #include "VirtualTouchpad.h"
 
-#include <cutils/log.h>
+#include <android/input.h>
 #include <inttypes.h>
 #include <linux/input.h>
+#include <log/log.h>
+
+// References:
+//  [0] Multi-touch (MT) Protocol,
+//      https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
 
 namespace android {
 namespace dvr {
 
 namespace {
 
-// Virtual evdev device properties.
+// Virtual evdev device properties. The name is arbitrary, but Android can
+// use it to look up device configuration, so it must be unique. Vendor and
+// product values must be 0 to indicate an internal device and prevent a
+// similar lookup that could conflict with a physical device.
 static const char* const kDeviceName = "vr window manager virtual touchpad";
 static constexpr int16_t kDeviceBusType = BUS_VIRTUAL;
-static constexpr int16_t kDeviceVendor = 0x18D1;   // Google USB vendor ID.
-static constexpr int16_t kDeviceProduct = 0x5652;  // 'VR'
+static constexpr int16_t kDeviceVendor = 0;
+static constexpr int16_t kDeviceProduct = 0;
 static constexpr int16_t kDeviceVersion = 0x0001;
+
 static constexpr int32_t kWidth = 0x10000;
 static constexpr int32_t kHeight = 0x10000;
 static constexpr int32_t kSlots = 2;
@@ -32,18 +41,24 @@
   injector_->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
   injector_->ConfigureAbsSlots(kSlots);
   injector_->ConfigureKey(BTN_TOUCH);
+  injector_->ConfigureKey(BTN_BACK);
   injector_->ConfigureEnd();
   return injector_->GetError();
 }
 
 int VirtualTouchpad::Touch(float x, float y, float pressure) {
-  int error = 0;
+  if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) {
+    return EINVAL;
+  }
   int32_t device_x = x * kWidth;
   int32_t device_y = y * kHeight;
   touches_ = ((touches_ & 1) << 1) | (pressure > 0);
   ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d",
         x, y, pressure, device_x, device_y, touches_);
 
+  if (!injector_) {
+    return EvdevInjector::ERROR_SEQUENCING;
+  }
   injector_->ResetError();
   switch (touches_) {
     case 0b00:  // Hover continues.
@@ -76,5 +91,30 @@
   return injector_->GetError();
 }
 
+int VirtualTouchpad::ButtonState(int buttons) {
+  const int changes = last_motion_event_buttons_ ^ buttons;
+  if (!changes) {
+    return 0;
+  }
+  if (buttons & ~AMOTION_EVENT_BUTTON_BACK) {
+    return ENOTSUP;
+  }
+  ALOGV("change %X from %X to %X", changes, last_motion_event_buttons_,
+        buttons);
+
+  if (!injector_) {
+    return EvdevInjector::ERROR_SEQUENCING;
+  }
+  injector_->ResetError();
+  if (changes & AMOTION_EVENT_BUTTON_BACK) {
+    injector_->SendKey(BTN_BACK,
+                       (buttons & AMOTION_EVENT_BUTTON_BACK)
+                           ? EvdevInjector::KEY_PRESS
+                           : EvdevInjector::KEY_RELEASE);
+  }
+  last_motion_event_buttons_ = buttons;
+  return injector_->GetError();
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpad.h b/services/vr/virtual_touchpad/VirtualTouchpad.h
index 7e7801e..17aeb35 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpad.h
+++ b/services/vr/virtual_touchpad/VirtualTouchpad.h
@@ -10,12 +10,39 @@
 
 class EvdevInjector;
 
+// Provides a virtual touchpad for injecting events into the input system.
+//
 class VirtualTouchpad {
  public:
   VirtualTouchpad() {}
+  ~VirtualTouchpad() {}
+
+  // |Intialize()| must be called once on a VirtualTouchpad before
+  // and other public method. Returns zero on success.
   int Initialize();
+
+  // Generate a simulated touch event.
+  //
+  // @param x Horizontal touch position.
+  // @param y Vertical touch position.
+  //            Values must be in the range [0.0, 1.0).
+  // @param pressure Touch pressure.
+  //            Positive values represent contact; use 1.0f if contact
+  //            is binary. Use 0.0f for no contact.
+  // @returns Zero on success.
+  //
   int Touch(float x, float y, float pressure);
 
+  // Generate a simulated touchpad button state.
+  //
+  // @param buttons A union of MotionEvent BUTTON_* values.
+  // @returns Zero on success.
+  //
+  // Currently only BUTTON_BACK is supported, as the implementation
+  // restricts itself to operations actually required by VrWindowManager.
+  //
+  int ButtonState(int buttons);
+
  protected:
   // Must be called only between construction and Initialize().
   inline void SetEvdevInjectorForTesting(EvdevInjector* injector) {
@@ -23,17 +50,23 @@
   }
 
  private:
-  // Active pointer to |owned_injector_| or to a testing injector.
-  EvdevInjector* injector_ = nullptr;
+  // Except for testing, the |EvdevInjector| used to inject evdev events.
   std::unique_ptr<EvdevInjector> owned_injector_;
 
-  // Previous (x,y) position to suppress redundant events.
+  // Active pointer to |owned_injector_| or to a testing injector.
+  EvdevInjector* injector_ = nullptr;
+
+  // Previous (x, y) position in device space, to suppress redundant events.
   int32_t last_device_x_ = INT32_MIN;
   int32_t last_device_y_ = INT32_MIN;
 
-  // Records current touch state in bit 0 and previous state in bit 1.
+  // Records current touch state (0=up 1=down) in bit 0, and previous state
+  // in bit 1, to track transitions.
   int touches_ = 0;
 
+  // Previous injected button state, to detect changes.
+  int32_t last_motion_event_buttons_ = 0;
+
   VirtualTouchpad(const VirtualTouchpad&) = delete;
   void operator=(const VirtualTouchpad&) = delete;
 };
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
index e5ead0e..25c1a4f 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -1,8 +1,8 @@
 #include "VirtualTouchpadService.h"
 
 #include <binder/Status.h>
-#include <cutils/log.h>
 #include <linux/input.h>
+#include <log/log.h>
 #include <utils/Errors.h>
 
 namespace android {
@@ -13,11 +13,16 @@
 }
 
 binder::Status VirtualTouchpadService::touch(float x, float y, float pressure) {
-  // Permissions check added and removed here :^)
   const int error = touchpad_.Touch(x, y, pressure);
   return error ? binder::Status::fromServiceSpecificError(error)
                : binder::Status::ok();
 }
 
+binder::Status VirtualTouchpadService::buttonState(int buttons) {
+  const int error = touchpad_.ButtonState(buttons);
+  return error ? binder::Status::fromServiceSpecificError(error)
+               : binder::Status::ok();
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.h b/services/vr/virtual_touchpad/VirtualTouchpadService.h
index 05a2a50..e2426e3 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.h
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.h
@@ -8,6 +8,9 @@
 namespace android {
 namespace dvr {
 
+// VirtualTouchpadService implements the service side of
+// the Binder interface defined in VirtualTouchpadService.aidl.
+//
 class VirtualTouchpadService : public BnVirtualTouchpadService {
  public:
   VirtualTouchpadService(VirtualTouchpad& touchpad)
@@ -22,6 +25,7 @@
  protected:
   // Implements IVirtualTouchpadService.
   ::android::binder::Status touch(float x, float y, float pressure) override;
+  ::android::binder::Status buttonState(int buttons) override;
 
  private:
   VirtualTouchpad& touchpad_;
diff --git a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
index da4de94..e048837 100644
--- a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
+++ b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
@@ -13,4 +13,11 @@
    * Position values in the range [0.0, 1.0) map to the screen.
    */
   void touch(float x, float y, float pressure);
+
+  /**
+   * Generate a simulated touchpad button state event.
+   *
+   * @param buttons A union of MotionEvent BUTTON_* values.
+   */
+  void buttonState(int buttons);
 }
diff --git a/services/vr/virtual_touchpad/main.cpp b/services/vr/virtual_touchpad/main.cpp
index 57471c5..1debe9f 100644
--- a/services/vr/virtual_touchpad/main.cpp
+++ b/services/vr/virtual_touchpad/main.cpp
@@ -1,7 +1,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include "VirtualTouchpadService.h"
 
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
index 874ef80..256c6bc 100644
--- a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -1,3 +1,4 @@
+#include <android/input.h>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
@@ -200,6 +201,28 @@
 
   expect.Reset();
   record.Reset();
+  touch_status = touchpad.ButtonState(AMOTION_EVENT_BUTTON_BACK);
+  EXPECT_EQ(0, touch_status);
+  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.ButtonState(AMOTION_EVENT_BUTTON_BACK);
+  EXPECT_EQ(0, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.ButtonState(0);
+  EXPECT_EQ(0, touch_status);
+  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
 }
 
 TEST_F(VirtualTouchpadTest, Badness) {
@@ -216,6 +239,14 @@
   EXPECT_NE(0, touch_status);
   EXPECT_EQ(expect.GetString(), record.GetString());
 
+  // Button change before initialization should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.ButtonState(AMOTION_EVENT_BUTTON_BACK);
+  EXPECT_NE(0, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
   expect.Reset();
   record.Reset();
   touchpad.Initialize();
@@ -227,6 +258,28 @@
   const int initialization_status = touchpad.Initialize();
   EXPECT_NE(0, initialization_status);
   EXPECT_EQ(expect.GetString(), record.GetString());
+
+  // Touch off-screen should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.Touch(-0.25f, 0.75f, 1.0f);
+  EXPECT_NE(0, touch_status);
+  touch_status = touchpad.Touch(0.25f, -0.75f, 1.0f);
+  EXPECT_NE(0, touch_status);
+  touch_status = touchpad.Touch(1.25f, 0.75f, 1.0f);
+  EXPECT_NE(0, touch_status);
+  touch_status = touchpad.Touch(0.25f, 1.75f, 1.0f);
+  EXPECT_NE(0, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  // Unsupported button should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.ButtonState(AMOTION_EVENT_BUTTON_FORWARD);
+  EXPECT_NE(0, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
 }
 
 }  // namespace dvr
diff --git a/services/vr/vr_window_manager/Android.mk_disable b/services/vr/vr_window_manager/Android.mk_disable
index adce4b9..9a6f752 100644
--- a/services/vr/vr_window_manager/Android.mk_disable
+++ b/services/vr/vr_window_manager/Android.mk_disable
@@ -14,6 +14,19 @@
 
 LOCAL_PATH := $(call my-dir)
 
+native_src := \
+  application.cpp \
+  controller_mesh.cpp \
+  elbow_model.cpp \
+  hwc_callback.cpp \
+  reticle.cpp \
+  render_thread.cpp \
+  shell_view.cpp \
+  surface_flinger_view.cpp \
+  texture.cpp \
+  vr_window_manager.cpp \
+  ../virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl \
+
 src := \
   vr_window_manager_jni.cpp \
   application.cpp \
@@ -38,7 +51,6 @@
   libsensor \
   libperformance \
   libpdx_default_transport \
-  libchrome \
   libcutils \
 
 shared_libs := \
@@ -66,7 +78,7 @@
 LOCAL_SRC_FILES := $(src)
 LOCAL_C_INCLUDES := hardware/qcom/display/msm8996/libgralloc
 LOCAL_STATIC_LIBRARIES := $(static_libs)
-LOCAL_SHARED_LIBRARIES := $(shared_libs) libevent
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
 LOCAL_SHARED_LIBRARIES += libgvr
 LOCAL_STATIC_LIBRARIES += libgvr_ext
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
@@ -80,6 +92,22 @@
 include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(native_src)
+LOCAL_C_INCLUDES := hardware/qcom/display/msm8996/libgralloc
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
+LOCAL_SHARED_LIBRARIES += libgvr
+LOCAL_STATIC_LIBRARIES += libgvr_ext
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DLOG_TAG=\"VrWindowManager\"
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE := vr_wm
+LOCAL_MODULE_TAGS := optional
+LOCAL_INIT_RC := vr_wm.rc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := VrWindowManager
 
 # We need to be priveleged to run as the system user, which is necessary for
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
index f84a0d1..b4568b8 100644
--- a/services/vr/vr_window_manager/application.cpp
+++ b/services/vr/vr_window_manager/application.cpp
@@ -1,14 +1,14 @@
 #include "application.h"
 
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
 #include <binder/IServiceManager.h>
-#include <cutils/log.h>
 #include <dvr/graphics.h>
 #include <dvr/performance_client_api.h>
 #include <dvr/pose_client.h>
-#include <EGL/egl.h>
-#include <GLES3/gl3.h>
 #include <gui/ISurfaceComposer.h>
 #include <hardware/hwcomposer_defs.h>
+#include <log/log.h>
 #include <private/dvr/graphics/vr_gl_extensions.h>
 
 #include <vector>
@@ -100,6 +100,16 @@
   fov_[1] = FieldOfView(lens_info.right_fov[0], lens_info.right_fov[1],
                         lens_info.right_fov[2], lens_info.right_fov[3]);
 
+  if (java_env_) {
+    int ret = InitializeController();
+    if (ret)
+      return ret;
+  }
+
+  return 0;
+}
+
+int Application::InitializeController() {
   gvr_context_ = gvr::GvrApi::Create(java_env_, app_context_, class_loader_);
   if (gvr_context_ == nullptr) {
     ALOGE("Gvr context creation failed");
@@ -170,7 +180,7 @@
   // Thread should block if we are invisible or not fully initialized.
   std::unique_lock<std::mutex> lock(mutex_);
   wake_up_init_and_render_.wait(lock, [this]() {
-    return is_visible_ && initialized_ || !main_thread_tasks_.empty();
+    return (is_visible_ && initialized_) || !main_thread_tasks_.empty();
   });
 
   // Process main thread tasks if there are any.
@@ -245,6 +255,9 @@
 }
 
 void Application::ProcessControllerInput() {
+  if (!controller_)
+    return;
+
   controller_state_->Update(*controller_);
   gvr::ControllerApiStatus new_api_status = controller_state_->GetApiStatus();
   gvr::ControllerConnectionState new_connection_state =
@@ -286,10 +299,12 @@
   if (changed) {
     is_visible_ = visible;
     dvrGraphicsSurfaceSetVisible(graphics_context_, is_visible_);
-    if (is_visible_)
-      controller_->Resume();
-    else
-      controller_->Pause();
+    if (controller_) {
+      if (is_visible_)
+        controller_->Resume();
+      else
+        controller_->Pause();
+    }
     OnVisibilityChanged(is_visible_);
   }
 }
diff --git a/services/vr/vr_window_manager/application.h b/services/vr/vr_window_manager/application.h
index 3321682..47a0927 100644
--- a/services/vr/vr_window_manager/application.h
+++ b/services/vr/vr_window_manager/application.h
@@ -54,6 +54,8 @@
 
   void QueueTask(MainThreadTask task);
 
+  int InitializeController();
+
   DvrGraphicsContext* graphics_context_ = nullptr;
   DvrPose* pose_client_ = nullptr;
 
diff --git a/services/vr/vr_window_manager/composer_view/Android.bp_disable b/services/vr/vr_window_manager/composer_view/Android.bp_disable
index 7e25c85..1658154 100644
--- a/services/vr/vr_window_manager/composer_view/Android.bp_disable
+++ b/services/vr/vr_window_manager/composer_view/Android.bp_disable
@@ -14,6 +14,7 @@
     "libbinder",
     "libhardware",
     "libhwbinder",
+    "liblog",
     "libutils",
     "libvrhwc",
   ],
diff --git a/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp b/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp
index b5030e3..54dff3d 100644
--- a/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp
+++ b/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp
@@ -26,15 +26,18 @@
 
   const char instance[] = "vr_hwcomposer";
   sp<IComposer> service = HIDL_FETCH_IComposer(instance);
-  LOG_FATAL_IF(!service, "Failed to get service");
-  LOG_FATAL_IF(service->isRemote(), "Service is remote");
+  LOG_ALWAYS_FATAL_IF(!service.get(), "Failed to get service");
+  LOG_ALWAYS_FATAL_IF(service->isRemote(), "Service is remote");
 
-  service->registerAsService(instance);
+  LOG_ALWAYS_FATAL_IF(service->registerAsService(instance) != ::android::OK,
+                      "Failed to register service");
 
   sp<IVrComposerView> composer_view = HIDL_FETCH_IVrComposerView(
       "DaydreamDisplay");
-  LOG_FATAL_IF(!composer_view, "Failed to get vr_composer_view service");
-  LOG_FATAL_IF(composer_view->isRemote(), "vr_composer_view service is remote");
+  LOG_ALWAYS_FATAL_IF(!composer_view.get(),
+                      "Failed to get vr_composer_view service");
+  LOG_ALWAYS_FATAL_IF(composer_view->isRemote(),
+                      "vr_composer_view service is remote");
 
   composer_view->registerAsService("DaydreamDisplay");
 
diff --git a/services/vr/vr_window_manager/elbow_model.cpp b/services/vr/vr_window_manager/elbow_model.cpp
index 54d1eb4..9543f17 100644
--- a/services/vr/vr_window_manager/elbow_model.cpp
+++ b/services/vr/vr_window_manager/elbow_model.cpp
@@ -1,6 +1,6 @@
 #include "elbow_model.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 namespace android {
 namespace dvr {
diff --git a/services/vr/vr_window_manager/render_thread.cpp b/services/vr/vr_window_manager/render_thread.cpp
index 00e3161..5f777e3 100644
--- a/services/vr/vr_window_manager/render_thread.cpp
+++ b/services/vr/vr_window_manager/render_thread.cpp
@@ -1,6 +1,6 @@
-#include <cutils/log.h>
-#include <future>
 #include <jni.h>
+#include <log/log.h>
+#include <future>
 
 #include "render_thread.h"
 #include "shell_view.h"
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
index cef111c..ca49db7 100644
--- a/services/vr/vr_window_manager/shell_view.cpp
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -1,10 +1,11 @@
 #include "shell_view.h"
 
-#include <binder/IServiceManager.h>
-#include <cutils/log.h>
 #include <EGL/eglext.h>
 #include <GLES3/gl3.h>
+#include <android/input.h>
+#include <binder/IServiceManager.h>
 #include <hardware/hwcomposer2.h>
+#include <log/log.h>
 
 #include "controller_mesh.h"
 #include "texture.h"
@@ -572,7 +573,7 @@
   vec3 pointer_location = last_pose_.GetPosition();
   quat view_quaternion = last_pose_.GetRotation();
 
-  if (controller_api_status_ == gvr::kControllerApiOk) {
+  if (controller_ && controller_api_status_ == gvr::kControllerApiOk) {
     view_quaternion = FromGvrQuatf(controller_orientation_);
     vec4 controller_location = controller_translate_ * vec4(0, 0, 0, 1);
     pointer_location = vec3(controller_location.x(), controller_location.y(),
@@ -583,6 +584,12 @@
 
     if (controller_state_->GetButtonUp(gvr::kControllerButtonClick))
       OnClick(false);
+
+    if (controller_state_->GetButtonDown(gvr::kControllerButtonApp))
+      OnTouchpadButton(true, AMOTION_EVENT_BUTTON_BACK);
+
+    if (controller_state_->GetButtonUp(gvr::kControllerButtonApp))
+      OnTouchpadButton(false, AMOTION_EVENT_BUTTON_BACK);
   }
 
   vec3 view_direction = vec3(view_quaternion * vec3(0, 0, -1));
@@ -611,6 +618,9 @@
 
 void ShellView::DrawController(const mat4& perspective, const mat4& eye_matrix,
                                const mat4& head_matrix) {
+  if (!controller_)
+    return;
+
   controller_program_->Use();
   mat4 mvp = perspective * eye_matrix * head_matrix;
 
@@ -665,5 +675,32 @@
   }
 }
 
+bool ShellView::OnTouchpadButton(bool down, int button) {
+  int buttons = touchpad_buttons_;
+  if (down) {
+    if (allow_input_) {
+      buttons |= button;
+    }
+  } else {
+    buttons &= ~button;
+  }
+  if (buttons == touchpad_buttons_) {
+    return true;
+  }
+  touchpad_buttons_ = buttons;
+  if (!virtual_touchpad_.get()) {
+    ALOGE("missing virtual touchpad");
+    return false;
+  }
+
+  const android::binder::Status status =
+      virtual_touchpad_->buttonState(touchpad_buttons_);
+  if (!status.isOk()) {
+    ALOGE("touchpad button failed: %d %s", touchpad_buttons_,
+          status.toString8().string());
+  }
+  return true;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/vr_window_manager/shell_view.h b/services/vr/vr_window_manager/shell_view.h
index 0688c94..589902e 100644
--- a/services/vr/vr_window_manager/shell_view.h
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -50,6 +50,7 @@
                 vec3 *hit_location);
   bool InitializeTouch();
   void Touch();
+  bool OnTouchpadButton(bool down, int button);
 
   void OnDrawFrame() override;
   void DrawWithTransform(const mat4& transform, const ShaderProgram& program);
@@ -81,6 +82,7 @@
 
   bool is_touching_ = false;
   bool allow_input_ = false;
+  int touchpad_buttons_ = 0;
   vec2 hit_location_in_window_coord_;
   vec2 ime_top_left_;
   vec2 ime_size_;
diff --git a/services/vr/vr_window_manager/texture.cpp b/services/vr/vr_window_manager/texture.cpp
index dbd91b7..2229efa 100644
--- a/services/vr/vr_window_manager/texture.cpp
+++ b/services/vr/vr_window_manager/texture.cpp
@@ -1,7 +1,7 @@
 #include "texture.h"
 
-#include <cutils/log.h>
 #include <GLES/glext.h>
+#include <log/log.h>
 #include <system/window.h>
 
 namespace android {
diff --git a/services/vr/vr_window_manager/vr_window_manager.cpp b/services/vr/vr_window_manager/vr_window_manager.cpp
new file mode 100644
index 0000000..736a14f
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager.cpp
@@ -0,0 +1,23 @@
+#include <binder/ProcessState.h>
+
+#include "shell_view.h"
+
+int main(int /* argc */, char** /* argv */) {
+  android::ProcessState::self()->startThreadPool();
+
+  android::dvr::ShellView app;
+  if (app.Initialize(nullptr, nullptr, nullptr)) {
+    ALOGE("Failed to initialize");
+    return 1;
+  }
+
+  if (app.AllocateResources()) {
+    ALOGE("Failed to allocate resources");
+    return 1;
+  }
+
+  while (true)
+    app.DrawFrame();
+
+  return 0;
+}
diff --git a/services/vr/vr_window_manager/vr_window_manager_jni.cpp b/services/vr/vr_window_manager/vr_window_manager_jni.cpp
index f52658a..49eaba1 100644
--- a/services/vr/vr_window_manager/vr_window_manager_jni.cpp
+++ b/services/vr/vr_window_manager/vr_window_manager_jni.cpp
@@ -1,5 +1,5 @@
-#include <cutils/log.h>
 #include <jni.h>
+#include <log/log.h>
 
 #include <memory>
 
diff --git a/services/vr/vr_window_manager/vr_wm.rc b/services/vr/vr_window_manager/vr_wm.rc
new file mode 100644
index 0000000..143b916
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_wm.rc
@@ -0,0 +1,9 @@
+service vr_wm /system/bin/vr_wm
+  class core
+  user system
+  group system graphics input
+  cpuset /system
+  disabled
+
+on property:persist.daydream.vr_wm=1
+  enable vr_wm