Add DaydreamVR native libraries and services

Upstreaming the main VR system components from master-dreamos-dev
into goog/master.

Bug: None
Test: `m -j32` succeeds. Sailfish boots and basic_vr sample app works
Change-Id: I853015872afc443aecee10411ef2d6b79184d051
diff --git a/libs/vr/libbufferhub/Android.mk b/libs/vr/libbufferhub/Android.mk
new file mode 100644
index 0000000..467f69f
--- /dev/null
+++ b/libs/vr/libbufferhub/Android.mk
@@ -0,0 +1,57 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	buffer_hub_client.cpp \
+	buffer_hub_rpc.cpp \
+	ion_buffer.cpp
+
+includeFiles := \
+	$(LOCAL_PATH)/include
+
+staticLibraries := \
+	libchrome \
+	libdvrcommon \
+	libpdx_default_transport \
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	libhardware \
+	liblog \
+	libui \
+	libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libbufferhub\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libbufferhub
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := bufferhub_tests.cpp
+LOCAL_STATIC_LIBRARIES := libbufferhub $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := bufferhub_tests
+include $(BUILD_NATIVE_TEST)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
new file mode 100644
index 0000000..146780e
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -0,0 +1,417 @@
+#include <private/dvr/buffer_hub_client.h>
+
+#include <cutils/log.h>
+#include <poll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/platform_defines.h>
+
+#include "include/private/dvr/bufferhub_rpc.h"
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::WrapBuffer;
+using android::pdx::Status;
+
+namespace {
+
+constexpr int kUncachedBlobUsageFlags = GRALLOC_USAGE_SW_READ_RARELY |
+                                        GRALLOC_USAGE_SW_WRITE_RARELY |
+                                        GRALLOC_USAGE_PRIVATE_UNCACHED;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
+    : Client{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))},
+      id_(-1) {}
+BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
+    : Client{pdx::default_transport::ClientChannelFactory::Create(
+          endpoint_path)},
+      id_(-1) {}
+
+BufferHubBuffer::~BufferHubBuffer() {}
+
+Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
+  Status<LocalChannelHandle> status =
+      InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
+  ALOGE_IF(!status,
+           "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
+           status.GetErrorMessage().c_str());
+  return status;
+}
+
+int BufferHubBuffer::ImportBuffer() {
+  ATRACE_NAME("BufferHubBuffer::ImportBuffer");
+  if (!IonBuffer::GetGrallocModule())
+    return -EIO;
+
+  Status<std::vector<NativeBufferHandle<LocalHandle>>> status =
+      InvokeRemoteMethod<BufferHubRPC::GetBuffers>();
+  if (!status) {
+    ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffers: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  } else if (status.get().empty()) {
+    ALOGE(
+        "BufferHubBuffer::ImportBuffer: Expected to receive at least one "
+        "buffer handle but got zero!");
+    return -EIO;
+  }
+
+  auto buffer_handles = status.take();
+
+  // Stash the buffer id to replace the value in id_. All sub-buffers of a
+  // buffer hub buffer have the same id.
+  const int new_id = buffer_handles[0].id();
+
+  // Import all of the buffers.
+  std::vector<IonBuffer> ion_buffers;
+  for (auto& handle : buffer_handles) {
+    const size_t i = &handle - buffer_handles.data();
+    ALOGD_IF(
+        TRACE,
+        "BufferHubBuffer::ImportBuffer: i=%zu id=%d FdCount=%zu IntCount=%zu",
+        i, handle.id(), handle.FdCount(), handle.IntCount());
+
+    IonBuffer buffer;
+    const int ret = handle.Import(&buffer);
+    if (ret < 0)
+      return ret;
+
+    ion_buffers.emplace_back(std::move(buffer));
+  }
+
+  // If all imports succeed, replace the previous buffers and id.
+  slices_ = std::move(ion_buffers);
+  id_ = new_id;
+  return 0;
+}
+
+int BufferHubBuffer::Poll(int timeout_ms) {
+  ATRACE_NAME("BufferHubBuffer::Poll");
+  pollfd p = {event_fd(), POLLIN, 0};
+  return poll(&p, 1, timeout_ms);
+}
+
+int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height,
+                          void** address, size_t index) {
+  return slices_[index].Lock(usage, x, y, width, height, address);
+}
+
+int BufferHubBuffer::Unlock(size_t index) { return slices_[index].Unlock(); }
+
+int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) {
+  int width = static_cast<int>(size);
+  int height = 1;
+  constexpr int usage = GRALLOC_USAGE_SW_READ_RARELY |
+                        GRALLOC_USAGE_SW_WRITE_RARELY |
+                        GRALLOC_USAGE_PRIVATE_UNCACHED;
+  int ret = Lock(usage, 0, 0, width, height, addr);
+  if (ret == 0)
+    Unlock();
+  return ret;
+}
+
+int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) {
+  int width = static_cast<int>(size);
+  int height = 1;
+  constexpr int usage =
+      GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_PRIVATE_UNCACHED;
+  int ret = Lock(usage, 0, 0, width, height, addr);
+  if (ret == 0)
+    Unlock();
+  return ret;
+}
+
+BufferConsumer::BufferConsumer(LocalChannelHandle channel)
+    : BASE(std::move(channel)) {
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s",
+          strerror(-ret));
+    Close(ret);
+  }
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+    LocalChannelHandle channel) {
+  ATRACE_NAME("BufferConsumer::Import");
+  ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value());
+  return BufferConsumer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+    Status<LocalChannelHandle> status) {
+  return Import(status ? status.take()
+                       : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence) {
+  return Acquire(ready_fence, nullptr, 0);
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta,
+                            size_t meta_size_bytes) {
+  ATRACE_NAME("BufferConsumer::Acquire");
+  LocalFence fence;
+  auto return_value =
+      std::make_pair(std::ref(fence), WrapBuffer(meta, meta_size_bytes));
+  auto status = InvokeRemoteMethodInPlace<BufferHubRPC::ConsumerAcquire>(
+      &return_value, meta_size_bytes);
+  if (status && ready_fence)
+    *ready_fence = fence.take();
+  return status ? 0 : -status.error();
+}
+
+int BufferConsumer::Release(const LocalHandle& release_fence) {
+  ATRACE_NAME("BufferConsumer::Release");
+  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
+      BorrowedFence(release_fence.Borrow())));
+}
+
+int BufferConsumer::ReleaseAsync() {
+  ATRACE_NAME("BufferConsumer::ReleaseAsync");
+  return ReturnStatusOrError(
+      SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
+}
+
+int BufferConsumer::Discard() { return Release(LocalHandle()); }
+
+int BufferConsumer::SetIgnore(bool ignore) {
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore));
+}
+
+BufferProducer::BufferProducer(int width, int height, int format, int usage,
+                               size_t metadata_size, size_t slice_count)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE,
+           "BufferProducer::BufferProducer: fd=%d width=%d height=%d format=%d "
+           "usage=%d, metadata_size=%zu, slice_count=%zu",
+           event_fd(), width, height, format, usage, metadata_size,
+           slice_count);
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, usage, metadata_size, slice_count);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
+        status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+                               int group_id, int width, int height, int format,
+                               int usage, size_t meta_size_bytes,
+                               size_t slice_count)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE,
+           "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d "
+           "group_id=%d width=%d height=%d format=%d usage=%d, "
+           "meta_size_bytes=%zu, slice_count=%zu",
+           event_fd(), name.c_str(), user_id, group_id, width, height, format,
+           usage, meta_size_bytes, slice_count);
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+      name, user_id, group_id, width, height, format, usage, meta_size_bytes,
+      slice_count);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create/get persistent "
+        "buffer \"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(int usage, size_t size)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE, "BufferProducer::BufferProducer: usage=%d size=%zu", usage,
+           size);
+  const int width = static_cast<int>(size);
+  const int height = 1;
+  const int format = HAL_PIXEL_FORMAT_BLOB;
+  const size_t meta_size_bytes = 0;
+  const size_t slice_count = 1;
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, usage, meta_size_bytes, slice_count);
+  if (!status) {
+    ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
+          status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+                               int group_id, int usage, size_t size)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE,
+           "BufferProducer::BufferProducer: name=%s user_id=%d group=%d "
+           "usage=%d size=%zu",
+           name.c_str(), user_id, group_id, usage, size);
+  const int width = static_cast<int>(size);
+  const int height = 1;
+  const int format = HAL_PIXEL_FORMAT_BLOB;
+  const size_t meta_size_bytes = 0;
+  const size_t slice_count = 1;
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+      name, user_id, group_id, width, height, format, usage, meta_size_bytes,
+      slice_count);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create persistent "
+        "buffer \"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s", name.c_str());
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::GetPersistentBuffer>(name);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to get producer buffer by name "
+        "\"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(LocalChannelHandle channel)
+    : BASE(std::move(channel)) {
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
+                         size_t meta_size_bytes) {
+  ATRACE_NAME("BufferProducer::Post");
+  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
+      BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes)));
+}
+
+int BufferProducer::Gain(LocalHandle* release_fence) {
+  ATRACE_NAME("BufferProducer::Gain");
+  auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
+  if (!status)
+    return -status.error();
+  if (release_fence)
+    *release_fence = status.take().take();
+  return 0;
+}
+
+int BufferProducer::GainAsync() {
+  ATRACE_NAME("BufferProducer::GainAsync");
+  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+    LocalChannelHandle channel) {
+  ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
+  return BufferProducer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+    Status<LocalChannelHandle> status) {
+  return Import(status ? status.take()
+                       : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferProducer::MakePersistent(const std::string& name, int user_id,
+                                   int group_id) {
+  ATRACE_NAME("BufferProducer::MakePersistent");
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ProducerMakePersistent>(name, user_id,
+                                                               group_id));
+}
+
+int BufferProducer::RemovePersistence() {
+  ATRACE_NAME("BufferProducer::RemovePersistence");
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ProducerRemovePersistence>());
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreateUncachedBlob(
+    size_t size) {
+  return BufferProducer::Create(kUncachedBlobUsageFlags, size);
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreatePersistentUncachedBlob(
+    const std::string& name, int user_id, int group_id, size_t size) {
+  return BufferProducer::Create(name, user_id, group_id,
+                                kUncachedBlobUsageFlags, size);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub_rpc.cpp b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
new file mode 100644
index 0000000..9a67faa
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/bufferhub_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char BufferHubRPC::kClientPath[];
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
new file mode 100644
index 0000000..cb45dbe
--- /dev/null
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -0,0 +1,219 @@
+#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>
+
+using android::dvr::BufferProducer;
+using android::dvr::BufferConsumer;
+using android::pdx::LocalHandle;
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const uint64_t kContext = 42;
+
+using LibBufferHubTest = ::testing::Test;
+
+TEST_F(LibBufferHubTest, TestBasicUsage) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  // Check that consumers can spawn other consumers.
+  std::unique_ptr<BufferConsumer> c2 =
+      BufferConsumer::Import(c->CreateConsumer());
+  ASSERT_TRUE(c2.get() != nullptr);
+
+  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)));
+
+  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_LE(0, c2->Acquire(&fence, &context));
+  EXPECT_EQ(kContext, context);
+  EXPECT_GE(0, HANDLE_EINTR(c2->Poll(0)));
+
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, c2->Discard());
+
+  EXPECT_LE(0, HANDLE_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, p->Gain(&fence));
+  EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  Metadata m = {1, 3};
+  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+  EXPECT_LE(0, HANDLE_EINTR(c->Poll(10)));
+
+  LocalHandle fence;
+  Metadata m2 = {};
+  EXPECT_EQ(0, c->Acquire(&fence, &m2));
+  EXPECT_EQ(m.field1, m2.field1);
+  EXPECT_EQ(m.field2, m2.field2);
+
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_LT(0, HANDLE_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+  EXPECT_GE(0, HANDLE_EINTR(c->Poll(10)));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  Metadata m = {1, 3};
+  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+
+  LocalHandle fence;
+  int64_t sequence;
+  EXPECT_NE(0, c->Acquire(&fence, &sequence));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+
+  LocalHandle fence;
+  EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestWithNoMeta) {
+  std::unique_ptr<BufferProducer> p =
+      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  LocalHandle fence;
+
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+  EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) {
+  std::unique_ptr<BufferProducer> p =
+      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferPersistence) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  // Record the original buffer id for later comparison.
+  const int buffer_id = p->id();
+
+  auto c = BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_NE(nullptr, c);
+
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+
+  // Close the connection to the producer. This should not affect the consumer.
+  p = nullptr;
+
+  LocalHandle fence;
+  EXPECT_EQ(0, c->Acquire(&fence));
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+
+  // Attempt to reconnect to the persistent buffer.
+  p = BufferProducer::Create("TestPersistentBuffer");
+  ASSERT_NE(nullptr, p);
+  EXPECT_EQ(buffer_id, p->id());
+  EXPECT_EQ(0, p->Gain(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferMismatchParams) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  // Close the connection to the producer.
+  p = nullptr;
+
+  // Mismatch the params.
+  p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth * 2,
+                             kHeight, kFormat, kUsage);
+  ASSERT_EQ(nullptr, p);
+}
+
+TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  LocalHandle fence;
+  auto c = BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_NE(nullptr, c);
+  EXPECT_NE(-EPIPE, c->Acquire(&fence));
+
+  // Test that removing persistence and closing the producer orphans the
+  // consumer.
+  EXPECT_EQ(0, p->RemovePersistence());
+  p = nullptr;
+
+  EXPECT_EQ(-EPIPE, c->Release(LocalHandle()));
+}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..b6ff5b6
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -0,0 +1,313 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+
+#include <hardware/gralloc.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+#include <vector>
+
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubBuffer : public pdx::Client {
+ public:
+  using LocalHandle = pdx::LocalHandle;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  template <typename T>
+  using Status = pdx::Status<T>;
+
+  // Create a new consumer channel that is attached to the producer. Returns
+  // a file descriptor for the new channel or a negative error code.
+  Status<LocalChannelHandle> CreateConsumer();
+
+  // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
+  int Poll(int timeout_ms);
+
+  // Locks the area specified by (x, y, width, height) for a specific usage. If
+  // the usage is software then |addr| will be updated to point to the address
+  // of the buffer in virtual memory. The caller should only access/modify the
+  // pixels in the specified area. anything else is undefined behavior.
+  int Lock(int usage, int x, int y, int width, int height, void** addr,
+           size_t index);
+
+  // Must be called after Lock() when the caller has finished changing the
+  // buffer.
+  int Unlock(size_t index);
+
+  // Helper for when index is 0.
+  int Lock(int usage, int x, int y, int width, int height, void** addr) {
+    return Lock(usage, x, y, width, height, addr, 0);
+  }
+
+  // Helper for when index is 0.
+  int Unlock() { return Unlock(0); }
+
+  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+  // Locking and Unlocking is handled internally. There's no need to Unlock
+  // after calling this method.
+  int GetBlobReadWritePointer(size_t size, void** addr);
+
+  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+  // Locking and Unlocking is handled internally. There's no need to Unlock
+  // after calling this method.
+  int GetBlobReadOnlyPointer(size_t size, void** addr);
+
+  // Returns a dup'd file descriptor for accessing the blob shared memory. The
+  // caller takes ownership of the file descriptor and must close it or pass on
+  // ownership. Some GPU API extensions can take file descriptors to bind shared
+  // memory gralloc buffers to GPU buffer objects.
+  LocalHandle GetBlobFd() const {
+    // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
+    // vendors and this is the wrong fd, late-latching and EDS will very clearly
+    // stop working and we will need to correct this. The alternative is to use
+    // a GL context in the pose service to allocate this buffer or to use the
+    // ION API directly instead of gralloc.
+    return LocalHandle(dup(native_handle()->data[0]));
+  }
+
+  using Client::event_fd;
+  native_handle_t* native_handle() const {
+    return const_cast<native_handle_t*>(slices_[0].handle());
+  }
+  // If index is greater than or equal to slice_count(), the result is
+  // undefined.
+  native_handle_t* native_handle(size_t index) const {
+    return const_cast<native_handle_t*>(slices_[index].handle());
+  }
+
+  IonBuffer* buffer() { return &slices_[0]; }
+  // If index is greater than or equal to slice_count(), the result is
+  // undefined.
+  IonBuffer* slice(size_t index) { return &slices_[index]; }
+
+  int slice_count() const { return static_cast<int>(slices_.size()); }
+  int id() const { return id_; }
+
+  // The following methods return settings of the first buffer. Currently,
+  // it is only possible to create multi-buffer BufferHubBuffers with the same
+  // settings.
+  int width() const { return slices_[0].width(); }
+  int height() const { return slices_[0].height(); }
+  int stride() const { return slices_[0].stride(); }
+  int format() const { return slices_[0].format(); }
+  int usage() const { return slices_[0].usage(); }
+
+ protected:
+  explicit BufferHubBuffer(LocalChannelHandle channel);
+  explicit BufferHubBuffer(const std::string& endpoint_path);
+  virtual ~BufferHubBuffer();
+
+  // Initialization helper.
+  int ImportBuffer();
+
+ private:
+  BufferHubBuffer(const BufferHubBuffer&) = delete;
+  void operator=(const BufferHubBuffer&) = delete;
+
+  // Global id for the buffer that is consistent across processes. It is meant
+  // for logging and debugging purposes only and should not be used for lookup
+  // or any other functional purpose as a security precaution.
+  int id_;
+
+  // A BufferHubBuffer may contain multiple slices of IonBuffers with same
+  // configurations.
+  std::vector<IonBuffer> slices_;
+};
+
+// This represents a writable buffer. Calling Post notifies all clients and
+// makes the buffer read-only. Call Gain to acquire write access. A buffer
+// may have many consumers.
+//
+// The user of BufferProducer is responsible with making sure that the Post() is
+// done with the correct metadata type and size. The user is also responsible
+// for making sure that remote ends (BufferConsumers) are also using the correct
+// metadata when acquiring the buffer. The API guarantees that a Post() with a
+// metadata of wrong size will fail. However, it currently does not do any
+// type checking.
+// The API also assumes that metadata is a serializable type (plain old data).
+class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> {
+ public:
+  // Create a buffer designed to hold arbitrary bytes that can be read and
+  // written from CPU, GPU and DSP. The buffer is mapped uncached so that CPU
+  // reads and writes are predictable.
+  static std::unique_ptr<BufferProducer> CreateUncachedBlob(size_t size);
+
+  // Creates a persistent uncached buffer with the given name and access.
+  static std::unique_ptr<BufferProducer> CreatePersistentUncachedBlob(
+      const std::string& name, int user_id, int group_id, size_t size);
+
+  // Imports a bufferhub producer channel, assuming ownership of its handle.
+  static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel);
+  static std::unique_ptr<BufferProducer> Import(
+      Status<LocalChannelHandle> status);
+
+  // Post this buffer, passing |ready_fence| to the consumers. The bytes in
+  // |meta| are passed unaltered to the consumers. The producer must not modify
+  // the buffer until it is re-gained.
+  // This returns zero or a negative unix error code.
+  int Post(const LocalHandle& ready_fence, const void* meta,
+           size_t meta_size_bytes);
+
+  template <typename Meta,
+            typename = typename std::enable_if<std::is_void<Meta>::value>::type>
+  int Post(const LocalHandle& ready_fence) {
+    return Post(ready_fence, nullptr, 0);
+  }
+  template <typename Meta, typename = typename std::enable_if<
+                               !std::is_void<Meta>::value>::type>
+  int Post(const LocalHandle& ready_fence, const Meta& meta) {
+    return Post(ready_fence, &meta, sizeof(meta));
+  }
+
+  // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
+  // must be waited on before using the buffer. If it is not valid then the
+  // buffer is free for immediate use. This call will only succeed if the buffer
+  // is in the released state.
+  // This returns zero or a negative unix error code.
+  int Gain(LocalHandle* release_fence);
+
+  // Asynchronously marks a released buffer as gained. This method is similar to
+  // the synchronous version above, except that it does not wait for BufferHub
+  // to acknowledge success or failure, nor does it transfer a release fence to
+  // the client. This version may be used in situations where a release fence is
+  // not needed. Because of the asynchronous nature of the underlying message,
+  // no error is returned if this method is called when the buffer is in an
+  // incorrect state. Returns zero if sending the message succeeded, or a
+  // negative errno code otherwise.
+  int GainAsync();
+
+  // Attaches the producer to |name| so that it becomes a persistent buffer that
+  // may be retrieved by name at a later time. This may be used in cases where a
+  // shared memory buffer should persist across the life of the producer process
+  // (i.e. the buffer may be held by clients across a service restart). The
+  // buffer may be associated with a user and/or group id to restrict access to
+  // the buffer. If user_id or group_id is -1 then checks for the respective id
+  // are disabled. If user_id or group_id is 0 then the respective id of the
+  // calling process is used instead.
+  int MakePersistent(const std::string& name, int user_id, int group_id);
+
+  // Removes the persistence of the producer.
+  int RemovePersistence();
+
+ private:
+  friend BASE;
+
+  // Constructors are automatically exposed through BufferProducer::Create(...)
+  // static template methods inherited from ClientBase, which take the same
+  // arguments as the constructors.
+
+  // Constructs a buffer with the given geometry and parameters.
+  BufferProducer(int width, int height, int format, int usage,
+                 size_t metadata_size = 0, size_t slice_count = 1);
+
+  // Constructs a persistent buffer with the given geometry and parameters and
+  // binds it to |name| in one shot. If a persistent buffer with the same name
+  // and settings already exists and matches the given geometry and parameters,
+  // that buffer is connected to this client instead of creating a new buffer.
+  // If the name matches but the geometry or settings do not match then
+  // construction fails and BufferProducer::Create() returns nullptr.
+  //
+  // Access to the persistent buffer may be restricted by |user_id| and/or
+  // |group_id|; these settings are established only when the buffer is first
+  // created and cannot be changed. A user or group id of -1 disables checks for
+  // that respective id. A user or group id of 0 is substituted with the
+  // effective user or group id of the calling process.
+  BufferProducer(const std::string& name, int user_id, int group_id, int width,
+                 int height, int format, int usage, size_t metadata_size = 0,
+                 size_t slice_count = 1);
+
+  // Constructs a blob (flat) buffer with the given usage flags.
+  BufferProducer(int usage, size_t size);
+
+  // Constructs a persistent blob (flat) buffer and binds it to |name|.
+  BufferProducer(const std::string& name, int user_id, int group_id, int usage,
+                 size_t size);
+
+  // Constructs a channel to persistent buffer by name only. The buffer must
+  // have been previously created or made persistent.
+  explicit BufferProducer(const std::string& name);
+
+  // Imports the given file handle to a producer channel, taking ownership.
+  explicit BufferProducer(LocalChannelHandle channel);
+};
+
+// This is a connection to a producer buffer, which can be located in another
+// application. When that buffer is Post()ed, this fd will be signaled and
+// Acquire allows read access. The user is responsible for making sure that
+// Acquire is called with the correct metadata structure. The only guarantee the
+// API currently provides is that an Acquire() with metadata of the wrong size
+// will fail.
+class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> {
+ public:
+  // This call assumes ownership of |fd|.
+  static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel);
+  static std::unique_ptr<BufferConsumer> Import(
+      Status<LocalChannelHandle> status);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| will be set to a fence to wait on until the buffer is ready.
+  // This call will only succeed after the fd is signalled. This call may be
+  // performed as an alternative to the Acquire() with metadata. In such cases
+  // the metadata is not read.
+  //
+  // This returns zero or negative unix error code.
+  int Acquire(LocalHandle* ready_fence);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| is set to a fence signaling that the contents of the buffer
+  // are available. This call will only succeed if the buffer is in the posted
+  // state.
+  // Returns zero on success, or a negative errno code otherwise.
+  int Acquire(LocalHandle* ready_fence, void* meta, size_t meta_size_bytes);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| is set to a fence to wait on until the buffer is ready. This
+  // call will only succeed after the fd is signaled. This returns zero or a
+  // negative unix error code.
+  template <typename Meta>
+  int Acquire(LocalHandle* ready_fence, Meta* meta) {
+    return Acquire(ready_fence, meta, sizeof(*meta));
+  }
+
+  // This should be called after a successful Acquire call. If the fence is
+  // valid the fence determines the buffer usage, otherwise the buffer is
+  // released immediately.
+  // This returns zero or a negative unix error code.
+  int Release(const LocalHandle& release_fence);
+
+  // Asynchronously releases a buffer. Similar to the synchronous version above,
+  // except that it does not wait for BufferHub to reply with success or error,
+  // nor does it transfer a release fence. This version may be used in
+  // situations where a release fence is not needed. Because of the asynchronous
+  // nature of the underlying message, no error is returned if this method is
+  // called when the buffer is in an incorrect state. Returns zero if sending
+  // the message succeeded, or a negative errno code otherwise.
+  int ReleaseAsync();
+
+  // May be called after or instead of Acquire to indicate that the consumer
+  // does not need to access the buffer this cycle. This returns zero or a
+  // negative unix error code.
+  int Discard();
+
+  // When set, this consumer is no longer notified when this buffer is
+  // available. The system behaves as if Discard() is immediately called
+  // whenever the buffer is posted. If ignore is set to true while a buffer is
+  // pending, it will act as if Discard() was also called.
+  // This returns zero or a negative unix error code.
+  int SetIgnore(bool ignore);
+
+ private:
+  friend BASE;
+
+  explicit BufferConsumer(LocalChannelHandle channel);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_CLIENT_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
new file mode 100644
index 0000000..ed11551
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -0,0 +1,214 @@
+#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_
+#define ANDROID_DVR_BUFFERHUB_RPC_H_
+
+#include <cutils/native_handle.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/types.h>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FileHandleType>
+class NativeBufferHandle {
+ public:
+  NativeBufferHandle() { Clear(); }
+  NativeBufferHandle(const IonBuffer& buffer, int id)
+      : id_(id),
+        stride_(buffer.stride()),
+        width_(buffer.width()),
+        height_(buffer.height()),
+        format_(buffer.format()),
+        usage_(buffer.usage()) {
+    // Populate the fd and int vectors: native_handle->data[] is an array of fds
+    // followed by an array of opaque ints.
+    const int fd_count = buffer.handle()->numFds;
+    const int int_count = buffer.handle()->numInts;
+    for (int i = 0; i < fd_count; i++) {
+      fds_.emplace_back(FileHandleType::AsDuplicate(buffer.handle()->data[i]));
+    }
+    for (int i = 0; i < int_count; i++) {
+      opaque_ints_.push_back(buffer.handle()->data[fd_count + i]);
+    }
+  }
+  NativeBufferHandle(NativeBufferHandle&& other) = default;
+
+  // Imports the native handle into the given IonBuffer instance.
+  int Import(IonBuffer* buffer) {
+    // This is annoying, but we need to convert the vector of FileHandles into a
+    // vector of ints for the Import API.
+    std::vector<int> fd_ints;
+    for (const auto& fd : fds_)
+      fd_ints.push_back(fd.Get());
+
+    const int ret = buffer->Import(fd_ints.data(), fd_ints.size(),
+                                   opaque_ints_.data(), opaque_ints_.size(),
+                                   width_, height_, stride_, format_, usage_);
+    if (ret < 0)
+      return ret;
+
+    // Import succeeded, release the file handles which are now owned by the
+    // IonBuffer and clear members.
+    for (auto& fd : fds_)
+      fd.Release();
+    opaque_ints_.clear();
+    Clear();
+
+    return 0;
+  }
+
+  int id() const { return id_; }
+  size_t IntCount() const { return opaque_ints_.size(); }
+  size_t FdCount() const { return fds_.size(); }
+
+ private:
+  int id_;
+  int stride_;
+  int width_;
+  int height_;
+  int format_;
+  int usage_;
+  std::vector<int> opaque_ints_;
+  std::vector<FileHandleType> fds_;
+
+  void Clear() { id_ = stride_ = width_ = height_ = format_ = usage_ = -1; }
+
+  PDX_SERIALIZABLE_MEMBERS(NativeBufferHandle<FileHandleType>, id_, stride_,
+                           width_, height_, format_, usage_, opaque_ints_,
+                           fds_);
+
+  NativeBufferHandle(const NativeBufferHandle&) = delete;
+  void operator=(const NativeBufferHandle&) = delete;
+};
+
+template <typename FileHandleType>
+class FenceHandle {
+ public:
+  FenceHandle() = default;
+  explicit FenceHandle(int fence) : fence_{fence} {}
+  explicit FenceHandle(FileHandleType&& fence) : fence_{std::move(fence)} {}
+  FenceHandle(FenceHandle&&) = default;
+  FenceHandle& operator=(FenceHandle&&) = default;
+
+  explicit operator bool() const { return fence_.IsValid(); }
+
+  const FileHandleType& get() const { fence_; }
+  FileHandleType&& take() { return std::move(fence_); }
+
+  int get_fd() const { return fence_.Get(); }
+  void close() { fence_.Close(); }
+
+  FenceHandle<pdx::BorrowedHandle> borrow() const {
+    return FenceHandle<pdx::BorrowedHandle>(fence_.Borrow());
+  }
+
+ private:
+  FileHandleType fence_;
+
+  PDX_SERIALIZABLE_MEMBERS(FenceHandle<FileHandleType>, fence_);
+
+  FenceHandle(const FenceHandle&) = delete;
+  void operator=(const FenceHandle&) = delete;
+};
+
+using LocalFence = FenceHandle<pdx::LocalHandle>;
+using BorrowedFence = FenceHandle<pdx::BorrowedHandle>;
+
+// BufferHub Service RPC interface. Defines the endpoints, op codes, and method
+// type signatures supported by bufferhubd.
+struct BufferHubRPC {
+  // Service path.
+  static constexpr char kClientPath[] = "system/buffer_hub/client";
+
+  // |BufferHubQueue| will keep track of at most this value of buffers.
+  // Attempts at runtime to increase the number of buffers past this
+  // will fail. Note that the value is in sync with |android::BufferQueue|, so
+  // that slot id can be shared between |android::dvr::BufferHubQueueProducer|
+  // and |android::BufferQueueProducer| which both implements the same
+  // interface: |android::IGraphicBufferProducer|.
+  static constexpr size_t kMaxQueueCapacity =
+      android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+  // Op codes.
+  enum {
+    kOpCreateBuffer = 0,
+    kOpCreatePersistentBuffer,
+    kOpGetPersistentBuffer,
+    kOpGetBuffer,
+    kOpGetBuffers,
+    kOpNewConsumer,
+    kOpProducerMakePersistent,
+    kOpProducerRemovePersistence,
+    kOpProducerPost,
+    kOpProducerGain,
+    kOpConsumerAcquire,
+    kOpConsumerRelease,
+    kOpConsumerSetIgnore,
+    kOpCreateProducerQueue,
+    kOpCreateConsumerQueue,
+    kOpProducerQueueAllocateBuffers,
+    kOpProducerQueueDetachBuffer,
+    kOpConsumerQueueImportBuffers,
+  };
+
+  // Aliases.
+  using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  using LocalHandle = pdx::LocalHandle;
+  using Void = pdx::rpc::Void;
+
+  // Methods.
+  PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer,
+                    int(int width, int height, int format, int usage,
+                        size_t meta_size_bytes, size_t slice_count));
+  PDX_REMOTE_METHOD(CreatePersistentBuffer, kOpCreatePersistentBuffer,
+                    int(const std::string& name, int user_id, int group_id,
+                        int width, int height, int format, int usage,
+                        size_t meta_size_bytes, size_t slice_count));
+  PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer,
+                    int(const std::string& name));
+  PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer,
+                    NativeBufferHandle<LocalHandle>(unsigned index));
+  PDX_REMOTE_METHOD(GetBuffers, kOpGetBuffers,
+                    std::vector<NativeBufferHandle<LocalHandle>>(Void));
+  PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void));
+  PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent,
+                    int(const std::string& name, int user_id, int group_id));
+  PDX_REMOTE_METHOD(ProducerRemovePersistence, kOpProducerRemovePersistence,
+                    int(Void));
+  PDX_REMOTE_METHOD(ProducerPost, kOpProducerPost,
+                    int(LocalFence acquire_fence, MetaData));
+  PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void));
+  PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire,
+                    std::pair<LocalFence, MetaData>(std::size_t metadata_size));
+  PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
+                    int(LocalFence release_fence));
+  PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, int(bool ignore));
+
+  // Buffer Queue Methods.
+  PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue,
+                    int(size_t meta_size_bytes, int usage_set_mask,
+                        int usage_clear_mask, int usage_deny_set_mask,
+                        int usage_deny_clear_mask));
+  PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue,
+                    std::pair<LocalChannelHandle, size_t>(Void));
+  PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers,
+                    kOpProducerQueueAllocateBuffers,
+                    std::vector<std::pair<LocalChannelHandle, size_t>>(
+                        int width, int height, int format, int usage,
+                        size_t slice_count, size_t buffer_count));
+  PDX_REMOTE_METHOD(ProducerQueueDetachBuffer, kOpProducerQueueDetachBuffer,
+                    int(size_t slot));
+  PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers,
+                    std::vector<std::pair<LocalChannelHandle, size_t>>(Void));
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFERHUB_RPC_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..8125c54
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -0,0 +1,108 @@
+#ifndef ANDROID_DVR_ION_BUFFER_H_
+#define ANDROID_DVR_ION_BUFFER_H_
+
+#include <hardware/gralloc.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+  IonBuffer();
+  IonBuffer(int width, int height, int format, int usage);
+  IonBuffer(buffer_handle_t handle, int width, int height, int stride,
+            int format, int usage);
+  IonBuffer(buffer_handle_t handle, int width, int height, int layer_count,
+            int stride, int layer_stride, int format, int usage);
+  ~IonBuffer();
+
+  IonBuffer(IonBuffer&& other);
+  IonBuffer& operator=(IonBuffer&& other);
+
+  // Frees the underlying native handle and leaves the instance initialized to
+  // empty.
+  void FreeHandle();
+
+  // Allocates a new native handle with the given parameters, freeing the
+  // previous native handle if necessary. Returns 0 on success or a negative
+  // errno code otherwise. If allocation fails the previous native handle is
+  // left intact.
+  int Alloc(int width, int height, int format, int usage);
+
+  // Resets the underlying native handle and parameters, freeing the previous
+  // native handle if necessary.
+  void Reset(buffer_handle_t handle, int width, int height, int stride,
+             int format, int usage);
+
+  // Like Reset but also registers the native handle, which is necessary for
+  // native handles received over IPC. Returns 0 on success or a negative errno
+  // code otherwise. If import fails the previous native handle is left intact.
+  int Import(buffer_handle_t handle, int width, int height, int stride,
+             int format, int usage);
+
+  // Like Reset but imports a native handle from raw fd and int arrays. Returns
+  // 0 on success or a negative errno code otherwise. If import fails the
+  // previous native handle is left intact.
+  int Import(const int* fd_array, int fd_count, const int* int_array,
+             int int_count, int width, int height, int stride, int format,
+             int usage);
+
+  // Duplicates the native handle underlying |other| and then imports it. This
+  // is useful for creating multiple, independent views of the same Ion/Gralloc
+  // buffer. Returns 0 on success or a negative errno code otherwise. If
+  // duplication or import fail the previous native handle is left intact.
+  int Duplicate(const IonBuffer* other);
+
+  int Lock(int usage, int x, int y, int width, int height, void** address);
+  int LockYUV(int usage, int x, int y, int width, int height,
+              struct android_ycbcr* yuv);
+  int Unlock();
+
+  buffer_handle_t handle() const { return handle_; }
+  int width() const { return width_; }
+  int height() const { return height_; }
+  int layer_count() const { return layer_count_; }
+  int stride() const { return stride_; }
+  int layer_stride() const { return layer_stride_; }
+  int format() const { return format_; }
+  int usage() const { return usage_; }
+
+  static gralloc_module_t const* GetGrallocModule() {
+    GrallocInit();
+    return gralloc_module_;
+  }
+
+  static alloc_device_t* GetGrallocDevice() {
+    GrallocInit();
+    return gralloc_device_;
+  }
+
+ private:
+  buffer_handle_t handle_;
+  int width_;
+  int height_;
+  int layer_count_;
+  int stride_;
+  int layer_stride_;
+  int format_;
+  int usage_;
+  bool locked_;
+  bool needs_unregister_;
+
+  void Replace(buffer_handle_t handle, int width, int height, int layer_count,
+               int stride, int layer_stride, int format, int usage,
+               bool needs_unregister);
+
+  static void GrallocInit();
+  static gralloc_module_t const* gralloc_module_;
+  static alloc_device_t* gralloc_device_;
+
+  IonBuffer(const IonBuffer&) = delete;
+  void operator=(const IonBuffer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_ION_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_buffer.h b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
new file mode 100644
index 0000000..f6c24d9
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
@@ -0,0 +1,226 @@
+#ifndef ANDROID_DVR_NATIVE_BUFFER_H_
+#define ANDROID_DVR_NATIVE_BUFFER_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android/native_window.h>
+#include <base/logging.h>
+#include <cutils/log.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+#include <utils/RefBase.h>
+
+#include <private/dvr/buffer_hub_client.h>
+
+namespace android {
+namespace dvr {
+
+// ANativeWindowBuffer is the abstraction Android HALs and frameworks use to
+// pass around hardware graphics buffers. The following classes implement this
+// abstraction with different DVR backing buffers, all of which provide
+// different semantics on top of ion/gralloc buffers.
+
+// An implementation of ANativeWindowBuffer backed by an IonBuffer.
+class NativeBuffer
+    : public android::ANativeObjectBase<ANativeWindowBuffer, NativeBuffer,
+                                        android::LightRefBase<NativeBuffer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  explicit NativeBuffer(const std::shared_ptr<IonBuffer>& buffer)
+      : BASE(), buffer_(buffer), fence_(kEmptyFence) {
+    ANativeWindowBuffer::width = buffer->width();
+    ANativeWindowBuffer::height = buffer->height();
+    ANativeWindowBuffer::stride = buffer->stride();
+    ANativeWindowBuffer::format = buffer->format();
+    ANativeWindowBuffer::usage = buffer->usage();
+    handle = buffer_->handle();
+  }
+
+  virtual ~NativeBuffer() {}
+
+  std::shared_ptr<IonBuffer> buffer() { return buffer_; }
+  int fence() const { return fence_.Get(); }
+
+  void SetFence(int fence) { fence_.Reset(fence); }
+
+ private:
+  friend class android::LightRefBase<NativeBuffer>;
+
+  std::shared_ptr<IonBuffer> buffer_;
+  pdx::LocalHandle fence_;
+
+  NativeBuffer(const NativeBuffer&) = delete;
+  void operator=(NativeBuffer&) = delete;
+};
+
+// NativeBufferProducerSlice is an implementation of ANativeWindowBuffer backed
+// by a buffer slice of a BufferProducer.
+class NativeBufferProducerSlice
+    : public android::ANativeObjectBase<
+          ANativeWindowBuffer, NativeBufferProducerSlice,
+          android::LightRefBase<NativeBufferProducerSlice>> {
+ public:
+  NativeBufferProducerSlice(const std::shared_ptr<BufferProducer>& buffer,
+                            int buffer_index)
+      : BASE(), buffer_(buffer) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle(buffer_index);
+  }
+
+  virtual ~NativeBufferProducerSlice() {}
+
+ private:
+  friend class android::LightRefBase<NativeBufferProducerSlice>;
+
+  std::shared_ptr<BufferProducer> buffer_;
+
+  NativeBufferProducerSlice(const NativeBufferProducerSlice&) = delete;
+  void operator=(NativeBufferProducerSlice&) = delete;
+};
+
+// NativeBufferProducer is an implementation of ANativeWindowBuffer backed by a
+// BufferProducer.
+class NativeBufferProducer : public android::ANativeObjectBase<
+  ANativeWindowBuffer, NativeBufferProducer,
+  android::LightRefBase<NativeBufferProducer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer,
+                       EGLDisplay display, uint32_t surface_buffer_index)
+      : BASE(),
+        buffer_(buffer),
+        surface_buffer_index_(surface_buffer_index),
+        display_(display) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle();
+    for (int i = 0; i < buffer->slice_count(); ++i) {
+      // display == null means don't create an EGL image. This is used by our
+      // Vulkan code.
+      slices_.push_back(new NativeBufferProducerSlice(buffer, i));
+      if (display_ != nullptr) {
+        egl_images_.push_back(eglCreateImageKHR(
+            display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+            static_cast<ANativeWindowBuffer*>(slices_.back().get()), nullptr));
+        if (egl_images_.back() == EGL_NO_IMAGE_KHR) {
+          ALOGE("NativeBufferProducer: eglCreateImageKHR failed");
+        }
+      }
+    }
+  }
+
+  explicit NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer)
+      : NativeBufferProducer(buffer, nullptr, 0) {}
+
+  virtual ~NativeBufferProducer() {
+    for (EGLImageKHR egl_image : egl_images_) {
+      if (egl_image != EGL_NO_IMAGE_KHR)
+        eglDestroyImageKHR(display_, egl_image);
+    }
+  }
+
+  EGLImageKHR image_khr(int index) const { return egl_images_[index]; }
+  std::shared_ptr<BufferProducer> buffer() const { return buffer_; }
+  int release_fence() const { return release_fence_.Get(); }
+  uint32_t surface_buffer_index() const { return surface_buffer_index_; }
+
+  // Return the release fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); }
+
+  // Post the buffer consumer, closing the acquire and release fences.
+  int Post(int acquire_fence, uint64_t sequence) {
+    release_fence_.Close();
+    return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence);
+  }
+
+  // Gain the buffer producer, closing the previous release fence if valid.
+  int Gain() { return buffer_->Gain(&release_fence_); }
+
+  // Asynchronously gain the buffer, closing the previous release fence.
+  int GainAsync() {
+    release_fence_.Close();
+    return buffer_->GainAsync();
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferProducer>;
+
+  std::shared_ptr<BufferProducer> buffer_;
+  pdx::LocalHandle release_fence_;
+  std::vector<android::sp<NativeBufferProducerSlice>> slices_;
+  std::vector<EGLImageKHR> egl_images_;
+  uint32_t surface_buffer_index_;
+  EGLDisplay display_;
+
+  NativeBufferProducer(const NativeBufferProducer&) = delete;
+  void operator=(NativeBufferProducer&) = delete;
+};
+
+// NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a
+// BufferConsumer.
+class NativeBufferConsumer : public android::ANativeObjectBase<
+                                 ANativeWindowBuffer, NativeBufferConsumer,
+                                 android::LightRefBase<NativeBufferConsumer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer,
+                                int index)
+      : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    CHECK(buffer_->slice_count() > index);
+    handle = buffer_->slice(index)->handle();
+  }
+
+  explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer)
+      : NativeBufferConsumer(buffer, 0) {}
+
+  virtual ~NativeBufferConsumer() {}
+
+  std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+  int acquire_fence() const { return acquire_fence_.Get(); }
+  uint64_t sequence() const { return sequence_; }
+
+  // Return the acquire fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); }
+
+  // Acquire the underlying buffer consumer, closing the previous acquire fence
+  // if valid.
+  int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); }
+
+  // Release the buffer consumer, closing the acquire and release fences if
+  // valid.
+  int Release(int release_fence) {
+    acquire_fence_.Close();
+    sequence_ = 0;
+    return buffer_->Release(pdx::LocalHandle(release_fence));
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferConsumer>;
+
+  std::shared_ptr<BufferConsumer> buffer_;
+  pdx::LocalHandle acquire_fence_;
+  uint64_t sequence_;
+
+  NativeBufferConsumer(const NativeBufferConsumer&) = delete;
+  void operator=(NativeBufferConsumer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_NATIVE_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
new file mode 100644
index 0000000..7d20049
--- /dev/null
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -0,0 +1,322 @@
+#include <private/dvr/ion_buffer.h>
+
+#include <cutils/log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+namespace android {
+namespace dvr {
+
+gralloc_module_t const* IonBuffer::gralloc_module_ = nullptr;
+alloc_device_t* IonBuffer::gralloc_device_ = nullptr;
+
+IonBuffer::IonBuffer() : IonBuffer(nullptr, 0, 0, 0, 0, 0, 0, 0) {}
+
+IonBuffer::IonBuffer(int width, int height, int format, int usage)
+    : IonBuffer() {
+  Alloc(width, height, format, usage);
+}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, int width, int height, int stride,
+                     int format, int usage)
+    : IonBuffer(handle, width, height, 1, stride, 0, format, usage) {}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, int width, int height,
+                     int layer_count, int stride, int layer_stride, int format,
+                     int usage)
+    : handle_(handle),
+      width_(width),
+      height_(height),
+      layer_count_(layer_count),
+      stride_(stride),
+      layer_stride_(layer_stride),
+      format_(format),
+      usage_(usage),
+      locked_(false),
+      needs_unregister_(false) {
+  ALOGD_IF(TRACE,
+           "IonBuffer::IonBuffer: handle=%p width=%d height=%d layer_count=%d "
+           "stride=%d layer stride=%d format=%d usage=%d",
+           handle_, width_, height_, layer_count_, stride_, layer_stride_,
+           format_, usage_);
+  GrallocInit();
+}
+
+IonBuffer::~IonBuffer() {
+  ALOGD_IF(TRACE,
+           "IonBuffer::~IonBuffer: handle=%p width=%d height=%d stride=%d "
+           "format=%d usage=%d",
+           handle_, width_, height_, stride_, format_, usage_);
+
+  FreeHandle();
+}
+
+IonBuffer::IonBuffer(IonBuffer&& other) : IonBuffer() {
+  *this = std::move(other);
+}
+
+IonBuffer& IonBuffer::operator=(IonBuffer&& other) {
+  ALOGD_IF(TRACE, "IonBuffer::operator=: handle_=%p other.handle_=%p", handle_,
+           other.handle_);
+
+  if (this != &other) {
+    Replace(other.handle_, other.width_, other.height_, other.layer_count_,
+            other.stride_, other.layer_stride_, other.format_, other.usage_,
+            other.needs_unregister_);
+    locked_ = other.locked_;
+    other.handle_ = nullptr;
+    other.FreeHandle();
+  }
+
+  return *this;
+}
+
+void IonBuffer::FreeHandle() {
+  if (handle_) {
+    // Lock/Unlock don't need to be balanced, but one Unlock is needed to
+    // clean/unmap the buffer. Warn if this didn't happen before freeing the
+    // native handle.
+    ALOGW_IF(locked_,
+             "IonBuffer::FreeHandle: freeing a locked handle!!! handle=%p",
+             handle_);
+
+    if (needs_unregister_) {
+      int ret = gralloc_module_->unregisterBuffer(gralloc_module_, handle_);
+      ALOGE_IF(ret < 0,
+               "IonBuffer::FreeHandle: Failed to unregister handle: %s",
+               strerror(-ret));
+
+      native_handle_close(const_cast<native_handle_t*>(handle_));
+      native_handle_delete(const_cast<native_handle_t*>(handle_));
+    } else {
+      int ret = gralloc_device_->free(gralloc_device_, handle_);
+      if (ret < 0) {
+        ALOGE("IonBuffer::FreeHandle: failed to free buffer: %s",
+              strerror(-ret));
+
+        // Not sure if this is the right thing to do. Attempting to prevent a
+        // memory leak of the native handle.
+        native_handle_close(const_cast<native_handle_t*>(handle_));
+        native_handle_delete(const_cast<native_handle_t*>(handle_));
+      }
+    }
+  }
+
+  // Always re-initialize these members, even if handle_ was nullptr, in case
+  // someone was dumb enough to pass a nullptr handle to the constructor or
+  // Reset.
+  handle_ = nullptr;
+  width_ = 0;
+  height_ = 0;
+  layer_count_ = 0;
+  stride_ = 0;
+  layer_stride_ = 0;
+  format_ = 0;
+  usage_ = 0;
+  locked_ = false;
+  needs_unregister_ = false;
+}
+
+int IonBuffer::Alloc(int width, int height, int format, int usage) {
+  ATRACE_NAME("IonBuffer::Alloc");
+  ALOGD_IF(TRACE, "IonBuffer::Alloc: width=%d height=%d format=%d usage=%d",
+           width, height, format, usage);
+
+  int stride;
+  buffer_handle_t handle;
+
+  int ret = gralloc_device_->alloc(gralloc_device_, width, height, format,
+                                   usage, &handle, &stride);
+  if (ret < 0) {
+    ALOGE("IonBuffer::Alloc: failed to allocate gralloc buffer: %s",
+          strerror(-ret));
+    return ret;
+  }
+
+  Replace(handle, width, height, 1, stride, 0, format, usage, false);
+  return 0;
+}
+
+void IonBuffer::Replace(buffer_handle_t handle, int width, int height,
+                        int layer_count, int stride, int layer_stride,
+                        int format, int usage, bool needs_unregister) {
+  FreeHandle();
+
+  handle_ = handle;
+  width_ = width;
+  height_ = height;
+  layer_count_ = layer_count;
+  stride_ = stride;
+  layer_stride_ = layer_stride;
+  format_ = format;
+  usage_ = usage;
+  needs_unregister_ = needs_unregister;
+}
+
+void IonBuffer::Reset(buffer_handle_t handle, int width, int height, int stride,
+                      int format, int usage) {
+  ALOGD_IF(TRACE,
+           "IonBuffer::Reset: handle=%p width=%d height=%d stride=%d format=%d "
+           "usage=%d",
+           handle, width, height, stride, format, usage);
+
+  Replace(handle, width, height, 1, stride, 0, format, usage, false);
+}
+
+int IonBuffer::Import(buffer_handle_t handle, int width, int height, int stride,
+                      int format, int usage) {
+  ATRACE_NAME("IonBuffer::Import1");
+  ALOGD_IF(
+      TRACE,
+      "IonBuffer::Import: handle=%p width=%d height=%d stride=%d format=%d "
+      "usage=%d",
+      handle, width, height, stride, format, usage);
+
+  int ret = gralloc_module_->registerBuffer(gralloc_module_, handle);
+  if (ret < 0) {
+    ALOGE("IonBuffer::Import: failed to import handle: %s", strerror(-ret));
+    return ret;
+  }
+
+  Replace(handle, width, height, 1, stride, 0, format, usage, true);
+  return 0;
+}
+
+int IonBuffer::Import(const int* fd_array, int fd_count, const int* int_array,
+                      int int_count, int width, int height, int stride,
+                      int format, int usage) {
+  ATRACE_NAME("IonBuffer::Import2");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Import: fd_count=%d int_count=%d width=%d height=%d "
+           "stride=%d format=%d usage=%d",
+           fd_count, int_count, width, height, stride, format, usage);
+
+  if (fd_count < 0 || int_count < 0) {
+    ALOGE("IonBuffer::Import: invalid arguments.");
+    return -EINVAL;
+  }
+
+  native_handle_t* handle = native_handle_create(fd_count, int_count);
+  if (!handle) {
+    ALOGE("IonBuffer::Import: failed to create new native handle.");
+    return -ENOMEM;
+  }
+
+  // Copy fd_array into the first part of handle->data and int_array right
+  // after it.
+  memcpy(handle->data, fd_array, sizeof(int) * fd_count);
+  memcpy(handle->data + fd_count, int_array, sizeof(int) * int_count);
+
+  int ret = Import(handle, width, height, stride, format, usage);
+  if (ret < 0) {
+    ALOGE("IonBuffer::Import: failed to import raw native handle: %s",
+          strerror(-ret));
+    native_handle_close(handle);
+    native_handle_delete(handle);
+  }
+
+  return ret;
+}
+
+int IonBuffer::Duplicate(const IonBuffer* other) {
+  if (!other->handle())
+    return -EINVAL;
+
+  const int fd_count = other->handle()->numFds;
+  const int int_count = other->handle()->numInts;
+
+  if (fd_count < 0 || int_count < 0)
+    return -EINVAL;
+
+  native_handle_t* handle = native_handle_create(fd_count, int_count);
+  if (!handle) {
+    ALOGE("IonBuffer::Duplicate: Failed to create new native handle.");
+    return -ENOMEM;
+  }
+
+  // Duplicate the file descriptors from the other native handle.
+  for (int i = 0; i < fd_count; i++)
+    handle->data[i] = dup(other->handle()->data[i]);
+
+  // Copy the ints after the file descriptors.
+  memcpy(handle->data + fd_count, other->handle()->data + fd_count,
+         sizeof(int) * int_count);
+
+  const int ret = Import(handle, other->width(), other->height(),
+                         other->stride(), other->format(), other->usage());
+  if (ret < 0) {
+    ALOGE("IonBuffer::Duplicate: Failed to import duplicate native handle: %s",
+          strerror(-ret));
+    native_handle_close(handle);
+    native_handle_delete(handle);
+  }
+
+  return ret;
+}
+
+int IonBuffer::Lock(int usage, int x, int y, int width, int height,
+                    void** address) {
+  ATRACE_NAME("IonBuffer::Lock");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d "
+           "address=%p",
+           handle_, usage, x, y, width, height, address);
+
+  // Lock may be called multiple times; but only one Unlock is required.
+  const int err = gralloc_module_->lock(gralloc_module_, handle_, usage, x, y,
+                                        width, height, address);
+  if (!err)
+    locked_ = true;
+
+  return err;
+}
+
+int IonBuffer::LockYUV(int usage, int x, int y, int width, int height,
+                       struct android_ycbcr* yuv) {
+  ATRACE_NAME("IonBuffer::LockYUV");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d",
+           handle_, usage, x, y, width, height);
+  const int err = gralloc_module_->lock_ycbcr(gralloc_module_, handle_, usage,
+                                              x, y, width, height, yuv);
+  if (!err)
+    locked_ = true;
+
+  return err;
+}
+
+int IonBuffer::Unlock() {
+  ATRACE_NAME("IonBuffer::Unlock");
+  ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle_);
+
+  // Lock may be called multiple times; but only one Unlock is required.
+  const int err = gralloc_module_->unlock(gralloc_module_, handle_);
+  if (!err)
+    locked_ = false;
+
+  return err;
+}
+
+void IonBuffer::GrallocInit() {
+  static std::once_flag gralloc_flag;
+  std::call_once(gralloc_flag, []() {
+    hw_module_t const* module = nullptr;
+    alloc_device_t* device = nullptr;
+
+    int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+    ALOGE_IF(err, "IonBuffer::GrallocInit: failed to find the %s module: %s",
+             GRALLOC_HARDWARE_MODULE_ID, strerror(-err));
+
+    err = gralloc_open(module, &device);
+    ALOGE_IF(err, "IonBuffer::GrallocInit: failed to open gralloc device: %s",
+             strerror(-err));
+
+    gralloc_module_ = reinterpret_cast<gralloc_module_t const*>(module);
+    gralloc_device_ = device;
+  });
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..33816fa
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
@@ -0,0 +1,57 @@
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_  // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+// TODO(jwcai) mock not need for now
+class native_handle_t;
+
+namespace android {
+namespace dvr {
+
+// TODO(jwcai) mock not need for now
+class IonBuffer;
+
+class BufferHubBuffer {
+ public:
+  MOCK_METHOD1(Poll, int(int timeout_ms));
+  MOCK_METHOD6(Lock, bool(int usage, int x, int y, int width, int height,
+                          void** addr));
+  MOCK_METHOD0(Unlock, int());
+
+  MOCK_METHOD0(native_handle, native_handle_t*());
+  MOCK_METHOD0(buffer, IonBuffer*());
+  MOCK_METHOD0(event_fd, int());
+
+  MOCK_METHOD0(id, int());
+  MOCK_METHOD0(width, int());
+  MOCK_METHOD0(height, int());
+  MOCK_METHOD0(stride, int());
+  MOCK_METHOD0(format, int());
+  MOCK_METHOD0(usage, int());
+};
+
+class BufferProducer : public BufferHubBuffer {
+ public:
+  // Note that static method |CreateBuffer| and |Import| are not mocked
+  // here, they are just implementation details and thus not needed.
+  MOCK_METHOD2(Post, int(int ready_fence, uint64_t sequence));
+  MOCK_METHOD1(Gain, int(int* release_fence));
+
+  static BufferProducer* staticObject;
+};
+
+class BufferConsumer : public BufferHubBuffer {
+ public:
+  MOCK_METHOD2(Acquire, int(int* ready_fence, uint64_t* sequence));
+  MOCK_METHOD1(Release, int(int release_fence));
+  MOCK_METHOD0(Discard, int());
+  MOCK_METHOD3(DoAcquire,
+               int(int* ready_fence, void* meta, size_t meta_size_bytes));
+
+  static BufferConsumer* staticObject;
+};
+
+}  // namespace dvr
+}  // namespace android
+#endif  // LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_  //NOLINT
diff --git a/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
new file mode 100644
index 0000000..9674c7c
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
@@ -0,0 +1,23 @@
+config("gralloc_config") {
+  include_dirs = [ "." ]
+}
+
+static_library("gralloc") {
+  testonly = true
+
+  sources = [
+    "gralloc.cpp",
+    "gralloc.h",
+  ]
+
+  include_dirs = [
+    "//system/core/include",
+    "//hardware/libhardware/include",
+  ]
+
+  public_deps = [
+    "//dreamos/external/gmock",
+  ]
+
+  public_configs = [ ":gralloc_config" ]
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
new file mode 100644
index 0000000..4a923ec
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
@@ -0,0 +1,68 @@
+#include <gralloc_mock.h>
+#include <hardware/gralloc.h>
+
+static alloc_device_t sdevice;
+
+static int local_registerBuffer(struct gralloc_module_t const*,
+                                buffer_handle_t handle) {
+  return GrallocMock::staticObject->registerBuffer(handle);
+}
+
+static int local_unregisterBuffer(struct gralloc_module_t const*,
+                                  buffer_handle_t handle) {
+  return GrallocMock::staticObject->unregisterBuffer(handle);
+}
+
+static int local_unlock(struct gralloc_module_t const*,
+                        buffer_handle_t handle) {
+  return GrallocMock::staticObject->unlock(handle);
+}
+
+static int local_lock(struct gralloc_module_t const*, buffer_handle_t handle,
+                      int usage, int l, int t, int w, int h, void** vaddr) {
+  return GrallocMock::staticObject->lock(handle, usage, l, t, w, h, vaddr);
+}
+
+static int local_alloc(struct alloc_device_t*, int w, int h, int format,
+                       int usage, buffer_handle_t* handle, int* stride) {
+  return GrallocMock::staticObject->alloc(w, h, format, usage, handle, stride);
+}
+
+static int local_free(struct alloc_device_t*, buffer_handle_t handle) {
+  return GrallocMock::staticObject->free(handle);
+}
+
+static int local_open(const struct hw_module_t*, const char*,
+                      struct hw_device_t** device) {
+  sdevice.alloc = local_alloc;
+  sdevice.free = local_free;
+  *device = reinterpret_cast<hw_device_t*>(&sdevice);
+  return 0;
+}
+
+static hw_module_methods_t smethods;
+
+static gralloc_module_t smodule;
+
+int hw_get_module(const char*, const struct hw_module_t** module) {
+  smodule.registerBuffer = local_registerBuffer;
+  smodule.unregisterBuffer = local_unregisterBuffer;
+  smodule.lock = local_lock;
+  smodule.unlock = local_unlock;
+  smethods.open = local_open;
+  smodule.common.methods = &smethods;
+  *module = reinterpret_cast<hw_module_t*>(&smodule);
+  return 0;
+}
+
+int native_handle_close(const native_handle_t* handle) {
+  return GrallocMock::staticObject->native_handle_close(handle);
+}
+
+int native_handle_delete(native_handle_t* handle) {
+  return GrallocMock::staticObject->native_handle_delete(handle);
+}
+
+native_handle_t* native_handle_create(int numFds, int numInts) {
+  return GrallocMock::staticObject->native_handle_create(numFds, numInts);
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
new file mode 100644
index 0000000..f62f579
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
@@ -0,0 +1,23 @@
+#ifndef LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#define LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hardware/gralloc.h>
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class GrallocMock {
+ public:
+  // Add methods here.
+  MOCK_METHOD1(native_handle_close, int(const native_handle_t*));
+  MOCK_METHOD1(native_handle_delete, int(native_handle_t*));
+  MOCK_METHOD2(native_handle_create, native_handle_t*(int, int));
+  MOCK_METHOD1(registerBuffer, int(buffer_handle_t));
+  MOCK_METHOD1(unregisterBuffer, int(buffer_handle_t));
+  MOCK_METHOD7(lock, int(buffer_handle_t, int, int, int, int, int, void**));
+  MOCK_METHOD1(unlock, int(buffer_handle_t));
+  MOCK_METHOD6(alloc, int(int, int, int, int, buffer_handle_t*, int*));
+  MOCK_METHOD1(free, int(buffer_handle_t));
+  static GrallocMock* staticObject;
+};
+
+#endif  // LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
diff --git a/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..fac6db0
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
@@ -0,0 +1,78 @@
+// This file has a big hack, it "mocks" the actual IonBuffer by redefining
+// it with mock methods and using the same header guard to prevent the original
+// definition from being included in the same context.
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_  // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <hardware/gralloc.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBufferMock {
+ public:
+  IonBufferMock() {}
+  MOCK_METHOD0(GetGrallocModuleImpl, gralloc_module_t const*());
+  MOCK_METHOD6(Import, int(buffer_handle_t handle, int width, int height,
+                           int stride, int format, int usage));
+  MOCK_METHOD9(Import, int(const int* fd_array, int fd_count,
+                           const int* int_array, int int_count, int width,
+                           int height, int stride, int format, int usage));
+  MOCK_METHOD6(Lock, int(int usage, int x, int y, int width, int height,
+                         void** address));
+  MOCK_METHOD0(Unlock, int());
+  MOCK_CONST_METHOD0(handle, buffer_handle_t());
+  MOCK_CONST_METHOD0(width, int());
+  MOCK_CONST_METHOD0(height, int());
+  MOCK_CONST_METHOD0(layer_count, int());
+  MOCK_CONST_METHOD0(stride, int());
+  MOCK_CONST_METHOD0(layer_stride, int());
+  MOCK_CONST_METHOD0(format, int());
+  MOCK_CONST_METHOD0(usage, int());
+};
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+  IonBuffer() : mock_(new IonBufferMock) {
+    if (initializer) {
+      initializer(mock_.get());
+    }
+  }
+  IonBuffer(IonBuffer&& other) = default;
+  static gralloc_module_t const* GetGrallocModule() {
+    return staticObject->GetGrallocModuleImpl();
+  }
+  int Import(buffer_handle_t handle, int width, int height, int stride,
+             int format, int usage) {
+    return mock_->Import(handle, width, height, stride, format, usage);
+  }
+  int Import(const int* fd_array, int fd_count, const int* int_array,
+             int int_count, int width, int height, int stride, int format,
+             int usage) {
+    return mock_->Import(fd_array, fd_count, int_array, int_count, width,
+                         height, stride, format, usage);
+  }
+  int Lock(int usage, int x, int y, int width, int height, void** address) {
+    return mock_->Lock(usage, x, y, width, height, address);
+  }
+  int Unlock() { return mock_->Unlock(); }
+  buffer_handle_t handle() const { return mock_->handle(); }
+  int width() const { return mock_->width(); }
+  int height() const { return mock_->height(); }
+  int layer_count() const { return mock_->layer_count(); }
+  int stride() const { return mock_->stride(); }
+  int layer_stride() const { return mock_->layer_stride(); }
+  int format() const { return mock_->format(); }
+  int usage() const { return mock_->usage(); }
+  std::unique_ptr<IonBufferMock> mock_;
+  static IonBufferMock* staticObject;
+  static void (*initializer)(IonBufferMock* target);
+};
+
+}  // namespace dvr
+}  // namespace android
+#endif  // LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_ - NOLINT
diff --git a/libs/vr/libbufferhub/tests/Android.mk b/libs/vr/libbufferhub/tests/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/libs/vr/libbufferhub/tests/ion_buffer/Android.mk b/libs/vr/libbufferhub/tests/ion_buffer/Android.mk
new file mode 100644
index 0000000..3bfdb7b
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/ion_buffer/Android.mk
@@ -0,0 +1,74 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+COMPONENT_TOP := ${LOCAL_PATH}/../..
+
+LOCAL_SRC_FILES := \
+        ion_buffer-test.cpp \
+        ../../ion_buffer.cpp \
+        ../../mocks/gralloc/gralloc.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+        libc \
+        libcutils \
+        libutils \
+        liblog
+
+LOCAL_STATIC_LIBRARIES := \
+        libgmock
+
+LOCAL_C_INCLUDES := \
+        ${COMPONENT_TOP}/mocks/gralloc \
+        ${COMPONENT_TOP}/include \
+        $(TOP)/system/core/base/include
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+
+LOCAL_NATIVE_COVERAGE := true
+
+LOCAL_CFLAGS := -DTRACE=0 -g
+
+LOCAL_MODULE := ion_buffer-test
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        ion_buffer-test.cpp \
+        ../../ion_buffer.cpp \
+        ../../mocks/gralloc/gralloc.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+        liblog
+
+LOCAL_STATIC_LIBRARIES := \
+        libgmock_host
+
+LOCAL_C_INCLUDES := \
+        ${COMPONENT_TOP}/mocks/gralloc \
+        ${COMPONENT_TOP}/include \
+        $(TOP)/system/core/base/include
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+
+LOCAL_NATIVE_COVERAGE := true
+
+LOCAL_CFLAGS := -DTRACE=0
+
+LOCAL_MODULE := ion_buffer-host_test
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_HOST_NATIVE_TEST)
+
+.PHONY: dvr_host_native_unit_tests
+dvr_host_native_unit_tests: ion_buffer-host_test
+ifeq (true,$(NATIVE_COVERAGE))
+  ion_buffer-host_test: llvm-cov
+  ion_buffer-test: llvm-cov
+  # This shouldn't be necessary, but the default build with
+  # NATIVE_COVERAGE=true manages to ion_buffer-test without
+  # building llvm-cov (droid is the default target).
+  droid: llvm-cov
+endif
diff --git a/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp b/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp
new file mode 100644
index 0000000..68f82d7
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp
@@ -0,0 +1,375 @@
+#include <gmock/gmock.h>
+#include <gralloc_mock.h>
+#include <gtest/gtest.h>
+#include <private/dvr/ion_buffer.h>
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using android::dvr::IonBuffer;
+
+GrallocMock* GrallocMock::staticObject = nullptr;
+
+namespace {
+
+const int w1 = 100;
+const int h1 = 200;
+const int d1 = 2;
+const int f1 = 1;
+const int u1 = 3;
+const int stride1 = 8;
+const int layer_stride1 = 8;
+native_handle_t handle1;
+const int w2 = 150;
+const int h2 = 300;
+const int d2 = 4;
+const int f2 = 2;
+const int u2 = 5;
+const int stride2 = 4;
+const int layer_stride2 = 4;
+native_handle_t handle2;
+const int kMaxFd = 10;
+const int kMaxInt = 10;
+char handleData[sizeof(native_handle_t) + (kMaxFd + kMaxInt) * sizeof(int)];
+native_handle_t* const dataHandle =
+    reinterpret_cast<native_handle_t*>(handleData);
+char refData[sizeof(native_handle_t) + (kMaxFd + kMaxInt) * sizeof(int)];
+native_handle_t* const refHandle = reinterpret_cast<native_handle_t*>(refData);
+
+class IonBufferUnitTest : public ::testing::Test {
+ protected:
+  // You can remove any or all of the following functions if its body
+  // is empty.
+
+  IonBufferUnitTest() {
+    GrallocMock::staticObject = new GrallocMock;
+    // You can do set-up work for each test here.
+    // most ServicefsClients will use this initializer. Use as the
+    // default.
+  }
+
+  virtual ~IonBufferUnitTest() {
+    delete GrallocMock::staticObject;
+    GrallocMock::staticObject = nullptr;
+    // You can do clean-up work that doesn't throw exceptions here.
+  }
+
+  // If the constructor and destructor are not enough for setting up
+  // and cleaning up each test, you can define the following methods:
+
+  void SetUp() override {
+    // Code here will be called immediately after the constructor (right
+    // before each test).
+  }
+
+  void TearDown() override {
+    // Code here will be called immediately after each test (right
+    // before the destructor).
+  }
+};
+
+void TestIonBufferState(const IonBuffer& buffer, int w, int h, int d, int f,
+                        int u, native_handle_t* handle, int stride,
+                        int layer_stride) {
+  EXPECT_EQ(buffer.width(), w);
+  EXPECT_EQ(buffer.height(), h);
+  EXPECT_EQ(buffer.layer_count(), d);
+  EXPECT_EQ(buffer.format(), f);
+  EXPECT_EQ(buffer.usage(), u);
+  EXPECT_EQ(buffer.handle(), handle);
+  EXPECT_EQ(buffer.stride(), stride);
+  EXPECT_EQ(buffer.layer_stride(), layer_stride);
+}
+
+TEST_F(IonBufferUnitTest, TestFreeOnDestruction) {
+  // Set up |alloc|(|w1...|) to succeed once and fail on others calls.
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(1)
+      .WillOnce(DoAll(SetArgPointee<4>(&handle1), SetArgPointee<5>(stride1),
+                      Return(0)));
+  // Set up |free| to be called once.
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  IonBuffer buffer;
+  // First call to |alloc| with |w1...| set up to succeed.
+  int ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+
+  // Scoped destructor will be called, calling |free| on |handle1|.
+}
+
+TEST_F(IonBufferUnitTest, TestAlloc) {
+  IonBuffer buffer;
+  // Set up |alloc|(|w1...|) to succeed once and fail on others calls.
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(2)
+      .WillOnce(DoAll(SetArgPointee<4>(&handle1), SetArgPointee<5>(stride1),
+                      Return(0)))
+      .WillRepeatedly(Return(-1));
+
+  // Set up |alloc|(|w2...|)  to succeed once and fail on others calls.
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w2, h2, f2, u2, _, _))
+      .Times(2)
+      .WillOnce(DoAll(SetArgPointee<4>(&handle2), SetArgPointee<5>(stride2),
+                      Return(0)))
+      .WillRepeatedly(Return(-1));
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle2))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  // First call to |alloc| with |w1...| set up to succeed.
+  int ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+
+  // First call to |alloc| with |w2...| set up to succeed, |free| should be
+  // called once on |handle1|.
+  ret = buffer.Alloc(w2, h2, f2, u2);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+
+  // Second call to |alloc| with |w1| is set up to fail.
+  ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+
+  // |free| on |handle2| should be called here.
+  buffer.FreeHandle();
+  TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+
+  // |alloc| is set up to fail.
+  ret = buffer.Alloc(w2, h2, f2, u2);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+}
+
+TEST_F(IonBufferUnitTest, TestReset) {
+  IonBuffer buffer;
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle2))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+  buffer.Reset(&handle1, w1, h1, stride1, f1, u1);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+  buffer.Reset(&handle2, w2, h2, stride2, f2, u2);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+  buffer.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestImport1) {
+  IonBuffer buffer;
+  EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(&handle1))
+      .Times(3)
+      .WillOnce(Return(0))
+      .WillRepeatedly(Return(-1));
+  EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(&handle2))
+      .Times(3)
+      .WillOnce(Return(0))
+      .WillOnce(Return(-1))
+      .WillOnce(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(&handle1))
+      .Times(1)
+      .WillOnce(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(&handle1))
+      .Times(1);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(&handle1))
+      .Times(1);
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(1)
+      .WillRepeatedly(DoAll(SetArgPointee<4>(&handle1),
+                            SetArgPointee<5>(stride1), Return(0)));
+  EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(&handle2))
+      .Times(2)
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(&handle2))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(&handle2))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  int ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+  ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+  ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+  ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+  ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+  buffer.FreeHandle();
+  ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+}
+
+native_handle_t* native_handle_create_impl(int nFds, int nInts) {
+  if ((nFds + nInts) > (kMaxFd + kMaxInt))
+    return nullptr;
+  dataHandle->version = sizeof(native_handle_t);
+  dataHandle->numFds = nFds;
+  dataHandle->numInts = nInts;
+  for (int i = 0; i < nFds + nInts; i++)
+    dataHandle->data[i] = 0;
+  return dataHandle;
+}
+
+TEST_F(IonBufferUnitTest, TestImport2) {
+  IonBuffer buffer;
+  int ints[] = {211, 313, 444};
+  int fds[] = {-1, -1};
+  int ni = sizeof(ints) / sizeof(ints[0]);
+  int nfd = sizeof(fds) / sizeof(fds[0]);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_create(nfd, ni))
+      .Times(3)
+      .WillOnce(Return(nullptr))
+      .WillRepeatedly(Invoke(native_handle_create_impl));
+  EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(dataHandle))
+      .Times(2)
+      .WillOnce(Return(-1))
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(dataHandle))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(dataHandle))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(dataHandle))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  int ret = buffer.Import(fds, -1, ints, ni, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Import(fds, nfd, ints, -1, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, dataHandle, stride1, 0);
+  EXPECT_EQ(dataHandle->numFds, nfd);
+  EXPECT_EQ(dataHandle->numInts, ni);
+  for (int i = 0; i < nfd; i++)
+    EXPECT_EQ(dataHandle->data[i], fds[i]);
+  for (int i = 0; i < ni; i++)
+    EXPECT_EQ(dataHandle->data[nfd + i], ints[i]);
+  buffer.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestDuplicate) {
+  IonBuffer buffer;
+  IonBuffer ref;
+  int ints[] = {211, 313, 444};
+  int fds[] = {-1, -1};
+  int ni = sizeof(ints) / sizeof(ints[0]);
+  int nfd = sizeof(fds) / sizeof(fds[0]);
+
+  for (int i = 0; i < nfd; i++) {
+    refHandle->data[i] = fds[i];
+  }
+  for (int i = 0; i < ni; i++) {
+    refHandle->data[i + nfd] = ints[i];
+  }
+
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(1)
+      .WillRepeatedly(DoAll(SetArgPointee<4>(refHandle),
+                            SetArgPointee<5>(stride1), Return(0)));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_create(nfd, ni))
+      .Times(3)
+      .WillOnce(Return(nullptr))
+      .WillRepeatedly(Invoke(native_handle_create_impl));
+  EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(dataHandle))
+      .Times(2)
+      .WillOnce(Return(-1))
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(dataHandle))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(dataHandle))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(dataHandle))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, free(refHandle))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  int ret = buffer.Duplicate(&ref);
+  EXPECT_LT(ret, 0);
+  ret = ref.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  refHandle->numFds = -1;
+  refHandle->numInts = 0;
+  ret = buffer.Duplicate(&ref);
+  EXPECT_LT(ret, 0);
+  refHandle->numFds = nfd;
+  refHandle->numInts = ni;
+  ret = buffer.Duplicate(&ref);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Duplicate(&ref);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Duplicate(&ref);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, dataHandle, stride1, 0);
+  EXPECT_EQ(dataHandle->numFds, nfd);
+  EXPECT_EQ(dataHandle->numInts, ni);
+  for (int i = 0; i < nfd; i++)
+    EXPECT_LT(dataHandle->data[i], 0);
+  for (int i = 0; i < ni; i++)
+    EXPECT_EQ(dataHandle->data[nfd + i], ints[i]);
+  buffer.FreeHandle();
+  ref.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestLockUnlock) {
+  IonBuffer buffer;
+  const int x = 12;
+  const int y = 24;
+  const int value1 = 17;
+  const int value2 = 25;
+  void* addr1;
+  void** addr = &addr1;
+
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(1)
+      .WillRepeatedly(DoAll(SetArgPointee<4>(&handle1),
+                            SetArgPointee<5>(stride1), Return(0)));
+  EXPECT_CALL(*GrallocMock::staticObject,
+              lock(&handle1, u2, x, y, w2, h2, addr))
+      .Times(1)
+      .WillRepeatedly(Return(value1));
+  EXPECT_CALL(*GrallocMock::staticObject, unlock(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(value2));
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  int ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  ret = buffer.Lock(u2, x, y, w2, h2, addr);
+  EXPECT_EQ(ret, value1);
+  ret = buffer.Unlock();
+  EXPECT_EQ(ret, value2);
+  buffer.FreeHandle();
+}
+
+}  // namespace