Snap for 4448085 from f2b791587c15ec5028d0b266d415c631a4078916 to oc-m3-release

Change-Id: Id0e8bb274f32339b9889c9b0de619e011711c5c7
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
index abf7b06..b3cbdef 100644
--- a/cmds/atrace/Android.bp
+++ b/cmds/atrace/Android.bp
@@ -11,6 +11,7 @@
         "libhidltransport",
         "liblog",
         "libutils",
+        "libcutils",
         "libz",
         "libbase",
     ],
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 3c4a933..f29da17 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1796,8 +1796,14 @@
     }
 
     const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+
+    // Note that we cannot validate the package path here because the file might not exist
+    // and we cannot call realpath to resolve system symlinks. Since /data/user/0 symlinks to
+    // /data/data/ a lot of validations will fail if we attempt to check the package path.
+    // It is still ok to be more relaxed because any file removal is done after forking and
+    // dropping capabilities.
     if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
-            uid, storage_flag)) {
+            uid, storage_flag, /*validate_package_path*/ false)) {
         LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
         return false;
     }
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index d277bd3..dd32ac6 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -801,7 +801,7 @@
 }
 
 bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
-        const char* volume_uuid, int uid, int storage_flag) {
+        const char* volume_uuid, int uid, int storage_flag, bool validate_package_path) {
     CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE);
 
     // Empty paths are not allowed.
@@ -815,15 +815,18 @@
     // The path should be at most PKG_PATH_MAX long.
     if (dex_path.size() > PKG_PATH_MAX) { return false; }
 
-    // The dex_path should be under the app data directory.
-    std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
-        ? create_data_user_ce_package_path(
-                volume_uuid, multiuser_get_user_id(uid), pkgname.c_str())
-        : create_data_user_de_package_path(
-                volume_uuid, multiuser_get_user_id(uid), pkgname.c_str());
+    if (validate_package_path) {
+        // If we are asked to validate the package path check that
+        // the dex_path is under the app data directory.
+        std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
+            ? create_data_user_ce_package_path(
+                    volume_uuid, multiuser_get_user_id(uid), pkgname.c_str())
+            : create_data_user_de_package_path(
+                    volume_uuid, multiuser_get_user_id(uid), pkgname.c_str());
 
-    if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) {
-        return false;
+        if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) {
+            return false;
+        }
     }
 
     // If we got here we have a valid path.
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index da3a293..e938042 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -125,7 +125,7 @@
 
 int validate_system_app_path(const char* path);
 bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
-        const char* volume_uuid, int uid, int storage_flag);
+        const char* volume_uuid, int uid, int storage_flag, bool validate_package_path = true);
 
 int get_path_from_env(dir_rec_t* rec, const char* var);
 
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 0d5d206..ec7be53 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -45,7 +45,7 @@
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
     <feature name="android.software.input_methods" />
-    <feature name="android.software.picture_in_picture" />
+    <feature name="android.software.picture_in_picture" notLowRam="true" />
     <feature name="android.software.activities_on_secondary_displays" />
     <feature name="android.software.print" />
     <feature name="android.software.companion_device_setup" />
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 6b7254c..3264666 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -38,4 +38,20 @@
      * strings.
      */
     @utf8InCpp String[] getNamesForUids(in int[] uids);
+
+    /**
+     * Returns the name of the installer (a package) which installed the named
+     * package. Preloaded packages return the string "preload". Sideloaded packages
+     * return an empty string. Unknown or unknowable are returned as empty strings.
+     */
+
+    @utf8InCpp String getInstallerForPackage(in String packageName);
+
+    /**
+     * Returns the version code of the named package.
+     * Unknown or unknowable versions are returned as 0.
+     */
+
+    int getVersionCodeForPackage(in String packageName);
+
 }
diff --git a/libs/hwc2on1adapter/HWC2On1Adapter.cpp b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
index 8c6ef69..77f06bb 100644
--- a/libs/hwc2on1adapter/HWC2On1Adapter.cpp
+++ b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
@@ -426,7 +426,13 @@
 
     std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
 
-    mCallbacks[descriptor] = {callbackData, pointer};
+    if (pointer != nullptr) {
+        mCallbacks[descriptor] = {callbackData, pointer};
+    } else {
+        ALOGI("unregisterCallback(%s)", to_string(descriptor).c_str());
+        mCallbacks.erase(descriptor);
+        return Error::None;
+    }
 
     bool hasPendingInvalidate = false;
     std::vector<hwc2_display_t> displayIds;
@@ -2005,10 +2011,21 @@
     return Error::None;
 }
 
+static bool compareRects(const hwc_rect_t& rect1, const hwc_rect_t& rect2) {
+    return rect1.left == rect2.left &&
+            rect1.right == rect2.right &&
+            rect1.top == rect2.top &&
+            rect1.bottom == rect2.bottom;
+}
+
 Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t visible) {
-    mVisibleRegion.resize(visible.numRects);
-    std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin());
-    mDisplay.markGeometryChanged();
+    if ((getNumVisibleRegions() != visible.numRects) ||
+        !std::equal(mVisibleRegion.begin(), mVisibleRegion.end(), visible.rects,
+                    compareRects)) {
+        mVisibleRegion.resize(visible.numRects);
+        std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin());
+        mDisplay.markGeometryChanged();
+    }
     return Error::None;
 }
 
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index da0ea24..f327200 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -37,7 +37,8 @@
     "libnativewindow"
 ]
 
-HeaderLibraries = [
+headerLibraries = [
+    "libdvr_headers",
     "libnativebase_headers",
 ]
 
@@ -45,12 +46,13 @@
     srcs: sourceFiles,
     cflags: [
         "-DLOG_TAG=\"libbufferhub\"",
-        "-DTRACE=0"
+        "-DTRACE=0",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
     ],
     export_include_dirs: localIncludeFiles,
     static_libs: staticLibraries,
     shared_libs: sharedLibraries,
-    header_libs: HeaderLibraries,
+    header_libs: headerLibraries,
     name: "libbufferhub",
     export_header_lib_headers: [
         "libnativebase_headers",
@@ -62,6 +64,7 @@
     srcs: ["bufferhub_tests.cpp"],
     static_libs: ["libbufferhub"] + staticLibraries,
     shared_libs: sharedLibraries,
+    header_libs: headerLibraries,
     name: "bufferhub_tests",
 }
 
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
index b9a53b0..97341b1 100644
--- a/libs/vr/libbufferhub/buffer_hub_client.cpp
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -2,7 +2,7 @@
 
 #include <log/log.h>
 #include <poll.h>
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <sys/epoll.h>
 #include <utils/Trace.h>
 
 #include <mutex>
@@ -12,9 +12,8 @@
 
 #include "include/private/dvr/bufferhub_rpc.h"
 
-using android::pdx::LocalHandle;
 using android::pdx::LocalChannelHandle;
-using android::pdx::rpc::WrapBuffer;
+using android::pdx::LocalHandle;
 using android::pdx::Status;
 
 namespace android {
@@ -29,7 +28,11 @@
           endpoint_path)},
       id_(-1) {}
 
-BufferHubBuffer::~BufferHubBuffer() {}
+BufferHubBuffer::~BufferHubBuffer() {
+  if (metadata_header_ != nullptr) {
+    metadata_buffer_.Unlock();
+  }
+}
 
 Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
   Status<LocalChannelHandle> status =
@@ -43,7 +46,7 @@
 int BufferHubBuffer::ImportBuffer() {
   ATRACE_NAME("BufferHubBuffer::ImportBuffer");
 
-  Status<NativeBufferHandle<LocalHandle>> status =
+  Status<BufferDescription<LocalHandle>> status =
       InvokeRemoteMethod<BufferHubRPC::GetBuffer>();
   if (!status) {
     ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s",
@@ -54,24 +57,135 @@
     return -EIO;
   }
 
-  auto buffer_handle = status.take();
+  auto buffer_desc = status.take();
 
   // Stash the buffer id to replace the value in id_.
-  const int new_id = buffer_handle.id();
+  const int new_id = buffer_desc.id();
 
   // Import the buffer.
   IonBuffer ion_buffer;
-  ALOGD_IF(
-      TRACE, "BufferHubBuffer::ImportBuffer: id=%d FdCount=%zu IntCount=%zu",
-      buffer_handle.id(), buffer_handle.FdCount(), buffer_handle.IntCount());
+  ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d.", buffer_desc.id());
 
-  const int ret = buffer_handle.Import(&ion_buffer);
-  if (ret < 0)
+  if (const int ret = buffer_desc.ImportBuffer(&ion_buffer))
     return ret;
 
-  // If the import succeeds, replace the previous buffer and id.
+  // Import the metadata.
+  IonBuffer metadata_buffer;
+  if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) {
+    ALOGE("Failed to import metadata buffer, error=%d", ret);
+    return ret;
+  }
+  size_t metadata_buf_size = metadata_buffer.width();
+  if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) {
+    ALOGE("BufferHubBuffer::ImportBuffer: metadata buffer too small: %zu",
+          metadata_buf_size);
+    return -ENOMEM;
+  }
+
+  // If all imports succee, replace the previous buffer and id.
   buffer_ = std::move(ion_buffer);
+  metadata_buffer_ = std::move(metadata_buffer);
+  metadata_buf_size_ = metadata_buf_size;
+  user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize;
+
+  void* metadata_ptr = nullptr;
+  if (const int ret =
+          metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0,
+                                /*y=*/0, metadata_buf_size_,
+                                /*height=*/1, &metadata_ptr)) {
+    ALOGE("BufferHubBuffer::ImportBuffer: Failed to lock metadata.");
+    return ret;
+  }
+
+  // Set up shared fences.
+  shared_acquire_fence_ = buffer_desc.take_acquire_fence();
+  shared_release_fence_ = buffer_desc.take_release_fence();
+  if (!shared_acquire_fence_ || !shared_release_fence_) {
+    ALOGE("BufferHubBuffer::ImportBuffer: Failed to import shared fences.");
+    return -EIO;
+  }
+
+  metadata_header_ =
+      reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr);
+  if (user_metadata_size_) {
+    user_metadata_ptr_ =
+        reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) +
+                                BufferHubDefs::kMetadataHeaderSize);
+  } else {
+    user_metadata_ptr_ = nullptr;
+  }
+
   id_ = new_id;
+  buffer_state_bit_ = buffer_desc.buffer_state_bit();
+
+  // Note that here the buffer state is mapped from shared memory as an atomic
+  // object. The std::atomic's constructor will not be called so that the
+  // original value stored in the memory region will be preserved.
+  buffer_state_ = &metadata_header_->buffer_state;
+  ALOGD_IF(TRACE,
+           "BufferHubBuffer::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".",
+           id(), buffer_state_->load());
+  fence_state_ = &metadata_header_->fence_state;
+  ALOGD_IF(TRACE,
+           "BufferHubBuffer::ImportBuffer: id=%d, fence_state=%" PRIx64 ".",
+           id(), fence_state_->load());
+
+  return 0;
+}
+
+inline int BufferHubBuffer::CheckMetadata(size_t user_metadata_size) const {
+  if (user_metadata_size && !user_metadata_ptr_) {
+    ALOGE("BufferHubBuffer::CheckMetadata: doesn't support custom metadata.");
+    return -EINVAL;
+  }
+  if (user_metadata_size > user_metadata_size_) {
+    ALOGE("BufferHubBuffer::CheckMetadata: too big: %zu, maximum: %zu.",
+          user_metadata_size, user_metadata_size_);
+    return -E2BIG;
+  }
+  return 0;
+}
+
+int BufferHubBuffer::UpdateSharedFence(const LocalHandle& new_fence,
+                                       const LocalHandle& shared_fence) {
+  if (pending_fence_fd_.Get() != new_fence.Get()) {
+    // First, replace the old fd if there was already one. Skipping if the new
+    // one is the same as the old.
+    if (pending_fence_fd_.IsValid()) {
+      const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL,
+                                pending_fence_fd_.Get(), nullptr);
+      ALOGW_IF(ret,
+               "BufferHubBuffer::UpdateSharedFence: failed to remove old fence "
+               "fd from epoll set, error: %s.",
+               strerror(errno));
+    }
+
+    if (new_fence.IsValid()) {
+      // If ready fence is valid, we put that into the epoll set.
+      epoll_event event;
+      event.events = EPOLLIN;
+      event.data.u64 = buffer_state_bit();
+      pending_fence_fd_ = new_fence.Duplicate();
+      if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(),
+                    &event) < 0) {
+        const int error = errno;
+        ALOGE(
+            "BufferHubBuffer::UpdateSharedFence: failed to add new fence fd "
+            "into epoll set, error: %s.",
+            strerror(error));
+        return -error;
+      }
+      // Set bit in fence state to indicate that there is a fence from this
+      // producer or consumer.
+      fence_state_->fetch_or(buffer_state_bit());
+    } else {
+      // Unset bit in fence state to indicate that there is no fence, so that
+      // when consumer to acquire or producer to acquire, it knows no need to
+      // check fence for this buffer.
+      fence_state_->fetch_and(~buffer_state_bit());
+    }
+  }
+
   return 0;
 }
 
@@ -131,31 +245,144 @@
                        : LocalChannelHandle{nullptr, -status.error()});
 }
 
+int BufferConsumer::LocalAcquire(DvrNativeBufferMetadata* out_meta,
+                                 LocalHandle* out_fence) {
+  if (!out_meta)
+    return -EINVAL;
+
+  // Only check producer bit and this consumer buffer's particular consumer bit.
+  // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit
+  // is not set.
+  uint64_t buffer_state = buffer_state_->load();
+  if (!BufferHubDefs::IsBufferPosted(buffer_state, buffer_state_bit())) {
+    ALOGE("BufferConsumer::LocalAcquire: not posted, id=%d state=%" PRIx64
+          " buffer_state_bit=%" PRIx64 ".",
+          id(), buffer_state, buffer_state_bit());
+    return -EBUSY;
+  }
+
+  // Copy the canonical metadata.
+  void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
+  memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata));
+  // Fill in the user_metadata_ptr in address space of the local process.
+  if (out_meta->user_metadata_size) {
+    out_meta->user_metadata_ptr =
+        reinterpret_cast<uint64_t>(user_metadata_ptr_);
+  } else {
+    out_meta->user_metadata_ptr = 0;
+  }
+
+  uint64_t fence_state = fence_state_->load();
+  // If there is an acquire fence from producer, we need to return it.
+  if (fence_state & BufferHubDefs::kProducerStateBit) {
+    *out_fence = shared_acquire_fence_.Duplicate();
+  }
+
+  // Set the consumer bit unique to this consumer.
+  BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, buffer_state_bit());
+  return 0;
+}
+
 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) {
+                            size_t user_metadata_size) {
   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();
+
+  if (const int error = CheckMetadata(user_metadata_size))
+    return error;
+
+  DvrNativeBufferMetadata canonical_meta;
+  if (const int error = LocalAcquire(&canonical_meta, ready_fence))
+    return error;
+
+  if (meta && user_metadata_size) {
+    void* metadata_src =
+        reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
+    if (metadata_src) {
+      memcpy(meta, metadata_src, user_metadata_size);
+    } else {
+      ALOGW("BufferConsumer::Acquire: no user-defined metadata.");
+    }
+  }
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>();
+  if (!status)
+    return -status.error();
+  return 0;
+}
+
+int BufferConsumer::AcquireAsync(DvrNativeBufferMetadata* out_meta,
+                                 LocalHandle* out_fence) {
+  ATRACE_NAME("BufferConsumer::AcquireAsync");
+
+  if (const int error = LocalAcquire(out_meta, out_fence))
+    return error;
+
+  auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode);
+  if (!status)
+    return -status.error();
+  return 0;
+}
+
+int BufferConsumer::LocalRelease(const DvrNativeBufferMetadata* meta,
+                                 const LocalHandle& release_fence) {
+  if (const int error = CheckMetadata(meta->user_metadata_size))
+    return error;
+
+  // Check invalid state transition.
+  uint64_t buffer_state = buffer_state_->load();
+  if (!BufferHubDefs::IsBufferAcquired(buffer_state)) {
+    ALOGE("BufferConsumer::LocalRelease: not acquired id=%d state=%" PRIx64 ".",
+          id(), buffer_state);
+    return -EBUSY;
+  }
+
+  // On release, only the user requested metadata is copied back into the shared
+  // memory for metadata. Since there are multiple consumers, it doesn't make
+  // sense to send the canonical metadata back to the producer. However, one of
+  // the consumer can still choose to write up to user_metadata_size bytes of
+  // data into user_metadata_ptr.
+  if (meta->user_metadata_ptr && meta->user_metadata_size) {
+    void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
+    memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
+  }
+
+  // Send out the release fence through the shared epoll fd. Note that during
+  // releasing the producer is not expected to be polling on the fence.
+  if (const int error = UpdateSharedFence(release_fence, shared_release_fence_))
+    return error;
+
+  // For release operation, the client don't need to change the state as it's
+  // bufferhubd's job to flip the produer bit once all consumers are released.
+  return 0;
 }
 
 int BufferConsumer::Release(const LocalHandle& release_fence) {
   ATRACE_NAME("BufferConsumer::Release");
+
+  DvrNativeBufferMetadata meta;
+  if (const int error = LocalRelease(&meta, release_fence))
+    return error;
+
   return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
       BorrowedFence(release_fence.Borrow())));
 }
 
 int BufferConsumer::ReleaseAsync() {
+  DvrNativeBufferMetadata meta;
+  return ReleaseAsync(&meta, LocalHandle());
+}
+
+int BufferConsumer::ReleaseAsync(const DvrNativeBufferMetadata* meta,
+                                 const LocalHandle& release_fence) {
   ATRACE_NAME("BufferConsumer::ReleaseAsync");
+
+  if (const int error = LocalRelease(meta, release_fence))
+    return error;
+
   return ReturnStatusOrError(
       SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
 }
@@ -168,24 +395,25 @@
 }
 
 BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
-                               uint32_t usage, size_t metadata_size)
-    : BufferProducer(width, height, format, usage, usage, metadata_size) {}
+                               uint32_t usage, size_t user_metadata_size)
+    : BufferProducer(width, height, format, usage, usage, user_metadata_size) {}
 
 BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
                                uint64_t producer_usage, uint64_t consumer_usage,
-                               size_t metadata_size)
+                               size_t user_metadata_size)
     : BASE(BufferHubRPC::kClientPath) {
   ATRACE_NAME("BufferProducer::BufferProducer");
   ALOGD_IF(TRACE,
            "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u "
            "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64
-           " metadata_size=%zu",
+           " user_metadata_size=%zu",
            event_fd(), width, height, format, producer_usage, consumer_usage,
-           metadata_size);
+           user_metadata_size);
 
   // (b/37881101) Deprecate producer/consumer usage
   auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
-      width, height, format, (producer_usage | consumer_usage), metadata_size);
+      width, height, format, (producer_usage | consumer_usage),
+      user_metadata_size);
   if (!status) {
     ALOGE(
         "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
@@ -206,27 +434,28 @@
 BufferProducer::BufferProducer(const std::string& name, int user_id,
                                int group_id, uint32_t width, uint32_t height,
                                uint32_t format, uint32_t usage,
-                               size_t meta_size_bytes)
+                               size_t user_metadata_size)
     : BufferProducer(name, user_id, group_id, width, height, format, usage,
-                     usage, meta_size_bytes) {}
+                     usage, user_metadata_size) {}
 
 BufferProducer::BufferProducer(const std::string& name, int user_id,
                                int group_id, uint32_t width, uint32_t height,
                                uint32_t format, uint64_t producer_usage,
-                               uint64_t consumer_usage, size_t meta_size_bytes)
+                               uint64_t consumer_usage,
+                               size_t user_metadata_size)
     : BASE(BufferHubRPC::kClientPath) {
   ATRACE_NAME("BufferProducer::BufferProducer");
   ALOGD_IF(TRACE,
            "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d "
            "group_id=%d width=%u height=%u format=%u producer_usage=%" PRIx64
-           " consumer_usage=%" PRIx64 " meta_size_bytes=%zu",
+           " consumer_usage=%" PRIx64 " user_metadata_size=%zu",
            event_fd(), name.c_str(), user_id, group_id, width, height, format,
-           producer_usage, consumer_usage, meta_size_bytes);
+           producer_usage, consumer_usage, user_metadata_size);
 
   // (b/37881101) Deprecate producer/consumer usage
   auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
       name, user_id, group_id, width, height, format,
-      (producer_usage | consumer_usage), meta_size_bytes);
+      (producer_usage | consumer_usage), user_metadata_size);
   if (!status) {
     ALOGE(
         "BufferProducer::BufferProducer: Failed to create/get persistent "
@@ -260,12 +489,12 @@
   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 user_metadata_size = 0;
 
   // (b/37881101) Deprecate producer/consumer usage
   auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
       width, height, format, (producer_usage | consumer_usage),
-      meta_size_bytes);
+      user_metadata_size);
   if (!status) {
     ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
           status.GetErrorMessage().c_str());
@@ -299,12 +528,12 @@
   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 user_metadata_size = 0;
 
   // (b/37881101) Deprecate producer/consumer usage
   auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
       name, user_id, group_id, width, height, format,
-      (producer_usage | consumer_usage), meta_size_bytes);
+      (producer_usage | consumer_usage), user_metadata_size);
   if (!status) {
     ALOGE(
         "BufferProducer::BufferProducer: Failed to create persistent "
@@ -360,28 +589,141 @@
   }
 }
 
+int BufferProducer::LocalPost(const DvrNativeBufferMetadata* meta,
+                              const LocalHandle& ready_fence) {
+  if (const int error = CheckMetadata(meta->user_metadata_size))
+    return error;
+
+  // Check invalid state transition.
+  uint64_t buffer_state = buffer_state_->load();
+  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+    ALOGE("BufferProducer::LocalPost: not gained, id=%d state=%" PRIx64 ".",
+          id(), buffer_state);
+    return -EBUSY;
+  }
+
+  // Copy the canonical metadata.
+  void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
+  memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata));
+  // Copy extra user requested metadata.
+  if (meta->user_metadata_ptr && meta->user_metadata_size) {
+    void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
+    memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
+  }
+
+  // Send out the acquire fence through the shared epoll fd. Note that during
+  // posting no consumer is not expected to be polling on the fence.
+  if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_))
+    return error;
+
+  // Set the producer bit atomically to transit into posted state.
+  BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL,
+                                   BufferHubDefs::kProducerStateBit);
+  return 0;
+}
+
 int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
-                         size_t meta_size_bytes) {
+                         size_t user_metadata_size) {
   ATRACE_NAME("BufferProducer::Post");
+
+  // Populate cononical metadata for posting.
+  DvrNativeBufferMetadata canonical_meta;
+  canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta);
+  canonical_meta.user_metadata_size = user_metadata_size;
+
+  if (const int error = LocalPost(&canonical_meta, ready_fence))
+    return error;
+
   return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
-      BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes)));
+      BorrowedFence(ready_fence.Borrow())));
+}
+
+int BufferProducer::PostAsync(const DvrNativeBufferMetadata* meta,
+                              const LocalHandle& ready_fence) {
+  ATRACE_NAME("BufferProducer::PostAsync");
+
+  if (const int error = LocalPost(meta, ready_fence))
+    return error;
+
+  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode));
+}
+
+int BufferProducer::LocalGain(DvrNativeBufferMetadata* out_meta,
+                              LocalHandle* out_fence) {
+  uint64_t buffer_state = buffer_state_->load();
+  ALOGD_IF(TRACE, "BufferProducer::LocalGain: buffer=%d, state=%" PRIx64 ".",
+           id(), buffer_state);
+
+  if (!out_meta)
+    return -EINVAL;
+
+  if (!BufferHubDefs::IsBufferReleased(buffer_state)) {
+    if (BufferHubDefs::IsBufferGained(buffer_state)) {
+      // We don't want to log error when gaining a newly allocated
+      // buffer.
+      ALOGI("BufferProducer::LocalGain: already gained id=%d.", id());
+      return -EALREADY;
+    }
+    ALOGE("BufferProducer::LocalGain: not released id=%d state=%" PRIx64 ".",
+          id(), buffer_state);
+    return -EBUSY;
+  }
+
+  // Canonical metadata is undefined on Gain. Except for user_metadata and
+  // release_fence_mask. Fill in the user_metadata_ptr in address space of the
+  // local process.
+  if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) {
+    out_meta->user_metadata_size =
+        metadata_header_->metadata.user_metadata_size;
+    out_meta->user_metadata_ptr =
+        reinterpret_cast<uint64_t>(user_metadata_ptr_);
+  } else {
+    out_meta->user_metadata_size = 0;
+    out_meta->user_metadata_ptr = 0;
+  }
+
+  uint64_t fence_state = fence_state_->load();
+  // If there is an release fence from consumer, we need to return it.
+  if (fence_state & BufferHubDefs::kConsumerStateMask) {
+    *out_fence = shared_release_fence_.Duplicate();
+    out_meta->release_fence_mask =
+        fence_state & BufferHubDefs::kConsumerStateMask;
+  }
+
+  // Clear out all bits and the buffer is now back to gained state.
+  buffer_state_->store(0ULL);
+  return 0;
 }
 
 int BufferProducer::Gain(LocalHandle* release_fence) {
   ATRACE_NAME("BufferProducer::Gain");
+
+  DvrNativeBufferMetadata meta;
+  if (const int error = LocalGain(&meta, release_fence))
+    return error;
+
   auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
   if (!status)
     return -status.error();
-  if (release_fence)
-    *release_fence = status.take().take();
   return 0;
 }
 
-int BufferProducer::GainAsync() {
+int BufferProducer::GainAsync(DvrNativeBufferMetadata* out_meta,
+                              LocalHandle* release_fence) {
   ATRACE_NAME("BufferProducer::GainAsync");
+
+  if (const int error = LocalGain(out_meta, release_fence))
+    return error;
+
   return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
 }
 
+int BufferProducer::GainAsync() {
+  DvrNativeBufferMetadata meta;
+  LocalHandle fence;
+  return GainAsync(&meta, &fence);
+}
+
 std::unique_ptr<BufferProducer> BufferProducer::Import(
     LocalChannelHandle channel) {
   ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
index 1daa5d6..c4b9a8c 100644
--- a/libs/vr/libbufferhub/bufferhub_tests.cpp
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -1,5 +1,9 @@
 #include <gtest/gtest.h>
+#include <poll.h>
 #include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
 
 #include <mutex>
 #include <thread>
@@ -13,8 +17,10 @@
     return result;                            \
   })()
 
-using android::dvr::BufferProducer;
 using android::dvr::BufferConsumer;
+using android::dvr::BufferHubDefs::kConsumerStateMask;
+using android::dvr::BufferHubDefs::kProducerStateBit;
+using android::dvr::BufferProducer;
 using android::pdx::LocalHandle;
 
 const int kWidth = 640;
@@ -37,29 +43,149 @@
       BufferConsumer::Import(c->CreateConsumer());
   ASSERT_TRUE(c2.get() != nullptr);
 
+  // Producer state mask is unique, i.e. 1.
+  EXPECT_EQ(p->buffer_state_bit(), kProducerStateBit);
+  // Consumer state mask cannot have producer bit on.
+  EXPECT_EQ(c->buffer_state_bit() & kProducerStateBit, 0);
+  // Consumer state mask must be a single, i.e. power of 2.
+  EXPECT_NE(c->buffer_state_bit(), 0);
+  EXPECT_EQ(c->buffer_state_bit() & (c->buffer_state_bit() - 1), 0);
+  // Consumer state mask cannot have producer bit on.
+  EXPECT_EQ(c2->buffer_state_bit() & kProducerStateBit, 0);
+  // Consumer state mask must be a single, i.e. power of 2.
+  EXPECT_NE(c2->buffer_state_bit(), 0);
+  EXPECT_EQ(c2->buffer_state_bit() & (c2->buffer_state_bit() - 1), 0);
+  // Each consumer should have unique bit.
+  EXPECT_EQ(c->buffer_state_bit() & c2->buffer_state_bit(), 0);
+
+  // Initial state: producer not available, consumers not available.
+  EXPECT_EQ(0, RETRY_EINTR(p->Poll(100)));
+  EXPECT_EQ(0, RETRY_EINTR(c->Poll(100)));
+  EXPECT_EQ(0, RETRY_EINTR(c2->Poll(100)));
+
   EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
-  // Both consumers should be triggered.
-  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
-  EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
-  EXPECT_LT(0, RETRY_EINTR(c2->Poll(10)));
+
+  // New state: producer not available, consumers available.
+  EXPECT_EQ(0, RETRY_EINTR(p->Poll(100)));
+  EXPECT_EQ(1, RETRY_EINTR(c->Poll(100)));
+  EXPECT_EQ(1, RETRY_EINTR(c2->Poll(100)));
 
   uint64_t context;
   LocalHandle fence;
-  EXPECT_LE(0, c->Acquire(&fence, &context));
+  EXPECT_EQ(0, c->Acquire(&fence, &context));
   EXPECT_EQ(kContext, context);
-  EXPECT_GE(0, RETRY_EINTR(c->Poll(0)));
+  EXPECT_EQ(0, RETRY_EINTR(c->Poll(100)));
+  EXPECT_EQ(1, RETRY_EINTR(c2->Poll(100)));
 
-  EXPECT_LE(0, c2->Acquire(&fence, &context));
+  EXPECT_EQ(0, c2->Acquire(&fence, &context));
   EXPECT_EQ(kContext, context);
-  EXPECT_GE(0, RETRY_EINTR(c2->Poll(0)));
+  EXPECT_EQ(0, RETRY_EINTR(c2->Poll(100)));
+  EXPECT_EQ(0, RETRY_EINTR(c->Poll(100)));
 
   EXPECT_EQ(0, c->Release(LocalHandle()));
-  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, RETRY_EINTR(p->Poll(100)));
   EXPECT_EQ(0, c2->Discard());
 
-  EXPECT_LE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_EQ(1, RETRY_EINTR(p->Poll(100)));
   EXPECT_EQ(0, p->Gain(&fence));
-  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, RETRY_EINTR(p->Poll(100)));
+  EXPECT_EQ(0, RETRY_EINTR(c->Poll(100)));
+  EXPECT_EQ(0, RETRY_EINTR(c2->Poll(100)));
+}
+
+TEST_F(LibBufferHubTest, TestEpoll) {
+  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);
+
+  LocalHandle epoll_fd{epoll_create1(EPOLL_CLOEXEC)};
+  ASSERT_TRUE(epoll_fd.IsValid());
+
+  epoll_event event;
+  std::array<epoll_event, 64> events;
+
+  auto event_sources = p->GetEventSources();
+  ASSERT_LT(event_sources.size(), events.size());
+
+  for (const auto& event_source : event_sources) {
+    event = {.events = event_source.event_mask | EPOLLET,
+             .data = {.fd = p->event_fd()}};
+    ASSERT_EQ(0, epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_source.event_fd,
+                           &event));
+  }
+
+  event_sources = c->GetEventSources();
+  ASSERT_LT(event_sources.size(), events.size());
+
+  for (const auto& event_source : event_sources) {
+    event = {.events = event_source.event_mask | EPOLLET,
+             .data = {.fd = c->event_fd()}};
+    ASSERT_EQ(0, epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_source.event_fd,
+                           &event));
+  }
+
+  // No events should be signaled initially.
+  ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0));
+
+  // Post the producer and check for consumer signal.
+  EXPECT_EQ(0, p->Post({}, kContext));
+  ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100));
+  ASSERT_TRUE(events[0].events & EPOLLIN);
+  ASSERT_EQ(c->event_fd(), events[0].data.fd);
+
+  // Save the event bits to translate later.
+  event = events[0];
+
+  // Check for events again. Edge-triggered mode should prevent any.
+  EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100));
+  EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100));
+  EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100));
+  EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100));
+
+  // Translate the events.
+  auto event_status = c->GetEventMask(event.events);
+  ASSERT_TRUE(event_status);
+  ASSERT_TRUE(event_status.get() & EPOLLIN);
+
+  // Check for events again. Edge-triggered mode should prevent any.
+  EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100));
+}
+
+TEST_F(LibBufferHubTest, TestStateMask) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+
+  // It's ok to create up to 63 consumer buffers.
+  uint64_t buffer_state_bits = p->buffer_state_bit();
+  std::array<std::unique_ptr<BufferConsumer>, 63> cs;
+  for (size_t i = 0; i < 63; i++) {
+    cs[i] = BufferConsumer::Import(p->CreateConsumer());
+    ASSERT_TRUE(cs[i].get() != nullptr);
+    // Expect all buffers have unique state mask.
+    EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0);
+    buffer_state_bits |= cs[i]->buffer_state_bit();
+  }
+  EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask);
+
+  // The 64th creation will fail with out-of-memory error.
+  auto state = p->CreateConsumer();
+  EXPECT_EQ(state.error(), E2BIG);
+
+  // Release any consumer should allow us to re-create.
+  for (size_t i = 0; i < 63; i++) {
+    buffer_state_bits &= ~cs[i]->buffer_state_bit();
+    cs[i] = nullptr;
+    cs[i] = BufferConsumer::Import(p->CreateConsumer());
+    ASSERT_TRUE(cs[i].get() != nullptr);
+    // The released state mask will be reused.
+    EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0);
+    buffer_state_bits |= cs[i]->buffer_state_bit();
+    EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask);
+  }
 }
 
 TEST_F(LibBufferHubTest, TestStateTransitions) {
@@ -98,6 +224,7 @@
 
   // Release in acquired state should succeed.
   EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
 
   // Release, acquire, and post in released state should fail.
   EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
@@ -144,6 +271,11 @@
     int64_t field1;
     int64_t field2;
   };
+  struct OverSizedMetadata {
+    int64_t field1;
+    int64_t field2;
+    int64_t field3;
+  };
   std::unique_ptr<BufferProducer> p = BufferProducer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
   ASSERT_TRUE(p.get() != nullptr);
@@ -151,9 +283,16 @@
       BufferConsumer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
 
-  int64_t sequence = 3;
-  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+  // It is illegal to post metadata larger than originally requested during
+  // buffer allocation.
+  OverSizedMetadata evil_meta = {};
+  EXPECT_NE(0, p->Post(LocalHandle(), evil_meta));
   EXPECT_GE(0, RETRY_EINTR(c->Poll(10)));
+
+  // It is ok to post metadata smaller than originally requested during
+  // buffer allocation.
+  int64_t sequence = 42;
+  EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
 }
 
 TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
@@ -161,6 +300,11 @@
     int64_t field1;
     int64_t field2;
   };
+  struct OverSizedMetadata {
+    int64_t field1;
+    int64_t field2;
+    int64_t field3;
+  };
   std::unique_ptr<BufferProducer> p = BufferProducer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
   ASSERT_TRUE(p.get() != nullptr);
@@ -173,7 +317,16 @@
 
   LocalHandle fence;
   int64_t sequence;
-  EXPECT_NE(0, c->Acquire(&fence, &sequence));
+  OverSizedMetadata e;
+
+  // It is illegal to acquire metadata larger than originally requested during
+  // buffer allocation.
+  EXPECT_NE(0, c->Acquire(&fence, &e));
+
+  // It is ok to acquire metadata smaller than originally requested during
+  // buffer allocation.
+  EXPECT_EQ(0, c->Acquire(&fence, &sequence));
+  EXPECT_EQ(m.field1, sequence);
 }
 
 TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
@@ -266,12 +419,140 @@
   LocalHandle fence;
   auto c = BufferConsumer::Import(p->CreateConsumer());
   ASSERT_NE(nullptr, c);
-  EXPECT_NE(-EPIPE, c->Acquire(&fence));
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+  EXPECT_EQ(0, c->Acquire(&fence));
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
 
   // Test that removing persistence and closing the producer orphans the
   // consumer.
+  EXPECT_EQ(0, p->Gain(&fence));
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
   EXPECT_EQ(0, p->RemovePersistence());
   p = nullptr;
 
+  // Orphaned consumer can acquire the posted buffer one more time in
+  // asynchronous manner. But synchronous call will fail.
+  DvrNativeBufferMetadata meta;
+  EXPECT_EQ(0, c->AcquireAsync(&meta, &fence));
   EXPECT_EQ(-EPIPE, c->Release(LocalHandle()));
 }
+
+namespace {
+
+int PollFd(int fd, int timeout_ms) {
+  pollfd p = {fd, POLLIN, 0};
+  return poll(&p, 1, timeout_ms);
+}
+
+}  // namespace
+
+TEST_F(LibBufferHubTest, TestAcquireFence) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, /*metadata_size=*/0);
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  DvrNativeBufferMetadata meta;
+  LocalHandle f1(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+
+  // Post with unsignaled fence.
+  EXPECT_EQ(0, p->PostAsync(&meta, f1));
+
+  // Should acquire a valid fence.
+  LocalHandle f2;
+  EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+  EXPECT_EQ(0, c->AcquireAsync(&meta, &f2));
+  EXPECT_TRUE(f2.IsValid());
+  // The original fence and acquired fence should have different fd number.
+  EXPECT_NE(f1.Get(), f2.Get());
+  EXPECT_GE(0, PollFd(f2.Get(), 0));
+
+  // Signal the original fence will trigger the new fence.
+  eventfd_write(f1.Get(), 1);
+  // Now the original FD has been signaled.
+  EXPECT_LT(0, PollFd(f2.Get(), 10));
+
+  // Release the consumer with an invalid fence.
+  EXPECT_EQ(0, c->ReleaseAsync(&meta, LocalHandle()));
+
+  // Should gain an invalid fence.
+  LocalHandle f3;
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+  EXPECT_EQ(0, p->GainAsync(&meta, &f3));
+  EXPECT_FALSE(f3.IsValid());
+
+  // Post with a signaled fence.
+  EXPECT_EQ(0, p->PostAsync(&meta, f1));
+
+  // Should acquire a valid fence and it's already signalled.
+  LocalHandle f4;
+  EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+  EXPECT_EQ(0, c->AcquireAsync(&meta, &f4));
+  EXPECT_TRUE(f4.IsValid());
+  EXPECT_LT(0, PollFd(f4.Get(), 10));
+
+  // Release with an unsignalled fence and signal it immediately after release
+  // without producer gainning.
+  LocalHandle f5(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+  EXPECT_EQ(0, c->ReleaseAsync(&meta, f5));
+  eventfd_write(f5.Get(), 1);
+
+  // Should gain a valid fence, which is already signaled.
+  LocalHandle f6;
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+  EXPECT_EQ(0, p->GainAsync(&meta, &f6));
+  EXPECT_TRUE(f6.IsValid());
+  EXPECT_LT(0, PollFd(f6.Get(), 10));
+}
+
+TEST_F(LibBufferHubTest, TestOrphanedAcquire) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c1 =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c1.get() != nullptr);
+  const uint64_t consumer_state_bit1 = c1->buffer_state_bit();
+
+  DvrNativeBufferMetadata meta;
+  EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
+
+  LocalHandle fence;
+  EXPECT_LT(0, RETRY_EINTR(c1->Poll(10)));
+  EXPECT_LE(0, c1->AcquireAsync(&meta, &fence));
+  // Destroy the consumer now will make it orphaned and the buffer is still
+  // acquired.
+  c1 = nullptr;
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(10)));
+
+  std::unique_ptr<BufferConsumer> c2 =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c2.get() != nullptr);
+  const uint64_t consumer_state_bit2 = c2->buffer_state_bit();
+  EXPECT_NE(consumer_state_bit1, consumer_state_bit2);
+
+  // The new consumer is available for acquire.
+  EXPECT_LT(0, RETRY_EINTR(c2->Poll(10)));
+  EXPECT_LE(0, c2->AcquireAsync(&meta, &fence));
+  // Releasing the consumer makes the buffer gainable.
+  EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle()));
+
+  // The buffer is now available for the producer to gain.
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+
+  // But if another consumer is created in released state.
+  std::unique_ptr<BufferConsumer> c3 =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c3.get() != nullptr);
+  const uint64_t consumer_state_bit3 = c3->buffer_state_bit();
+  EXPECT_NE(consumer_state_bit2, consumer_state_bit3);
+  // The consumer buffer is not acquirable.
+  EXPECT_GE(0, RETRY_EINTR(c3->Poll(10)));
+  EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence));
+
+  // Producer should be able to gain no matter what.
+  EXPECT_EQ(0, p->GainAsync(&meta, &fence));
+}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
index be20e72..1186f93 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -11,6 +11,8 @@
 
 #include <private/dvr/ion_buffer.h>
 
+#include "bufferhub_rpc.h"
+
 namespace android {
 namespace dvr {
 
@@ -75,6 +77,14 @@
     }
   }
 
+  std::vector<pdx::ClientChannel::EventSource> GetEventSources() const {
+    if (auto* client_channel = GetChannel()) {
+      return client_channel->GetEventSources();
+    } else {
+      return {};
+    }
+  }
+
   native_handle_t* native_handle() const {
     return const_cast<native_handle_t*>(buffer_.handle());
   }
@@ -84,6 +94,10 @@
 
   int id() const { return id_; }
 
+  // A state mask which is unique to a buffer hub client among all its siblings
+  // sharing the same concrete graphic buffer.
+  uint64_t buffer_state_bit() const { return buffer_state_bit_; }
+
   // The following methods return settings of the first buffer. Currently,
   // it is only possible to create multi-buffer BufferHubBuffers with the same
   // settings.
@@ -98,6 +112,9 @@
   uint64_t producer_usage() const { return buffer_.usage(); }
   uint64_t consumer_usage() const { return buffer_.usage(); }
 
+  uint64_t GetQueueIndex() const { return metadata_header_->queue_index; }
+  void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; }
+
  protected:
   explicit BufferHubBuffer(LocalChannelHandle channel);
   explicit BufferHubBuffer(const std::string& endpoint_path);
@@ -106,6 +123,31 @@
   // Initialization helper.
   int ImportBuffer();
 
+  // Check invalid metadata operation. Returns 0 if requested metadata is valid.
+  int CheckMetadata(size_t user_metadata_size) const;
+
+  // Send out the new fence by updating the shared fence (shared_release_fence
+  // for producer and shared_acquire_fence for consumer). Note that during this
+  // should only be used in LocalPost() or LocalRelease, and the shared fence
+  // shouldn't be poll'ed by the other end.
+  int UpdateSharedFence(const LocalHandle& new_fence,
+                        const LocalHandle& shared_fence);
+
+  // IonBuffer that is shared between bufferhubd, producer, and consumers.
+  size_t metadata_buf_size_{0};
+  size_t user_metadata_size_{0};
+  BufferHubDefs::MetadataHeader* metadata_header_{nullptr};
+  void* user_metadata_ptr_{nullptr};
+  std::atomic<uint64_t>* buffer_state_{nullptr};
+  std::atomic<uint64_t>* fence_state_{nullptr};
+
+  LocalHandle shared_acquire_fence_;
+  LocalHandle shared_release_fence_;
+
+  // A local fence fd that holds the ownership of the fence fd on Post (for
+  // producer) and Release (for consumer).
+  LocalHandle pending_fence_fd_;
+
  private:
   BufferHubBuffer(const BufferHubBuffer&) = delete;
   void operator=(const BufferHubBuffer&) = delete;
@@ -114,8 +156,9 @@
   // for logging and debugging purposes only and should not be used for lookup
   // or any other functional purpose as a security precaution.
   int id_;
-
+  uint64_t buffer_state_bit_{0ULL};
   IonBuffer buffer_;
+  IonBuffer metadata_buffer_;
 };
 
 // This represents a writable buffer. Calling Post notifies all clients and
@@ -136,12 +179,17 @@
   static std::unique_ptr<BufferProducer> Import(
       Status<LocalChannelHandle> status);
 
+  // Asynchronously posts a buffer. The fence and metadata are passed to
+  // consumer via shared fd and shared memory.
+  int PostAsync(const DvrNativeBufferMetadata* meta,
+                const LocalHandle& ready_fence);
+
   // 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);
+           size_t user_metadata_size);
 
   template <typename Meta,
             typename = typename std::enable_if<std::is_void<Meta>::value>::type>
@@ -160,16 +208,15 @@
   // is in the released state.
   // This returns zero or a negative unix error code.
   int Gain(LocalHandle* release_fence);
+  int GainAsync();
 
   // 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();
+  // to acknowledge success or failure. 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 if local error check fails.
+  int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
 
   // 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
@@ -216,7 +263,7 @@
   BufferProducer(const std::string& name, int user_id, int group_id,
                  uint32_t width, uint32_t height, uint32_t format,
                  uint64_t producer_usage, uint64_t consumer_usage,
-                 size_t meta_size_bytes);
+                 size_t user_metadata_size);
 
   // Constructs a blob (flat) buffer with the given usage flags.
   BufferProducer(uint32_t usage, size_t size);
@@ -234,6 +281,11 @@
 
   // Imports the given file handle to a producer channel, taking ownership.
   explicit BufferProducer(LocalChannelHandle channel);
+
+  // Local state transition helpers.
+  int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
+  int LocalPost(const DvrNativeBufferMetadata* meta,
+                const LocalHandle& ready_fence);
 };
 
 // This is a connection to a producer buffer, which can be located in another
@@ -263,7 +315,7 @@
   // 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);
+  int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size);
 
   // 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
@@ -274,20 +326,22 @@
     return Acquire(ready_fence, meta, sizeof(*meta));
   }
 
+  // Asynchronously acquires a bufer.
+  int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
+
   // 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);
+  int ReleaseAsync();
 
   // 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();
+  // except that it does not wait for BufferHub to reply with success or error.
+  // The fence and metadata are passed to consumer via shared fd and shared
+  // memory.
+  int ReleaseAsync(const DvrNativeBufferMetadata* meta,
+                   const LocalHandle& release_fence);
 
   // 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
@@ -305,6 +359,11 @@
   friend BASE;
 
   explicit BufferConsumer(LocalChannelHandle channel);
+
+  // Local state transition helpers.
+  int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
+  int LocalRelease(const DvrNativeBufferMetadata* meta,
+                   const LocalHandle& release_fence);
 };
 
 }  // namespace dvr
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
index ca0e0e0..f9fd42d 100644
--- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -5,6 +5,7 @@
 #include <gui/BufferQueueDefs.h>
 #include <sys/types.h>
 
+#include <dvr/dvr_api.h>
 #include <pdx/channel_handle.h>
 #include <pdx/file_handle.h>
 #include <pdx/rpc/remote_method.h>
@@ -14,6 +15,71 @@
 namespace android {
 namespace dvr {
 
+namespace BufferHubDefs {
+
+static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB;
+static constexpr uint32_t kMetadataUsage =
+    GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
+
+// Single producuer multiple (up to 63) consumers ownership signal.
+// 64-bit atomic unsigned int.
+//
+// MSB           LSB
+//  |             |
+//  v             v
+// [P|C62|...|C1|C0]
+// Gain'ed state:     [0|..|0|0] -> Exclusively Writable.
+// Post'ed state:     [1|..|0|0]
+// Acquired'ed state: [1|..|X|X] -> At least one bit is set in lower 63 bits
+// Released'ed state: [0|..|X|X] -> At least one bit is set in lower 63 bits
+static constexpr uint64_t kProducerStateBit = 1ULL << 63;
+static constexpr uint64_t kConsumerStateMask = (1ULL << 63) - 1;
+
+static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state,
+                                     uint64_t clear_mask, uint64_t set_mask) {
+  uint64_t old_state;
+  uint64_t new_state;
+  do {
+    old_state = buffer_state->load();
+    new_state = (old_state & ~clear_mask) | set_mask;
+  } while (!buffer_state->compare_exchange_weak(old_state, new_state));
+}
+
+static inline bool IsBufferGained(uint64_t state) { return state == 0; }
+
+static inline bool IsBufferPosted(uint64_t state,
+                                  uint64_t consumer_bit = kConsumerStateMask) {
+  return (state & kProducerStateBit) && !(state & consumer_bit);
+}
+
+static inline bool IsBufferAcquired(uint64_t state) {
+  return (state & kProducerStateBit) && (state & kConsumerStateMask);
+}
+
+static inline bool IsBufferReleased(uint64_t state) {
+  return !(state & kProducerStateBit) && (state & kConsumerStateMask);
+}
+
+struct __attribute__((packed, aligned(8))) MetadataHeader {
+  // Internal data format, which can be updated as long as the size, padding and
+  // field alignment of the struct is consistent within the same ABI. As this
+  // part is subject for future updates, it's not stable cross Android version,
+  // so don't have it visible from outside of the Android platform (include Apps
+  // and vendor HAL).
+  std::atomic<uint64_t> buffer_state;
+  std::atomic<uint64_t> fence_state;
+  uint64_t queue_index;
+
+  // Public data format, which should be updated with caution. See more details
+  // in dvr_api.h
+  DvrNativeBufferMetadata metadata;
+};
+
+static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size");
+static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader);
+
+}  // namespace BufferHubDefs
+
 template <typename FileHandleType>
 class NativeBufferHandle {
  public:
@@ -93,6 +159,57 @@
   void operator=(const NativeBufferHandle&) = delete;
 };
 
+template <typename FileHandleType>
+class BufferDescription {
+ public:
+  BufferDescription() = default;
+  BufferDescription(const IonBuffer& buffer, const IonBuffer& metadata, int id,
+                    uint64_t buffer_state_bit,
+                    const FileHandleType& acquire_fence_fd,
+                    const FileHandleType& release_fence_fd)
+      : id_(id),
+        buffer_state_bit_(buffer_state_bit),
+        buffer_(buffer, id),
+        metadata_(metadata, id),
+        acquire_fence_fd_(acquire_fence_fd.Borrow()),
+        release_fence_fd_(release_fence_fd.Borrow()) {}
+
+  BufferDescription(BufferDescription&& other) = default;
+  BufferDescription& operator=(BufferDescription&& other) = default;
+
+  // ID of the buffer client. All BufferHubBuffer clients derived from the same
+  // buffer in bufferhubd share the same buffer id.
+  int id() const { return id_; }
+  // State mask of the buffer client. Each BufferHubBuffer client backed by the
+  // same buffer channel has uniqued state bit among its siblings. For a
+  // producer buffer the bit must be kProducerStateBit; for a consumer the bit
+  // must be one of the kConsumerStateMask.
+  uint64_t buffer_state_bit() const { return buffer_state_bit_; }
+  FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); }
+  FileHandleType take_release_fence() { return std::move(release_fence_fd_); }
+
+  int ImportBuffer(IonBuffer* buffer) { return buffer_.Import(buffer); }
+  int ImportMetadata(IonBuffer* metadata) { return metadata_.Import(metadata); }
+
+ private:
+  int id_{-1};
+  uint64_t buffer_state_bit_{0};
+  // Two IonBuffers: one for the graphic buffer and one for metadata.
+  NativeBufferHandle<FileHandleType> buffer_;
+  NativeBufferHandle<FileHandleType> metadata_;
+
+  // Pamameters for shared fences.
+  FileHandleType acquire_fence_fd_;
+  FileHandleType release_fence_fd_;
+
+  PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_,
+                           buffer_state_bit_, buffer_, metadata_,
+                           acquire_fence_fd_, release_fence_fd_);
+
+  BufferDescription(const BufferDescription&) = delete;
+  void operator=(const BufferDescription&) = delete;
+};
+
 using BorrowedNativeBufferHandle = NativeBufferHandle<pdx::BorrowedHandle>;
 using LocalNativeBufferHandle = NativeBufferHandle<pdx::LocalHandle>;
 
@@ -149,11 +266,11 @@
 
   // Size of the meta data associated with all the buffers allocated from the
   // queue.
-  size_t meta_size_bytes;
+  size_t user_metadata_size;
 
  private:
   PDX_SERIALIZABLE_MEMBERS(ProducerQueueConfig, is_async, default_width,
-                           default_height, default_format, meta_size_bytes);
+                           default_height, default_format, user_metadata_size);
 };
 
 class ProducerQueueConfigBuilder {
@@ -161,7 +278,7 @@
   // Build a ProducerQueueConfig object.
   ProducerQueueConfig Build() {
     return {is_async_, default_width_, default_height_, default_format_,
-            meta_size_bytes_};
+            user_metadata_size_};
   }
 
   ProducerQueueConfigBuilder& SetIsAsync(bool is_async) {
@@ -186,12 +303,12 @@
 
   template <typename Meta>
   ProducerQueueConfigBuilder& SetMetadata() {
-    meta_size_bytes_ = sizeof(Meta);
+    user_metadata_size_ = sizeof(Meta);
     return *this;
   }
 
-  ProducerQueueConfigBuilder& SetMetadataSize(size_t meta_size_bytes) {
-    meta_size_bytes_ = meta_size_bytes;
+  ProducerQueueConfigBuilder& SetMetadataSize(size_t user_metadata_size) {
+    user_metadata_size_ = user_metadata_size;
     return *this;
   }
 
@@ -200,7 +317,7 @@
   uint32_t default_width_{1};
   uint32_t default_height_{1};
   uint32_t default_format_{1};  // PIXEL_FORMAT_RGBA_8888
-  size_t meta_size_bytes_{0};
+  size_t user_metadata_size_{0};
 };
 
 // Explicit specializations of ProducerQueueConfigBuilder::Build for void
@@ -208,7 +325,7 @@
 template <>
 inline ProducerQueueConfigBuilder&
 ProducerQueueConfigBuilder::SetMetadata<void>() {
-  meta_size_bytes_ = 0;
+  user_metadata_size_ = 0;
   return *this;
 }
 
@@ -269,7 +386,6 @@
   };
 
   // Aliases.
-  using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
   using LocalChannelHandle = pdx::LocalChannelHandle;
   using LocalHandle = pdx::LocalHandle;
   using Void = pdx::rpc::Void;
@@ -277,25 +393,24 @@
   // Methods.
   PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer,
                     void(uint32_t width, uint32_t height, uint32_t format,
-                         uint64_t usage, size_t meta_size_bytes));
+                         uint64_t usage, size_t user_metadata_size));
   PDX_REMOTE_METHOD(CreatePersistentBuffer, kOpCreatePersistentBuffer,
                     void(const std::string& name, int user_id, int group_id,
                          uint32_t width, uint32_t height, uint32_t format,
-                         uint64_t usage, size_t meta_size_bytes));
+                         uint64_t usage, size_t user_metadata_size));
   PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer,
                     void(const std::string& name));
   PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer,
-                    NativeBufferHandle<LocalHandle>(Void));
+                    BufferDescription<LocalHandle>(Void));
   PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void));
   PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent,
                     void(const std::string& name, int user_id, int group_id));
   PDX_REMOTE_METHOD(ProducerRemovePersistence, kOpProducerRemovePersistence,
                     void(Void));
   PDX_REMOTE_METHOD(ProducerPost, kOpProducerPost,
-                    void(LocalFence acquire_fence, MetaData));
+                    void(LocalFence acquire_fence));
   PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void));
-  PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire,
-                    std::pair<LocalFence, MetaData>(std::size_t metadata_size));
+  PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire, LocalFence(Void));
   PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
                     void(LocalFence release_fence));
   PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore));
@@ -305,7 +420,7 @@
                     QueueInfo(const ProducerQueueConfig& producer_config,
                               const UsagePolicy& usage_policy));
   PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue,
-                    LocalChannelHandle(Void));
+                    LocalChannelHandle(bool silent_queue));
   PDX_REMOTE_METHOD(GetQueueInfo, kOpGetQueueInfo, QueueInfo(Void));
   PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers,
                     kOpProducerQueueAllocateBuffers,
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index 0b3b2f0..93ccd0f 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -48,6 +48,7 @@
     cflags: [
         "-DLOG_TAG=\"libbufferhubqueue\"",
         "-DTRACE=0",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
     ],
     srcs: sourceFiles,
     export_include_dirs: includeFiles,
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index f9f87ff..8bea0cd 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -10,6 +10,7 @@
 #include <pdx/default_transport/client_channel.h>
 #include <pdx/default_transport/client_channel_factory.h>
 #include <pdx/file_handle.h>
+#include <pdx/trace.h>
 
 #define RETRY_EINTR(fnc_call)                 \
   ([&]() -> decltype(fnc_call) {              \
@@ -44,17 +45,6 @@
   }
 }
 
-// Polls a buffer for the given events, taking care to do the proper
-// translation.
-Status<int> PollEvents(const std::shared_ptr<BufferHubBuffer>& buffer,
-                       short events) {
-  auto poll_status = PollEvents(buffer->event_fd(), events);
-  if (!poll_status)
-    return poll_status;
-
-  return buffer->GetEventMask(poll_status.get());
-}
-
 std::pair<int32_t, int32_t> Unstuff(uint64_t value) {
   return {static_cast<int32_t>(value >> 32),
           static_cast<int32_t>(value & ((1ull << 32) - 1))};
@@ -115,27 +105,27 @@
   default_width_ = queue_info.producer_config.default_width;
   default_height_ = queue_info.producer_config.default_height;
   default_format_ = queue_info.producer_config.default_format;
-  meta_size_ = queue_info.producer_config.meta_size_bytes;
+  user_metadata_size_ = queue_info.producer_config.user_metadata_size;
   id_ = queue_info.id;
 }
 
 std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
-  if (auto status = CreateConsumerQueueHandle())
+  if (auto status = CreateConsumerQueueHandle(/*silent*/ false))
     return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
   else
     return nullptr;
 }
 
 std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() {
-  if (auto status = CreateConsumerQueueHandle())
-    return std::unique_ptr<ConsumerQueue>(
-        new ConsumerQueue(status.take(), true));
+  if (auto status = CreateConsumerQueueHandle(/*silent*/ true))
+    return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
   else
     return nullptr;
 }
 
-Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle() {
-  auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>();
+Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle(
+    bool silent) {
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(silent);
   if (!status) {
     ALOGE(
         "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: "
@@ -148,6 +138,7 @@
 }
 
 bool BufferHubQueue::WaitForBuffers(int timeout) {
+  ATRACE_NAME("BufferHubQueue::WaitForBuffers");
   std::array<epoll_event, kMaxEvents> events;
 
   // Loop at least once to check for hangups.
@@ -178,13 +169,18 @@
     const int num_events = ret;
 
     // A BufferQueue's epoll fd tracks N+1 events, where there are N events,
-    // one for each buffer, in the queue and one extra event for the queue
+    // one for each buffer in the queue, and one extra event for the queue
     // client itself.
     for (int i = 0; i < num_events; i++) {
       int32_t event_fd;
       int32_t index;
       std::tie(event_fd, index) = Unstuff(events[i].data.u64);
 
+      PDX_TRACE_FORMAT(
+          "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;"
+          "slot=%d|",
+          id(), num_events, i, event_fd, index);
+
       ALOGD_IF(TRACE,
                "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d",
                i, event_fd, index);
@@ -208,6 +204,7 @@
 
 Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd,
                                                int poll_events) {
+  ATRACE_NAME("BufferHubQueue::HandleBufferEvent");
   if (!buffers_[slot]) {
     ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
     return ErrorStatus(ENOENT);
@@ -221,58 +218,19 @@
   }
 
   const int events = status.get();
+  PDX_TRACE_FORMAT(
+      "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;"
+      "events=%d|",
+      id(), buffers_[slot]->id(), slot, event_fd, poll_events, events);
+
   if (events & EPOLLIN) {
-    auto entry_status = OnBufferReady(buffers_[slot], slot);
-    if (entry_status.ok() || entry_status.error() == EALREADY) {
-      // Only enqueue the buffer if it moves to or is already in the state
-      // requested in OnBufferReady().
-      return Enqueue(entry_status.take());
-    } else if (entry_status.error() == EBUSY) {
-      // If the buffer is busy this means that the buffer moved from released to
-      // posted when a new consumer was created before the ProducerQueue had a
-      // chance to regain it. This is a valid transition that we have to handle
-      // because edge triggered poll events latch the ready state even if it is
-      // later de-asserted -- don't enqueue or print an error log in this case.
-    } else {
-      ALOGE(
-          "BufferHubQueue::HandleBufferEvent: Failed to set buffer ready, "
-          "queue_id=%d buffer_id=%d: %s",
-          id(), buffers_[slot]->id(), entry_status.GetErrorMessage().c_str());
-    }
+    return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()});
   } else if (events & EPOLLHUP) {
-    // Check to see if the current buffer in the slot hung up. This is a bit of
-    // paranoia to deal with the epoll set getting out of sync with the buffer
-    // slots.
-    auto poll_status = PollEvents(buffers_[slot], POLLIN);
-    if (!poll_status && poll_status.error() != ETIMEDOUT) {
-      ALOGE("BufferHubQueue::HandleBufferEvent: Failed to poll buffer: %s",
-            poll_status.GetErrorMessage().c_str());
-      return poll_status.error_status();
-    }
-
-    const bool hangup_pending = status.ok() && (poll_status.get() & EPOLLHUP);
-
     ALOGW(
         "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu "
-        "event_fd=%d buffer_id=%d hangup_pending=%d poll_status=%x",
-        slot, buffers_[slot]->event_fd(), buffers_[slot]->id(), hangup_pending,
-        poll_status.get());
-
-    if (hangup_pending) {
-      return RemoveBuffer(slot);
-    } else {
-      // Clean up the bookkeeping for the event fd. This is a bit of paranoia to
-      // deal with the epoll set getting out of sync with the buffer slots.
-      // Hitting this path should be very unusual.
-      const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, event_fd, nullptr);
-      if (ret < 0) {
-        ALOGE(
-            "BufferHubQueue::HandleBufferEvent: Failed to remove fd=%d from "
-            "epoll set: %s",
-            event_fd, strerror(-ret));
-        return ErrorStatus(-ret);
-      }
-    }
+        "event_fd=%d buffer_id=%d",
+        slot, buffers_[slot]->event_fd(), buffers_[slot]->id());
+    return RemoveBuffer(slot);
   } else {
     ALOGW(
         "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll "
@@ -284,6 +242,7 @@
 }
 
 Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) {
+  ATRACE_NAME("BufferHubQueue::HandleQueueEvent");
   auto status = GetEventMask(poll_event);
   if (!status) {
     ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
@@ -330,13 +289,16 @@
       return remove_status.error_status();
   }
 
-  epoll_event event = {.events = EPOLLIN | EPOLLET,
-                       .data = {.u64 = Stuff(buffer->event_fd(), slot)}};
-  const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, buffer->event_fd(), &event);
-  if (ret < 0) {
-    ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s",
-          strerror(-ret));
-    return ErrorStatus(-ret);
+  for (const auto& event_source : buffer->GetEventSources()) {
+    epoll_event event = {.events = event_source.event_mask | EPOLLET,
+                         .data = {.u64 = Stuff(buffer->event_fd(), slot)}};
+    const int ret =
+        epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event);
+    if (ret < 0) {
+      ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s",
+            strerror(-ret));
+      return ErrorStatus(-ret);
+    }
   }
 
   buffers_[slot] = buffer;
@@ -348,15 +310,16 @@
   ALOGD_IF(TRACE, "BufferHubQueue::RemoveBuffer: slot=%zu", slot);
 
   if (buffers_[slot]) {
-    const int ret =
-        epoll_fd_.Control(EPOLL_CTL_DEL, buffers_[slot]->event_fd(), nullptr);
-    if (ret < 0) {
-      ALOGE(
-          "BufferHubQueue::RemoveBuffer: Failed to remove buffer from epoll "
-          "set: "
-          "%s",
-          strerror(-ret));
-      return ErrorStatus(-ret);
+    for (const auto& event_source : buffers_[slot]->GetEventSources()) {
+      const int ret =
+          epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr);
+      if (ret < 0) {
+        ALOGE(
+            "BufferHubQueue::RemoveBuffer: Failed to remove buffer from epoll "
+            "set: %s",
+            strerror(-ret));
+        return ErrorStatus(-ret);
+      }
     }
 
     // Trigger OnBufferRemoved callback if registered.
@@ -372,7 +335,7 @@
 
 Status<void> BufferHubQueue::Enqueue(Entry entry) {
   if (!is_full()) {
-    available_buffers_.Append(std::move(entry));
+    available_buffers_.push(std::move(entry));
 
     // Trigger OnBufferAvailable callback if registered.
     if (on_buffer_available_)
@@ -385,25 +348,26 @@
   }
 }
 
-Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(
-    int timeout, size_t* slot, void* meta, LocalHandle* fence) {
+Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(int timeout,
+                                                                 size_t* slot) {
   ALOGD_IF(TRACE, "BufferHubQueue::Dequeue: count=%zu, timeout=%d", count(),
            timeout);
 
-  if (!WaitForBuffers(timeout))
-    return ErrorStatus(ETIMEDOUT);
+  PDX_TRACE_FORMAT("BufferHubQueue::Dequeue|count=%zu|", count());
 
-  auto& entry = available_buffers_.Front();
+  if (count() == 0) {
+    if (!WaitForBuffers(timeout))
+      return ErrorStatus(ETIMEDOUT);
+  }
+
+  auto& entry = available_buffers_.top();
+  PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(),
+                   entry.slot);
 
   std::shared_ptr<BufferHubBuffer> buffer = std::move(entry.buffer);
   *slot = entry.slot;
-  *fence = std::move(entry.fence);
-  if (meta && entry.metadata) {
-    std::copy(entry.metadata.get(), entry.metadata.get() + meta_size_,
-              reinterpret_cast<uint8_t*>(meta));
-  }
 
-  available_buffers_.PopFront();
+  available_buffers_.pop();
 
   return {std::move(buffer)};
 }
@@ -419,7 +383,8 @@
 
 pdx::Status<void> BufferHubQueue::FreeAllBuffers() {
   // Clear all available buffers.
-  available_buffers_.Clear();
+  while (!available_buffers_.empty())
+    available_buffers_.pop();
 
   pdx::Status<void> last_error;  // No error.
   // Clear all buffers this producer queue is tracking.
@@ -429,7 +394,7 @@
       if (!status) {
         ALOGE(
             "ProducerQueue::FreeAllBuffers: Failed to remove buffer at "
-            "slot=%d.",
+            "slot=%zu.",
             slot);
         last_error = status.error_status();
       }
@@ -548,7 +513,7 @@
   if (!status)
     return status;
 
-  return Enqueue(buffer, slot);
+  return BufferHubQueue::Enqueue({buffer, slot, 0ULL});
 }
 
 Status<void> ProducerQueue::RemoveBuffer(size_t slot) {
@@ -565,40 +530,33 @@
 
 Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue(
     int timeout, size_t* slot, LocalHandle* release_fence) {
-  if (slot == nullptr || release_fence == nullptr) {
-    ALOGE("ProducerQueue::Dequeue: Invalid parameter: slot=%p release_fence=%p",
-          slot, release_fence);
+  DvrNativeBufferMetadata canonical_meta;
+  return Dequeue(timeout, slot, &canonical_meta, release_fence);
+}
+
+pdx::Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue(
+    int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
+    pdx::LocalHandle* release_fence) {
+  ATRACE_NAME("ProducerQueue::Dequeue");
+  if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) {
+    ALOGE("ProducerQueue::Dequeue: Invalid parameter.");
     return ErrorStatus(EINVAL);
   }
 
-  auto buffer_status =
-      BufferHubQueue::Dequeue(timeout, slot, nullptr, release_fence);
-  if (!buffer_status)
-    return buffer_status.error_status();
+  auto status = BufferHubQueue::Dequeue(timeout, slot);
+  if (!status)
+    return status.error_status();
 
-  return {std::static_pointer_cast<BufferProducer>(buffer_status.take())};
-}
-
-Status<BufferHubQueue::Entry> ProducerQueue::OnBufferReady(
-    const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) {
-  ALOGD_IF(TRACE,
-           "ProducerQueue::OnBufferReady: queue_id=%d buffer_id=%d slot=%zu",
-           id(), buffer->id(), slot);
-
-  // Avoid taking a transient reference, buffer is valid for the duration of
-  // this method call.
-  auto* producer_buffer = static_cast<BufferProducer*>(buffer.get());
-  LocalHandle release_fence;
-
-  const int ret = producer_buffer->Gain(&release_fence);
-  if (ret < 0)
+  auto buffer = std::static_pointer_cast<BufferProducer>(status.take());
+  const int ret = buffer->GainAsync(out_meta, release_fence);
+  if (ret < 0 && ret != -EALREADY)
     return ErrorStatus(-ret);
-  else
-    return {{buffer, nullptr, std::move(release_fence), slot}};
+
+  return {std::move(buffer)};
 }
 
-ConsumerQueue::ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import)
-    : BufferHubQueue(std::move(handle)), ignore_on_import_(ignore_on_import) {
+ConsumerQueue::ConsumerQueue(LocalChannelHandle handle)
+    : BufferHubQueue(std::move(handle)) {
   auto status = ImportQueue();
   if (!status) {
     ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s",
@@ -619,9 +577,17 @@
 Status<size_t> ConsumerQueue::ImportBuffers() {
   auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
   if (!status) {
-    ALOGE("ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s",
+    if (status.error() == EBADR) {
+      ALOGI(
+          "ConsumerQueue::ImportBuffers: Queue is silent, no buffers "
+          "imported.");
+      return {0};
+    } else {
+      ALOGE(
+          "ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s",
           status.GetErrorMessage().c_str());
-    return status.error_status();
+      return status.error_status();
+    }
   }
 
   int ret;
@@ -642,22 +608,6 @@
       continue;
     }
 
-    // Setup ignore state before adding buffer to the queue.
-    if (ignore_on_import_) {
-      ALOGD_IF(TRACE,
-               "ConsumerQueue::ImportBuffers: Setting buffer to ignored state: "
-               "buffer_id=%d",
-               buffer_consumer->id());
-      ret = buffer_consumer->SetIgnore(true);
-      if (ret < 0) {
-        ALOGE(
-            "ConsumerQueue::ImportBuffers: Failed to set ignored state on "
-            "imported buffer buffer_id=%d: %s",
-            buffer_consumer->id(), strerror(-ret));
-        last_error = ErrorStatus(-ret);
-      }
-    }
-
     auto add_status =
         AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second);
     if (!add_status) {
@@ -685,7 +635,7 @@
 
   // Check to see if the buffer is already signaled. This is necessary to catch
   // cases where buffers are already available; epoll edge triggered mode does
-  // not fire until and edge transition when adding new buffers to the epoll
+  // not fire until an edge transition when adding new buffers to the epoll
   // set. Note that we only poll the fd events because HandleBufferEvent() takes
   // care of checking the translated buffer events.
   auto poll_status = PollEvents(buffer->event_fd(), POLLIN);
@@ -703,51 +653,53 @@
 }
 
 Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue(
-    int timeout, size_t* slot, void* meta, size_t meta_size,
+    int timeout, size_t* slot, void* meta, size_t user_metadata_size,
     LocalHandle* acquire_fence) {
-  if (meta_size != meta_size_) {
+  if (user_metadata_size != user_metadata_size_) {
     ALOGE(
         "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer "
         "does not match metadata size (%zu) for the queue.",
-        meta_size, meta_size_);
+        user_metadata_size, user_metadata_size_);
     return ErrorStatus(EINVAL);
   }
 
-  if (slot == nullptr || acquire_fence == nullptr) {
-    ALOGE(
-        "ConsumerQueue::Dequeue: Invalid parameter: slot=%p meta=%p "
-        "acquire_fence=%p",
-        slot, meta, acquire_fence);
-    return ErrorStatus(EINVAL);
+  DvrNativeBufferMetadata canonical_meta;
+  auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence);
+  if (!status)
+    return status.error_status();
+
+  if (meta && user_metadata_size) {
+    void* metadata_src =
+        reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
+    if (metadata_src) {
+      memcpy(meta, metadata_src, user_metadata_size);
+    } else {
+      ALOGW("ConsumerQueue::Dequeue: no user-defined metadata.");
+    }
   }
 
-  auto buffer_status =
-      BufferHubQueue::Dequeue(timeout, slot, meta, acquire_fence);
-  if (!buffer_status)
-    return buffer_status.error_status();
-
-  return {std::static_pointer_cast<BufferConsumer>(buffer_status.take())};
+  return status;
 }
 
-Status<BufferHubQueue::Entry> ConsumerQueue::OnBufferReady(
-    const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) {
-  ALOGD_IF(TRACE,
-           "ConsumerQueue::OnBufferReady: queue_id=%d buffer_id=%d slot=%zu",
-           id(), buffer->id(), slot);
+Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue(
+    int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
+    pdx::LocalHandle* acquire_fence) {
+  ATRACE_NAME("ConsumerQueue::Dequeue");
+  if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) {
+    ALOGE("ConsumerQueue::Dequeue: Invalid parameter.");
+    return ErrorStatus(EINVAL);
+  }
 
-  // Avoid taking a transient reference, buffer is valid for the duration of
-  // this method call.
-  auto* consumer_buffer = static_cast<BufferConsumer*>(buffer.get());
-  std::unique_ptr<uint8_t[]> metadata(meta_size_ ? new uint8_t[meta_size_]
-                                                 : nullptr);
-  LocalHandle acquire_fence;
+  auto status = BufferHubQueue::Dequeue(timeout, slot);
+  if (!status)
+    return status.error_status();
 
-  const int ret =
-      consumer_buffer->Acquire(&acquire_fence, metadata.get(), meta_size_);
+  auto buffer = std::static_pointer_cast<BufferConsumer>(status.take());
+  const int ret = buffer->AcquireAsync(out_meta, acquire_fence);
   if (ret < 0)
     return ErrorStatus(-ret);
-  else
-    return {{buffer, std::move(metadata), std::move(acquire_fence), slot}};
+
+  return {std::move(buffer)};
 }
 
 Status<void> ConsumerQueue::OnBufferAllocated() {
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
index 53eed89..221bc4f 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -328,7 +328,7 @@
 
   LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1);
 
-  DvrNativeBufferMetadata meta_data = {};
+  DvrNativeBufferMetadata meta_data;
   meta_data.timestamp = timestamp;
   meta_data.is_auto_timestamp = static_cast<int32_t>(is_auto_timestamp);
   meta_data.dataspace = static_cast<int32_t>(dataspace);
@@ -339,7 +339,7 @@
   meta_data.scaling_mode = static_cast<int32_t>(scaling_mode);
   meta_data.transform = static_cast<int32_t>(transform);
 
-  buffer_producer->Post(fence_fd, &meta_data, sizeof(meta_data));
+  buffer_producer->PostAsync(&meta_data, fence_fd);
   buffers_[slot].mBufferState.queue();
 
   output->width = buffer_producer->width();
@@ -384,7 +384,7 @@
   }
 
   auto buffer_producer = buffers_[slot].mBufferProducer;
-  queue_->Enqueue(buffer_producer, slot);
+  queue_->Enqueue(buffer_producer, slot, 0ULL);
   buffers_[slot].mBufferState.cancel();
   buffers_[slot].mFence = fence;
   ALOGD_IF(TRACE, "cancelBuffer: slot %d", slot);
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
index 3e93788..6962d6c 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -5,12 +5,13 @@
 
 #include <pdx/client.h>
 #include <pdx/status.h>
-#include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/epoll_file_descriptor.h>
 #include <private/dvr/ring_buffer.h>
 
 #include <memory>
+#include <queue>
 #include <vector>
 
 namespace android {
@@ -50,22 +51,30 @@
   uint32_t default_format() const { return default_format_; }
 
   // Creates a new consumer in handle form for immediate transport over RPC.
-  pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle();
+  pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle(
+      bool silent = false);
 
   // Returns the number of buffers avaiable for dequeue.
-  size_t count() const { return available_buffers_.GetSize(); }
+  size_t count() const { return available_buffers_.size(); }
 
   // Returns the total number of buffers that the queue is tracking.
   size_t capacity() const { return capacity_; }
 
   // Returns the size of metadata structure associated with this queue.
-  size_t metadata_size() const { return meta_size_; }
+  size_t metadata_size() const { return user_metadata_size_; }
 
   // Returns whether the buffer queue is full.
-  bool is_full() const { return available_buffers_.IsFull(); }
+  bool is_full() const {
+    return available_buffers_.size() >= kMaxQueueCapacity;
+  }
 
   explicit operator bool() const { return epoll_fd_.IsValid(); }
 
+  int GetBufferId(size_t slot) const {
+    return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id()
+                                                      : -1;
+  }
+
   std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const {
     return buffers_[slot];
   }
@@ -131,8 +140,8 @@
   // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely,
   // while specifying a timeout equal to zero cause Dequeue() to return
   // immediately, even if no buffers are available.
-  pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue(
-      int timeout, size_t* slot, void* meta, pdx::LocalHandle* fence);
+  pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue(int timeout,
+                                                        size_t* slot);
 
   // Waits for buffers to become available and adds them to the available queue.
   bool WaitForBuffers(int timeout);
@@ -145,8 +154,9 @@
   // per-buffer data.
   struct Entry {
     Entry() : slot(0) {}
-    Entry(const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot)
-        : buffer(buffer), slot(slot) {}
+    Entry(const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot,
+          uint64_t index)
+        : buffer(buffer), slot(slot), index(index) {}
     Entry(const std::shared_ptr<BufferHubBuffer>& buffer,
           std::unique_ptr<uint8_t[]> metadata, pdx::LocalHandle fence,
           size_t slot)
@@ -161,20 +171,24 @@
     std::unique_ptr<uint8_t[]> metadata;
     pdx::LocalHandle fence;
     size_t slot;
+    uint64_t index;
+  };
+
+  struct EntryComparator {
+    bool operator()(const Entry& lhs, const Entry& rhs) {
+      return lhs.index > rhs.index;
+    }
   };
 
   // Enqueues a buffer to the available list (Gained for producer or Acquireed
   // for consumer).
   pdx::Status<void> Enqueue(Entry entry);
 
-  virtual pdx::Status<Entry> OnBufferReady(
-      const std::shared_ptr<BufferHubBuffer>& buf, size_t slot) = 0;
-
   // Called when a buffer is allocated remotely.
   virtual pdx::Status<void> OnBufferAllocated() { return {}; }
 
   // Size of the metadata that buffers in this queue cary.
-  size_t meta_size_{0};
+  size_t user_metadata_size_{0};
 
  private:
   void Initialize();
@@ -218,10 +232,12 @@
   // Tracks the buffers belonging to this queue. Buffers are stored according to
   // "slot" in this vector. Each slot is a logical id of the buffer within this
   // queue regardless of its queue position or presence in the ring buffer.
-  std::vector<std::shared_ptr<BufferHubBuffer>> buffers_{kMaxQueueCapacity};
+  std::array<std::shared_ptr<BufferHubBuffer>, kMaxQueueCapacity> buffers_;
 
   // Buffers and related data that are available for dequeue.
-  RingBuffer<Entry> available_buffers_{kMaxQueueCapacity};
+  // RingBuffer<Entry> available_buffers_{kMaxQueueCapacity};
+  std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
+      available_buffers_;
 
   // Keeps track with how many buffers have been added into the queue.
   size_t capacity_{0};
@@ -311,11 +327,14 @@
   // to the consumer side.
   pdx::Status<std::shared_ptr<BufferProducer>> Dequeue(
       int timeout, size_t* slot, pdx::LocalHandle* release_fence);
+  pdx::Status<std::shared_ptr<BufferProducer>> Dequeue(
+      int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
+      pdx::LocalHandle* release_fence);
 
   // Enqueues a producer buffer in the queue.
   pdx::Status<void> Enqueue(const std::shared_ptr<BufferProducer>& buffer,
-                            size_t slot) {
-    return BufferHubQueue::Enqueue({buffer, slot});
+                            size_t slot, uint64_t index) {
+    return BufferHubQueue::Enqueue({buffer, slot, index});
   }
 
  private:
@@ -326,9 +345,6 @@
   // arguments as the constructors.
   explicit ProducerQueue(pdx::LocalChannelHandle handle);
   ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage);
-
-  pdx::Status<Entry> OnBufferReady(
-      const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) override;
 };
 
 class ConsumerQueue : public BufferHubQueue {
@@ -347,10 +363,9 @@
   // used to avoid participation in the buffer lifecycle by a consumer queue
   // that is only used to spawn other consumer queues, such as in an
   // intermediate service.
-  static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle,
-                                               bool ignore_on_import = false) {
+  static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle) {
     return std::unique_ptr<ConsumerQueue>(
-        new ConsumerQueue(std::move(handle), ignore_on_import));
+        new ConsumerQueue(std::move(handle)));
   }
 
   // Import newly created buffers from the service side.
@@ -374,13 +389,16 @@
   }
 
   pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
-      int timeout, size_t* slot, void* meta, size_t meta_size,
+      int timeout, size_t* slot, void* meta, size_t user_metadata_size,
+      pdx::LocalHandle* acquire_fence);
+  pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+      int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
       pdx::LocalHandle* acquire_fence);
 
  private:
   friend BufferHubQueue;
 
-  ConsumerQueue(pdx::LocalChannelHandle handle, bool ignore_on_import = false);
+  ConsumerQueue(pdx::LocalChannelHandle handle);
 
   // Add a consumer buffer to populate the queue. Once added, a consumer buffer
   // is NOT available to use until the producer side |Post| it. |WaitForBuffers|
@@ -389,14 +407,7 @@
   pdx::Status<void> AddBuffer(const std::shared_ptr<BufferConsumer>& buffer,
                               size_t slot);
 
-  pdx::Status<Entry> OnBufferReady(
-      const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) override;
-
   pdx::Status<void> OnBufferAllocated() override;
-
-  // Flag indicating that imported (consumer) buffers should be ignored when
-  // imported to avoid participating in the buffer ownership flow.
-  bool ignore_on_import_;
 };
 
 }  // namespace dvr
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index 865573c..8bd1ef1 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -1,4 +1,7 @@
 
+header_libraries = [
+    "libdvr_headers",
+]
 
 shared_libraries = [
     "libbase",
@@ -21,6 +24,7 @@
 
 cc_test {
     srcs: ["buffer_hub_queue-test.cpp"],
+    header_libs: header_libraries,
     static_libs: static_libraries,
     shared_libs: shared_libraries,
     cflags: [
@@ -35,6 +39,7 @@
 
 cc_test {
     srcs: ["buffer_hub_queue_producer-test.cpp"],
+    header_libs: header_libraries,
     static_libs: static_libraries,
     shared_libs: shared_libraries,
     cflags: [
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index 7581a06..8a72531 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -3,6 +3,8 @@
 #include <private/dvr/buffer_hub_queue_client.h>
 
 #include <gtest/gtest.h>
+#include <poll.h>
+#include <sys/eventfd.h>
 
 #include <vector>
 
@@ -46,9 +48,9 @@
 
   void AllocateBuffer(size_t* slot_out = nullptr) {
     // Create producer buffer.
-    auto status = producer_queue_->AllocateBuffer(
-        kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-        kBufferUsage);
+    auto status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+                                                  kBufferLayerCount,
+                                                  kBufferFormat, kBufferUsage);
 
     ASSERT_TRUE(status.ok());
     size_t slot = status.take();
@@ -56,6 +58,23 @@
       *slot_out = slot;
   }
 
+  bool WaitAndHandleOnce(BufferHubQueue* queue, int timeout_ms) {
+    pollfd pfd{queue->queue_fd(), POLLIN, 0};
+    int ret;
+    do {
+      ret = poll(&pfd, 1, timeout_ms);
+    } while (ret == -1 && errno == EINTR);
+
+    if (ret < 0) {
+      ALOGW("Failed to poll queue %d's event fd, error: %s.", queue->id(),
+            strerror(errno));
+      return false;
+    } else if (ret == 0) {
+      return false;
+    }
+    return queue->HandleQueueEvents();
+  }
+
  protected:
   ProducerQueueConfigBuilder config_builder_;
   std::unique_ptr<ProducerQueue> producer_queue_;
@@ -75,7 +94,7 @@
   for (size_t i = 0; i < nb_dequeue_times; i++) {
     size_t slot;
     LocalHandle fence;
-    auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+    auto p1_status = producer_queue_->Dequeue(100, &slot, &fence);
     ASSERT_TRUE(p1_status.ok());
     auto p1 = p1_status.take();
     ASSERT_NE(nullptr, p1);
@@ -113,31 +132,26 @@
     // Dequeue returns timeout since no buffer is ready to consumer, but
     // this implicitly triggers buffer import and bump up |capacity|.
     LocalHandle fence;
-    auto status = consumer_queue_->Dequeue(0, &slot, &seq, &fence);
+    auto status = consumer_queue_->Dequeue(100, &slot, &seq, &fence);
     ASSERT_FALSE(status.ok());
     ASSERT_EQ(ETIMEDOUT, status.error());
     ASSERT_EQ(consumer_queue_->capacity(), i + 1);
   }
 
-  // Use /dev/zero as a stand-in for a fence. As long as BufferHub does not need
-  // to merge fences, which only happens when multiple consumers release the
-  // same buffer with release fences, the file object should simply pass
-  // through.
-  LocalHandle post_fence("/dev/zero", O_RDONLY);
-  struct stat post_fence_stat;
-  ASSERT_EQ(0, fstat(post_fence.Get(), &post_fence_stat));
+  // Use eventfd as a stand-in for a fence.
+  LocalHandle post_fence(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
 
   for (size_t i = 0; i < kBufferCount; i++) {
     LocalHandle fence;
 
     // First time there is no buffer available to dequeue.
-    auto consumer_status = consumer_queue_->Dequeue(0, &slot, &seq, &fence);
+    auto consumer_status = consumer_queue_->Dequeue(100, &slot, &seq, &fence);
     ASSERT_FALSE(consumer_status.ok());
     ASSERT_EQ(ETIMEDOUT, consumer_status.error());
 
     // Make sure Producer buffer is POSTED so that it's ready to Accquire
     // in the consumer's Dequeue() function.
-    auto producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+    auto producer_status = producer_queue_->Dequeue(100, &slot, &fence);
     ASSERT_TRUE(producer_status.ok());
     auto producer = producer_status.take();
     ASSERT_NE(nullptr, producer);
@@ -147,20 +161,10 @@
 
     // Second time the just the POSTED buffer should be dequeued.
     uint64_t seq_out = 0;
-    consumer_status = consumer_queue_->Dequeue(0, &slot, &seq_out, &fence);
+    consumer_status = consumer_queue_->Dequeue(100, &slot, &seq_out, &fence);
     ASSERT_TRUE(consumer_status.ok());
     EXPECT_TRUE(fence.IsValid());
 
-    struct stat acquire_fence_stat;
-    ASSERT_EQ(0, fstat(fence.Get(), &acquire_fence_stat));
-
-    // The file descriptors should refer to the same file object. Testing the
-    // device id and inode is a proxy for testing that the fds refer to the same
-    // file object.
-    EXPECT_NE(post_fence.Get(), fence.Get());
-    EXPECT_EQ(post_fence_stat.st_dev, acquire_fence_stat.st_dev);
-    EXPECT_EQ(post_fence_stat.st_ino, acquire_fence_stat.st_ino);
-
     auto consumer = consumer_status.take();
     ASSERT_NE(nullptr, consumer);
     ASSERT_EQ(seq_in, seq_out);
@@ -196,12 +200,11 @@
 
   for (size_t i = 0; i < kBufferCount; i++) {
     Entry* entry = &buffers[i];
-    auto producer_status =
-        producer_queue_->Dequeue(0, &entry->slot, &entry->fence);
+    auto producer_status = producer_queue_->Dequeue(
+        /*timeout_ms=*/100, &entry->slot, &entry->fence);
     ASSERT_TRUE(producer_status.ok());
     entry->buffer = producer_status.take();
     ASSERT_NE(nullptr, entry->buffer);
-    EXPECT_EQ(i, entry->slot);
   }
 
   // Remove a buffer and make sure both queues reflect the change.
@@ -218,8 +221,8 @@
   buffers[0].buffer = nullptr;
 
   // Now the consumer queue should know it's gone.
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount - 1, consumer_queue_->capacity());
+  EXPECT_FALSE(WaitAndHandleOnce(consumer_queue_.get(), /*timeout_ms=*/100));
+  ASSERT_EQ(kBufferCount - 1, consumer_queue_->capacity());
 
   // Allocate a new buffer. This should take the first empty slot.
   size_t slot;
@@ -286,17 +289,20 @@
   auto silent_queue = producer_queue_->CreateSilentConsumerQueue();
   ASSERT_NE(nullptr, silent_queue);
 
-  // Check that buffers are correctly imported on construction.
-  EXPECT_EQ(kBufferCount, silent_queue->capacity());
+  // Check that silent queue doesn't import buffers on creation.
+  EXPECT_EQ(0, silent_queue->capacity());
 
   // Dequeue and post a buffer.
   size_t slot;
   LocalHandle fence;
-  auto producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+  auto producer_status =
+      producer_queue_->Dequeue(/*timeout_ms=*/100, &slot, &fence);
   ASSERT_TRUE(producer_status.ok());
   auto producer_buffer = producer_status.take();
   ASSERT_NE(nullptr, producer_buffer);
   ASSERT_EQ(0, producer_buffer->Post<void>({}));
+  // After post, check the number of remaining available buffers.
+  EXPECT_EQ(kBufferCount - 1, producer_queue_->count());
 
   // Currently we expect no buffer to be available prior to calling
   // WaitForBuffers/HandleQueueEvents.
@@ -314,23 +320,30 @@
   EXPECT_EQ(1u, consumer_queue_->count());
 
   // Reclaim released/ignored buffers.
-  producer_queue_->HandleQueueEvents();
+  ASSERT_EQ(kBufferCount - 1, producer_queue_->count());
+
+  usleep(10000);
+  WaitAndHandleOnce(producer_queue_.get(), /*timeout_ms=*/100);
   ASSERT_EQ(kBufferCount - 1, producer_queue_->count());
 
   // Post another buffer.
-  producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+  producer_status = producer_queue_->Dequeue(/*timeout_ms=*/100, &slot, &fence);
   ASSERT_TRUE(producer_status.ok());
   producer_buffer = producer_status.take();
   ASSERT_NE(nullptr, producer_buffer);
   ASSERT_EQ(0, producer_buffer->Post<void>({}));
 
   // Verify that the consumer queue receives it.
-  EXPECT_EQ(1u, consumer_queue_->count());
-  EXPECT_TRUE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(2u, consumer_queue_->count());
+  size_t consumer_queue_count = consumer_queue_->count();
+  WaitAndHandleOnce(consumer_queue_.get(), /*timeout_ms=*/100);
+  EXPECT_LT(consumer_queue_count, consumer_queue_->count());
+
+  // Save the current consumer queue buffer count to compare after the dequeue.
+  consumer_queue_count = consumer_queue_->count();
 
   // Dequeue and acquire/release (discard) buffers on the consumer end.
-  auto consumer_status = consumer_queue_->Dequeue(0, &slot, &fence);
+  auto consumer_status =
+      consumer_queue_->Dequeue(/*timeout_ms=*/100, &slot, &fence);
   ASSERT_TRUE(consumer_status.ok());
   auto consumer_buffer = consumer_status.take();
   ASSERT_NE(nullptr, consumer_buffer);
@@ -338,7 +351,7 @@
 
   // Buffer should be returned to the producer queue without being handled by
   // the silent consumer queue.
-  EXPECT_EQ(1u, consumer_queue_->count());
+  EXPECT_GT(consumer_queue_count, consumer_queue_->count());
   EXPECT_EQ(kBufferCount - 2, producer_queue_->count());
   EXPECT_TRUE(producer_queue_->HandleQueueEvents());
   EXPECT_EQ(kBufferCount - 1, producer_queue_->count());
@@ -362,13 +375,13 @@
   for (auto mi : ms) {
     size_t slot;
     LocalHandle fence;
-    auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+    auto p1_status = producer_queue_->Dequeue(100, &slot, &fence);
     ASSERT_TRUE(p1_status.ok());
     auto p1 = p1_status.take();
     ASSERT_NE(nullptr, p1);
     ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0);
     TestMetadata mo;
-    auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+    auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence);
     ASSERT_TRUE(c1_status.ok());
     auto c1 = c1_status.take();
     ASSERT_EQ(mi.a, mo.a);
@@ -387,7 +400,7 @@
   int64_t mi = 3;
   size_t slot;
   LocalHandle fence;
-  auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+  auto p1_status = producer_queue_->Dequeue(100, &slot, &fence);
   ASSERT_TRUE(p1_status.ok());
   auto p1 = p1_status.take();
   ASSERT_NE(nullptr, p1);
@@ -395,7 +408,7 @@
 
   int32_t mo;
   // Acquire a buffer with mismatched metadata is not OK.
-  auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+  auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence);
   ASSERT_FALSE(c1_status.ok());
 }
 
@@ -406,14 +419,14 @@
 
   size_t slot;
   LocalHandle fence;
-  auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+  auto p1_status = producer_queue_->Dequeue(100, &slot, &fence);
   ASSERT_TRUE(p1_status.ok());
   auto p1 = p1_status.take();
   ASSERT_NE(nullptr, p1);
 
   int64_t mo;
-  producer_queue_->Enqueue(p1, slot);
-  auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+  producer_queue_->Enqueue(p1, slot, 0ULL);
+  auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence);
   ASSERT_FALSE(c1_status.ok());
 }
 
@@ -424,14 +437,14 @@
   size_t s1;
   AllocateBuffer();
   LocalHandle fence;
-  auto p1_status = producer_queue_->Dequeue(0, &s1, &fence);
+  auto p1_status = producer_queue_->Dequeue(100, &s1, &fence);
   ASSERT_TRUE(p1_status.ok());
   auto p1 = p1_status.take();
   ASSERT_NE(nullptr, p1);
 
   // producer queue is exhausted
   size_t s2;
-  auto p2_status = producer_queue_->Dequeue(0, &s2, &fence);
+  auto p2_status = producer_queue_->Dequeue(100, &s2, &fence);
   ASSERT_FALSE(p2_status.ok());
   ASSERT_EQ(ETIMEDOUT, p2_status.error());
 
@@ -441,7 +454,7 @@
   ASSERT_EQ(producer_queue_->capacity(), 2U);
 
   // now we can dequeue again
-  p2_status = producer_queue_->Dequeue(0, &s2, &fence);
+  p2_status = producer_queue_->Dequeue(100, &s2, &fence);
   ASSERT_TRUE(p2_status.ok());
   auto p2 = p2_status.take();
   ASSERT_NE(nullptr, p2);
@@ -456,7 +469,7 @@
   int64_t seq = 1;
   ASSERT_EQ(p1->Post(LocalHandle(), seq), 0);
   size_t cs1, cs2;
-  auto c1_status = consumer_queue_->Dequeue(0, &cs1, &seq, &fence);
+  auto c1_status = consumer_queue_->Dequeue(100, &cs1, &seq, &fence);
   ASSERT_TRUE(c1_status.ok());
   auto c1 = c1_status.take();
   ASSERT_NE(nullptr, c1);
@@ -465,7 +478,7 @@
   ASSERT_EQ(cs1, s1);
 
   ASSERT_EQ(p2->Post(LocalHandle(), seq), 0);
-  auto c2_status = consumer_queue_->Dequeue(0, &cs2, &seq, &fence);
+  auto c2_status = consumer_queue_->Dequeue(100, &cs2, &seq, &fence);
   ASSERT_TRUE(c2_status.ok());
   auto c2 = c2_status.take();
   ASSERT_NE(nullptr, c2);
@@ -485,7 +498,7 @@
 
   LocalHandle fence;
   size_t slot;
-  auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+  auto p1_status = producer_queue_->Dequeue(100, &slot, &fence);
   ASSERT_TRUE(p1_status.ok());
   auto p1 = p1_status.take();
   ASSERT_EQ(p1->usage() & set_mask, set_mask);
@@ -504,7 +517,7 @@
 
   LocalHandle fence;
   size_t slot;
-  auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+  auto p1_status = producer_queue_->Dequeue(100, &slot, &fence);
   ASSERT_TRUE(p1_status.ok());
   auto p1 = p1_status.take();
   ASSERT_EQ(0u, p1->usage() & clear_mask);
@@ -543,9 +556,9 @@
   ASSERT_TRUE(status.ok());
 
   // While allocation without those bits should fail.
-  status = producer_queue_->AllocateBuffer(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage & ~deny_clear_mask);
+  status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+                                           kBufferLayerCount, kBufferFormat,
+                                           kBufferUsage & ~deny_clear_mask);
   ASSERT_FALSE(status.ok());
   ASSERT_EQ(EINVAL, status.error());
 }
@@ -603,7 +616,7 @@
 
   // Free all buffers when one buffer is dequeued.
   CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+  producer_status = producer_queue_->Dequeue(100, &slot, &fence);
   ASSERT_TRUE(producer_status.ok());
   status = producer_queue_->FreeAllBuffers();
   EXPECT_TRUE(status.ok());
@@ -611,7 +624,7 @@
   // Free all buffers when all buffers are dequeued.
   CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
   for (size_t i = 0; i < kBufferCount; i++) {
-    producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+    producer_status = producer_queue_->Dequeue(100, &slot, &fence);
     ASSERT_TRUE(producer_status.ok());
   }
   status = producer_queue_->FreeAllBuffers();
@@ -619,7 +632,7 @@
 
   // Free all buffers when one buffer is posted.
   CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+  producer_status = producer_queue_->Dequeue(100, &slot, &fence);
   ASSERT_TRUE(producer_status.ok());
   producer_buffer = producer_status.take();
   ASSERT_NE(nullptr, producer_buffer);
@@ -630,7 +643,7 @@
   // Free all buffers when all buffers are posted.
   CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
   for (size_t i = 0; i < kBufferCount; i++) {
-    producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+    producer_status = producer_queue_->Dequeue(100, &slot, &fence);
     ASSERT_TRUE(producer_status.ok());
     producer_buffer = producer_status.take();
     ASSERT_NE(nullptr, producer_buffer);
@@ -642,12 +655,12 @@
   // Free all buffers when all buffers are acquired.
   CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
   for (size_t i = 0; i < kBufferCount; i++) {
-    producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+    producer_status = producer_queue_->Dequeue(100, &slot, &fence);
     ASSERT_TRUE(producer_status.ok());
     producer_buffer = producer_status.take();
     ASSERT_NE(nullptr, producer_buffer);
     ASSERT_EQ(0, producer_buffer->Post(fence, &seq, sizeof(seq)));
-    consumer_status = consumer_queue_->Dequeue(0, &slot, &seq, &fence);
+    consumer_status = consumer_queue_->Dequeue(100, &slot, &seq, &fence);
     ASSERT_TRUE(consumer_status.ok());
   }
 
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
index 4d9b215..1a99234 100644
--- a/libs/vr/libdvr/dvr_buffer.cpp
+++ b/libs/vr/libdvr/dvr_buffer.cpp
@@ -44,7 +44,13 @@
 }
 
 void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) {
-  delete write_buffer;
+  if (write_buffer != nullptr) {
+    ALOGW_IF(
+        write_buffer->slot != -1,
+        "dvrWriteBufferDestroy: Destroying a buffer associated with a valid "
+        "buffer queue slot. This may indicate possible leaks.");
+    delete write_buffer;
+  }
 }
 
 int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) {
@@ -107,7 +113,15 @@
     *read_buffer = new DvrReadBuffer;
 }
 
-void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { delete read_buffer; }
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) {
+  if (read_buffer != nullptr) {
+    ALOGW_IF(
+        read_buffer->slot != -1,
+        "dvrReadBufferDestroy: Destroying a buffer associated with a valid "
+        "buffer queue slot. This may indicate possible leaks.");
+    delete read_buffer;
+  }
+}
 
 int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) {
   return read_buffer && read_buffer->read_buffer;
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
index 4adb5d2..09a49dd 100644
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -27,15 +27,6 @@
       format_(producer_queue->default_format()) {}
 
 int DvrWriteBufferQueue::GetNativeWindow(ANativeWindow** out_window) {
-  if (producer_queue_->metadata_size() != sizeof(DvrNativeBufferMetadata)) {
-    ALOGE(
-        "DvrWriteBufferQueue::GetNativeWindow: The size of buffer metadata "
-        "(%zu) of the write queue does not match  of size of "
-        "DvrNativeBufferMetadata (%zu).",
-        producer_queue_->metadata_size(), sizeof(DvrNativeBufferMetadata));
-    return -EINVAL;
-  }
-
   if (native_window_ == nullptr) {
     // Lazy creation of |native_window|, as not everyone is using
     // DvrWriteBufferQueue as an external surface.
@@ -64,9 +55,26 @@
 
 int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer,
                                  int* out_fence_fd) {
+  DvrNativeBufferMetadata meta;
+  DvrWriteBuffer* buffer = nullptr;
+  int fence_fd = -1;
+  if (const int ret = GainBuffer(timeout, &buffer, &meta, &fence_fd))
+    return ret;
+  if (!buffer)
+    return -ENOMEM;
+
+  write_buffers_[buffer->slot].reset(buffer);
+  write_buffer->write_buffer = std::move(buffer->write_buffer);
+  *out_fence_fd = fence_fd;
+  return 0;
+}
+
+int DvrWriteBufferQueue::GainBuffer(int timeout,
+                                    DvrWriteBuffer** out_write_buffer,
+                                    DvrNativeBufferMetadata* out_meta,
+                                    int* out_fence_fd) {
   size_t slot;
-  pdx::LocalHandle fence;
-  std::shared_ptr<BufferProducer> buffer_producer;
+  pdx::LocalHandle release_fence;
 
   // Need to retry N+1 times, where N is total number of buffers in the queue.
   // As in the worst case, we will dequeue all N buffers and reallocate them, on
@@ -75,15 +83,29 @@
   size_t retry = 0;
 
   for (; retry < max_retries; retry++) {
-    auto buffer_status = producer_queue_->Dequeue(timeout, &slot, &fence);
+    auto buffer_status =
+        producer_queue_->Dequeue(timeout, &slot, out_meta, &release_fence);
     if (!buffer_status) {
       ALOGE_IF(buffer_status.error() != ETIMEDOUT,
-               "DvrWriteBufferQueue::Dequeue: Failed to dequeue buffer: %s",
+               "DvrWriteBufferQueue::GainBuffer: Failed to dequeue buffer: %s",
                buffer_status.GetErrorMessage().c_str());
       return -buffer_status.error();
     }
 
-    buffer_producer = buffer_status.take();
+    if (write_buffers_[slot] == nullptr) {
+      // Lazy initialization of a write_buffers_ slot. Note that a slot will
+      // only be dynamically allocated once during the entire cycle life of a
+      // queue.
+      write_buffers_[slot] = std::make_unique<DvrWriteBuffer>();
+      write_buffers_[slot]->slot = slot;
+    }
+
+    LOG_ALWAYS_FATAL_IF(
+        write_buffers_[slot]->write_buffer,
+        "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot);
+    write_buffers_[slot]->write_buffer = std::move(buffer_status.take());
+
+    const auto& buffer_producer = write_buffers_[slot]->write_buffer;
     if (!buffer_producer)
       return -ENOMEM;
 
@@ -122,6 +144,9 @@
             remove_status.GetErrorMessage().c_str());
       return -remove_status.error();
     }
+    // Make sure that the previously allocated buffer is dereferenced from
+    // write_buffers_ array.
+    write_buffers_[slot]->write_buffer = nullptr;
 
     auto allocate_status = producer_queue_->AllocateBuffer(
         width_, height_, old_layer_count, format_, old_usage);
@@ -139,8 +164,52 @@
     return -ENOMEM;
   }
 
-  write_buffer->write_buffer = std::move(buffer_producer);
-  *out_fence_fd = fence.Release();
+  *out_write_buffer = write_buffers_[slot].release();
+  *out_fence_fd = release_fence.Release();
+
+  return 0;
+}
+
+int DvrWriteBufferQueue::PostBuffer(DvrWriteBuffer* write_buffer,
+                                    const DvrNativeBufferMetadata* meta,
+                                    int ready_fence_fd) {
+  LOG_FATAL_IF(
+      (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()),
+      "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
+
+  // Some basic sanity checks before we put the buffer back into a slot.
+  size_t slot = static_cast<size_t>(write_buffer->slot);
+  if (write_buffers_[slot] != nullptr) {
+    ALOGE("DvrWriteBufferQueue::PostBuffer: Slot is not empty: %zu", slot);
+    return -EINVAL;
+  }
+  if (write_buffer->write_buffer == nullptr) {
+    ALOGE("DvrWriteBufferQueue::PostBuffer: Invalid write buffer.");
+    return -EINVAL;
+  }
+  if (write_buffer->write_buffer->id() != producer_queue_->GetBufferId(slot)) {
+    ALOGE(
+        "DvrWriteBufferQueue::PostBuffer: Buffer to be posted does not "
+        "belong to this buffer queue. Posting buffer: id=%d, buffer in "
+        "queue: id=%d",
+        write_buffer->write_buffer->id(), producer_queue_->GetBufferId(slot));
+    return -EINVAL;
+  }
+
+  write_buffer->write_buffer->SetQueueIndex(next_post_index_++);
+  pdx::LocalHandle fence(ready_fence_fd);
+  const int ret = write_buffer->write_buffer->PostAsync(meta, fence);
+  if (ret < 0) {
+    ALOGE("DvrWriteBufferQueue::PostBuffer: Failed to post buffer, ret=%d",
+          ret);
+    return ret;
+  }
+
+  // Put the DvrWriteBuffer pointer back into its slot for reuse.
+  write_buffers_[slot].reset(write_buffer);
+  // It's import to reset the write buffer client now. It should stay invalid
+  // until next GainBuffer on the same slot.
+  write_buffers_[slot]->write_buffer = nullptr;
   return 0;
 }
 
@@ -239,6 +308,27 @@
   return write_queue->Dequeue(timeout, write_buffer, out_fence_fd);
 }
 
+int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout,
+                                  DvrWriteBuffer** out_write_buffer,
+                                  DvrNativeBufferMetadata* out_meta,
+                                  int* out_fence_fd) {
+  if (!write_queue || !out_write_buffer || !out_meta || !out_fence_fd)
+    return -EINVAL;
+
+  return write_queue->GainBuffer(timeout, out_write_buffer, out_meta,
+                                 out_fence_fd);
+}
+
+int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue,
+                                  DvrWriteBuffer* write_buffer,
+                                  const DvrNativeBufferMetadata* meta,
+                                  int ready_fence_fd) {
+  if (!write_queue || !write_buffer || !write_buffer->write_buffer || !meta)
+    return -EINVAL;
+
+  return write_queue->PostBuffer(write_buffer, meta, ready_fence_fd);
+}
+
 int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue,
                                     uint32_t width, uint32_t height) {
   if (!write_queue)
@@ -291,6 +381,82 @@
 
   read_buffer->read_buffer = buffer_status.take();
   *out_fence_fd = acquire_fence.Release();
+
+  return 0;
+}
+
+int DvrReadBufferQueue::AcquireBuffer(int timeout,
+                                      DvrReadBuffer** out_read_buffer,
+                                      DvrNativeBufferMetadata* out_meta,
+                                      int* out_fence_fd) {
+  size_t slot;
+  pdx::LocalHandle acquire_fence;
+  auto buffer_status =
+      consumer_queue_->Dequeue(timeout, &slot, out_meta, &acquire_fence);
+  if (!buffer_status) {
+    ALOGE_IF(buffer_status.error() != ETIMEDOUT,
+             "DvrReadBufferQueue::AcquireBuffer: Failed to dequeue buffer: %s",
+             buffer_status.GetErrorMessage().c_str());
+    return -buffer_status.error();
+  }
+
+  if (read_buffers_[slot] == nullptr) {
+    // Lazy initialization of a read_buffers_ slot. Note that a slot will only
+    // be dynamically allocated once during the entire cycle life of a queue.
+    read_buffers_[slot] = std::make_unique<DvrReadBuffer>();
+    read_buffers_[slot]->slot = slot;
+  }
+
+  LOG_FATAL_IF(
+      read_buffers_[slot]->read_buffer,
+      "DvrReadBufferQueue::AcquireBuffer: Buffer slot is not empty: %zu", slot);
+  read_buffers_[slot]->read_buffer = std::move(buffer_status.take());
+
+  *out_read_buffer = read_buffers_[slot].release();
+  *out_fence_fd = acquire_fence.Release();
+
+  return 0;
+}
+
+int DvrReadBufferQueue::ReleaseBuffer(DvrReadBuffer* read_buffer,
+                                      const DvrNativeBufferMetadata* meta,
+                                      int release_fence_fd) {
+  LOG_FATAL_IF(
+      (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()),
+      "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
+
+  // Some basic sanity checks before we put the buffer back into a slot.
+  size_t slot = static_cast<size_t>(read_buffer->slot);
+  if (read_buffers_[slot] != nullptr) {
+    ALOGE("DvrReadBufferQueue::ReleaseBuffer: Slot is not empty: %zu", slot);
+    return -EINVAL;
+  }
+  if (read_buffer->read_buffer == nullptr) {
+    ALOGE("DvrReadBufferQueue::ReleaseBuffer: Invalid read buffer.");
+    return -EINVAL;
+  }
+  if (read_buffer->read_buffer->id() != consumer_queue_->GetBufferId(slot)) {
+    ALOGE(
+        "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released does not "
+        "belong to this buffer queue. Releasing buffer: id=%d, buffer in "
+        "queue: id=%d",
+        read_buffer->read_buffer->id(), consumer_queue_->GetBufferId(slot));
+    return -EINVAL;
+  }
+
+  pdx::LocalHandle fence(release_fence_fd);
+  int ret = read_buffer->read_buffer->ReleaseAsync(meta, fence);
+  if (ret < 0) {
+    ALOGE("DvrReadBufferQueue::ReleaseBuffer: Failed to release buffer, ret=%d",
+          ret);
+    return ret;
+  }
+
+  // Put the DvrReadBuffer pointer back into its slot for reuse.
+  read_buffers_[slot].reset(read_buffer);
+  // It's import to reset the read buffer client now. It should stay invalid
+  // until next AcquireBuffer on the same slot.
+  read_buffers_[slot]->read_buffer = nullptr;
   return 0;
 }
 
@@ -311,9 +477,11 @@
   } else {
     consumer_queue_->SetBufferRemovedCallback(
         [callback, context](const std::shared_ptr<BufferHubBuffer>& buffer) {
-          DvrReadBuffer read_buffer{
-              std::static_pointer_cast<BufferConsumer>(buffer)};
-          callback(&read_buffer, context);
+          // When buffer is removed from the queue, the slot is already invalid.
+          auto read_buffer = std::make_unique<DvrReadBuffer>();
+          read_buffer->read_buffer =
+              std::static_pointer_cast<BufferConsumer>(buffer);
+          callback(read_buffer.release(), context);
         });
   }
 }
@@ -370,6 +538,27 @@
                              meta_size_bytes);
 }
 
+int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout,
+                                    DvrReadBuffer** out_read_buffer,
+                                    DvrNativeBufferMetadata* out_meta,
+                                    int* out_fence_fd) {
+  if (!read_queue || !out_read_buffer || !out_meta || !out_fence_fd)
+    return -EINVAL;
+
+  return read_queue->AcquireBuffer(timeout, out_read_buffer, out_meta,
+                                   out_fence_fd);
+}
+
+int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue,
+                                    DvrReadBuffer* read_buffer,
+                                    const DvrNativeBufferMetadata* meta,
+                                    int release_fence_fd) {
+  if (!read_queue || !read_buffer || !read_buffer->read_buffer || !meta)
+    return -EINVAL;
+
+  return read_queue->ReleaseBuffer(read_buffer, meta, release_fence_fd);
+}
+
 int dvrReadBufferQueueSetBufferAvailableCallback(
     DvrReadBufferQueue* read_queue,
     DvrReadBufferQueueBufferAvailableCallback callback, void* context) {
diff --git a/libs/vr/libdvr/dvr_buffer_queue_internal.h b/libs/vr/libdvr/dvr_buffer_queue_internal.h
index 795d6cd..e53a686 100644
--- a/libs/vr/libdvr/dvr_buffer_queue_internal.h
+++ b/libs/vr/libdvr/dvr_buffer_queue_internal.h
@@ -5,10 +5,14 @@
 #include <private/dvr/buffer_hub_queue_client.h>
 #include <sys/cdefs.h>
 
+#include <array>
 #include <memory>
 
+#include "dvr_internal.h"
+
 struct ANativeWindow;
 
+typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata;
 typedef struct DvrReadBuffer DvrReadBuffer;
 typedef struct DvrReadBufferQueue DvrReadBufferQueue;
 typedef struct DvrWriteBuffer DvrWriteBuffer;
@@ -17,6 +21,7 @@
                                                         void* context);
 
 struct DvrWriteBufferQueue {
+  using BufferHubQueue = android::dvr::BufferHubQueue;
   using ProducerQueue = android::dvr::ProducerQueue;
 
   // Create a concrete object for DvrWriteBufferQueue.
@@ -38,18 +43,27 @@
   int GetNativeWindow(ANativeWindow** out_window);
   int CreateReadQueue(DvrReadBufferQueue** out_read_queue);
   int Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd);
+  int GainBuffer(int timeout, DvrWriteBuffer** out_write_buffer,
+                 DvrNativeBufferMetadata* out_meta, int* out_fence_fd);
+  int PostBuffer(DvrWriteBuffer* write_buffer,
+                 const DvrNativeBufferMetadata* meta, int ready_fence_fd);
   int ResizeBuffer(uint32_t width, uint32_t height);
 
  private:
   std::shared_ptr<ProducerQueue> producer_queue_;
+  std::array<std::unique_ptr<DvrWriteBuffer>, BufferHubQueue::kMaxQueueCapacity>
+      write_buffers_;
 
+  int64_t next_post_index_ = 0;
   uint32_t width_;
   uint32_t height_;
   uint32_t format_;
+
   android::sp<android::Surface> native_window_;
 };
 
 struct DvrReadBufferQueue {
+  using BufferHubQueue = android::dvr::BufferHubQueue;
   using ConsumerQueue = android::dvr::ConsumerQueue;
 
   explicit DvrReadBufferQueue(
@@ -61,7 +75,11 @@
 
   int CreateReadQueue(DvrReadBufferQueue** out_read_queue);
   int Dequeue(int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd,
-              void* out_meta, size_t meta_size_bytes);
+              void* out_meta, size_t user_metadata_size);
+  int AcquireBuffer(int timeout, DvrReadBuffer** out_read_buffer,
+                    DvrNativeBufferMetadata* out_meta, int* out_fence_fd);
+  int ReleaseBuffer(DvrReadBuffer* read_buffer,
+                    const DvrNativeBufferMetadata* meta, int release_fence_fd);
   void SetBufferAvailableCallback(
       DvrReadBufferQueueBufferAvailableCallback callback, void* context);
   void SetBufferRemovedCallback(
@@ -70,6 +88,8 @@
 
  private:
   std::shared_ptr<ConsumerQueue> consumer_queue_;
+  std::array<std::unique_ptr<DvrReadBuffer>, BufferHubQueue::kMaxQueueCapacity>
+      read_buffers_;
 };
 
 #endif  // ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_
diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h
index 28b6c28..de8bb96 100644
--- a/libs/vr/libdvr/dvr_internal.h
+++ b/libs/vr/libdvr/dvr_internal.h
@@ -34,10 +34,20 @@
 extern "C" {
 
 struct DvrWriteBuffer {
+  // The slot nubmer of the buffer, a valid slot number must be in the range of
+  // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for
+  // DvrWriteBuffer acquired from a DvrWriteBufferQueue.
+  int32_t slot = -1;
+
   std::shared_ptr<android::dvr::BufferProducer> write_buffer;
 };
 
 struct DvrReadBuffer {
+  // The slot nubmer of the buffer, a valid slot number must be in the range of
+  // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for
+  // DvrReadBuffer acquired from a DvrReadBufferQueue.
+  int32_t slot = -1;
+
   std::shared_ptr<android::dvr::BufferConsumer> read_buffer;
 };
 
diff --git a/libs/vr/libdvr/dvr_pose.cpp b/libs/vr/libdvr/dvr_pose.cpp
index 2ac3c0c..c379ef5 100644
--- a/libs/vr/libdvr/dvr_pose.cpp
+++ b/libs/vr/libdvr/dvr_pose.cpp
@@ -9,8 +9,7 @@
 
 using android::dvr::ConsumerQueue;
 
-int dvrPoseClientGetDataReader(DvrPoseClient* client,
-                               DvrPoseRawDataType data_type,
+int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type,
                                DvrReadBufferQueue** queue_out) {
   if (!client || !queue_out)
     return -EINVAL;
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index 8d4995a..499b7c1 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -15,6 +15,12 @@
 extern "C" {
 #endif
 
+#ifdef __GNUC__
+#define ALIGNED_DVR_STRUCT(x) __attribute__((packed, aligned(x)))
+#else
+#define ALIGNED_DVR_STRUCT(x)
+#endif
+
 typedef struct ANativeWindow ANativeWindow;
 
 typedef struct DvrPoseAsync DvrPoseAsync;
@@ -34,6 +40,7 @@
 
 typedef struct DvrReadBufferQueue DvrReadBufferQueue;
 typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata;
 
 typedef struct DvrSurface DvrSurface;
 typedef uint64_t DvrSurfaceAttributeType;
@@ -180,6 +187,13 @@
                                              int timeout,
                                              DvrWriteBuffer* out_buffer,
                                              int* out_fence_fd);
+typedef int (*DvrWriteBufferQueueGainBufferPtr)(
+    DvrWriteBufferQueue* write_queue, int timeout,
+    DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta,
+    int* out_fence_fd);
+typedef int (*DvrWriteBufferQueuePostBufferPtr)(
+    DvrWriteBufferQueue* write_queue, DvrWriteBuffer* write_buffer,
+    const DvrNativeBufferMetadata* meta, int ready_fence_fd);
 typedef int (*DvrWriteBufferQueueResizeBufferPtr)(
     DvrWriteBufferQueue* write_queue, uint32_t width, uint32_t height);
 typedef void (*DvrReadBufferQueueDestroyPtr)(DvrReadBufferQueue* read_queue);
@@ -194,6 +208,13 @@
                                             DvrReadBuffer* out_buffer,
                                             int* out_fence_fd, void* out_meta,
                                             size_t meta_size_bytes);
+typedef int (*DvrReadBufferQueueAcquireBufferPtr)(
+    DvrReadBufferQueue* read_queue, int timeout,
+    DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta,
+    int* out_fence_fd);
+typedef int (*DvrReadBufferQueueReleaseBufferPtr)(
+    DvrReadBufferQueue* read_queue, DvrReadBuffer* read_buffer,
+    const DvrNativeBufferMetadata* meta, int release_fence_fd);
 typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context);
 typedef int (*DvrReadBufferQueueSetBufferAvailableCallbackPtr)(
     DvrReadBufferQueue* read_queue,
@@ -250,11 +271,11 @@
 typedef int (*DvrPoseClientDataCapturePtr)(DvrPoseClient* client,
     const DvrPoseDataCaptureRequest* request);
 typedef int (*DvrPoseClientDataReaderDestroyPtr)(DvrPoseClient* client,
-                                                 DvrPoseRawDataType data_type);
+                                                 uint64_t data_type);
 
 // dvr_pose.h
 typedef int (*DvrPoseClientGetDataReaderPtr)(DvrPoseClient* client,
-                                             DvrPoseRawDataType data_type,
+                                             uint64_t data_type,
                                              DvrReadBufferQueue** read_queue);
 
 // services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h
@@ -352,7 +373,24 @@
 // existing data members. If new fields need to be added, please take extra care
 // to make sure that new data field is padded properly the size of the struct
 // stays same.
-struct DvrNativeBufferMetadata {
+struct ALIGNED_DVR_STRUCT(8) DvrNativeBufferMetadata {
+#ifdef __cplusplus
+  DvrNativeBufferMetadata()
+      : timestamp(0),
+        is_auto_timestamp(0),
+        dataspace(0),
+        crop_left(0),
+        crop_top(0),
+        crop_right(0),
+        crop_bottom(0),
+        scaling_mode(0),
+        transform(0),
+        index(0),
+        user_metadata_size(0),
+        user_metadata_ptr(0),
+        release_fence_mask(0),
+        reserved{0} {}
+#endif
   // Timestamp of the frame.
   int64_t timestamp;
 
@@ -376,10 +414,32 @@
   // android/native_window.h
   int32_t transform;
 
-  // Reserved bytes for so that the struct is forward compatible.
-  int32_t reserved[16];
+  // The index of the frame.
+  int64_t index;
+
+  // Size of additional metadata requested by user.
+  uint64_t user_metadata_size;
+
+  // Raw memory address of the additional user defined metadata. Only valid when
+  // user_metadata_size is non-zero.
+  uint64_t user_metadata_ptr;
+
+  // Only applicable for metadata retrieved from GainAsync. This indicates which
+  // consumer has pending fence that producer should epoll on.
+  uint64_t release_fence_mask;
+
+  // Reserved bytes for so that the struct is forward compatible and padding to
+  // 104 bytes so the size is a multiple of 8.
+  int32_t reserved[8];
 };
 
+#ifdef __cplusplus
+// Warning: DvrNativeBufferMetadata is part of the DVR API and changing its size
+// will cause compatiblity issues between different DVR API releases.
+static_assert(sizeof(DvrNativeBufferMetadata) == 104,
+              "Unexpected size for DvrNativeBufferMetadata");
+#endif
+
 struct DvrApi_v1 {
 // Defines an API entry for V1 (no version suffix).
 #define DVR_V1_API_ENTRY(name) Dvr##name##Ptr name
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
index 9036773..cce8c7e 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -167,6 +167,12 @@
 // Gets an ANativeWindow from DvrWriteBufferQueue.
 DVR_V1_API_ENTRY(WriteBufferQueueGetANativeWindow);
 
+// Dvr{Read,Write}BufferQueue API for asynchronous IPC.
+DVR_V1_API_ENTRY(WriteBufferQueueGainBuffer);
+DVR_V1_API_ENTRY(WriteBufferQueuePostBuffer);
+DVR_V1_API_ENTRY(ReadBufferQueueAcquireBuffer);
+DVR_V1_API_ENTRY(ReadBufferQueueReleaseBuffer);
+
 // Pose client
 DVR_V1_API_ENTRY(PoseClientGetDataReader);
 DVR_V1_API_ENTRY(PoseClientDataCapture);
diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
index 8b9e048..bf695c7 100644
--- a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
@@ -89,21 +89,44 @@
 int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
                                        DvrReadBufferQueue** out_read_queue);
 
-// Dequeue a buffer to write into.
+// @deprecated Please use dvrWriteBufferQueueGainBuffer instead.
+int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
+                               DvrWriteBuffer* out_buffer, int* out_fence_fd);
+
+// Gains a buffer to write into.
 //
-// @param write_queue The DvrWriteBufferQueue of interest.
+// @param write_queue The DvrWriteBufferQueue to gain buffer from.
 // @param timeout Specifies the number of milliseconds that the method will
 //     block. Specifying a timeout of -1 causes it to block indefinitely,
 //     while specifying a timeout equal to zero cause it to return immediately,
 //     even if no buffers are available.
 // @param out_buffer A targeting DvrWriteBuffer object to hold the output of the
-//     dequeue operation. Must be created by |dvrWriteBufferCreateEmpty|.
+//     dequeue operation.
+// @param out_meta A DvrNativeBufferMetadata object populated by the
+//     corresponding dvrReadBufferQueueReleaseBuffer API.
 // @param out_fence_fd A sync fence fd defined in NDK's sync.h API, which
 //     signals the release of underlying buffer. The producer should wait until
 //     this fence clears before writing data into it.
 // @return Zero on success, or negative error code.
-int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
-                               DvrWriteBuffer* out_buffer, int* out_fence_fd);
+int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout,
+                                  DvrWriteBuffer** out_write_buffer,
+                                  DvrNativeBufferMetadata* out_meta,
+                                  int* out_fence_fd);
+
+// Posts a buffer and signals its readiness to be read from.
+//
+// @param write_queue The DvrWriteBufferQueue to post buffer into.
+// @param write_buffer The buffer to be posted.
+// @param meta The buffer metadata describing the buffer.
+// @param ready_fence_fd  A sync fence fd defined in NDK's sync.h API, which
+//     signals the readdiness of underlying buffer. When a valid fence gets
+//     passed in, the consumer will wait the fence to be ready before it starts
+//     to ready from the buffer.
+// @return Zero on success, or negative error code.
+int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue,
+                                  DvrWriteBuffer* write_buffer,
+                                  const DvrNativeBufferMetadata* meta,
+                                  int ready_fence_fd);
 
 // Overrides buffer dimension with new width and height.
 //
@@ -153,28 +176,45 @@
 int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
                                       DvrReadBufferQueue** out_read_queue);
 
-// Dequeue a buffer to read from.
+// @deprecated Please use dvrReadBufferQueueAcquireBuffer instead.
+int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
+                              DvrReadBuffer* out_buffer, int* out_fence_fd,
+                              void* out_meta, size_t meta_size_bytes);
+
+// Dequeues a buffer to read from.
 //
-// @param read_queue The DvrReadBufferQueue of interest.
+// @param read_queue The DvrReadBufferQueue to acquire buffer from.
 // @param timeout Specifies the number of milliseconds that the method will
 //     block. Specifying a timeout of -1 causes it to block indefinitely,
 //     while specifying a timeout equal to zero cause it to return immediately,
 //     even if no buffers are available.
 // @param out_buffer A targeting DvrReadBuffer object to hold the output of the
 //     dequeue operation. Must be created by |dvrReadBufferCreateEmpty|.
+// @param out_meta A DvrNativeBufferMetadata object populated by the
+//     corresponding dvrWriteBufferQueuePostBuffer API.
 // @param out_fence_fd A sync fence fd defined in NDK's sync.h API, which
 //     signals the release of underlying buffer. The consumer should wait until
 //     this fence clears before reading data from it.
-// @param out_meta The memory area where a metadata object will be filled.
-//     Can be nullptr iff |meta_size_bytes| is zero (i.e., there is no
-//     metadata).
-// @param meta_size_bytes Size of the metadata object caller expects. If it
-//     doesn't match the size of actually metadata transported by the buffer
-//     queue, the method returns -EINVAL.
 // @return Zero on success, or negative error code.
-int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
-                              DvrReadBuffer* out_buffer, int* out_fence_fd,
-                              void* out_meta, size_t meta_size_bytes);
+int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout,
+                                    DvrReadBuffer** out_read_buffer,
+                                    DvrNativeBufferMetadata* out_meta,
+                                    int* out_fence_fd);
+
+// Releases a buffer and signals its readiness to be written into.
+//
+// @param read_queue The DvrReadBufferQueue to release buffer into.
+// @param read_buffer The buffer to be released.
+// @param meta The buffer metadata describing the buffer.
+// @param release_fence_fd A sync fence fd defined in NDK's sync.h API, which
+//     signals the readdiness of underlying buffer. When a valid fence gets
+//     passed in, the producer will wait the fence to be ready before it starts
+//     to write into the buffer again.
+// @return Zero on success, or negative error code.
+int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue,
+                                    DvrReadBuffer* read_buffer,
+                                    const DvrNativeBufferMetadata* meta,
+                                    int release_fence_fd);
 
 // Callback function which will be called when a buffer is avaiable.
 //
diff --git a/libs/vr/libdvr/include/dvr/dvr_pose.h b/libs/vr/libdvr/include/dvr/dvr_pose.h
index 85631f7..8752751 100644
--- a/libs/vr/libdvr/include/dvr/dvr_pose.h
+++ b/libs/vr/libdvr/include/dvr/dvr_pose.h
@@ -99,20 +99,20 @@
 } DvrPose;
 
 // Represents a data type that can be streamed from pose service.
-typedef enum DvrPoseRawDataType {
-  DVR_POSE_RAW_DATA_STEREO_IMAGE,
-  DVR_POSE_RAW_DATA_POINT_CLOUD,
-  DVR_POSE_RAW_DATA_FEATURES,
+enum {
+  DVR_POSE_RAW_DATA_STEREO_IMAGE = (1ULL << 0),
+  DVR_POSE_RAW_DATA_POINT_CLOUD = (1ULL << 1),
+  DVR_POSE_RAW_DATA_FEATURES = (1ULL << 2),
 
   // Always last.
-  DVR_POSE_RAW_DATA_COUNT,
-} DvrPoseRawDataType;
+  DVR_POSE_RAW_DATA_COUNT = (1ULL << 3),
+};
 
 // A request to retrieve data from the pose service. Expects that a buffer
 // queue has been initialized through dvrPoseClientGetDataReader().
 typedef struct DvrPoseDataCaptureRequest {
-  // The type of data to capture. Refer to enum DvrPoseRawDataType for types.
-  DvrPoseRawDataType data_type;
+  // The type of data to capture. Refer to enum DVR_POSE_RAW_DATA_* for types.
+  uint64_t data_type;
   // The sample interval. This can be used to skip samples. For example, a
   // value of 5 will capture every fifth frame and discard the 4 frames in
   // between. Set to 1 to capture all frames.
@@ -144,8 +144,7 @@
 // be found in the metadata struct DvrNativeBufferMetadata, where width is
 // |crop_right| and height is |crop_bottom|/2. Each image is contiguous in
 // memory with stride equal to width.
-int dvrPoseClientGetDataReader(DvrPoseClient* client,
-                               DvrPoseRawDataType data_type,
+int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type,
                                DvrReadBufferQueue** queue_out);
 
 // TODO(b/65067592): Move pose api's from pose_client.h to here.
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
index ab2ee75..887766a 100644
--- a/libs/vr/libdvr/tests/Android.bp
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -42,11 +42,13 @@
         "dvr_named_buffer-test.cpp",
     ],
 
+    header_libs: ["libdvr_headers"],
     static_libs: static_libraries,
     shared_libs: shared_libraries,
     cflags: [
         "-DLOG_TAG=\"dvr_api-test\"",
         "-DTRACE=0",
+        "-Wno-missing-field-initializers",
         "-O0",
         "-g",
     ],
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
index 0b30c38..62cd8d4 100644
--- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -27,8 +27,6 @@
 static constexpr uint64_t kBufferUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
 static constexpr size_t kQueueCapacity = 3;
 
-typedef uint64_t TestMeta;
-
 class DvrBufferQueueTest : public ::testing::Test {
  public:
   static void BufferAvailableCallback(void* context) {
@@ -65,20 +63,20 @@
   int buffer_removed_count_{0};
 };
 
-TEST_F(DvrBufferQueueTest, TestWrite_QueueCreateDestroy) {
+TEST_F(DvrBufferQueueTest, WriteQueueCreateDestroy) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      /*capacity=*/0, sizeof(TestMeta), &write_queue_);
+      /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
   ASSERT_EQ(0, ret);
 
   dvrWriteBufferQueueDestroy(write_queue_);
   write_queue_ = nullptr;
 }
 
-TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) {
+TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      kQueueCapacity, sizeof(TestMeta), &write_queue_);
+      kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
   ASSERT_EQ(0, ret);
 
   size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_);
@@ -87,10 +85,10 @@
   ASSERT_EQ(kQueueCapacity, capacity);
 }
 
-TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) {
+TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      /*capacity=*/0, sizeof(TestMeta), &write_queue_);
+      /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
   ASSERT_EQ(0, ret);
 
   DvrReadBufferQueue* read_queue = nullptr;
@@ -102,10 +100,10 @@
   dvrReadBufferQueueDestroy(read_queue);
 }
 
-TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) {
+TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      /*capacity=*/0, sizeof(TestMeta), &write_queue_);
+      /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
   ASSERT_EQ(0, ret);
 
   DvrReadBufferQueue* read_queue1 = nullptr;
@@ -124,102 +122,86 @@
   dvrReadBufferQueueDestroy(read_queue2);
 }
 
-TEST_F(DvrBufferQueueTest, CreateEmptyBuffer) {
+TEST_F(DvrBufferQueueTest, GainBuffer) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      kQueueCapacity, sizeof(TestMeta), &write_queue_);
-  ASSERT_EQ(0, ret);
+      kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
+  ASSERT_EQ(ret, 0);
 
-  DvrReadBuffer* read_buffer = nullptr;
-  DvrWriteBuffer* write_buffer = nullptr;
+  DvrWriteBuffer* wb = nullptr;
+  EXPECT_FALSE(dvrWriteBufferIsValid(wb));
 
-  EXPECT_FALSE(dvrReadBufferIsValid(read_buffer));
-  EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer));
-
-  dvrReadBufferCreateEmpty(&read_buffer);
-  ASSERT_NE(nullptr, read_buffer);
-
-  dvrWriteBufferCreateEmpty(&write_buffer);
-  ASSERT_NE(nullptr, write_buffer);
-
-  EXPECT_FALSE(dvrReadBufferIsValid(read_buffer));
-  EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer));
-
-  DvrReadBufferQueue* read_queue = nullptr;
-
-  ASSERT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
-
-  const int kTimeoutMs = 0;
+  DvrNativeBufferMetadata meta;
   int fence_fd = -1;
-  ASSERT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, kTimeoutMs,
-                                          write_buffer, &fence_fd));
-  EXPECT_EQ(-1, fence_fd);
-  EXPECT_TRUE(dvrWriteBufferIsValid(write_buffer));
-
-  ASSERT_EQ(0, dvrWriteBufferClear(write_buffer));
-  EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer));
+  ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta,
+                                      &fence_fd);
+  ASSERT_EQ(ret, 0);
+  EXPECT_EQ(fence_fd, -1);
+  EXPECT_NE(wb, nullptr);
+  EXPECT_TRUE(dvrWriteBufferIsValid(wb));
 }
 
-TEST_F(DvrBufferQueueTest, TestDequeuePostDequeueRelease) {
+TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      kQueueCapacity, sizeof(TestMeta), &write_queue_);
-  ASSERT_EQ(0, ret);
+      kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
+  ASSERT_EQ(ret, 0);
 
-  static constexpr int kTimeout = 0;
   DvrReadBufferQueue* read_queue = nullptr;
   DvrReadBuffer* rb = nullptr;
   DvrWriteBuffer* wb = nullptr;
+  DvrNativeBufferMetadata meta1;
+  DvrNativeBufferMetadata meta2;
   int fence_fd = -1;
 
   ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
 
-  ASSERT_EQ(0, ret);
-  ASSERT_NE(nullptr, read_queue);
+  ASSERT_EQ(ret, 0);
+  ASSERT_NE(read_queue, nullptr);
 
   dvrReadBufferQueueSetBufferAvailableCallback(read_queue,
                                                &BufferAvailableCallback, this);
 
-  dvrWriteBufferCreateEmpty(&wb);
-  ASSERT_NE(nullptr, wb);
-
-  dvrReadBufferCreateEmpty(&rb);
-  ASSERT_NE(nullptr, rb);
-
   // Gain buffer for writing.
-  ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb, &fence_fd);
-  ASSERT_EQ(0, ret);
+  ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta1,
+                                      &fence_fd);
+  ASSERT_EQ(ret, 0);
+  ASSERT_NE(wb, nullptr);
   ASSERT_TRUE(dvrWriteBufferIsValid(wb));
   ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d",
            wb, fence_fd);
   android::base::unique_fd release_fence(fence_fd);
 
   // Post buffer to the read_queue.
-  TestMeta seq = 42U;
-  ret = dvrWriteBufferPost(wb, /* fence */ -1, &seq, sizeof(seq));
-  ASSERT_EQ(0, ret);
-  dvrWriteBufferDestroy(wb);
+  meta1.timestamp = 42;
+  ret = dvrWriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1);
+  ASSERT_EQ(ret, 0);
+  ASSERT_FALSE(dvrWriteBufferIsValid(wb));
   wb = nullptr;
 
   // Acquire buffer for reading.
-  TestMeta acquired_seq = 0U;
-  ret = dvrReadBufferQueueDequeue(read_queue, kTimeout, rb, &fence_fd,
-                                  &acquired_seq, sizeof(acquired_seq));
-  ASSERT_EQ(0, ret);
+  ret = dvrReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb, &meta2,
+                                        &fence_fd);
+  ASSERT_EQ(ret, 0);
+  ASSERT_NE(rb, nullptr);
 
   // Dequeue is successfully, BufferAvailableCallback should be fired once.
-  ASSERT_EQ(1, buffer_available_count_);
+  ASSERT_EQ(buffer_available_count_, 1);
   ASSERT_TRUE(dvrReadBufferIsValid(rb));
-  ASSERT_EQ(seq, acquired_seq);
+
+  // Metadata should be passed along from producer to consumer properly.
+  ASSERT_EQ(meta1.timestamp, meta2.timestamp);
+
   ALOGD_IF(TRACE,
            "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb,
            fence_fd);
   android::base::unique_fd acquire_fence(fence_fd);
 
   // Release buffer to the write_queue.
-  ret = dvrReadBufferRelease(rb, -1);
-  ASSERT_EQ(0, ret);
-  dvrReadBufferDestroy(rb);
+  ret = dvrReadBufferQueueReleaseBuffer(read_queue, rb, &meta2,
+                                        /*release_fence_fd=*/-1);
+  ASSERT_EQ(ret, 0);
+  ASSERT_FALSE(dvrReadBufferIsValid(rb));
   rb = nullptr;
 
   // TODO(b/34387835) Currently buffer allocation has to happen after all queues
@@ -232,37 +214,18 @@
   dvrReadBufferQueueDestroy(read_queue);
 }
 
-TEST_F(DvrBufferQueueTest, TestGetANativeWindow) {
+TEST_F(DvrBufferQueueTest, GetANativeWindow) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      /*capacity=*/0, sizeof(TestMeta), &write_queue_);
-  ASSERT_EQ(0, ret);
-
-  ANativeWindow* window = nullptr;
-
-  // The |write_queue_| doesn't have proper metadata (must be
-  // DvrNativeBufferMetadata) configured during creation.
-  ret = dvrWriteBufferQueueGetANativeWindow(write_queue_, &window);
-  ASSERT_EQ(-EINVAL, ret);
-  ASSERT_EQ(nullptr, window);
-  dvrWriteBufferQueueDestroy(write_queue_);
-  write_queue_ = nullptr;
-
-  // A write queue with DvrNativeBufferMetadata should work fine.
-  ASSERT_EQ(nullptr, write_queue_);
-
-  ret = dvrWriteBufferQueueCreate(
-      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
       /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
   ASSERT_EQ(0, ret);
   ASSERT_NE(nullptr, write_queue_);
 
+  ANativeWindow* window = nullptr;
   ret = dvrWriteBufferQueueGetANativeWindow(write_queue_, &window);
   ASSERT_EQ(0, ret);
   ASSERT_NE(nullptr, window);
 
-  // TODO(b/64723700): Remove dependencies of Android platform bits so that we
-  // can run dvr_buffer_queue-test in DTS.
   uint32_t width = ANativeWindow_getWidth(window);
   uint32_t height = ANativeWindow_getHeight(window);
   uint32_t format = ANativeWindow_getFormat(window);
@@ -274,15 +237,15 @@
 // Create buffer queue of three buffers and dequeue three buffers out of it.
 // Before each dequeue operation, we resize the buffer queue and expect the
 // queue always return buffer with desired dimension.
-TEST_F(DvrBufferQueueTest, TestResizeBuffer) {
+TEST_F(DvrBufferQueueTest, ResizeBuffer) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      kQueueCapacity, sizeof(TestMeta), &write_queue_);
+      kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
   ASSERT_EQ(0, ret);
 
-  static constexpr int kTimeout = 0;
   int fence_fd = -1;
 
+  DvrNativeBufferMetadata meta;
   DvrReadBufferQueue* read_queue = nullptr;
   DvrWriteBuffer* wb1 = nullptr;
   DvrWriteBuffer* wb2 = nullptr;
@@ -300,13 +263,6 @@
   dvrReadBufferQueueSetBufferRemovedCallback(read_queue, &BufferRemovedCallback,
                                              this);
 
-  dvrWriteBufferCreateEmpty(&wb1);
-  ASSERT_NE(nullptr, wb1);
-  dvrWriteBufferCreateEmpty(&wb2);
-  ASSERT_NE(nullptr, wb2);
-  dvrWriteBufferCreateEmpty(&wb3);
-  ASSERT_NE(nullptr, wb3);
-
   // Handle all pending events on the read queue.
   ret = dvrReadBufferQueueHandleEvents(read_queue);
   ASSERT_EQ(0, ret);
@@ -321,7 +277,8 @@
   ASSERT_EQ(0, ret);
 
   // Gain first buffer for writing. All buffers will be resized.
-  ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb1, &fence_fd);
+  ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb1, &meta,
+                                      &fence_fd);
   ASSERT_EQ(0, ret);
   ASSERT_TRUE(dvrWriteBufferIsValid(wb1));
   ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1);
@@ -347,7 +304,8 @@
   ASSERT_EQ(0, ret);
 
   // The next buffer we dequeued should have new width.
-  ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb2, &fence_fd);
+  ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb2, &meta,
+                                      &fence_fd);
   ASSERT_EQ(0, ret);
   ASSERT_TRUE(dvrWriteBufferIsValid(wb2));
   ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2,
@@ -373,7 +331,8 @@
   ASSERT_EQ(0, ret);
 
   // The next buffer we dequeued should have new width.
-  ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb3, &fence_fd);
+  ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb3, &meta,
+                                      &fence_fd);
   ASSERT_EQ(0, ret);
   ASSERT_TRUE(dvrWriteBufferIsValid(wb3));
   ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3,
@@ -396,78 +355,10 @@
   dvrReadBufferQueueDestroy(read_queue);
 }
 
-TEST_F(DvrBufferQueueTest, DequeueEmptyMetadata) {
-  // Overrides default queue parameters: Empty metadata.
+TEST_F(DvrBufferQueueTest, ReadQueueEventFd) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      /*capacity=*/1, /*metadata_size=*/0, &write_queue_);
-  ASSERT_EQ(0, ret);
-
-  DvrReadBuffer* rb = nullptr;
-  DvrWriteBuffer* wb = nullptr;
-  dvrReadBufferCreateEmpty(&rb);
-  dvrWriteBufferCreateEmpty(&wb);
-
-  DvrReadBufferQueue* read_queue = nullptr;
-  EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
-
-  const int kTimeoutMs = 0;
-  int fence_fd = -1;
-  EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd));
-
-  EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, nullptr, 0));
-  EXPECT_EQ(0, dvrWriteBufferClear(wb));
-  dvrWriteBufferDestroy(wb);
-  wb = nullptr;
-
-  // When acquire buffer, it's legit to pass nullptr as out_meta iff metadata
-  // size is Zero.
-  EXPECT_EQ(0, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd,
-                                         nullptr, 0));
-  EXPECT_TRUE(dvrReadBufferIsValid(rb));
-}
-
-TEST_F(DvrBufferQueueTest, DequeueMismatchMetadata) {
-  int ret = dvrWriteBufferQueueCreate(
-      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      /*capacity=*/1, sizeof(TestMeta), &write_queue_);
-  ASSERT_EQ(0, ret);
-
-  DvrReadBuffer* rb = nullptr;
-  DvrWriteBuffer* wb = nullptr;
-  dvrReadBufferCreateEmpty(&rb);
-  dvrWriteBufferCreateEmpty(&wb);
-
-  DvrReadBufferQueue* read_queue = nullptr;
-  EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
-
-  const int kTimeoutMs = 0;
-  int fence_fd = -1;
-  EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd));
-
-  TestMeta seq = 42U;
-  EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, &seq, sizeof(seq)));
-  EXPECT_EQ(0, dvrWriteBufferClear(wb));
-  dvrWriteBufferDestroy(wb);
-  wb = nullptr;
-
-  // Dequeue with wrong metadata will cause EINVAL.
-  int8_t wrong_metadata;
-  EXPECT_EQ(-EINVAL,
-            dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd,
-                                      &wrong_metadata, sizeof(wrong_metadata)));
-  EXPECT_FALSE(dvrReadBufferIsValid(rb));
-
-  // Dequeue with empty metadata will cause EINVAL.
-  EXPECT_EQ(-EINVAL, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb,
-                                               &fence_fd, nullptr, 0));
-  EXPECT_FALSE(dvrReadBufferIsValid(rb));
-}
-
-TEST_F(DvrBufferQueueTest, TestReadQueueEventFd) {
-  int ret = dvrWriteBufferQueueCreate(
-      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      kQueueCapacity, sizeof(TestMeta), &write_queue_);
+      kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
   ASSERT_EQ(0, ret);
 
   DvrReadBufferQueue* read_queue = nullptr;
@@ -483,10 +374,10 @@
 // Verifies a Dvr{Read,Write}BufferQueue contains the same set of
 // Dvr{Read,Write}Buffer(s) during their lifecycles. And for the same buffer_id,
 // the corresponding AHardwareBuffer handle stays the same.
-TEST_F(DvrBufferQueueTest, TestStableBufferIdAndHardwareBuffer) {
+TEST_F(DvrBufferQueueTest, StableBufferIdAndHardwareBuffer) {
   int ret = dvrWriteBufferQueueCreate(
       kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
-      kQueueCapacity, sizeof(TestMeta), &write_queue_);
+      kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
   ASSERT_EQ(0, ret);
 
   int fence_fd = -1;
@@ -497,25 +388,21 @@
   std::array<DvrReadBuffer*, kQueueCapacity> rbs;
   // Write buffers.
   std::array<DvrWriteBuffer*, kQueueCapacity> wbs;
+  // Buffer metadata.
+  std::array<DvrNativeBufferMetadata, kQueueCapacity> metas;
   // Hardware buffers for Read buffers.
   std::unordered_map<int, AHardwareBuffer*> rhbs;
   // Hardware buffers for Write buffers.
   std::unordered_map<int, AHardwareBuffer*> whbs;
 
-  for (size_t i = 0; i < kQueueCapacity; i++) {
-    dvrReadBufferCreateEmpty(&rbs[i]);
-    dvrWriteBufferCreateEmpty(&wbs[i]);
-  }
-
   constexpr int kNumTests = 100;
-  constexpr int kTimeout = 0;
-  TestMeta seq = 0U;
 
   // This test runs the following operations many many times. Thus we prefer to
   // use ASSERT_XXX rather than EXPECT_XXX to avoid spamming the output.
   std::function<void(size_t i)> Gain = [&](size_t i) {
-    ASSERT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wbs[i],
-                                            &fence_fd));
+    int ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/10,
+                                            &wbs[i], &metas[i], &fence_fd);
+    ASSERT_EQ(ret, 0);
     ASSERT_LT(fence_fd, 0);  // expect invalid fence.
     ASSERT_TRUE(dvrWriteBufferIsValid(wbs[i]));
     int buffer_id = dvrWriteBufferGetId(wbs[i]);
@@ -540,15 +427,16 @@
   std::function<void(size_t i)> Post = [&](size_t i) {
     ASSERT_TRUE(dvrWriteBufferIsValid(wbs[i]));
 
-    seq++;
-    ASSERT_EQ(0, dvrWriteBufferPost(wbs[i], /*fence=*/-1, &seq, sizeof(seq)));
+    metas[i].timestamp++;
+    int ret = dvrWriteBufferQueuePostBuffer(write_queue_, wbs[i], &metas[i],
+                                            /*fence=*/-1);
+    ASSERT_EQ(ret, 0);
   };
 
   std::function<void(size_t i)> Acquire = [&](size_t i) {
-    TestMeta out_seq = 0U;
-    ASSERT_EQ(0,
-              dvrReadBufferQueueDequeue(read_queue, kTimeout, rbs[i], &fence_fd,
-                                        &out_seq, sizeof(out_seq)));
+    int ret = dvrReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10,
+                                              &rbs[i], &metas[i], &fence_fd);
+    ASSERT_EQ(ret, 0);
     ASSERT_LT(fence_fd, 0);  // expect invalid fence.
     ASSERT_TRUE(dvrReadBufferIsValid(rbs[i]));
 
@@ -574,8 +462,9 @@
   std::function<void(size_t i)> Release = [&](size_t i) {
     ASSERT_TRUE(dvrReadBufferIsValid(rbs[i]));
 
-    seq++;
-    ASSERT_EQ(0, dvrReadBufferRelease(rbs[i], /*fence=*/-1));
+    int ret = dvrReadBufferQueueReleaseBuffer(read_queue, rbs[i], &metas[i],
+                                              /*release_fence_fd=*/-1);
+    ASSERT_EQ(ret, 0);
   };
 
   // Scenario one:
@@ -630,12 +519,6 @@
       ASSERT_NO_FATAL_FAILURE(Release(kQueueCapacity - 1 - i));
     }
   }
-
-  // Clean up all read buffers and write buffers.
-  for (size_t i = 0; i < kQueueCapacity; i++) {
-    dvrReadBufferDestroy(rbs[i]);
-    dvrWriteBufferDestroy(wbs[i]);
-  }
 }
 
 }  // namespace
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index 8fce140..10c0b31 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -36,6 +36,7 @@
         "variant_tests.cpp",
     ],
     static_libs: [
+        "libcutils",
         "libgmock",
         "libpdx",
         "liblog",
diff --git a/libs/vr/libpdx/private/pdx/client_channel.h b/libs/vr/libpdx/private/pdx/client_channel.h
index dbfd626..10a49bb 100644
--- a/libs/vr/libpdx/private/pdx/client_channel.h
+++ b/libs/vr/libpdx/private/pdx/client_channel.h
@@ -1,6 +1,8 @@
 #ifndef ANDROID_PDX_CLIENT_CHANNEL_H_
 #define ANDROID_PDX_CLIENT_CHANNEL_H_
 
+#include <vector>
+
 #include <pdx/channel_handle.h>
 #include <pdx/file_handle.h>
 #include <pdx/status.h>
@@ -20,6 +22,15 @@
   virtual int event_fd() const = 0;
   virtual Status<int> GetEventMask(int events) = 0;
 
+  struct EventSource {
+    int event_fd;
+    int event_mask;
+  };
+
+  // Returns a set of event-generating fds with and event mask for each. These
+  // fds are owned by the ClientChannel and must never be closed by the caller.
+  virtual std::vector<EventSource> GetEventSources() const = 0;
+
   virtual LocalChannelHandle& GetChannelHandle() = 0;
   virtual void* AllocateTransactionState() = 0;
   virtual void FreeTransactionState(void* state) = 0;
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel.h b/libs/vr/libpdx/private/pdx/mock_client_channel.h
index 561c939..49e0682 100644
--- a/libs/vr/libpdx/private/pdx/mock_client_channel.h
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel.h
@@ -11,6 +11,7 @@
  public:
   MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
   MOCK_CONST_METHOD0(event_fd, int());
+  MOCK_CONST_METHOD0(GetEventSources, std::vector<EventSource>());
   MOCK_METHOD1(GetEventMask, Status<int>(int));
   MOCK_METHOD0(GetChannelHandle, LocalChannelHandle&());
   MOCK_METHOD0(AllocateTransactionState, void*());
diff --git a/libs/vr/libpdx/private/pdx/trace.h b/libs/vr/libpdx/private/pdx/trace.h
index ebe8491..c687fd6 100644
--- a/libs/vr/libpdx/private/pdx/trace.h
+++ b/libs/vr/libpdx/private/pdx/trace.h
@@ -1,35 +1,82 @@
 #ifndef ANDROID_PDX_TRACE_H_
 #define ANDROID_PDX_TRACE_H_
 
-// Tracing utilities for libpdx. Tracing in the service framework is enabled
-// under these conditions:
-//    1. ATRACE_TAG is defined, AND
-//    2. ATRACE_TAG does not equal ATRACE_TAG_NEVER, AND
-//    3. PDX_TRACE_ENABLED is defined, AND
-//    4. PDX_TRACE_ENABLED is equal to logical true.
-//
-// If any of these conditions are not met tracing is completely removed from the
-// library and headers.
+#include <array>
 
-// If ATRACE_TAG is not defined, default to never.
-#ifndef ATRACE_TAG
-#define ATRACE_TAG ATRACE_TAG_NEVER
-#endif
-
-// Include tracing functions after the trace tag is defined.
 #include <utils/Trace.h>
 
-// If PDX_TRACE_ENABLED is not defined, default to off.
-#ifndef PDX_TRACE_ENABLED
-#define PDX_TRACE_ENABLED 0
+// Enables internal tracing in libpdx. This is disabled by default to avoid
+// spamming the trace buffers during normal trace activities. libpdx must be
+// built with this set to true to enable internal tracing.
+#ifndef PDX_LIB_TRACE_ENABLED
+#define PDX_LIB_TRACE_ENABLED false
 #endif
 
-#if (ATRACE_TAG) != (ATRACE_TAG_NEVER) && (PDX_TRACE_ENABLED)
-#define PDX_TRACE_NAME ATRACE_NAME
-#else
-#define PDX_TRACE_NAME(name) \
-  do {                       \
-  } while (0)
-#endif
+namespace android {
+namespace pdx {
+
+// Utility to generate scoped tracers with arguments.
+class ScopedTraceArgs {
+ public:
+  template <typename... Args>
+  ScopedTraceArgs(uint64_t tag, const char* format, Args&&... args)
+      : tag_{tag} {
+    if (atrace_is_tag_enabled(tag_)) {
+      std::array<char, 1024> buffer;
+      snprintf(buffer.data(), buffer.size(), format,
+               std::forward<Args>(args)...);
+      atrace_begin(tag_, buffer.data());
+    }
+  }
+
+  ~ScopedTraceArgs() { atrace_end(tag_); }
+
+ private:
+  uint64_t tag_;
+
+  ScopedTraceArgs(const ScopedTraceArgs&) = delete;
+  void operator=(const ScopedTraceArgs&) = delete;
+};
+
+// Utility to generate scoped tracers.
+class ScopedTrace {
+ public:
+  template <typename... Args>
+  ScopedTrace(uint64_t tag, bool enabled, const char* name)
+      : tag_{tag}, enabled_{enabled} {
+    if (enabled_)
+      atrace_begin(tag_, name);
+  }
+
+  ~ScopedTrace() {
+    if (enabled_)
+      atrace_end(tag_);
+  }
+
+ private:
+  uint64_t tag_;
+  bool enabled_;
+
+  ScopedTrace(const ScopedTrace&) = delete;
+  void operator=(const ScopedTrace&) = delete;
+};
+
+}  // namespace pdx
+}  // namespace android
+
+// Macro to define a scoped tracer with arguments. Uses PASTE(x, y) macro
+// defined in utils/Trace.h.
+#define PDX_TRACE_FORMAT(format, ...)                         \
+  ::android::pdx::ScopedTraceArgs PASTE(__tracer, __LINE__) { \
+    ATRACE_TAG, format, ##__VA_ARGS__                         \
+  }
+
+// TODO(eieio): Rename this to PDX_LIB_TRACE_NAME() for internal use by libpdx
+// and rename internal uses inside the library. This version is only enabled
+// when PDX_LIB_TRACE_ENABLED is true.
+#define PDX_TRACE_NAME(name)                              \
+  ::android::pdx::ScopedTrace PASTE(__tracer, __LINE__) { \
+    ATRACE_TAG, PDX_LIB_TRACE_ENABLED, name               \
+  }
 
 #endif  // ANDROID_PDX_TRACE_H_
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index 0f6511b..f891c59 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -42,6 +42,7 @@
         "pdx_tool.cpp",
     ],
     shared_libs: [
+        "libcutils",
         "liblog",
     ],
     static_libs: [
diff --git a/libs/vr/libpdx_uds/channel_event_set.cpp b/libs/vr/libpdx_uds/channel_event_set.cpp
index ebe7cea..c68968e 100644
--- a/libs/vr/libpdx_uds/channel_event_set.cpp
+++ b/libs/vr/libpdx_uds/channel_event_set.cpp
@@ -1,6 +1,10 @@
 #include "private/uds/channel_event_set.h"
 
+#include <errno.h>
 #include <log/log.h>
+#include <poll.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
 
 #include <uds/ipc_helper.h>
 
@@ -8,41 +12,34 @@
 namespace pdx {
 namespace uds {
 
-ChannelEventSet::ChannelEventSet() {
-  const int flags = EFD_CLOEXEC | EFD_NONBLOCK;
-  LocalHandle epoll_fd, event_fd;
+namespace {
 
-  if (!SetupHandle(epoll_create1(EPOLL_CLOEXEC), &epoll_fd, "epoll") ||
-      !SetupHandle(eventfd(0, flags), &event_fd, "event")) {
-    return;
-  }
-
-  epoll_event event;
-  event.events = 0;
-  event.data.u32 = 0;
-  if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_fd.Get(), &event) < 0) {
-    const int error = errno;
-    ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s",
-          strerror(error));
-    return;
-  }
-
-  epoll_fd_ = std::move(epoll_fd);
-  event_fd_ = std::move(event_fd);
-}
-
-Status<void> ChannelEventSet::AddDataFd(const LocalHandle& data_fd) {
-  epoll_event event;
-  event.events = EPOLLHUP | EPOLLRDHUP;
-  event.data.u32 = event.events;
-  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, data_fd.Get(), &event) < 0) {
-    const int error = errno;
-    ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s",
+template <typename FileHandleType>
+Status<void> SetupHandle(int fd, FileHandleType* handle,
+                         const char* error_name) {
+  const int error = errno;
+  handle->Reset(fd);
+  if (!*handle) {
+    ALOGE("SetupHandle: Failed to setup %s handle: %s", error_name,
           strerror(error));
     return ErrorStatus{error};
-  } else {
-    return {};
   }
+  return {};
+}
+
+}  // anonymous namespace
+
+ChannelEventSet::ChannelEventSet() {
+  const int flags = EFD_CLOEXEC | EFD_NONBLOCK;
+  LocalHandle pollin_event_fd, pollhup_event_fd;
+
+  if (!SetupHandle(eventfd(0, flags), &pollin_event_fd, "pollin_event") ||
+      !SetupHandle(eventfd(0, flags), &pollhup_event_fd, "pollhup_event")) {
+    return;
+  }
+
+  pollin_event_fd_ = std::move(pollin_event_fd);
+  pollhup_event_fd_ = std::move(pollhup_event_fd);
 }
 
 int ChannelEventSet::ModifyEvents(int clear_mask, int set_mask) {
@@ -51,66 +48,101 @@
   const int old_bits = event_bits_;
   const int new_bits = (event_bits_ & ~clear_mask) | set_mask;
   event_bits_ = new_bits;
+  eventfd_t value;
 
-  // If anything changed clear the event and update the event mask.
-  if (old_bits != new_bits) {
-    eventfd_t value;
-    eventfd_read(event_fd_.Get(), &value);
+  // Calculate which bits changed and how. Bits that haven't changed since last
+  // modification will not change the state of an eventfd.
+  const int set_bits = new_bits & ~old_bits;
+  const int clear_bits = ~new_bits & old_bits;
 
-    epoll_event event;
-    event.events = POLLIN;
-    event.data.u32 = event_bits_;
-    if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, event_fd_.Get(), &event) <
-        0) {
-      const int error = errno;
-      ALOGE("ChannelEventSet::AddEventHandle: Failed to update event: %s",
-            strerror(error));
-      return -error;
-    }
-  }
+  if (set_bits & EPOLLIN)
+    eventfd_write(pollin_event_fd_.Get(), 1);
+  else if (clear_bits & EPOLLIN)
+    eventfd_read(pollin_event_fd_.Get(), &value);
 
-  // If there are any bits set, re-trigger the eventfd.
-  if (new_bits)
-    eventfd_write(event_fd_.Get(), 1);
+  if (set_bits & EPOLLHUP)
+    eventfd_write(pollhup_event_fd_.Get(), 1);
+  else if (clear_bits & EPOLLHUP)
+    eventfd_read(pollhup_event_fd_.Get(), &value);
 
   return 0;
 }
 
-Status<void> ChannelEventSet::SetupHandle(int fd, LocalHandle* handle,
-                                          const char* error_name) {
-  const int error = errno;
-  handle->Reset(fd);
-  if (!*handle) {
-    ALOGE("ChannelEventSet::SetupHandle: Failed to setup %s handle: %s",
-          error_name, strerror(error));
+ChannelEventReceiver::ChannelEventReceiver(LocalHandle data_fd,
+                                           LocalHandle pollin_event_fd,
+                                           LocalHandle pollhup_event_fd) {
+  LocalHandle epoll_fd;
+  if (!SetupHandle(epoll_create1(EPOLL_CLOEXEC), &epoll_fd, "epoll")) {
+    return;
+  }
+
+  epoll_event event;
+  event.events = EPOLLHUP | EPOLLRDHUP;
+  event.data.u32 = 0;
+  if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, data_fd.Get(), &event) < 0) {
+    const int error = errno;
+    ALOGE("ChannelEventSet::ChannelEventSet: Failed to add data_fd: %s",
+          strerror(error));
+    return;
+  }
+
+  event.events = EPOLLIN;
+  event.data.u32 = 0;
+  if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, pollin_event_fd.Get(), &event) <
+      0) {
+    const int error = errno;
+    ALOGE("ChannelEventSet::ChannelEventSet: Failed to add pollin_event_fd: %s",
+          strerror(error));
+    return;
+  }
+
+  event.events = EPOLLIN;
+  event.data.u32 = 0;
+  if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, pollhup_event_fd.Get(), &event) <
+      0) {
+    const int error = errno;
+    ALOGE(
+        "ChannelEventSet::ChannelEventSet: Failed to add pollhup_event_fd: %s",
+        strerror(error));
+    return;
+  }
+
+  pollin_event_fd_ = std::move(pollin_event_fd);
+  pollhup_event_fd_ = std::move(pollhup_event_fd);
+  data_fd_ = std::move(data_fd);
+  epoll_fd_ = std::move(epoll_fd);
+}
+
+Status<int> ChannelEventReceiver::PollPendingEvents(int timeout_ms) const {
+  std::array<pollfd, 3> pfds = {{{pollin_event_fd_.Get(), POLLIN, 0},
+                                 {pollhup_event_fd_.Get(), POLLIN, 0},
+                                 {data_fd_.Get(), POLLHUP | POLLRDHUP, 0}}};
+  if (RETRY_EINTR(poll(pfds.data(), pfds.size(), timeout_ms)) < 0) {
+    const int error = errno;
+    ALOGE(
+        "ChannelEventReceiver::PollPendingEvents: Failed to poll for events: "
+        "%s",
+        strerror(error));
     return ErrorStatus{error};
   }
-  return {};
+
+  const int event_mask =
+      ((pfds[0].revents & POLLIN) ? EPOLLIN : 0) |
+      ((pfds[1].revents & POLLIN) ? EPOLLHUP : 0) |
+      ((pfds[2].revents & (POLLHUP | POLLRDHUP)) ? EPOLLHUP : 0);
+  return {event_mask};
 }
 
 Status<int> ChannelEventReceiver::GetPendingEvents() const {
   constexpr long kTimeoutMs = 0;
-  epoll_event event;
-  const int count =
-      RETRY_EINTR(epoll_wait(epoll_fd_.Get(), &event, 1, kTimeoutMs));
+  return PollPendingEvents(kTimeoutMs);
+}
 
-  Status<int> status;
-  if (count < 0) {
-    status.SetError(errno);
-    ALOGE("ChannelEventReceiver::GetPendingEvents: Failed to get events: %s",
-          status.GetErrorMessage().c_str());
-    return status;
-  } else if (count == 0) {
-    status.SetError(ETIMEDOUT);
-    return status;
-  }
-
-  const int mask_out = event.data.u32;
-  ALOGD_IF(TRACE, "ChannelEventReceiver::GetPendingEvents: mask_out=%x",
-           mask_out);
-
-  status.SetValue(mask_out);
-  return status;
+std::vector<ClientChannel::EventSource> ChannelEventReceiver::GetEventSources()
+    const {
+  return {{data_fd_.Get(), EPOLLHUP | EPOLLRDHUP},
+          {pollin_event_fd_.Get(), EPOLLIN},
+          {pollhup_event_fd_.Get(), POLLIN}};
 }
 
 }  // namespace uds
diff --git a/libs/vr/libpdx_uds/channel_manager.cpp b/libs/vr/libpdx_uds/channel_manager.cpp
index afc0a4f..43ebe05 100644
--- a/libs/vr/libpdx_uds/channel_manager.cpp
+++ b/libs/vr/libpdx_uds/channel_manager.cpp
@@ -22,18 +22,26 @@
 }
 
 LocalChannelHandle ChannelManager::CreateHandle(LocalHandle data_fd,
-                                                LocalHandle event_fd) {
-  if (data_fd && event_fd) {
+                                                LocalHandle pollin_event_fd,
+                                                LocalHandle pollhup_event_fd) {
+  if (data_fd && pollin_event_fd && pollhup_event_fd) {
     std::lock_guard<std::mutex> autolock(mutex_);
-    int32_t handle = data_fd.Get();
-    channels_.emplace(handle,
-                      ChannelData{std::move(data_fd), std::move(event_fd)});
+    const int32_t handle = data_fd.Get();
+    channels_.emplace(
+        handle,
+        ChannelEventReceiver{std::move(data_fd), std::move(pollin_event_fd),
+                             std::move(pollhup_event_fd)});
     return LocalChannelHandle(this, handle);
+  } else {
+    ALOGE(
+        "ChannelManager::CreateHandle: Invalid arguments: data_fd=%d "
+        "pollin_event_fd=%d pollhup_event_fd=%d",
+        data_fd.Get(), pollin_event_fd.Get(), pollhup_event_fd.Get());
+    return LocalChannelHandle(nullptr, -1);
   }
-  return LocalChannelHandle(nullptr, -1);
 }
 
-ChannelManager::ChannelData* ChannelManager::GetChannelData(int32_t handle) {
+ChannelEventReceiver* ChannelManager::GetChannelData(int32_t handle) {
   std::lock_guard<std::mutex> autolock(mutex_);
   auto channel = channels_.find(handle);
   return channel != channels_.end() ? &channel->second : nullptr;
diff --git a/libs/vr/libpdx_uds/client_channel.cpp b/libs/vr/libpdx_uds/client_channel.cpp
index 9d91617..2e9c1de 100644
--- a/libs/vr/libpdx_uds/client_channel.cpp
+++ b/libs/vr/libpdx_uds/client_channel.cpp
@@ -33,7 +33,9 @@
     } else if (static_cast<size_t>(index) < response.channels.size()) {
       auto& channel_info = response.channels[index];
       *handle = ChannelManager::Get().CreateHandle(
-          std::move(channel_info.data_fd), std::move(channel_info.event_fd));
+          std::move(channel_info.data_fd),
+          std::move(channel_info.pollin_event_fd),
+          std::move(channel_info.pollhup_event_fd));
     } else {
       return false;
     }
@@ -53,9 +55,9 @@
 
     if (auto* channel_data =
             ChannelManager::Get().GetChannelData(handle.value())) {
-      ChannelInfo<BorrowedHandle> channel_info;
-      channel_info.data_fd.Reset(handle.value());
-      channel_info.event_fd = channel_data->event_receiver.event_fd();
+      ChannelInfo<BorrowedHandle> channel_info{
+          channel_data->data_fd(), channel_data->pollin_event_fd(),
+          channel_data->pollhup_event_fd()};
       request.channels.push_back(std::move(channel_info));
       return request.channels.size() - 1;
     } else {
@@ -90,10 +92,12 @@
   size_t send_len = CountVectorSize(send_vector, send_count);
   InitRequest(&transaction_state->request, opcode, send_len, max_recv_len,
               false);
-  auto status = SendData(socket_fd, transaction_state->request);
-  if (status && send_len > 0)
-    status = SendDataVector(socket_fd, send_vector, send_count);
-  return status;
+  if (send_len == 0) {
+    send_vector = nullptr;
+    send_count = 0;
+  }
+  return SendData(socket_fd, transaction_state->request, send_vector,
+                  send_count);
 }
 
 Status<void> ReceiveResponse(const BorrowedHandle& socket_fd,
diff --git a/libs/vr/libpdx_uds/client_channel_factory.cpp b/libs/vr/libpdx_uds/client_channel_factory.cpp
index 433f459..09dc7be 100644
--- a/libs/vr/libpdx_uds/client_channel_factory.cpp
+++ b/libs/vr/libpdx_uds/client_channel_factory.cpp
@@ -139,20 +139,33 @@
 
   RequestHeader<BorrowedHandle> request;
   InitRequest(&request, opcodes::CHANNEL_OPEN, 0, 0, false);
+
   status = SendData(socket_.Borrow(), request);
   if (!status)
     return status.error_status();
+
   ResponseHeader<LocalHandle> response;
   status = ReceiveData(socket_.Borrow(), &response);
   if (!status)
     return status.error_status();
-  int ref = response.ret_code;
-  if (ref < 0 || static_cast<size_t>(ref) > response.file_descriptors.size())
+  else if (response.ret_code < 0 || response.channels.size() != 1)
     return ErrorStatus(EIO);
 
-  LocalHandle event_fd = std::move(response.file_descriptors[ref]);
+  LocalHandle pollin_event_fd = std::move(response.channels[0].pollin_event_fd);
+  LocalHandle pollhup_event_fd =
+      std::move(response.channels[0].pollhup_event_fd);
+
+  if (!pollin_event_fd || !pollhup_event_fd) {
+    ALOGE(
+        "ClientChannelFactory::Connect: Required fd was not returned from the "
+        "service: pollin_event_fd=%d pollhup_event_fd=%d",
+        pollin_event_fd.Get(), pollhup_event_fd.Get());
+    return ErrorStatus(EIO);
+  }
+
   return ClientChannel::Create(ChannelManager::Get().CreateHandle(
-      std::move(socket_), std::move(event_fd)));
+      std::move(socket_), std::move(pollin_event_fd),
+      std::move(pollhup_event_fd)));
 }
 
 }  // namespace uds
diff --git a/libs/vr/libpdx_uds/ipc_helper.cpp b/libs/vr/libpdx_uds/ipc_helper.cpp
index d75ce86..f85b3bb 100644
--- a/libs/vr/libpdx_uds/ipc_helper.cpp
+++ b/libs/vr/libpdx_uds/ipc_helper.cpp
@@ -20,6 +20,9 @@
 
 namespace {
 
+constexpr size_t kMaxFdCount =
+    256;  // Total of 1KiB of data to transfer these FDs.
+
 // Default implementations of Send/Receive interfaces to use standard socket
 // send/sendmsg/recv/recvmsg functions.
 class SocketSender : public SendInterface {
@@ -175,20 +178,31 @@
 }
 
 Status<void> SendPayload::Send(const BorrowedHandle& socket_fd,
-                               const ucred* cred) {
+                               const ucred* cred, const iovec* data_vec,
+                               size_t vec_count) {
+  if (file_handles_.size() > kMaxFdCount) {
+    ALOGE(
+        "SendPayload::Send: Trying to send too many file descriptors (%zu), "
+        "max allowed = %zu",
+        file_handles_.size(), kMaxFdCount);
+    return ErrorStatus{EINVAL};
+  }
+
   SendInterface* sender = sender_ ? sender_ : &g_socket_sender;
   MessagePreamble preamble;
   preamble.magic = kMagicPreamble;
   preamble.data_size = buffer_.size();
   preamble.fd_count = file_handles_.size();
-  Status<void> ret = SendAll(sender, socket_fd, &preamble, sizeof(preamble));
-  if (!ret)
-    return ret;
 
   msghdr msg = {};
-  iovec recv_vect = {buffer_.data(), buffer_.size()};
-  msg.msg_iov = &recv_vect;
-  msg.msg_iovlen = 1;
+  msg.msg_iovlen = 2 + vec_count;
+  msg.msg_iov = static_cast<iovec*>(alloca(sizeof(iovec) * msg.msg_iovlen));
+  msg.msg_iov[0].iov_base = &preamble;
+  msg.msg_iov[0].iov_len = sizeof(preamble);
+  msg.msg_iov[1].iov_base = buffer_.data();
+  msg.msg_iov[1].iov_len = buffer_.size();
+  for (size_t i = 0; i < vec_count; i++)
+    msg.msg_iov[i + 2] = data_vec[i];
 
   if (cred || !file_handles_.empty()) {
     const size_t fd_bytes = file_handles_.size() * sizeof(int);
@@ -270,7 +284,15 @@
                                      ucred* cred) {
   RecvInterface* receiver = receiver_ ? receiver_ : &g_socket_receiver;
   MessagePreamble preamble;
-  Status<void> ret = RecvAll(receiver, socket_fd, &preamble, sizeof(preamble));
+  msghdr msg = {};
+  iovec recv_vect = {&preamble, sizeof(preamble)};
+  msg.msg_iov = &recv_vect;
+  msg.msg_iovlen = 1;
+  const size_t receive_fd_bytes = kMaxFdCount * sizeof(int);
+  msg.msg_controllen = CMSG_SPACE(sizeof(ucred)) + CMSG_SPACE(receive_fd_bytes);
+  msg.msg_control = alloca(msg.msg_controllen);
+
+  Status<void> ret = RecvMsgAll(receiver, socket_fd, &msg);
   if (!ret)
     return ret;
 
@@ -284,23 +306,6 @@
   file_handles_.clear();
   read_pos_ = 0;
 
-  msghdr msg = {};
-  iovec recv_vect = {buffer_.data(), buffer_.size()};
-  msg.msg_iov = &recv_vect;
-  msg.msg_iovlen = 1;
-
-  if (cred || preamble.fd_count) {
-    const size_t receive_fd_bytes = preamble.fd_count * sizeof(int);
-    msg.msg_controllen =
-        (cred ? CMSG_SPACE(sizeof(ucred)) : 0) +
-        (receive_fd_bytes == 0 ? 0 : CMSG_SPACE(receive_fd_bytes));
-    msg.msg_control = alloca(msg.msg_controllen);
-  }
-
-  ret = RecvMsgAll(receiver, socket_fd, &msg);
-  if (!ret)
-    return ret;
-
   bool cred_available = false;
   file_handles_.reserve(preamble.fd_count);
   cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
@@ -320,6 +325,10 @@
     cmsg = CMSG_NXTHDR(&msg, cmsg);
   }
 
+  ret = RecvAll(receiver, socket_fd, buffer_.data(), buffer_.size());
+  if (!ret)
+    return ret;
+
   if (cred && !cred_available) {
     ALOGE("ReceivePayload::Receive: Failed to obtain message credentials");
     ret.SetError(EIO);
diff --git a/libs/vr/libpdx_uds/private/uds/channel_event_set.h b/libs/vr/libpdx_uds/private/uds/channel_event_set.h
index 1f464d5..99e7502 100644
--- a/libs/vr/libpdx_uds/private/uds/channel_event_set.h
+++ b/libs/vr/libpdx_uds/private/uds/channel_event_set.h
@@ -1,11 +1,9 @@
 #ifndef ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
 #define ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
 
-#include <errno.h>
-#include <poll.h>
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
+#include <vector>
 
+#include <pdx/client_channel.h>
 #include <pdx/file_handle.h>
 #include <pdx/status.h>
 
@@ -19,21 +17,20 @@
   ChannelEventSet(ChannelEventSet&&) = default;
   ChannelEventSet& operator=(ChannelEventSet&&) = default;
 
-  BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); }
+  BorrowedHandle pollin_event_fd() const { return pollin_event_fd_.Borrow(); }
+  BorrowedHandle pollhup_event_fd() const { return pollhup_event_fd_.Borrow(); }
 
-  explicit operator bool() const { return !!epoll_fd_ && !!event_fd_; }
+  explicit operator bool() const {
+    return !!pollin_event_fd_ && !!pollhup_event_fd_;
+  }
 
-  Status<void> AddDataFd(const LocalHandle& data_fd);
   int ModifyEvents(int clear_mask, int set_mask);
 
  private:
-  LocalHandle epoll_fd_;
-  LocalHandle event_fd_;
+  LocalHandle pollin_event_fd_;
+  LocalHandle pollhup_event_fd_;
   uint32_t event_bits_ = 0;
 
-  static Status<void> SetupHandle(int fd, LocalHandle* handle,
-                                  const char* error_name);
-
   ChannelEventSet(const ChannelEventSet&) = delete;
   void operator=(const ChannelEventSet&) = delete;
 };
@@ -41,14 +38,31 @@
 class ChannelEventReceiver {
  public:
   ChannelEventReceiver() = default;
-  ChannelEventReceiver(LocalHandle epoll_fd) : epoll_fd_{std::move(epoll_fd)} {}
+  ChannelEventReceiver(LocalHandle data_fd, LocalHandle pollin_event_fd,
+                       LocalHandle pollhup_event_fd);
   ChannelEventReceiver(ChannelEventReceiver&&) = default;
   ChannelEventReceiver& operator=(ChannelEventReceiver&&) = default;
 
+  explicit operator bool() const {
+    return !!pollin_event_fd_ && !!pollhup_event_fd_ && !!data_fd_ &&
+           !!epoll_fd_;
+  }
+
   BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); }
+
+  BorrowedHandle pollin_event_fd() const { return pollin_event_fd_.Borrow(); }
+  BorrowedHandle pollhup_event_fd() const { return pollhup_event_fd_.Borrow(); }
+  BorrowedHandle data_fd() const { return data_fd_.Borrow(); }
+
   Status<int> GetPendingEvents() const;
+  Status<int> PollPendingEvents(int timeout_ms) const;
+
+  std::vector<ClientChannel::EventSource> GetEventSources() const;
 
  private:
+  LocalHandle data_fd_;
+  LocalHandle pollin_event_fd_;
+  LocalHandle pollhup_event_fd_;
   LocalHandle epoll_fd_;
 
   ChannelEventReceiver(const ChannelEventReceiver&) = delete;
diff --git a/libs/vr/libpdx_uds/private/uds/channel_manager.h b/libs/vr/libpdx_uds/private/uds/channel_manager.h
index 2aca414..5f6a514 100644
--- a/libs/vr/libpdx_uds/private/uds/channel_manager.h
+++ b/libs/vr/libpdx_uds/private/uds/channel_manager.h
@@ -16,13 +16,11 @@
  public:
   static ChannelManager& Get();
 
-  LocalChannelHandle CreateHandle(LocalHandle data_fd, LocalHandle event_fd);
-  struct ChannelData {
-    LocalHandle data_fd;
-    ChannelEventReceiver event_receiver;
-  };
+  LocalChannelHandle CreateHandle(LocalHandle data_fd,
+                                  LocalHandle pollin_event_fd,
+                                  LocalHandle pollhup_event_fd);
 
-  ChannelData* GetChannelData(int32_t handle);
+  ChannelEventReceiver* GetChannelData(int32_t handle);
 
  private:
   ChannelManager() = default;
@@ -30,7 +28,7 @@
   void CloseHandle(int32_t handle) override;
 
   std::mutex mutex_;
-  std::unordered_map<int32_t, ChannelData> channels_;
+  std::unordered_map<int32_t, ChannelEventReceiver> channels_;
 };
 
 }  // namespace uds
diff --git a/libs/vr/libpdx_uds/private/uds/client_channel.h b/libs/vr/libpdx_uds/private/uds/client_channel.h
index 8f607f5..7a5ddf4 100644
--- a/libs/vr/libpdx_uds/private/uds/client_channel.h
+++ b/libs/vr/libpdx_uds/private/uds/client_channel.h
@@ -23,11 +23,19 @@
   uint32_t GetIpcTag() const override { return Endpoint::kIpcTag; }
 
   int event_fd() const override {
-    return channel_data_ ? channel_data_->event_receiver.event_fd().Get() : -1;
+    return channel_data_ ? channel_data_->event_fd().Get() : -1;
   }
+
+  std::vector<EventSource> GetEventSources() const override {
+    if (channel_data_)
+      return channel_data_->GetEventSources();
+    else
+      return {};
+  }
+
   Status<int> GetEventMask(int /*events*/) override {
     if (channel_data_)
-      return channel_data_->event_receiver.GetPendingEvents();
+      return channel_data_->GetPendingEvents();
     else
       return ErrorStatus(EINVAL);
   }
@@ -74,7 +82,7 @@
                              const iovec* receive_vector, size_t receive_count);
 
   LocalChannelHandle channel_handle_;
-  ChannelManager::ChannelData* channel_data_;
+  ChannelEventReceiver* channel_data_;
   std::mutex socket_mutex_;
 };
 
diff --git a/libs/vr/libpdx_uds/private/uds/ipc_helper.h b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
index bde16d3..63b5b10 100644
--- a/libs/vr/libpdx_uds/private/uds/ipc_helper.h
+++ b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
@@ -59,7 +59,8 @@
  public:
   SendPayload(SendInterface* sender = nullptr) : sender_{sender} {}
   Status<void> Send(const BorrowedHandle& socket_fd);
-  Status<void> Send(const BorrowedHandle& socket_fd, const ucred* cred);
+  Status<void> Send(const BorrowedHandle& socket_fd, const ucred* cred,
+                    const iovec* data_vec = nullptr, size_t vec_count = 0);
 
   // MessageWriter
   void* GetNextWriteBufferSection(size_t size) override;
@@ -109,10 +110,12 @@
 class ChannelInfo {
  public:
   FileHandleType data_fd;
-  FileHandleType event_fd;
+  FileHandleType pollin_event_fd;
+  FileHandleType pollhup_event_fd;
 
  private:
-  PDX_SERIALIZABLE_MEMBERS(ChannelInfo, data_fd, event_fd);
+  PDX_SERIALIZABLE_MEMBERS(ChannelInfo, data_fd, pollin_event_fd,
+                           pollhup_event_fd);
 };
 
 template <typename FileHandleType>
@@ -156,18 +159,22 @@
 };
 
 template <typename T>
-inline Status<void> SendData(const BorrowedHandle& socket_fd, const T& data) {
+inline Status<void> SendData(const BorrowedHandle& socket_fd, const T& data,
+                             const iovec* data_vec = nullptr,
+                             size_t vec_count = 0) {
   SendPayload payload;
   rpc::Serialize(data, &payload);
-  return payload.Send(socket_fd);
+  return payload.Send(socket_fd, nullptr, data_vec, vec_count);
 }
 
 template <typename FileHandleType>
 inline Status<void> SendData(const BorrowedHandle& socket_fd,
-                             const RequestHeader<FileHandleType>& request) {
+                             const RequestHeader<FileHandleType>& request,
+                             const iovec* data_vec = nullptr,
+                             size_t vec_count = 0) {
   SendPayload payload;
   rpc::Serialize(request, &payload);
-  return payload.Send(socket_fd, &request.cred);
+  return payload.Send(socket_fd, &request.cred, data_vec, vec_count);
 }
 
 Status<void> SendData(const BorrowedHandle& socket_fd, const void* data,
diff --git a/libs/vr/libpdx_uds/private/uds/service_endpoint.h b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
index a163812..01ebf65 100644
--- a/libs/vr/libpdx_uds/private/uds/service_endpoint.h
+++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
@@ -7,6 +7,7 @@
 #include <mutex>
 #include <string>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include <pdx/service.h>
@@ -139,7 +140,8 @@
   Status<void> ReenableEpollEvent(const BorrowedHandle& channel_fd);
   Channel* GetChannelState(int32_t channel_id);
   BorrowedHandle GetChannelSocketFd(int32_t channel_id);
-  BorrowedHandle GetChannelEventFd(int32_t channel_id);
+  Status<std::pair<BorrowedHandle, BorrowedHandle>> GetChannelEventFd(
+      int32_t channel_id);
   int32_t GetChannelId(const BorrowedHandle& channel_fd);
   Status<void> CreateChannelSocketPair(LocalHandle* local_socket,
                                        LocalHandle* remote_socket);
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
index 27a56f9..0ee77f4 100644
--- a/libs/vr/libpdx_uds/service_endpoint.cpp
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -49,7 +49,9 @@
     } else if (static_cast<size_t>(index) < request.channels.size()) {
       auto& channel_info = request.channels[index];
       *handle = ChannelManager::Get().CreateHandle(
-          std::move(channel_info.data_fd), std::move(channel_info.event_fd));
+          std::move(channel_info.data_fd),
+          std::move(channel_info.pollin_event_fd),
+          std::move(channel_info.pollhup_event_fd));
     } else {
       return false;
     }
@@ -69,9 +71,9 @@
 
     if (auto* channel_data =
             ChannelManager::Get().GetChannelData(handle.value())) {
-      ChannelInfo<BorrowedHandle> channel_info;
-      channel_info.data_fd.Reset(handle.value());
-      channel_info.event_fd = channel_data->event_receiver.event_fd();
+      ChannelInfo<BorrowedHandle> channel_info{
+          channel_data->data_fd(), channel_data->pollin_event_fd(),
+          channel_data->pollhup_event_fd()};
       response.channels.push_back(std::move(channel_info));
       return response.channels.size() - 1;
     } else {
@@ -80,12 +82,13 @@
   }
 
   Status<ChannelReference> PushChannelHandle(BorrowedHandle data_fd,
-                                             BorrowedHandle event_fd) {
-    if (!data_fd || !event_fd)
+                                             BorrowedHandle pollin_event_fd,
+                                             BorrowedHandle pollhup_event_fd) {
+    if (!data_fd || !pollin_event_fd || !pollhup_event_fd)
       return ErrorStatus{EINVAL};
-    ChannelInfo<BorrowedHandle> channel_info;
-    channel_info.data_fd = std::move(data_fd);
-    channel_info.event_fd = std::move(event_fd);
+    ChannelInfo<BorrowedHandle> channel_info{std::move(data_fd),
+                                             std::move(pollin_event_fd),
+                                             std::move(pollhup_event_fd)};
     response.channels.push_back(std::move(channel_info));
     return response.channels.size() - 1;
   }
@@ -287,7 +290,6 @@
     return ErrorStatus(errno);
   }
   ChannelData channel_data;
-  channel_data.event_set.AddDataFd(channel_fd);
   channel_data.data_fd = std::move(channel_fd);
   channel_data.channel_state = channel_state;
   for (;;) {
@@ -431,18 +433,21 @@
     return status.error_status();
 
   std::lock_guard<std::mutex> autolock(channel_mutex_);
-  auto channel_data = OnNewChannelLocked(std::move(local_socket), channel);
-  if (!channel_data)
-    return channel_data.error_status();
-  *channel_id = channel_data.get().first;
+  auto channel_data_status =
+      OnNewChannelLocked(std::move(local_socket), channel);
+  if (!channel_data_status)
+    return channel_data_status.error_status();
+
+  ChannelData* channel_data;
+  std::tie(*channel_id, channel_data) = channel_data_status.take();
 
   // Flags are ignored for now.
   // TODO(xiaohuit): Implement those.
 
   auto* state = static_cast<MessageState*>(message->GetState());
   Status<ChannelReference> ref = state->PushChannelHandle(
-      remote_socket.Borrow(),
-      channel_data.get().second->event_set.event_fd().Borrow());
+      remote_socket.Borrow(), channel_data->event_set.pollin_event_fd(),
+      channel_data->event_set.pollhup_event_fd());
   if (!ref)
     return ref.error_status();
   state->sockets_to_close.push_back(std::move(remote_socket));
@@ -472,13 +477,15 @@
   return handle;
 }
 
-BorrowedHandle Endpoint::GetChannelEventFd(int32_t channel_id) {
+Status<std::pair<BorrowedHandle, BorrowedHandle>> Endpoint::GetChannelEventFd(
+    int32_t channel_id) {
   std::lock_guard<std::mutex> autolock(channel_mutex_);
-  BorrowedHandle handle;
   auto channel_data = channels_.find(channel_id);
-  if (channel_data != channels_.end())
-    handle = channel_data->second.event_set.event_fd().Borrow();
-  return handle;
+  if (channel_data != channels_.end()) {
+    return {{channel_data->second.event_set.pollin_event_fd(),
+             channel_data->second.event_set.pollhup_event_fd()}};
+  }
+  return ErrorStatus(ENOENT);
 }
 
 int32_t Endpoint::GetChannelId(const BorrowedHandle& channel_fd) {
@@ -593,11 +600,6 @@
   }
 
   BorrowedHandle channel_fd{event.data.fd};
-  if (event.events & (EPOLLRDHUP | EPOLLHUP)) {
-    BuildCloseMessage(GetChannelId(channel_fd), message);
-    return {};
-  }
-
   return ReceiveMessageForChannel(channel_fd, message);
 }
 
@@ -616,12 +618,23 @@
       if (return_code < 0) {
         return CloseChannel(channel_id);
       } else {
-        // Reply with the event fd.
-        auto push_status = state->PushFileHandle(GetChannelEventFd(channel_id));
-        state->response_data.clear();  // Just in case...
-        if (!push_status)
-          return push_status.error_status();
-        return_code = push_status.get();
+        // Open messages do not have a payload and may not transfer any channels
+        // or file descriptors on behalf of the service.
+        state->response_data.clear();
+        state->response.file_descriptors.clear();
+        state->response.channels.clear();
+
+        // Return the channel event-related fds in a single ChannelInfo entry
+        // with an empty data_fd member.
+        auto status = GetChannelEventFd(channel_id);
+        if (!status)
+          return status.error_status();
+
+        auto handles = status.take();
+        state->response.channels.push_back({BorrowedHandle(),
+                                            std::move(handles.first),
+                                            std::move(handles.second)});
+        return_code = 0;
       }
       break;
   }
diff --git a/libs/vr/libpdx_uds/service_framework_tests.cpp b/libs/vr/libpdx_uds/service_framework_tests.cpp
index 5943b0a..2742716 100644
--- a/libs/vr/libpdx_uds/service_framework_tests.cpp
+++ b/libs/vr/libpdx_uds/service_framework_tests.cpp
@@ -1,5 +1,6 @@
 #include <errno.h>
 #include <fcntl.h>
+#include <poll.h>
 #include <sys/epoll.h>
 #include <sys/eventfd.h>
 #include <unistd.h>
@@ -506,6 +507,37 @@
   EXPECT_EQ(-EINVAL, client->SendAsync(invalid_pointer, sizeof(int)));
 }
 
+// Test impulses.
+TEST_F(ServiceFrameworkTest, ImpulseHangup) {
+  // Create a test service and add it to the dispatcher.
+  auto service = TestService::Create(kTestService1);
+  ASSERT_NE(nullptr, service);
+  ASSERT_EQ(0, dispatcher_->AddService(service));
+
+  auto client = TestClient::Create(kTestService1);
+  ASSERT_NE(nullptr, client);
+
+  const int kMaxIterations = 1000;
+  for (int i = 0; i < kMaxIterations; i++) {
+    auto impulse_client = TestClient::Create(kTestService1);
+    ASSERT_NE(nullptr, impulse_client);
+
+    const uint8_t a = (i >> 0) & 0xff;
+    const uint8_t b = (i >> 8) & 0xff;
+    const uint8_t c = (i >> 16) & 0xff;
+    const uint8_t d = (i >> 24) & 0xff;
+    ImpulsePayload expected_payload = {{a, b, c, d}};
+    EXPECT_EQ(0, impulse_client->SendAsync(expected_payload.data(), 4));
+
+    // Hangup the impulse test client, then send a sync message over client to
+    // make sure the hangup message is handled before checking the impulse
+    // payload.
+    impulse_client = nullptr;
+    client->GetThisChannelId();
+    EXPECT_EQ(expected_payload, service->GetImpulsePayload());
+  }
+}
+
 // Test Message::PushChannel/Service::PushChannel API.
 TEST_F(ServiceFrameworkTest, PushChannel) {
   // Create a test service and add it to the dispatcher.
@@ -574,9 +606,7 @@
 
   pid_t process_id2;
 
-  std::thread thread([&]() {
-    process_id2 = client->GetThisProcessId();
-  });
+  std::thread thread([&]() { process_id2 = client->GetThisProcessId(); });
   thread.join();
 
   EXPECT_LT(2, process_id2);
@@ -614,15 +644,15 @@
   auto client = TestClient::Create(kTestService1);
   ASSERT_NE(nullptr, client);
 
-  epoll_event event;
-  int count = epoll_wait(client->event_fd(), &event, 1, 0);
+  pollfd pfd{client->event_fd(), POLLIN, 0};
+  int count = poll(&pfd, 1, 0);
   ASSERT_EQ(0, count);
 
   client->SendPollInEvent();
 
-  count = epoll_wait(client->event_fd(), &event, 1, -1);
+  count = poll(&pfd, 1, 10000 /*10s*/);
   ASSERT_EQ(1, count);
-  ASSERT_TRUE((EPOLLIN & event.events) != 0);
+  ASSERT_TRUE((POLLIN & pfd.revents) != 0);
 }
 
 TEST_F(ServiceFrameworkTest, PollHup) {
@@ -635,15 +665,15 @@
   auto client = TestClient::Create(kTestService1);
   ASSERT_NE(nullptr, client);
 
-  epoll_event event;
-  int count = epoll_wait(client->event_fd(), &event, 1, 0);
+  pollfd pfd{client->event_fd(), POLLIN, 0};
+  int count = poll(&pfd, 1, 0);
   ASSERT_EQ(0, count);
 
   client->SendPollHupEvent();
 
-  count = epoll_wait(client->event_fd(), &event, 1, -1);
+  count = poll(&pfd, 1, 10000 /*10s*/);
   ASSERT_EQ(1, count);
-  auto event_status = client->GetEventMask(event.events);
+  auto event_status = client->GetEventMask(pfd.revents);
   ASSERT_TRUE(event_status.ok());
   ASSERT_TRUE((EPOLLHUP & event_status.get()) != 0);
 }
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
index fda9585..9614c6d 100644
--- a/libs/vr/libvrflinger/acquired_buffer.cpp
+++ b/libs/vr/libvrflinger/acquired_buffer.cpp
@@ -9,8 +9,8 @@
 namespace dvr {
 
 AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
-                               LocalHandle acquire_fence)
-    : buffer_(buffer), acquire_fence_(std::move(acquire_fence)) {}
+                               LocalHandle acquire_fence, std::size_t slot)
+    : buffer_(buffer), acquire_fence_(std::move(acquire_fence)), slot_(slot) {}
 
 AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
                                int* error) {
@@ -31,18 +31,20 @@
   }
 }
 
-AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other)
-    : buffer_(std::move(other.buffer_)),
-      acquire_fence_(std::move(other.acquire_fence_)) {}
+AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other) {
+  *this = std::move(other);
+}
 
 AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); }
 
 AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) {
   if (this != &other) {
-    Release(LocalHandle(kEmptyFence));
+    Release();
 
-    buffer_ = std::move(other.buffer_);
-    acquire_fence_ = std::move(other.acquire_fence_);
+    using std::swap;
+    swap(buffer_, other.buffer_);
+    swap(acquire_fence_, other.acquire_fence_);
+    swap(slot_, other.slot_);
   }
   return *this;
 }
@@ -81,8 +83,6 @@
   ALOGD_IF(TRACE, "AcquiredBuffer::Release: buffer_id=%d release_fence=%d",
            buffer_ ? buffer_->id() : -1, release_fence.Get());
   if (buffer_) {
-    // Close the release fence since we can't transfer it with an async release.
-    release_fence.Close();
     const int ret = buffer_->ReleaseAsync();
     if (ret < 0) {
       ALOGE("AcquiredBuffer::Release: Failed to release buffer %d: %s",
@@ -92,9 +92,10 @@
     }
 
     buffer_ = nullptr;
-    acquire_fence_.Close();
   }
 
+  acquire_fence_.Close();
+  slot_ = 0;
   return 0;
 }
 
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
index e0dc9f2..32e912a 100644
--- a/libs/vr/libvrflinger/acquired_buffer.h
+++ b/libs/vr/libvrflinger/acquired_buffer.h
@@ -21,7 +21,7 @@
   // this constructor; the constructor does not attempt to ACQUIRE the buffer
   // itself.
   AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
-                 pdx::LocalHandle acquire_fence);
+                 pdx::LocalHandle acquire_fence, std::size_t slot = 0);
 
   // Constructs an AcquiredBuffer from a BufferConsumer. The BufferConsumer MUST
   // be in the POSTED state prior to calling this constructor, as this
@@ -64,13 +64,18 @@
   // to the producer. On success, the BufferConsumer and acquire fence are set
   // to empty state; if release fails, the BufferConsumer and acquire fence are
   // left in place and a negative error code is returned.
-  int Release(pdx::LocalHandle release_fence);
+  int Release(pdx::LocalHandle release_fence = {});
+
+  // Returns the slot in the queue this buffer belongs to. Buffers that are not
+  // part of a queue return 0.
+  std::size_t slot() const { return slot_; }
 
  private:
   std::shared_ptr<BufferConsumer> buffer_;
   // Mutable so that the fence can be closed when it is determined to be
   // signaled during IsAvailable().
   mutable pdx::LocalHandle acquire_fence_;
+  std::size_t slot_{0};
 
   AcquiredBuffer(const AcquiredBuffer&) = delete;
   void operator=(const AcquiredBuffer&) = delete;
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
index 40396b9..ef8cca3 100644
--- a/libs/vr/libvrflinger/display_manager_service.cpp
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -65,6 +65,7 @@
 }
 
 pdx::Status<void> DisplayManagerService::HandleMessage(pdx::Message& message) {
+  ATRACE_NAME("DisplayManagerService::HandleMessage");
   auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel());
 
   switch (message.GetOp()) {
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
index af18e21..ac68a5e 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -124,6 +124,8 @@
 // surface-specific messages to the per-instance handlers.
 Status<void> DisplayService::HandleMessage(pdx::Message& message) {
   ALOGD_IF(TRACE, "DisplayService::HandleMessage: opcode=%d", message.GetOp());
+  ATRACE_NAME("DisplayService::HandleMessage");
+
   switch (message.GetOp()) {
     case DisplayProtocol::GetMetrics::Opcode:
       DispatchRemoteMethod<DisplayProtocol::GetMetrics>(
@@ -349,17 +351,9 @@
 
 void DisplayService::UpdateActiveDisplaySurfaces() {
   auto visible_surfaces = GetVisibleDisplaySurfaces();
-
-  std::sort(visible_surfaces.begin(), visible_surfaces.end(),
-            [](const std::shared_ptr<DisplaySurface>& a,
-               const std::shared_ptr<DisplaySurface>& b) {
-              return a->z_order() < b->z_order();
-            });
-
   ALOGD_IF(TRACE,
            "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces",
            visible_surfaces.size());
-
   hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces));
 }
 
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
index 6853781..87c823e 100644
--- a/libs/vr/libvrflinger/display_surface.cpp
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -213,8 +213,8 @@
   ATRACE_NAME("ApplicationDisplaySurface::OnCreateQueue");
   ALOGD_IF(TRACE,
            "ApplicationDisplaySurface::OnCreateQueue: surface_id=%d, "
-           "meta_size_bytes=%zu",
-           surface_id(), config.meta_size_bytes);
+           "user_metadata_size=%zu",
+           surface_id(), config.user_metadata_size);
 
   std::lock_guard<std::mutex> autolock(lock_);
   auto producer = ProducerQueue::Create(config, UsagePolicy{});
@@ -280,10 +280,10 @@
 Status<LocalChannelHandle> DirectDisplaySurface::OnCreateQueue(
     Message& /*message*/, const ProducerQueueConfig& config) {
   ATRACE_NAME("DirectDisplaySurface::OnCreateQueue");
-  ALOGD_IF(
-      TRACE,
-      "DirectDisplaySurface::OnCreateQueue: surface_id=%d meta_size_bytes=%zu",
-      surface_id(), config.meta_size_bytes);
+  ALOGD_IF(TRACE,
+           "DirectDisplaySurface::OnCreateQueue: surface_id=%d "
+           "user_metadata_size=%zu",
+           surface_id(), config.user_metadata_size);
 
   std::lock_guard<std::mutex> autolock(lock_);
   if (!direct_queue_) {
@@ -382,7 +382,7 @@
     }
 
     acquired_buffers_.Append(
-        AcquiredBuffer(buffer_consumer, std::move(acquire_fence)));
+        AcquiredBuffer(buffer_consumer, std::move(acquire_fence), slot));
   }
 }
 
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 3418d65..b3da120 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -5,12 +5,14 @@
 #include <fcntl.h>
 #include <log/log.h>
 #include <poll.h>
+#include <stdint.h>
 #include <sync/sync.h>
 #include <sys/eventfd.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
 #include <sys/system_properties.h>
 #include <sys/timerfd.h>
+#include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 #include <utils/Trace.h>
@@ -30,7 +32,9 @@
 
 using android::hardware::Return;
 using android::hardware::Void;
+using android::pdx::ErrorStatus;
 using android::pdx::LocalHandle;
+using android::pdx::Status;
 using android::pdx::rpc::EmptyVariant;
 using android::pdx::rpc::IfAnyOf;
 
@@ -44,9 +48,8 @@
 const char kBacklightBrightnessSysFile[] =
     "/sys/class/leds/lcd-backlight/brightness";
 
-const char kPrimaryDisplayWaitPPEventFile[] = "/sys/class/graphics/fb0/wait_pp";
-
 const char kDvrPerformanceProperty[] = "sys.dvr.performance";
+const char kDvrStandaloneProperty[] = "ro.boot.vr";
 
 const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
 
@@ -83,10 +86,30 @@
   return true;
 }
 
-}  // anonymous namespace
+// Utility to generate scoped tracers with arguments.
+// TODO(eieio): Move/merge this into utils/Trace.h?
+class TraceArgs {
+ public:
+  template <typename... Args>
+  TraceArgs(const char* format, Args&&... args) {
+    std::array<char, 1024> buffer;
+    snprintf(buffer.data(), buffer.size(), format, std::forward<Args>(args)...);
+    atrace_begin(ATRACE_TAG, buffer.data());
+  }
 
-// HardwareComposer static data;
-constexpr size_t HardwareComposer::kMaxHardwareLayers;
+  ~TraceArgs() { atrace_end(ATRACE_TAG); }
+
+ private:
+  TraceArgs(const TraceArgs&) = delete;
+  void operator=(const TraceArgs&) = delete;
+};
+
+// Macro to define a scoped tracer with arguments. Uses PASTE(x, y) macro
+// defined in utils/Trace.h.
+#define TRACE_FORMAT(format, ...) \
+  TraceArgs PASTE(__tracer, __LINE__) { format, ##__VA_ARGS__ }
+
+}  // anonymous namespace
 
 HardwareComposer::HardwareComposer()
     : initialized_(false), request_display_callback_(nullptr) {}
@@ -98,18 +121,20 @@
 }
 
 bool HardwareComposer::Initialize(
-    Hwc2::Composer* hidl, RequestDisplayCallback request_display_callback) {
+    Hwc2::Composer* composer, RequestDisplayCallback request_display_callback) {
   if (initialized_) {
     ALOGE("HardwareComposer::Initialize: already initialized.");
     return false;
   }
 
+  is_standalone_device_ = property_get_bool(kDvrStandaloneProperty, false);
+
   request_display_callback_ = request_display_callback;
 
   HWC::Error error = HWC::Error::None;
 
   Hwc2::Config config;
-  error = hidl->getActiveConfig(HWC_DISPLAY_PRIMARY, &config);
+  error = composer->getActiveConfig(HWC_DISPLAY_PRIMARY, &config);
 
   if (error != HWC::Error::None) {
     ALOGE("HardwareComposer: Failed to get current display config : %d",
@@ -117,7 +142,7 @@
     return false;
   }
 
-  error = GetDisplayMetrics(hidl, HWC_DISPLAY_PRIMARY, config,
+  error = GetDisplayMetrics(composer, HWC_DISPLAY_PRIMARY, config,
                             &native_display_metrics_);
 
   if (error != HWC::Error::None) {
@@ -140,6 +165,9 @@
   display_transform_ = HWC_TRANSFORM_NONE;
   display_metrics_ = native_display_metrics_;
 
+  // Setup the display metrics used by all Layer instances.
+  Layer::SetDisplayMetrics(native_display_metrics_);
+
   post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
   LOG_ALWAYS_FATAL_IF(
       !post_thread_event_fd_,
@@ -198,9 +226,17 @@
 }
 
 void HardwareComposer::OnPostThreadResumed() {
-  hidl_.reset(new Hwc2::Composer(false));
-  hidl_callback_ = new ComposerCallback;
-  hidl_->registerCallback(hidl_callback_);
+  // Phones create a new composer client on resume and destroy it on pause.
+  // Standalones only create the composer client once and then use SetPowerMode
+  // to control the screen on pause/resume.
+  if (!is_standalone_device_ || !composer_) {
+    composer_.reset(new Hwc2::Composer(false));
+    composer_callback_ = new ComposerCallback;
+    composer_->registerCallback(composer_callback_);
+    Layer::SetComposer(composer_.get());
+  } else {
+    SetPowerMode(true);
+  }
 
   EnableVsync(true);
 
@@ -217,19 +253,19 @@
 
 void HardwareComposer::OnPostThreadPaused() {
   retire_fence_fds_.clear();
-  display_surfaces_.clear();
+  layers_.clear();
 
-  for (size_t i = 0; i < kMaxHardwareLayers; ++i) {
-    layers_[i].Reset();
-  }
-  active_layer_count_ = 0;
-
-  if (hidl_) {
+  if (composer_) {
     EnableVsync(false);
   }
 
-  hidl_callback_ = nullptr;
-  hidl_.reset(nullptr);
+  if (!is_standalone_device_) {
+    composer_callback_ = nullptr;
+    composer_.reset(nullptr);
+    Layer::SetComposer(nullptr);
+  } else {
+    SetPowerMode(false);
+  }
 
   // Trigger target-specific performance mode change.
   property_set(kDvrPerformanceProperty, "idle");
@@ -239,29 +275,35 @@
   uint32_t num_types;
   uint32_t num_requests;
   HWC::Error error =
-      hidl_->validateDisplay(display, &num_types, &num_requests);
+      composer_->validateDisplay(display, &num_types, &num_requests);
 
   if (error == HWC2_ERROR_HAS_CHANGES) {
     // TODO(skiazyk): We might need to inspect the requested changes first, but
     // so far it seems like we shouldn't ever hit a bad state.
     // error = hwc2_funcs_.accept_display_changes_fn_(hardware_composer_device_,
     //                                               display);
-    error = hidl_->acceptDisplayChanges(display);
+    error = composer_->acceptDisplayChanges(display);
   }
 
   return error;
 }
 
 HWC::Error HardwareComposer::EnableVsync(bool enabled) {
-  return hidl_->setVsyncEnabled(
+  return composer_->setVsyncEnabled(
       HWC_DISPLAY_PRIMARY,
       (Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE
                                              : HWC2_VSYNC_DISABLE));
 }
 
+HWC::Error HardwareComposer::SetPowerMode(bool active) {
+  HWC::PowerMode power_mode = active ? HWC::PowerMode::On : HWC::PowerMode::Off;
+  return composer_->setPowerMode(
+      HWC_DISPLAY_PRIMARY, power_mode.cast<Hwc2::IComposerClient::PowerMode>());
+}
+
 HWC::Error HardwareComposer::Present(hwc2_display_t display) {
   int32_t present_fence;
-  HWC::Error error = hidl_->presentDisplay(display, &present_fence);
+  HWC::Error error = composer_->presentDisplay(display, &present_fence);
 
   // According to the documentation, this fence is signaled at the time of
   // vsync/DMA for physical displays.
@@ -275,21 +317,21 @@
   return error;
 }
 
-HWC::Error HardwareComposer::GetDisplayAttribute(Hwc2::Composer* hidl,
+HWC::Error HardwareComposer::GetDisplayAttribute(Hwc2::Composer* composer,
                                                  hwc2_display_t display,
                                                  hwc2_config_t config,
                                                  hwc2_attribute_t attribute,
                                                  int32_t* out_value) const {
-  return hidl->getDisplayAttribute(
+  return composer->getDisplayAttribute(
       display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value);
 }
 
 HWC::Error HardwareComposer::GetDisplayMetrics(
-    Hwc2::Composer* hidl, hwc2_display_t display, hwc2_config_t config,
+    Hwc2::Composer* composer, hwc2_display_t display, hwc2_config_t config,
     HWCDisplayMetrics* out_metrics) const {
   HWC::Error error;
 
-  error = GetDisplayAttribute(hidl, display, config, HWC2_ATTRIBUTE_WIDTH,
+  error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_WIDTH,
                               &out_metrics->width);
   if (error != HWC::Error::None) {
     ALOGE(
@@ -298,7 +340,7 @@
     return error;
   }
 
-  error = GetDisplayAttribute(hidl, display, config, HWC2_ATTRIBUTE_HEIGHT,
+  error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_HEIGHT,
                               &out_metrics->height);
   if (error != HWC::Error::None) {
     ALOGE(
@@ -307,7 +349,7 @@
     return error;
   }
 
-  error = GetDisplayAttribute(hidl, display, config,
+  error = GetDisplayAttribute(composer, display, config,
                               HWC2_ATTRIBUTE_VSYNC_PERIOD,
                               &out_metrics->vsync_period_ns);
   if (error != HWC::Error::None) {
@@ -317,7 +359,7 @@
     return error;
   }
 
-  error = GetDisplayAttribute(hidl, display, config, HWC2_ATTRIBUTE_DPI_X,
+  error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_DPI_X,
                               &out_metrics->dpi.x);
   if (error != HWC::Error::None) {
     ALOGE(
@@ -326,7 +368,7 @@
     return error;
   }
 
-  error = GetDisplayAttribute(hidl, display, config, HWC2_ATTRIBUTE_DPI_Y,
+  error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_DPI_Y,
                               &out_metrics->dpi.y);
   if (error != HWC::Error::None) {
     ALOGE(
@@ -349,10 +391,10 @@
          << std::endl;
 
   stream << "Post thread resumed: " << post_thread_resumed_ << std::endl;
-  stream << "Active layers:       " << active_layer_count_ << std::endl;
+  stream << "Active layers:       " << layers_.size() << std::endl;
   stream << std::endl;
 
-  for (size_t i = 0; i < active_layer_count_; i++) {
+  for (size_t i = 0; i < layers_.size(); i++) {
     stream << "Layer " << i << ":";
     stream << " type=" << layers_[i].GetCompositionType().to_string();
     stream << " surface_id=" << layers_[i].GetSurfaceId();
@@ -363,7 +405,7 @@
 
   if (post_thread_resumed_) {
     stream << "Hardware Composer Debug Info:" << std::endl;
-    stream << hidl_->dumpDebugInfo();
+    stream << composer_->dumpDebugInfo();
   }
 
   return stream.str();
@@ -373,8 +415,8 @@
   ATRACE_NAME("HardwareComposer::PostLayers");
 
   // Setup the hardware composer layers with current buffers.
-  for (size_t i = 0; i < active_layer_count_; i++) {
-    layers_[i].Prepare();
+  for (auto& layer : layers_) {
+    layer.Prepare();
   }
 
   HWC::Error error = Validate(HWC_DISPLAY_PRIMARY);
@@ -396,20 +438,18 @@
     retire_fence_fds_.erase(retire_fence_fds_.begin());
   }
 
-  const bool is_frame_pending = IsFramePendingInDriver();
   const bool is_fence_pending = static_cast<int32_t>(retire_fence_fds_.size()) >
                                 post_thread_config_.allowed_pending_fence_count;
 
-  if (is_fence_pending || is_frame_pending) {
+  if (is_fence_pending) {
     ATRACE_INT("frame_skip_count", ++frame_skip_count_);
 
-    ALOGW_IF(is_frame_pending, "Warning: frame already queued, dropping frame");
     ALOGW_IF(is_fence_pending,
              "Warning: dropping a frame to catch up with HWC (pending = %zd)",
              retire_fence_fds_.size());
 
-    for (size_t i = 0; i < active_layer_count_; i++) {
-      layers_[i].Drop();
+    for (auto& layer : layers_) {
+      layer.Drop();
     }
     return;
   } else {
@@ -419,7 +459,7 @@
   }
 
 #if TRACE > 1
-  for (size_t i = 0; i < active_layer_count_; i++) {
+  for (size_t i = 0; i < layers_.size(); i++) {
     ALOGI("HardwareComposer::PostLayers: layer=%zu buffer_id=%d composition=%s",
           i, layers_[i].GetBufferId(),
           layers_[i].GetCompositionType().to_string().c_str());
@@ -435,18 +475,18 @@
 
   std::vector<Hwc2::Layer> out_layers;
   std::vector<int> out_fences;
-  error = hidl_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers,
-                                  &out_fences);
+  error = composer_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers,
+                                      &out_fences);
   ALOGE_IF(error != HWC::Error::None,
            "HardwareComposer::PostLayers: Failed to get release fences: %s",
            error.to_string().c_str());
 
-  // Perform post-frame bookkeeping. Unused layers are a no-op.
+  // Perform post-frame bookkeeping.
   uint32_t num_elements = out_layers.size();
   for (size_t i = 0; i < num_elements; ++i) {
-    for (size_t j = 0; j < active_layer_count_; ++j) {
-      if (layers_[j].GetLayerHandle() == out_layers[i]) {
-        layers_[j].Finish(out_fences[i]);
+    for (auto& layer : layers_) {
+      if (layer.GetLayerHandle() == out_layers[i]) {
+        layer.Finish(out_fences[i]);
       }
     }
   }
@@ -462,7 +502,7 @@
     pending_surfaces_ = std::move(surfaces);
   }
 
-  if (request_display_callback_)
+  if (request_display_callback_ && (!is_standalone_device_ || !composer_))
     request_display_callback_(!display_idle);
 
   // Set idle state based on whether there are any surfaces to handle.
@@ -565,67 +605,35 @@
   } else if (pfd[0].revents != 0) {
     return 0;
   } else if (pfd[1].revents != 0) {
-    ALOGI("VrHwcPost thread interrupted");
+    ALOGI("VrHwcPost thread interrupted: revents=%x", pfd[1].revents);
     return kPostThreadInterrupted;
   } else {
     return 0;
   }
 }
 
-// Reads the value of the display driver wait_pingpong state. Returns 0 or 1
-// (the value of the state) on success or a negative error otherwise.
-// TODO(eieio): This is pretty driver specific, this should be moved to a
-// separate class eventually.
-int HardwareComposer::ReadWaitPPState() {
-  // Gracefully handle when the kernel does not support this feature.
-  if (!primary_display_wait_pp_fd_)
-    return 0;
-
-  const int wait_pp_fd = primary_display_wait_pp_fd_.Get();
-  int ret, error;
-
-  ret = lseek(wait_pp_fd, 0, SEEK_SET);
-  if (ret < 0) {
-    error = errno;
-    ALOGE("HardwareComposer::ReadWaitPPState: Failed to seek wait_pp fd: %s",
-          strerror(error));
-    return -error;
-  }
-
-  char data = -1;
-  ret = read(wait_pp_fd, &data, sizeof(data));
-  if (ret < 0) {
-    error = errno;
-    ALOGE("HardwareComposer::ReadWaitPPState: Failed to read wait_pp state: %s",
-          strerror(error));
-    return -error;
-  }
-
-  switch (data) {
-    case '0':
-      return 0;
-    case '1':
-      return 1;
-    default:
-      ALOGE(
-          "HardwareComposer::ReadWaitPPState: Unexpected value for wait_pp: %d",
-          data);
-      return -EINVAL;
-  }
+Status<int64_t> HardwareComposer::GetVSyncTime() {
+  auto status = composer_callback_->GetVsyncTime(HWC_DISPLAY_PRIMARY);
+  ALOGE_IF(!status,
+           "HardwareComposer::GetVSyncTime: Failed to get vsync timestamp: %s",
+           status.GetErrorMessage().c_str());
+  return status;
 }
 
 // Waits for the next vsync and returns the timestamp of the vsync event. If
 // vsync already passed since the last call, returns the latest vsync timestamp
 // instead of blocking.
-int HardwareComposer::WaitForVSync(int64_t* timestamp) {
-  int error = PostThreadPollInterruptible(
-      hidl_callback_->GetVsyncEventFd(), POLLIN, /*timeout_ms*/ 1000);
-  if (error == kPostThreadInterrupted || error < 0) {
+Status<int64_t> HardwareComposer::WaitForVSync() {
+  const int64_t predicted_vsync_time =
+      last_vsync_timestamp_ +
+      display_metrics_.vsync_period_ns * vsync_prediction_interval_;
+  const int error = SleepUntil(predicted_vsync_time);
+  if (error < 0) {
+    ALOGE("HardwareComposer::WaifForVSync:: Failed to sleep: %s",
+          strerror(-error));
     return error;
-  } else {
-    *timestamp = hidl_callback_->GetVsyncTime();
-    return 0;
   }
+  return {predicted_vsync_time};
 }
 
 int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) {
@@ -643,8 +651,8 @@
     return -error;
   }
 
-  return PostThreadPollInterruptible(
-      vsync_sleep_timer_fd_, POLLIN, /*timeout_ms*/ -1);
+  return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN,
+                                     /*timeout_ms*/ -1);
 }
 
 void HardwareComposer::PostThread() {
@@ -667,15 +675,6 @@
            strerror(errno));
 #endif  // ENABLE_BACKLIGHT_BRIGHTNESS
 
-  // Open the wait pingpong status node for the primary display.
-  // TODO(eieio): Move this into a platform-specific class.
-  primary_display_wait_pp_fd_ =
-      LocalHandle(kPrimaryDisplayWaitPPEventFile, O_RDONLY);
-  ALOGW_IF(
-      !primary_display_wait_pp_fd_,
-      "HardwareComposer: Failed to open wait_pp node for primary display: %s",
-      strerror(errno));
-
   // Create a timerfd based on CLOCK_MONOTINIC.
   vsync_sleep_timer_fd_.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
   LOG_ALWAYS_FATAL_IF(
@@ -740,26 +739,41 @@
         thread_policy_setup =
             SetThreadPolicy("graphics:high", "/system/performance");
       }
+
+      // Initialize the last vsync timestamp with the current time. The
+      // predictor below uses this time + the vsync interval in absolute time
+      // units for the initial delay. Once the driver starts reporting vsync the
+      // predictor will sync up with the real vsync.
+      last_vsync_timestamp_ = GetSystemClockNs();
     }
 
     int64_t vsync_timestamp = 0;
     {
-      std::array<char, 128> buf;
-      snprintf(buf.data(), buf.size(), "wait_vsync|vsync=%d|",
-               vsync_count_ + 1);
-      ATRACE_NAME(buf.data());
+      TRACE_FORMAT("wait_vsync|vsync=%u;last_timestamp=%" PRId64
+                   ";prediction_interval=%d|",
+                   vsync_count_ + 1, last_vsync_timestamp_,
+                   vsync_prediction_interval_);
 
-      const int error = WaitForVSync(&vsync_timestamp);
+      auto status = WaitForVSync();
       ALOGE_IF(
-          error < 0,
+          !status,
           "HardwareComposer::PostThread: Failed to wait for vsync event: %s",
-          strerror(-error));
-      // Don't bother processing this frame if a pause was requested
-      if (error == kPostThreadInterrupted)
+          status.GetErrorMessage().c_str());
+
+      // If there was an error either sleeping was interrupted due to pausing or
+      // there was an error getting the latest timestamp.
+      if (!status)
         continue;
+
+      // Predicted vsync timestamp for this interval. This is stable because we
+      // use absolute time for the wakeup timer.
+      vsync_timestamp = status.get();
     }
 
-    ++vsync_count_;
+    // Advance the vsync counter only if the system is keeping up with hardware
+    // vsync to give clients an indication of the delays.
+    if (vsync_prediction_interval_ == 1)
+      ++vsync_count_;
 
     const bool layer_config_changed = UpdateLayerConfig();
 
@@ -809,6 +823,38 @@
       }
     }
 
+    {
+      auto status = GetVSyncTime();
+      if (!status) {
+        ALOGE("HardwareComposer::PostThread: Failed to get VSYNC time: %s",
+              status.GetErrorMessage().c_str());
+      }
+
+      // If we failed to read vsync there might be a problem with the driver.
+      // Since there's nothing we can do just behave as though we didn't get an
+      // updated vsync time and let the prediction continue.
+      const int64_t current_vsync_timestamp =
+          status ? status.get() : last_vsync_timestamp_;
+
+      const bool vsync_delayed =
+          last_vsync_timestamp_ == current_vsync_timestamp;
+      ATRACE_INT("vsync_delayed", vsync_delayed);
+
+      // If vsync was delayed advance the prediction interval and allow the
+      // fence logic in PostLayers() to skip the frame.
+      if (vsync_delayed) {
+        ALOGW(
+            "HardwareComposer::PostThread: VSYNC timestamp did not advance "
+            "since last frame: timestamp=%" PRId64 " prediction_interval=%d",
+            current_vsync_timestamp, vsync_prediction_interval_);
+        vsync_prediction_interval_++;
+      } else {
+        // We have an updated vsync timestamp, reset the prediction interval.
+        last_vsync_timestamp_ = current_vsync_timestamp;
+        vsync_prediction_interval_ = 1;
+      }
+    }
+
     PostLayers();
   }
 }
@@ -827,38 +873,60 @@
 
   ATRACE_NAME("UpdateLayerConfig_HwLayers");
 
-  display_surfaces_.clear();
+  // Sort the new direct surface list by z-order to determine the relative order
+  // of the surfaces. This relative order is used for the HWC z-order value to
+  // insulate VrFlinger and HWC z-order semantics from each other.
+  std::sort(surfaces.begin(), surfaces.end(), [](const auto& a, const auto& b) {
+    return a->z_order() < b->z_order();
+  });
 
-  Layer* target_layer;
-  size_t layer_index;
-  for (layer_index = 0;
-       layer_index < std::min(surfaces.size(), kMaxHardwareLayers);
-       layer_index++) {
+  // Prepare a new layer stack, pulling in layers from the previous
+  // layer stack that are still active and updating their attributes.
+  std::vector<Layer> layers;
+  size_t layer_index = 0;
+  for (const auto& surface : surfaces) {
     // The bottom layer is opaque, other layers blend.
     HWC::BlendMode blending =
         layer_index == 0 ? HWC::BlendMode::None : HWC::BlendMode::Coverage;
-    layers_[layer_index].Setup(surfaces[layer_index], native_display_metrics_,
-                               hidl_.get(), blending,
-                               display_transform_, HWC::Composition::Device,
-                               layer_index);
-    display_surfaces_.push_back(surfaces[layer_index]);
+
+    // Try to find a layer for this surface in the set of active layers.
+    auto search =
+        std::lower_bound(layers_.begin(), layers_.end(), surface->surface_id());
+    const bool found = search != layers_.end() &&
+                       search->GetSurfaceId() == surface->surface_id();
+    if (found) {
+      // Update the attributes of the layer that may have changed.
+      search->SetBlending(blending);
+      search->SetZOrder(layer_index);  // Relative z-order.
+
+      // Move the existing layer to the new layer set and remove the empty layer
+      // object from the current set.
+      layers.push_back(std::move(*search));
+      layers_.erase(search);
+    } else {
+      // Insert a layer for the new surface.
+      layers.emplace_back(surface, blending, display_transform_,
+                          HWC::Composition::Device, layer_index);
+    }
+
+    ALOGI_IF(
+        TRACE,
+        "HardwareComposer::UpdateLayerConfig: layer_index=%zu surface_id=%d",
+        layer_index, layers[layer_index].GetSurfaceId());
+
+    layer_index++;
   }
 
-  // Clear unused layers.
-  for (size_t i = layer_index; i < kMaxHardwareLayers; i++)
-    layers_[i].Reset();
+  // Sort the new layer stack by ascending surface id.
+  std::sort(layers.begin(), layers.end());
 
-  active_layer_count_ = layer_index;
+  // Replace the previous layer set with the new layer set. The destructor of
+  // the previous set will clean up the remaining Layers that are not moved to
+  // the new layer set.
+  layers_ = std::move(layers);
+
   ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers",
-           active_layer_count_);
-
-  // Any surfaces left over could not be assigned a hardware layer and will
-  // not be displayed.
-  ALOGW_IF(surfaces.size() != display_surfaces_.size(),
-           "HardwareComposer::UpdateLayerConfig: More surfaces than layers: "
-           "pending_surfaces=%zu display_surfaces=%zu",
-           surfaces.size(), display_surfaces_.size());
-
+           layers_.size());
   return true;
 }
 
@@ -874,17 +942,28 @@
   }
 }
 
-HardwareComposer::ComposerCallback::ComposerCallback() {
-  vsync_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
-  LOG_ALWAYS_FATAL_IF(
-      !vsync_event_fd_,
-      "Failed to create vsync event fd : %s",
-      strerror(errno));
-}
-
 Return<void> HardwareComposer::ComposerCallback::onHotplug(
-    Hwc2::Display /*display*/,
-    IComposerCallback::Connection /*conn*/) {
+    Hwc2::Display display, IComposerCallback::Connection /*conn*/) {
+  // See if the driver supports the vsync_event node in sysfs.
+  if (display < HWC_NUM_PHYSICAL_DISPLAY_TYPES &&
+      !displays_[display].driver_vsync_event_fd) {
+    std::array<char, 1024> buffer;
+    snprintf(buffer.data(), buffer.size(),
+             "/sys/class/graphics/fb%" PRIu64 "/vsync_event", display);
+    if (LocalHandle handle{buffer.data(), O_RDONLY}) {
+      ALOGI(
+          "HardwareComposer::ComposerCallback::onHotplug: Driver supports "
+          "vsync_event node for display %" PRIu64,
+          display);
+      displays_[display].driver_vsync_event_fd = std::move(handle);
+    } else {
+      ALOGI(
+          "HardwareComposer::ComposerCallback::onHotplug: Driver does not "
+          "support vsync_event node for display %" PRIu64,
+          display);
+    }
+  }
+
   return Void();
 }
 
@@ -893,40 +972,94 @@
   return hardware::Void();
 }
 
-Return<void> HardwareComposer::ComposerCallback::onVsync(
-    Hwc2::Display display, int64_t timestamp) {
-  if (display == HWC_DISPLAY_PRIMARY) {
-    std::lock_guard<std::mutex> lock(vsync_mutex_);
-    vsync_time_ = timestamp;
-    int error = eventfd_write(vsync_event_fd_.Get(), 1);
-    LOG_ALWAYS_FATAL_IF(error != 0, "Failed writing to vsync event fd");
+Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display,
+                                                         int64_t timestamp) {
+  TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
+               display, timestamp);
+  if (display < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
+    displays_[display].callback_vsync_timestamp = timestamp;
+  } else {
+    ALOGW(
+        "HardwareComposer::ComposerCallback::onVsync: Received vsync on "
+        "non-physical display: display=%" PRId64,
+        display);
   }
   return Void();
 }
 
-const pdx::LocalHandle&
-HardwareComposer::ComposerCallback::GetVsyncEventFd() const {
-  return vsync_event_fd_;
+Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime(
+    Hwc2::Display display) {
+  if (display >= HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
+    ALOGE(
+        "HardwareComposer::ComposerCallback::GetVsyncTime: Invalid physical "
+        "display requested: display=%" PRIu64,
+        display);
+    return ErrorStatus(EINVAL);
+  }
+
+  // See if the driver supports direct vsync events.
+  LocalHandle& event_fd = displays_[display].driver_vsync_event_fd;
+  if (!event_fd) {
+    // Fall back to returning the last timestamp returned by the vsync
+    // callback.
+    std::lock_guard<std::mutex> autolock(vsync_mutex_);
+    return displays_[display].callback_vsync_timestamp;
+  }
+
+  // When the driver supports the vsync_event sysfs node we can use it to
+  // determine the latest vsync timestamp, even if the HWC callback has been
+  // delayed.
+
+  // The driver returns data in the form "VSYNC=<timestamp ns>".
+  std::array<char, 32> data;
+  data.fill('\0');
+
+  // Seek back to the beginning of the event file.
+  int ret = lseek(event_fd.Get(), 0, SEEK_SET);
+  if (ret < 0) {
+    const int error = errno;
+    ALOGE(
+        "HardwareComposer::ComposerCallback::GetVsyncTime: Failed to seek "
+        "vsync event fd: %s",
+        strerror(error));
+    return ErrorStatus(error);
+  }
+
+  // Read the vsync event timestamp.
+  ret = read(event_fd.Get(), data.data(), data.size());
+  if (ret < 0) {
+    const int error = errno;
+    ALOGE_IF(error != EAGAIN,
+             "HardwareComposer::ComposerCallback::GetVsyncTime: Error "
+             "while reading timestamp: %s",
+             strerror(error));
+    return ErrorStatus(error);
+  }
+
+  int64_t timestamp;
+  ret = sscanf(data.data(), "VSYNC=%" PRIu64,
+               reinterpret_cast<uint64_t*>(&timestamp));
+  if (ret < 0) {
+    const int error = errno;
+    ALOGE(
+        "HardwareComposer::ComposerCallback::GetVsyncTime: Error while "
+        "parsing timestamp: %s",
+        strerror(error));
+    return ErrorStatus(error);
+  }
+
+  return {timestamp};
 }
 
-int64_t HardwareComposer::ComposerCallback::GetVsyncTime() {
-  std::lock_guard<std::mutex> lock(vsync_mutex_);
-  eventfd_t event;
-  eventfd_read(vsync_event_fd_.Get(), &event);
-  LOG_ALWAYS_FATAL_IF(vsync_time_ < 0,
-                      "Attempt to read vsync time before vsync event");
-  int64_t return_val = vsync_time_;
-  vsync_time_ = -1;
-  return return_val;
-}
+Hwc2::Composer* Layer::composer_{nullptr};
+HWCDisplayMetrics Layer::display_metrics_{0, 0, {0, 0}, 0};
 
 void Layer::Reset() {
-  if (hidl_ != nullptr && hardware_composer_layer_) {
-    hidl_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
+  if (hardware_composer_layer_) {
+    composer_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
     hardware_composer_layer_ = 0;
   }
 
-  hidl_ = nullptr;
   z_order_ = 0;
   blending_ = HWC::BlendMode::None;
   transform_ = HWC::Transform::None;
@@ -935,38 +1068,54 @@
   source_ = EmptyVariant{};
   acquire_fence_.Close();
   surface_rect_functions_applied_ = false;
+  pending_visibility_settings_ = true;
+  cached_buffer_map_.clear();
 }
 
-void Layer::Setup(const std::shared_ptr<DirectDisplaySurface>& surface,
-                  const HWCDisplayMetrics& display_metrics,
-                  Hwc2::Composer* hidl, HWC::BlendMode blending,
-                  HWC::Transform transform, HWC::Composition composition_type,
-                  size_t z_order) {
-  Reset();
-  hidl_ = hidl;
-  z_order_ = z_order;
-  blending_ = blending;
-  transform_ = transform;
-  composition_type_ = HWC::Composition::Invalid;
-  target_composition_type_ = composition_type;
-  source_ = SourceSurface{surface};
-  CommonLayerSetup(display_metrics);
+Layer::Layer(const std::shared_ptr<DirectDisplaySurface>& surface,
+             HWC::BlendMode blending, HWC::Transform transform,
+             HWC::Composition composition_type, size_t z_order)
+    : z_order_{z_order},
+      blending_{blending},
+      transform_{transform},
+      target_composition_type_{composition_type},
+      source_{SourceSurface{surface}} {
+  CommonLayerSetup();
 }
 
-void Layer::Setup(const std::shared_ptr<IonBuffer>& buffer,
-                  const HWCDisplayMetrics& display_metrics,
-                  Hwc2::Composer* hidl, HWC::BlendMode blending,
-                  HWC::Transform transform, HWC::Composition composition_type,
-                  size_t z_order) {
-  Reset();
-  hidl_ = hidl;
-  z_order_ = z_order;
-  blending_ = blending;
-  transform_ = transform;
-  composition_type_ = HWC::Composition::Invalid;
-  target_composition_type_ = composition_type;
-  source_ = SourceBuffer{buffer};
-  CommonLayerSetup(display_metrics);
+Layer::Layer(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
+             HWC::Transform transform, HWC::Composition composition_type,
+             size_t z_order)
+    : z_order_{z_order},
+      blending_{blending},
+      transform_{transform},
+      target_composition_type_{composition_type},
+      source_{SourceBuffer{buffer}} {
+  CommonLayerSetup();
+}
+
+Layer::~Layer() { Reset(); }
+
+Layer::Layer(Layer&& other) { *this = std::move(other); }
+
+Layer& Layer::operator=(Layer&& other) {
+  if (this != &other) {
+    Reset();
+    using std::swap;
+    swap(hardware_composer_layer_, other.hardware_composer_layer_);
+    swap(z_order_, other.z_order_);
+    swap(blending_, other.blending_);
+    swap(transform_, other.transform_);
+    swap(composition_type_, other.composition_type_);
+    swap(target_composition_type_, other.target_composition_type_);
+    swap(source_, other.source_);
+    swap(acquire_fence_, other.acquire_fence_);
+    swap(surface_rect_functions_applied_,
+         other.surface_rect_functions_applied_);
+    swap(pending_visibility_settings_, other.pending_visibility_settings_);
+    swap(cached_buffer_map_, other.cached_buffer_map_);
+  }
+  return *this;
 }
 
 void Layer::UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer) {
@@ -974,8 +1123,19 @@
     std::get<SourceBuffer>(source_) = {buffer};
 }
 
-void Layer::SetBlending(HWC::BlendMode blending) { blending_ = blending; }
-void Layer::SetZOrder(size_t z_order) { z_order_ = z_order; }
+void Layer::SetBlending(HWC::BlendMode blending) {
+  if (blending_ != blending) {
+    blending_ = blending;
+    pending_visibility_settings_ = true;
+  }
+}
+
+void Layer::SetZOrder(size_t z_order) {
+  if (z_order_ != z_order) {
+    z_order_ = z_order;
+    pending_visibility_settings_ = true;
+  }
+}
 
 IonBuffer* Layer::GetBuffer() {
   struct Visitor {
@@ -986,91 +1146,108 @@
   return source_.Visit(Visitor{});
 }
 
-void Layer::UpdateLayerSettings(const HWCDisplayMetrics& display_metrics) {
-  if (!IsLayerSetup()) {
-    ALOGE(
-        "HardwareComposer::Layer::UpdateLayerSettings: Attempt to update "
-        "unused Layer!");
-    return;
-  }
+void Layer::UpdateVisibilitySettings() {
+  if (pending_visibility_settings_) {
+    pending_visibility_settings_ = false;
 
+    HWC::Error error;
+    hwc2_display_t display = HWC_DISPLAY_PRIMARY;
+
+    error = composer_->setLayerBlendMode(
+        display, hardware_composer_layer_,
+        blending_.cast<Hwc2::IComposerClient::BlendMode>());
+    ALOGE_IF(error != HWC::Error::None,
+             "Layer::UpdateLayerSettings: Error setting layer blend mode: %s",
+             error.to_string().c_str());
+
+    error =
+        composer_->setLayerZOrder(display, hardware_composer_layer_, z_order_);
+    ALOGE_IF(error != HWC::Error::None,
+             "Layer::UpdateLayerSettings: Error setting z_ order: %s",
+             error.to_string().c_str());
+  }
+}
+
+void Layer::UpdateLayerSettings() {
   HWC::Error error;
   hwc2_display_t display = HWC_DISPLAY_PRIMARY;
 
-  error = hidl_->setLayerCompositionType(
-      display, hardware_composer_layer_,
-      composition_type_.cast<Hwc2::IComposerClient::Composition>());
-  ALOGE_IF(
-      error != HWC::Error::None,
-      "Layer::UpdateLayerSettings: Error setting layer composition type: %s",
-      error.to_string().c_str());
-
-  error = hidl_->setLayerBlendMode(
-      display, hardware_composer_layer_,
-      blending_.cast<Hwc2::IComposerClient::BlendMode>());
-  ALOGE_IF(error != HWC::Error::None,
-           "Layer::UpdateLayerSettings: Error setting layer blend mode: %s",
-           error.to_string().c_str());
+  UpdateVisibilitySettings();
 
   // TODO(eieio): Use surface attributes or some other mechanism to control
   // the layer display frame.
-  error = hidl_->setLayerDisplayFrame(
+  error = composer_->setLayerDisplayFrame(
       display, hardware_composer_layer_,
-      {0, 0, display_metrics.width, display_metrics.height});
+      {0, 0, display_metrics_.width, display_metrics_.height});
   ALOGE_IF(error != HWC::Error::None,
            "Layer::UpdateLayerSettings: Error setting layer display frame: %s",
            error.to_string().c_str());
 
-  error = hidl_->setLayerVisibleRegion(
+  error = composer_->setLayerVisibleRegion(
       display, hardware_composer_layer_,
-      {{0, 0, display_metrics.width, display_metrics.height}});
+      {{0, 0, display_metrics_.width, display_metrics_.height}});
   ALOGE_IF(error != HWC::Error::None,
            "Layer::UpdateLayerSettings: Error setting layer visible region: %s",
            error.to_string().c_str());
 
-  error = hidl_->setLayerPlaneAlpha(display, hardware_composer_layer_, 1.0f);
+  error =
+      composer_->setLayerPlaneAlpha(display, hardware_composer_layer_, 1.0f);
   ALOGE_IF(error != HWC::Error::None,
            "Layer::UpdateLayerSettings: Error setting layer plane alpha: %s",
            error.to_string().c_str());
-
-  error = hidl_->setLayerZOrder(display, hardware_composer_layer_, z_order_);
-  ALOGE_IF(error != HWC::Error::None,
-           "Layer::UpdateLayerSettings: Error setting z_ order: %s",
-           error.to_string().c_str());
 }
 
-void Layer::CommonLayerSetup(const HWCDisplayMetrics& display_metrics) {
+void Layer::CommonLayerSetup() {
   HWC::Error error =
-      hidl_->createLayer(HWC_DISPLAY_PRIMARY, &hardware_composer_layer_);
-  ALOGE_IF(
-      error != HWC::Error::None,
-      "Layer::CommonLayerSetup: Failed to create layer on primary display: %s",
-      error.to_string().c_str());
-  UpdateLayerSettings(display_metrics);
+      composer_->createLayer(HWC_DISPLAY_PRIMARY, &hardware_composer_layer_);
+  ALOGE_IF(error != HWC::Error::None,
+           "Layer::CommonLayerSetup: Failed to create layer on primary "
+           "display: %s",
+           error.to_string().c_str());
+  UpdateLayerSettings();
+}
+
+bool Layer::CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id) {
+  auto search = cached_buffer_map_.find(slot);
+  if (search != cached_buffer_map_.end() && search->second == buffer_id)
+    return true;
+
+  // Assign or update the buffer slot.
+  if (buffer_id >= 0)
+    cached_buffer_map_[slot] = buffer_id;
+  return false;
 }
 
 void Layer::Prepare() {
-  int right, bottom;
+  int right, bottom, id;
   sp<GraphicBuffer> handle;
+  std::size_t slot;
 
   // Acquire the next buffer according to the type of source.
   IfAnyOf<SourceSurface, SourceBuffer>::Call(&source_, [&](auto& source) {
-    std::tie(right, bottom, handle, acquire_fence_) = source.Acquire();
+    std::tie(right, bottom, id, handle, acquire_fence_, slot) =
+        source.Acquire();
   });
 
-  // When a layer is first setup there may be some time before the first buffer
-  // arrives. Setup the HWC layer as a solid color to stall for time until the
-  // first buffer arrives. Once the first buffer arrives there will always be a
-  // buffer for the frame even if it is old.
+  TRACE_FORMAT("Layer::Prepare|buffer_id=%d;slot=%zu|", id, slot);
+
+  // Update any visibility (blending, z-order) changes that occurred since
+  // last prepare.
+  UpdateVisibilitySettings();
+
+  // When a layer is first setup there may be some time before the first
+  // buffer arrives. Setup the HWC layer as a solid color to stall for time
+  // until the first buffer arrives. Once the first buffer arrives there will
+  // always be a buffer for the frame even if it is old.
   if (!handle.get()) {
     if (composition_type_ == HWC::Composition::Invalid) {
       composition_type_ = HWC::Composition::SolidColor;
-      hidl_->setLayerCompositionType(
+      composer_->setLayerCompositionType(
           HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
           composition_type_.cast<Hwc2::IComposerClient::Composition>());
       Hwc2::IComposerClient::Color layer_color = {0, 0, 0, 0};
-      hidl_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
-                           layer_color);
+      composer_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+                               layer_color);
     } else {
       // The composition type is already set. Nothing else to do until a
       // buffer arrives.
@@ -1078,15 +1255,20 @@
   } else {
     if (composition_type_ != target_composition_type_) {
       composition_type_ = target_composition_type_;
-      hidl_->setLayerCompositionType(
+      composer_->setLayerCompositionType(
           HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
           composition_type_.cast<Hwc2::IComposerClient::Composition>());
     }
 
+    // See if the HWC cache already has this buffer.
+    const bool cached = CheckAndUpdateCachedBuffer(slot, id);
+    if (cached)
+      handle = nullptr;
+
     HWC::Error error{HWC::Error::None};
-    error = hidl_->setLayerBuffer(HWC_DISPLAY_PRIMARY,
-                                  hardware_composer_layer_, 0, handle,
-                                  acquire_fence_.Get());
+    error =
+        composer_->setLayerBuffer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+                                  slot, handle, acquire_fence_.Get());
 
     ALOGE_IF(error != HWC::Error::None,
              "Layer::Prepare: Error setting layer buffer: %s",
@@ -1095,9 +1277,9 @@
     if (!surface_rect_functions_applied_) {
       const float float_right = right;
       const float float_bottom = bottom;
-      error = hidl_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY,
-                                        hardware_composer_layer_,
-                                        {0, 0, float_right, float_bottom});
+      error = composer_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY,
+                                            hardware_composer_layer_,
+                                            {0, 0, float_right, float_bottom});
 
       ALOGE_IF(error != HWC::Error::None,
                "Layer::Prepare: Error setting layer source crop: %s",
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index fc0efee..7010db9 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -52,10 +52,7 @@
 // source supplying buffers for the layer's contents.
 class Layer {
  public:
-  Layer() {}
-
-  // Releases any shared pointers and fence handles held by this instance.
-  void Reset();
+  Layer() = default;
 
   // Sets up the layer to use a display surface as its content source. The Layer
   // automatically handles ACQUIRE/RELEASE phases for the surface's buffer train
@@ -66,10 +63,9 @@
   // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
   // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
   // |index| is the index of this surface in the DirectDisplaySurface array.
-  void Setup(const std::shared_ptr<DirectDisplaySurface>& surface,
-             const HWCDisplayMetrics& display_metrics, Hwc2::Composer* hidl,
-             HWC::BlendMode blending, HWC::Transform transform,
-             HWC::Composition composition_type, size_t z_roder);
+  Layer(const std::shared_ptr<DirectDisplaySurface>& surface,
+        HWC::BlendMode blending, HWC::Transform transform,
+        HWC::Composition composition_type, size_t z_roder);
 
   // Sets up the layer to use a direct buffer as its content source. No special
   // handling of the buffer is performed; responsibility for updating or
@@ -79,10 +75,17 @@
   // |transform| receives HWC_TRANSFORM_* values.
   // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
   // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
-  void Setup(const std::shared_ptr<IonBuffer>& buffer,
-             const HWCDisplayMetrics& display_metrics, Hwc2::Composer* hidl,
-             HWC::BlendMode blending, HWC::Transform transform,
-             HWC::Composition composition_type, size_t z_order);
+  Layer(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
+        HWC::Transform transform, HWC::Composition composition_type,
+        size_t z_order);
+
+  Layer(Layer&&);
+  Layer& operator=(Layer&&);
+
+  ~Layer();
+
+  // Releases any shared pointers and fence handles held by this instance.
+  void Reset();
 
   // Layers that use a direct IonBuffer should call this each frame to update
   // which buffer will be used for the next PostLayers.
@@ -117,9 +120,6 @@
   HWC::Layer GetLayerHandle() const { return hardware_composer_layer_; }
   bool IsLayerSetup() const { return !source_.empty(); }
 
-  // Applies all of the settings to this layer using the hwc functions
-  void UpdateLayerSettings(const HWCDisplayMetrics& display_metrics);
-
   int GetSurfaceId() const {
     int surface_id = -1;
     pdx::rpc::IfAnyOf<SourceSurface>::Call(
@@ -138,10 +138,47 @@
     return buffer_id;
   }
 
- private:
-  void CommonLayerSetup(const HWCDisplayMetrics& display_metrics);
+  // Compares Layers by surface id.
+  bool operator<(const Layer& other) const {
+    return GetSurfaceId() < other.GetSurfaceId();
+  }
+  bool operator<(int surface_id) const { return GetSurfaceId() < surface_id; }
 
-  Hwc2::Composer* hidl_ = nullptr;
+  // Sets the composer instance used by all Layer instances.
+  static void SetComposer(Hwc2::Composer* composer) { composer_ = composer; }
+
+  // Sets the display metrics used by all Layer instances.
+  static void SetDisplayMetrics(HWCDisplayMetrics display_metrics) {
+    display_metrics_ = display_metrics;
+  }
+
+ private:
+  void CommonLayerSetup();
+
+  // Applies all of the settings to this layer using the hwc functions
+  void UpdateLayerSettings();
+
+  // Applies visibility settings that may have changed.
+  void UpdateVisibilitySettings();
+
+  // Checks whether the buffer, given by id, is associated with the given slot
+  // in the HWC buffer cache. If the slot is not associated with the given
+  // buffer the cache is updated to establish the association and the buffer
+  // should be sent to HWC using setLayerBuffer. Returns true if the association
+  // was already established, false if not. A buffer_id of -1 is never
+  // associated and always returns false.
+  bool CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id);
+
+  // Composer instance shared by all instances of Layer. This must be set
+  // whenever a new instance of the Composer is created. This may be set to
+  // nullptr as long as there are no instances of Layer that might need to use
+  // it.
+  static Hwc2::Composer* composer_;
+
+  // Display metrics shared by all instances of Layer. This must be set at least
+  // once during VrFlinger initialization and is expected to remain constant
+  // thereafter.
+  static HWCDisplayMetrics display_metrics_;
 
   // The hardware composer layer and metrics to use during the prepare cycle.
   hwc2_layer_t hardware_composer_layer_ = 0;
@@ -169,19 +206,21 @@
     // the previous buffer is returned or an empty value if no buffer has ever
     // been posted. When a new buffer is acquired the previous buffer's release
     // fence is passed out automatically.
-    std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() {
+    std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t>
+    Acquire() {
       if (surface->IsBufferAvailable()) {
         acquired_buffer.Release(std::move(release_fence));
         acquired_buffer = surface->AcquireCurrentBuffer();
         ATRACE_ASYNC_END("BufferPost", acquired_buffer.buffer()->id());
       }
       if (!acquired_buffer.IsEmpty()) {
-        return std::make_tuple(acquired_buffer.buffer()->width(),
-                               acquired_buffer.buffer()->height(),
-                               acquired_buffer.buffer()->buffer()->buffer(),
-                               acquired_buffer.ClaimAcquireFence());
+        return std::make_tuple(
+            acquired_buffer.buffer()->width(),
+            acquired_buffer.buffer()->height(), acquired_buffer.buffer()->id(),
+            acquired_buffer.buffer()->buffer()->buffer(),
+            acquired_buffer.ClaimAcquireFence(), acquired_buffer.slot());
       } else {
-        return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{});
+        return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
       }
     }
 
@@ -213,12 +252,13 @@
   struct SourceBuffer {
     std::shared_ptr<IonBuffer> buffer;
 
-    std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() {
+    std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t>
+    Acquire() {
       if (buffer)
-        return std::make_tuple(buffer->width(), buffer->height(),
-                               buffer->buffer(), pdx::LocalHandle{});
+        return std::make_tuple(buffer->width(), buffer->height(), -1,
+                               buffer->buffer(), pdx::LocalHandle{}, 0);
       else
-        return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{});
+        return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
     }
 
     void Finish(pdx::LocalHandle /*fence*/) {}
@@ -235,6 +275,13 @@
 
   pdx::LocalHandle acquire_fence_;
   bool surface_rect_functions_applied_ = false;
+  bool pending_visibility_settings_ = true;
+
+  // Map of buffer slot assignments that have already been established with HWC:
+  // slot -> buffer_id. When this map contains a matching slot and buffer_id the
+  // buffer argument to setLayerBuffer may be nullptr to avoid the cost of
+  // importing a buffer HWC already knows about.
+  std::map<std::size_t, int> cached_buffer_map_;
 
   Layer(const Layer&) = delete;
   void operator=(const Layer&) = delete;
@@ -254,14 +301,10 @@
   using VSyncCallback = std::function<void(int, int64_t, int64_t, uint32_t)>;
   using RequestDisplayCallback = std::function<void(bool)>;
 
-  // Since there is no universal way to query the number of hardware layers,
-  // just set it to 4 for now.
-  static constexpr size_t kMaxHardwareLayers = 4;
-
   HardwareComposer();
   ~HardwareComposer();
 
-  bool Initialize(Hwc2::Composer* hidl,
+  bool Initialize(Hwc2::Composer* composer,
                   RequestDisplayCallback request_display_callback);
 
   bool IsInitialized() const { return initialized_; }
@@ -299,30 +342,36 @@
   void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
 
  private:
-  HWC::Error GetDisplayAttribute(Hwc2::Composer* hidl, hwc2_display_t display,
-                                 hwc2_config_t config,
+  HWC::Error GetDisplayAttribute(Hwc2::Composer* composer,
+                                 hwc2_display_t display, hwc2_config_t config,
                                  hwc2_attribute_t attributes,
                                  int32_t* out_value) const;
-  HWC::Error GetDisplayMetrics(Hwc2::Composer* hidl, hwc2_display_t display,
+  HWC::Error GetDisplayMetrics(Hwc2::Composer* composer, hwc2_display_t display,
                                hwc2_config_t config,
                                HWCDisplayMetrics* out_metrics) const;
 
   HWC::Error EnableVsync(bool enabled);
+  HWC::Error SetPowerMode(bool active);
 
   class ComposerCallback : public Hwc2::IComposerCallback {
    public:
-    ComposerCallback();
+    ComposerCallback() = default;
     hardware::Return<void> onHotplug(Hwc2::Display display,
                                      Connection conn) override;
     hardware::Return<void> onRefresh(Hwc2::Display display) override;
     hardware::Return<void> onVsync(Hwc2::Display display,
                                    int64_t timestamp) override;
-    const pdx::LocalHandle& GetVsyncEventFd() const;
-    int64_t GetVsyncTime();
+
+    pdx::Status<int64_t> GetVsyncTime(Hwc2::Display display);
+
    private:
     std::mutex vsync_mutex_;
-    pdx::LocalHandle vsync_event_fd_;
-    int64_t vsync_time_ = -1;
+
+    struct Display {
+      pdx::LocalHandle driver_vsync_event_fd;
+      int64_t callback_vsync_timestamp{0};
+    };
+    std::array<Display, HWC_NUM_PHYSICAL_DISPLAY_TYPES> displays_;
   };
 
   HWC::Error Validate(hwc2_display_t display);
@@ -356,18 +405,16 @@
   // the case of a timeout. If we're interrupted, kPostThreadInterrupted will be
   // returned.
   int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd,
-                                  int requested_events,
-                                  int timeout_ms);
+                                  int requested_events, int timeout_ms);
 
   // WaitForVSync and SleepUntil are blocking calls made on the post thread that
   // can be interrupted by a control thread. If interrupted, these calls return
   // kPostThreadInterrupted.
   int ReadWaitPPState();
-  int WaitForVSync(int64_t* timestamp);
+  pdx::Status<int64_t> WaitForVSync();
+  pdx::Status<int64_t> GetVSyncTime();
   int SleepUntil(int64_t wakeup_timestamp);
 
-  bool IsFramePendingInDriver() { return ReadWaitPPState() == 1; }
-
   // Reconfigures the layer stack if the display surfaces changed since the last
   // frame. Called only from the post thread.
   bool UpdateLayerConfig();
@@ -385,9 +432,10 @@
   void UpdateConfigBuffer();
 
   bool initialized_;
+  bool is_standalone_device_;
 
-  std::unique_ptr<Hwc2::Composer> hidl_;
-  sp<ComposerCallback> hidl_callback_;
+  std::unique_ptr<Hwc2::Composer> composer_;
+  sp<ComposerCallback> composer_callback_;
   RequestDisplayCallback request_display_callback_;
 
   // Display metrics of the physical display.
@@ -403,13 +451,9 @@
   // thread and read by the post thread.
   std::vector<std::shared_ptr<DirectDisplaySurface>> pending_surfaces_;
 
-  // The surfaces displayed by the post thread. Used exclusively by the post
-  // thread.
-  std::vector<std::shared_ptr<DirectDisplaySurface>> display_surfaces_;
-
-  // Layer array for handling buffer flow into hardware composer layers.
-  std::array<Layer, kMaxHardwareLayers> layers_;
-  size_t active_layer_count_ = 0;
+  // Layer set for handling buffer flow into hardware composer layers. This
+  // vector must be sorted by surface_id in ascending order.
+  std::vector<Layer> layers_;
 
   // Handler to hook vsync events outside of this class.
   VSyncCallback vsync_callback_;
@@ -419,8 +463,8 @@
   std::thread post_thread_;
 
   // Post thread state machine and synchronization primitives.
-  PostThreadStateType post_thread_state_{
-      PostThreadState::Idle | PostThreadState::Suspended};
+  PostThreadStateType post_thread_state_{PostThreadState::Idle |
+                                         PostThreadState::Suspended};
   std::atomic<bool> post_thread_quiescent_{true};
   bool post_thread_resumed_{false};
   pdx::LocalHandle post_thread_event_fd_;
@@ -431,15 +475,15 @@
   // Backlight LED brightness sysfs node.
   pdx::LocalHandle backlight_brightness_fd_;
 
-  // Primary display wait_pingpong state sysfs node.
-  pdx::LocalHandle primary_display_wait_pp_fd_;
-
   // VSync sleep timerfd.
   pdx::LocalHandle vsync_sleep_timer_fd_;
 
   // The timestamp of the last vsync.
   int64_t last_vsync_timestamp_ = 0;
 
+  // The number of vsync intervals to predict since the last vsync.
+  int vsync_prediction_interval_ = 1;
+
   // Vsync count since display on.
   uint32_t vsync_count_ = 0;
 
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
index fcf94f0..85dc586 100644
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -64,9 +64,6 @@
 
   ALOGI("Starting up VrFlinger...");
 
-  setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY);
-  set_sched_policy(0, SP_FOREGROUND);
-
   // We need to be able to create endpoints with full perms.
   umask(0000);
 
@@ -100,6 +97,9 @@
     prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0);
     ALOGI("Entering message loop.");
 
+    setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY);
+    set_sched_policy(0, SP_FOREGROUND);
+
     int ret = dispatcher_->EnterDispatchLoop();
     if (ret < 0) {
       ALOGE("Dispatch loop exited because: %s\n", strerror(-ret));
diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp
index 3098b43..fdeb899 100644
--- a/libs/vr/libvrflinger/vsync_service.cpp
+++ b/libs/vr/libvrflinger/vsync_service.cpp
@@ -110,6 +110,7 @@
 }
 
 pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) {
+  ATRACE_NAME("VSyncService::HandleMessage");
   switch (message.GetOp()) {
     case VSyncProtocol::Wait::Opcode:
       AddWaiter(message);
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
index d69d825..bb25f1d 100644
--- a/libs/vr/libvrsensor/include/dvr/pose_client.h
+++ b/libs/vr/libvrsensor/include/dvr/pose_client.h
@@ -167,8 +167,7 @@
                              const DvrPoseDataCaptureRequest* request);
 
 // Destroys the write buffer queue for the given |data_type|.
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client,
-                                   DvrPoseRawDataType data_type);
+int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
index 7198fe8..39592bb 100644
--- a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
+++ b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
@@ -10,8 +10,7 @@
 namespace android {
 namespace dvr {
 
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client,
-                                     DvrPoseRawDataType data_type,
+int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type,
                                      ConsumerQueue **queue_out);
 
 }  // namespace dvr
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
index 4e23e25..4acc085 100644
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -141,7 +141,7 @@
     return ReturnStatusOrError(status);
   }
 
-  int GetTangoReaderHandle(DvrPoseRawDataType data_type, ConsumerQueue** queue_out) {
+  int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) {
     // Get buffer.
     Transaction trans{*this};
     Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
@@ -169,7 +169,7 @@
     return ReturnStatusOrError(status);
   }
 
-  int DataReaderDestroy(DvrPoseRawDataType data_type) {
+  int DataReaderDestroy(uint64_t data_type) {
     Transaction trans{*this};
     Status<int> status = trans.Send<int>(DVR_POSE_TANGO_READER_DESTROY,
                                          &data_type, sizeof(data_type), nullptr,
@@ -296,8 +296,7 @@
   ControllerClientState controllers_[MAX_CONTROLLERS];
 };
 
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client,
-                                     DvrPoseRawDataType type,
+int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type,
                                      ConsumerQueue** queue_out) {
   return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out);
 }
@@ -362,8 +361,7 @@
   return PoseClient::FromC(client)->DataCapture(request);
 }
 
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client,
-                                   DvrPoseRawDataType data_type) {
+int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) {
   return PoseClient::FromC(client)->DataReaderDestroy(data_type);
 }
 
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 935d0f6..d4266f6 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -257,7 +257,7 @@
         const String8* uniqueDisplayId, DisplayViewport* outViewport) const {
     const DisplayViewport* viewport = NULL;
     if (viewportType == ViewportType::VIEWPORT_VIRTUAL && uniqueDisplayId != NULL) {
-        for (DisplayViewport currentViewport : mVirtualDisplays) {
+        for (const DisplayViewport& currentViewport : mVirtualDisplays) {
             if (currentViewport.uniqueId == *uniqueDisplayId) {
                 viewport = &currentViewport;
                 break;
diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp
index b54752b..3ae7972 100644
--- a/services/inputflinger/InputWindow.cpp
+++ b/services/inputflinger/InputWindow.cpp
@@ -49,7 +49,8 @@
             || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL
             || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY
             || layoutParamsType == TYPE_DOCK_DIVIDER
-            || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY;
+            || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY
+            || layoutParamsType == TYPE_INPUT_CONSUMER;
 }
 
 bool InputWindowInfo::supportsSplitTouch() const {
diff --git a/services/inputflinger/InputWindow.h b/services/inputflinger/InputWindow.h
index 610290b..9eb2798 100644
--- a/services/inputflinger/InputWindow.h
+++ b/services/inputflinger/InputWindow.h
@@ -101,6 +101,7 @@
         TYPE_NAVIGATION_BAR     = FIRST_SYSTEM_WINDOW+19,
         TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20,
         TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21,
+        TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22,
         TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24,
         TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27,
         TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32,
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index ac739a2..cf01ad0 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -755,7 +755,7 @@
     }
 
     Error error = kDefaultError;
-    mClient->executeCommands(commandLength, commandHandles,
+    auto ret = mClient->executeCommands(commandLength, commandHandles,
             [&](const auto& tmpError, const auto& tmpOutChanged,
                 const auto& tmpOutLength, const auto& tmpOutHandles)
             {
@@ -788,6 +788,11 @@
                     error = Error::NO_RESOURCES;
                 }
             });
+    // executeCommands can fail because of out-of-fd and we do not want to
+    // abort() in that case
+    if (!ret.isOk()) {
+        ALOGE("executeCommands failed because of %s", ret.description().c_str());
+    }
 
     if (error == Error::NONE) {
         std::vector<CommandReader::CommandError> commandErrors =
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 12205af..bb0e33c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -604,11 +604,18 @@
 
     if (useVrFlinger) {
         auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) {
-            ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
-            mVrFlingerRequestsDisplay = requestDisplay;
-            ConditionalLock _l(mStateLock,
-                    std::this_thread::get_id() != mMainThreadId);
-            signalTransaction();
+            // This callback is called from the vr flinger dispatch thread. We
+            // need to call signalTransaction(), which requires holding
+            // mStateLock when we're not on the main thread. Acquiring
+            // mStateLock from the vr flinger dispatch thread might trigger a
+            // deadlock in surface flinger (see b/66916578), so post a message
+            // to be handled on the main thread instead.
+            sp<LambdaMessage> message = new LambdaMessage([=]() {
+                ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
+                mVrFlingerRequestsDisplay = requestDisplay;
+                signalTransaction();
+            });
+            postMessageAsync(message);
         };
         mVrFlinger = dvr::VrFlinger::Create(mHwc->getComposer(),
                                             vrFlingerRequestDisplayCallback);
@@ -649,6 +656,12 @@
     property_get("persist.sys.sf.color_saturation", value, "1.0");
     mSaturation = atof(value);
     ALOGV("Saturation is set to %.2f", mSaturation);
+
+    property_get("persist.sys.sf.native_mode", value, "0");
+    mForceNativeColorMode = atoi(value) == 1;
+    if (mForceNativeColorMode) {
+        ALOGV("Forcing native color mode");
+    }
 }
 
 void SurfaceFlinger::startBootAnim() {
@@ -1228,12 +1241,13 @@
                 break;
         }
     }
+    bool useWideColorMode = hasWideColorModes && hasWideColorDisplay && !mForceNativeColorMode;
     sp<DisplayDevice> hw = new DisplayDevice(this, DisplayDevice::DISPLAY_PRIMARY, type, isSecure,
                                              token, fbs, producer, mRenderEngine->getEGLConfig(),
-                                             hasWideColorModes && hasWideColorDisplay);
+                                             useWideColorMode);
     mDisplays.add(token, hw);
     android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
-    if (hasWideColorModes && hasWideColorDisplay) {
+    if (useWideColorMode) {
         defaultColorMode = HAL_COLOR_MODE_SRGB;
     }
     setActiveColorModeInternal(hw, defaultColorMode);
@@ -1749,6 +1763,10 @@
 // pickColorMode translates a given dataspace into the best available color mode.
 // Currently only support sRGB and Display-P3.
 android_color_mode SurfaceFlinger::pickColorMode(android_dataspace dataSpace) const {
+    if (mForceNativeColorMode) {
+        return HAL_COLOR_MODE_NATIVE;
+    }
+
     switch (dataSpace) {
         // treat Unknown as regular SRGB buffer, since that's what the rest of the
         // system expects.
@@ -2583,8 +2601,10 @@
         ALOGV("hasClientComposition");
 
 #ifdef USE_HWC2
-        mRenderEngine->setWideColor(displayDevice->getWideColorSupport());
-        mRenderEngine->setColorMode(displayDevice->getActiveColorMode());
+        mRenderEngine->setWideColor(
+                displayDevice->getWideColorSupport() && !mForceNativeColorMode);
+        mRenderEngine->setColorMode(mForceNativeColorMode ?
+                HAL_COLOR_MODE_NATIVE : displayDevice->getActiveColorMode());
 #endif
         if (!displayDevice->makeCurrent(mEGLDisplay, mEGLContext)) {
             ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
@@ -3625,6 +3645,7 @@
 
 void SurfaceFlinger::dumpWideColorInfo(String8& result) const {
     result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay);
+    result.appendFormat("forceNativeColorMode: %d\n", mForceNativeColorMode);
 
     // TODO: print out if wide-color mode is active or not
 
@@ -4078,6 +4099,17 @@
                 repaintEverything();
                 return NO_ERROR;
             }
+            case 1023: { // Set native mode
+                mForceNativeColorMode = data.readInt32() == 1;
+
+                invalidateHwcGeometry();
+                repaintEverything();
+                return NO_ERROR;
+            }
+            case 1024: { // Is wide color gamut rendering/color management supported?
+                reply->writeBool(hasWideColorDisplay);
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -4235,8 +4267,9 @@
     WindowDisconnector disconnector(window, NATIVE_WINDOW_API_EGL);
 
     ANativeWindowBuffer* buffer = nullptr;
-    result = getWindowBuffer(window, reqWidth, reqHeight, hasWideColorDisplay,
-                                      getRenderEngine().usesWideColor(), &buffer);
+    result = getWindowBuffer(window, reqWidth, reqHeight,
+            hasWideColorDisplay && !mForceNativeColorMode,
+            getRenderEngine().usesWideColor(), &buffer);
     if (result != NO_ERROR) {
         return result;
     }
@@ -4338,8 +4371,8 @@
     }
 
 #ifdef USE_HWC2
-     engine.setWideColor(hw->getWideColorSupport());
-     engine.setColorMode(hw->getActiveColorMode());
+     engine.setWideColor(hw->getWideColorSupport() && !mForceNativeColorMode);
+     engine.setColorMode(mForceNativeColorMode ? HAL_COLOR_MODE_NATIVE : hw->getActiveColorMode());
 #endif
 
     // make sure to clear all GL error flags
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 13a057f..7606e10 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -814,6 +814,7 @@
 #endif
 
     float mSaturation = 1.0f;
+    bool mForceNativeColorMode = false;
 };
 }; // namespace android
 
diff --git a/services/vr/bufferhubd/Android.mk b/services/vr/bufferhubd/Android.mk
index 97f0332..28cf53d 100644
--- a/services/vr/bufferhubd/Android.mk
+++ b/services/vr/bufferhubd/Android.mk
@@ -22,6 +22,9 @@
     consumer_queue_channel.cpp \
     producer_queue_channel.cpp \
 
+headerLibraries := \
+	libdvr_headers
+
 staticLibraries := \
 	libperformance \
 	libpdx_default_transport \
@@ -41,6 +44,7 @@
 LOCAL_CFLAGS := -DLOG_TAG=\"bufferhubd\"
 LOCAL_CFLAGS += -DTRACE=0
 LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_GRAPHICS
+LOCAL_HEADER_LIBRARIES := $(headerLibraries)
 LOCAL_STATIC_LIBRARIES := $(staticLibraries)
 LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
 LOCAL_MODULE := bufferhubd
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index 26843c9..cdb1f91 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -20,8 +20,8 @@
 using android::pdx::ErrorStatus;
 using android::pdx::Message;
 using android::pdx::Status;
-using android::pdx::rpc::DispatchRemoteMethod;
 using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
 
 namespace android {
 namespace dvr {
@@ -53,7 +53,15 @@
   stream << " ";
   stream << std::setw(6) << "Format";
   stream << " ";
-  stream << std::setw(11) << "Usage";
+  stream << std::setw(10) << "Usage";
+  stream << " ";
+  stream << std::setw(9) << "Pending";
+  stream << " ";
+  stream << std::setw(18) << "State";
+  stream << " ";
+  stream << std::setw(18) << "Signaled";
+  stream << " ";
+  stream << std::setw(10) << "Index";
   stream << " ";
   stream << "Name";
   stream << std::endl;
@@ -83,46 +91,15 @@
       stream << std::setw(8) << info.usage;
       stream << std::dec << std::setfill(' ');
       stream << " ";
-      stream << info.name;
-      stream << std::endl;
-    }
-  }
-
-  stream << "Active Consumer Buffers:\n";
-  stream << std::right;
-  stream << std::setw(6) << "Id";
-  stream << " ";
-  stream << std::setw(14) << "Geometry";
-  stream << " ";
-  stream << "Name";
-  stream << std::endl;
-
-  for (const auto& channel : channels) {
-    if (channel->channel_type() == BufferHubChannel::kConsumerType) {
-      BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
-
-      stream << std::right;
-      stream << std::setw(6) << info.id;
+      stream << std::setw(9) << info.pending_count;
       stream << " ";
-
-      if (info.consumer_count == 0) {
-        // consumer_count is tracked by producer. When it's zero, producer must
-        // have already hung up and the consumer is orphaned.
-        stream << std::setw(14) << "Orphaned.";
-        stream << (" channel_id=" + std::to_string(channel->channel_id()));
-        stream << std::endl;
-        continue;
-      }
-
-      if (info.format == HAL_PIXEL_FORMAT_BLOB) {
-        std::string size = std::to_string(info.width) + " B";
-        stream << std::setw(14) << size;
-      } else {
-        std::string dimensions = std::to_string(info.width) + "x" +
-                                 std::to_string(info.height) + "x" +
-                                 std::to_string(info.layer_count);
-        stream << std::setw(14) << dimensions;
-      }
+      stream << "0x" << std::hex << std::setfill('0');
+      stream << std::setw(16) << info.state;
+      stream << " ";
+      stream << "0x" << std::setw(16) << info.signaled_mask;
+      stream << std::dec << std::setfill(' ');
+      stream << " ";
+      stream << std::setw(8) << info.index;
       stream << " ";
       stream << info.name;
       stream << std::endl;
@@ -184,6 +161,32 @@
     }
   }
 
+  stream << std::endl;
+  stream << "Orphaned Consumer Buffers:\n";
+  stream << std::right;
+  stream << std::setw(6) << "Id";
+  stream << " ";
+  stream << std::setw(14) << "Geometry";
+  stream << " ";
+  stream << "Name";
+  stream << std::endl;
+
+  for (const auto& channel : channels) {
+    BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+    // consumer_count is tracked by producer. When it's zero, producer must have
+    // already hung up and the consumer is orphaned.
+    if (channel->channel_type() == BufferHubChannel::kConsumerType &&
+        info.consumer_count == 0) {
+      stream << std::right;
+      stream << std::setw(6) << info.id;
+      stream << " ";
+
+      stream << std::setw(14) << "Orphaned.";
+      stream << (" channel_id=" + std::to_string(channel->channel_id()));
+      stream << std::endl;
+    }
+  }
+
   return stream.str();
 }
 
@@ -444,6 +447,7 @@
            "BufferHubChannel::SignalAvailable: channel_id=%d buffer_id=%d",
            channel_id(), buffer_id());
   if (!IsDetached()) {
+    signaled_ = true;
     const auto status = service_->ModifyChannelEvents(channel_id_, 0, POLLIN);
     ALOGE_IF(!status,
              "BufferHubChannel::SignalAvailable: failed to signal availability "
@@ -460,6 +464,7 @@
            "BufferHubChannel::ClearAvailable: channel_id=%d buffer_id=%d",
            channel_id(), buffer_id());
   if (!IsDetached()) {
+    signaled_ = false;
     const auto status = service_->ModifyChannelEvents(channel_id_, POLLIN, 0);
     ALOGE_IF(!status,
              "BufferHubChannel::ClearAvailable: failed to clear availability "
diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/buffer_hub.h
index b0df11f..270ac95 100644
--- a/services/vr/bufferhubd/buffer_hub.h
+++ b/services/vr/bufferhubd/buffer_hub.h
@@ -53,6 +53,10 @@
     uint32_t layer_count = 0;
     uint32_t format = 0;
     uint64_t usage = 0;
+    size_t pending_count = 0;
+    uint64_t state = 0;
+    uint64_t signaled_mask = 0;
+    uint64_t index = 0;
     std::string name;
 
     // Data filed for producer queue.
@@ -60,7 +64,9 @@
     UsagePolicy usage_policy{0, 0, 0, 0};
 
     BufferInfo(int id, size_t consumer_count, uint32_t width, uint32_t height,
-               uint32_t layer_count, uint32_t format, uint64_t usage, const std::string& name)
+               uint32_t layer_count, uint32_t format, uint64_t usage,
+               size_t pending_count, uint64_t state, uint64_t signaled_mask,
+               uint64_t index, const std::string& name)
         : id(id),
           type(kProducerType),
           consumer_count(consumer_count),
@@ -69,6 +75,10 @@
           layer_count(layer_count),
           format(format),
           usage(usage),
+          pending_count(pending_count),
+          state(state),
+          signaled_mask(signaled_mask),
+          index(index),
           name(name) {}
 
     BufferInfo(int id, size_t consumer_count, size_t capacity,
@@ -101,6 +111,8 @@
   int channel_id() const { return channel_id_; }
   bool IsDetached() const { return channel_id_ == kDetachedId; }
 
+  bool signaled() const { return signaled_; }
+
   void Detach() {
     if (channel_type_ == kProducerType)
       channel_id_ = kDetachedId;
@@ -124,6 +136,8 @@
   // buffer if it is detached and re-attached to another channel.
   int channel_id_;
 
+  bool signaled_;
+
   ChannelType channel_type_;
 
   BufferHubChannel(const BufferHubChannel&) = delete;
diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp
index 1613821..b27f218 100644
--- a/services/vr/bufferhubd/bufferhubd.cpp
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -2,6 +2,7 @@
 #include <unistd.h>
 
 #include <log/log.h>
+#include <sys/resource.h>
 
 #include <dvr/performance_client_api.h>
 #include <pdx/service_dispatcher.h>
@@ -16,6 +17,23 @@
   // We need to be able to create endpoints with full perms.
   umask(0000);
 
+  // Bump up the soft limit of open fd to the hard limit.
+  struct rlimit64 rlim;
+  ret = getrlimit64(RLIMIT_NOFILE, &rlim);
+  LOG_ALWAYS_FATAL_IF(ret != 0, "Failed to get nofile limit.");
+
+  ALOGI("Current nofile limit is %llu/%llu.", rlim.rlim_cur, rlim.rlim_max);
+  rlim.rlim_cur = rlim.rlim_max;
+  ret = setrlimit64(RLIMIT_NOFILE, &rlim);
+  ALOGE_IF(ret < 0, "Failed to set nofile limit, error=%s", strerror(errno));
+
+  rlim.rlim_cur = -1;
+  rlim.rlim_max = -1;
+  if (getrlimit64(RLIMIT_NOFILE, &rlim) < 0)
+    ALOGE("Failed to get nofile limit.");
+  else
+    ALOGI("New nofile limit is %llu/%llu.", rlim.rlim_cur, rlim.rlim_max);
+
   dispatcher = android::pdx::ServiceDispatcher::Create();
   CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher\n");
 
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
index ac6896a..a6d2dbb 100644
--- a/services/vr/bufferhubd/consumer_channel.cpp
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -19,9 +19,10 @@
 namespace dvr {
 
 ConsumerChannel::ConsumerChannel(BufferHubService* service, int buffer_id,
-                                 int channel_id,
+                                 int channel_id, uint64_t consumer_state_bit,
                                  const std::shared_ptr<Channel> producer)
     : BufferHubChannel(service, buffer_id, channel_id, kConsumerType),
+      consumer_state_bit_(consumer_state_bit),
       producer_(producer) {
   GetProducer()->AddConsumer(this);
 }
@@ -32,8 +33,6 @@
            channel_id(), buffer_id());
 
   if (auto producer = GetProducer()) {
-    if (!released_)  // Producer is waiting for our Release.
-      producer->OnConsumerIgnored();
     producer->RemoveConsumer(this);
   }
 }
@@ -43,6 +42,8 @@
   if (auto producer = GetProducer()) {
     // If producer has not hung up, copy most buffer info from the producer.
     info = producer->GetBufferInfo();
+  } else {
+    info.signaled_mask = consumer_state_bit();
   }
   info.id = buffer_id();
   return info;
@@ -55,6 +56,9 @@
 void ConsumerChannel::HandleImpulse(Message& message) {
   ATRACE_NAME("ConsumerChannel::HandleImpulse");
   switch (message.GetOp()) {
+    case BufferHubRPC::ConsumerAcquire::Opcode:
+      OnConsumerAcquire(message);
+      break;
     case BufferHubRPC::ConsumerRelease::Opcode:
       OnConsumerRelease(message, {});
       break;
@@ -70,7 +74,7 @@
   switch (message.GetOp()) {
     case BufferHubRPC::GetBuffer::Opcode:
       DispatchRemoteMethod<BufferHubRPC::GetBuffer>(
-          *producer, &ProducerChannel::OnGetBuffer, message);
+          *this, &ConsumerChannel::OnGetBuffer, message);
       return true;
 
     case BufferHubRPC::NewConsumer::Opcode:
@@ -98,9 +102,18 @@
   }
 }
 
-Status<std::pair<BorrowedFence, ConsumerChannel::MetaData>>
-ConsumerChannel::OnConsumerAcquire(Message& message,
-                                   std::size_t metadata_size) {
+Status<BufferDescription<BorrowedHandle>> ConsumerChannel::OnGetBuffer(
+    Message& /*message*/) {
+  ATRACE_NAME("ConsumerChannel::OnGetBuffer");
+  ALOGD_IF(TRACE, "ConsumerChannel::OnGetBuffer: buffer=%d", buffer_id());
+  if (auto producer = GetProducer()) {
+    return {producer->GetBuffer(consumer_state_bit_)};
+  } else {
+    return ErrorStatus(EPIPE);
+  }
+}
+
+Status<LocalFence> ConsumerChannel::OnConsumerAcquire(Message& message) {
   ATRACE_NAME("ConsumerChannel::OnConsumerAcquire");
   auto producer = GetProducer();
   if (!producer)
@@ -114,7 +127,7 @@
         producer->buffer_id());
     return ErrorStatus(EBUSY);
   } else {
-    auto status = producer->OnConsumerAcquire(message, metadata_size);
+    auto status = producer->OnConsumerAcquire(message);
     if (status) {
       ClearAvailable();
       acquired_ = true;
diff --git a/services/vr/bufferhubd/consumer_channel.h b/services/vr/bufferhubd/consumer_channel.h
index 208a002..55cf969 100644
--- a/services/vr/bufferhubd/consumer_channel.h
+++ b/services/vr/bufferhubd/consumer_channel.h
@@ -12,32 +12,35 @@
 // Consumer channels are attached to a Producer channel
 class ConsumerChannel : public BufferHubChannel {
  public:
+  using BorrowedHandle = pdx::BorrowedHandle;
   using Channel = pdx::Channel;
   using Message = pdx::Message;
 
   ConsumerChannel(BufferHubService* service, int buffer_id, int channel_id,
+                  uint64_t consumer_state_bit,
                   const std::shared_ptr<Channel> producer);
   ~ConsumerChannel() override;
 
   bool HandleMessage(Message& message) override;
   void HandleImpulse(Message& message) override;
 
+  uint64_t consumer_state_bit() const { return consumer_state_bit_; }
   BufferInfo GetBufferInfo() const override;
 
   bool OnProducerPosted();
   void OnProducerClosed();
 
  private:
-  using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
-
   std::shared_ptr<ProducerChannel> GetProducer() const;
 
-  pdx::Status<std::pair<BorrowedFence, MetaData>> OnConsumerAcquire(
-      Message& message, std::size_t metadata_size);
+  pdx::Status<BufferDescription<BorrowedHandle>> OnGetBuffer(Message& message);
+
+  pdx::Status<LocalFence> OnConsumerAcquire(Message& message);
   pdx::Status<void> OnConsumerRelease(Message& message,
                                       LocalFence release_fence);
   pdx::Status<void> OnConsumerSetIgnore(Message& message, bool ignore);
 
+  uint64_t consumer_state_bit_{0};
   bool acquired_{false};
   bool released_{true};
   bool ignored_{false};  // True if we are ignoring events.
diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp
index f447e00..4d43001 100644
--- a/services/vr/bufferhubd/consumer_queue_channel.cpp
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -15,10 +15,11 @@
 
 ConsumerQueueChannel::ConsumerQueueChannel(
     BufferHubService* service, int buffer_id, int channel_id,
-    const std::shared_ptr<Channel>& producer)
+    const std::shared_ptr<Channel>& producer, bool silent)
     : BufferHubChannel(service, buffer_id, channel_id, kConsumerQueueType),
       producer_(producer),
-      capacity_(0) {
+      capacity_(0),
+      silent_(silent) {
   GetProducer()->AddConsumer(this);
 }
 
@@ -83,23 +84,30 @@
 void ConsumerQueueChannel::RegisterNewBuffer(
     const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot) {
   ALOGD_IF(TRACE,
-           "ConsumerQueueChannel::RegisterNewBuffer: buffer_id=%d slot=%zu",
-           producer_channel->buffer_id(), slot);
-  pending_buffer_slots_.emplace(producer_channel, slot);
+           "ConsumerQueueChannel::RegisterNewBuffer: queue_id=%d buffer_id=%d "
+           "slot=%zu silent=%d",
+           buffer_id(), producer_channel->buffer_id(), slot, silent_);
+  // Only register buffers if the queue is not silent.
+  if (!silent_) {
+    pending_buffer_slots_.emplace(producer_channel, slot);
 
-  // Signal the client that there is new buffer available throught POLLIN.
-  SignalAvailable();
+    // Signal the client that there is new buffer available.
+    SignalAvailable();
+  }
 }
 
 Status<std::vector<std::pair<RemoteChannelHandle, size_t>>>
 ConsumerQueueChannel::OnConsumerQueueImportBuffers(Message& message) {
   std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
   ATRACE_NAME("ConsumerQueueChannel::OnConsumerQueueImportBuffers");
-  ALOGD_IF(
-      TRACE,
-      "ConsumerQueueChannel::OnConsumerQueueImportBuffers number of buffers to "
-      "import: %zu",
-      pending_buffer_slots_.size());
+  ALOGD_IF(TRACE,
+           "ConsumerQueueChannel::OnConsumerQueueImportBuffers: "
+           "pending_buffer_slots=%zu",
+           pending_buffer_slots_.size());
+
+  // Indicate this is a silent queue that will not import buffers.
+  if (silent_)
+    return ErrorStatus(EBADR);
 
   while (!pending_buffer_slots_.empty()) {
     auto producer_channel = pending_buffer_slots_.front().first.lock();
diff --git a/services/vr/bufferhubd/consumer_queue_channel.h b/services/vr/bufferhubd/consumer_queue_channel.h
index aa3f531..8437c4c 100644
--- a/services/vr/bufferhubd/consumer_queue_channel.h
+++ b/services/vr/bufferhubd/consumer_queue_channel.h
@@ -19,7 +19,7 @@
   using RemoteChannelHandle = pdx::RemoteChannelHandle;
 
   ConsumerQueueChannel(BufferHubService* service, int buffer_id, int channel_id,
-                       const std::shared_ptr<Channel>& producer);
+                       const std::shared_ptr<Channel>& producer, bool silent);
   ~ConsumerQueueChannel() override;
 
   bool HandleMessage(Message& message) override;
@@ -54,6 +54,10 @@
   // Tracks how many buffers have this queue imported.
   size_t capacity_;
 
+  // A silent queue does not signal or export buffers. It is only used to spawn
+  // another consumer queue.
+  bool silent_;
+
   ConsumerQueueChannel(const ConsumerQueueChannel&) = delete;
   void operator=(const ConsumerQueueChannel&) = delete;
 };
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index b2db795..716db5e 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -2,6 +2,8 @@
 
 #include <log/log.h>
 #include <sync/sync.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
 #include <sys/poll.h>
 #include <utils/Trace.h>
 
@@ -24,24 +26,88 @@
 namespace android {
 namespace dvr {
 
+namespace {
+
+static inline uint64_t FindNextClearedBit(uint64_t bits) {
+  return ~bits - (~bits & (~bits - 1));
+}
+
+}  // namespace
+
 ProducerChannel::ProducerChannel(BufferHubService* service, int channel_id,
                                  uint32_t width, uint32_t height,
                                  uint32_t layer_count, uint32_t format,
-                                 uint64_t usage, size_t meta_size_bytes,
+                                 uint64_t usage, size_t user_metadata_size,
                                  int* error)
     : BufferHubChannel(service, channel_id, channel_id, kProducerType),
       pending_consumers_(0),
       producer_owns_(true),
-      meta_size_bytes_(meta_size_bytes),
-      meta_(meta_size_bytes ? new uint8_t[meta_size_bytes] : nullptr) {
-  const int ret = buffer_.Alloc(width, height, layer_count, format, usage);
-  if (ret < 0) {
+      user_metadata_size_(user_metadata_size),
+      metadata_buf_size_(BufferHubDefs::kMetadataHeaderSize +
+                         user_metadata_size) {
+  if (int ret = buffer_.Alloc(width, height, layer_count, format, usage)) {
     ALOGE("ProducerChannel::ProducerChannel: Failed to allocate buffer: %s",
           strerror(-ret));
     *error = ret;
     return;
   }
 
+  if (int ret = metadata_buffer_.Alloc(metadata_buf_size_, /*height=*/1,
+                                       /*layer_count=*/1,
+                                       BufferHubDefs::kMetadataFormat,
+                                       BufferHubDefs::kMetadataUsage)) {
+    ALOGE("ProducerChannel::ProducerChannel: Failed to allocate metadata: %s",
+          strerror(-ret));
+    *error = ret;
+    return;
+  }
+
+  void* metadata_ptr = nullptr;
+  if (int ret = metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0,
+                                      /*y=*/0, metadata_buf_size_,
+                                      /*height=*/1, &metadata_ptr)) {
+    ALOGE("ProducerChannel::ProducerChannel: Failed to lock metadata.");
+    *error = -ret;
+    return;
+  }
+  metadata_header_ =
+      reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr);
+
+  // Using placement new here to reuse shared memory instead of new allocation
+  // and also initialize the value to zero.
+  buffer_state_ =
+      new (&metadata_header_->buffer_state) std::atomic<uint64_t>(0);
+  fence_state_ =
+      new (&metadata_header_->fence_state) std::atomic<uint64_t>(0);
+
+  acquire_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
+  release_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
+  if (!acquire_fence_fd_ || !release_fence_fd_) {
+    ALOGE("ProducerChannel::ProducerChannel: Failed to create shared fences.");
+    *error = -EIO;
+    return;
+  }
+
+  dummy_fence_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+  if (!dummy_fence_fd_) {
+    ALOGE("ProducerChannel::ProducerChannel: Failed to create dummy fences.");
+    *error = -EIO;
+    return;
+  }
+
+  epoll_event event;
+  event.events = 0;
+  event.data.u64 = 0ULL;
+  if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_ADD, dummy_fence_fd_.Get(),
+                &event) < 0) {
+    ALOGE(
+        "ProducerChannel::ProducerChannel: Failed to modify the shared "
+        "release fence to include the dummy fence: %s",
+        strerror(errno));
+    *error = -EIO;
+    return;
+  }
+
   // Success.
   *error = 0;
 }
@@ -49,11 +115,11 @@
 Status<std::shared_ptr<ProducerChannel>> ProducerChannel::Create(
     BufferHubService* service, int channel_id, uint32_t width, uint32_t height,
     uint32_t layer_count, uint32_t format, uint64_t usage,
-    size_t meta_size_bytes) {
+    size_t user_metadata_size) {
   int error;
   std::shared_ptr<ProducerChannel> producer(
       new ProducerChannel(service, channel_id, width, height, layer_count,
-                          format, usage, meta_size_bytes, &error));
+                          format, usage, user_metadata_size, &error));
   if (error < 0)
     return ErrorStatus(-error);
   else
@@ -62,16 +128,24 @@
 
 ProducerChannel::~ProducerChannel() {
   ALOGD_IF(TRACE,
-           "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d",
-           channel_id(), buffer_id());
+           "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d "
+           "state=%" PRIx64 ".",
+           channel_id(), buffer_id(), buffer_state_->load());
   for (auto consumer : consumer_channels_)
     consumer->OnProducerClosed();
 }
 
 BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
+  // Derive the mask of signaled buffers in this producer / consumer set.
+  uint64_t signaled_mask = signaled() ? BufferHubDefs::kProducerStateBit : 0;
+  for (const ConsumerChannel* consumer : consumer_channels_) {
+    signaled_mask |= consumer->signaled() ? consumer->consumer_state_bit() : 0;
+  }
+
   return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(),
                     buffer_.height(), buffer_.layer_count(), buffer_.format(),
-                    buffer_.usage(), name_);
+                    buffer_.usage(), pending_consumers_, buffer_state_->load(),
+                    signaled_mask, metadata_header_->queue_index, name_);
 }
 
 void ProducerChannel::HandleImpulse(Message& message) {
@@ -80,6 +154,9 @@
     case BufferHubRPC::ProducerGain::Opcode:
       OnProducerGain(message);
       break;
+    case BufferHubRPC::ProducerPost::Opcode:
+      OnProducerPost(message, {});
+      break;
   }
 }
 
@@ -121,16 +198,26 @@
   }
 }
 
-Status<NativeBufferHandle<BorrowedHandle>> ProducerChannel::OnGetBuffer(
+BufferDescription<BorrowedHandle> ProducerChannel::GetBuffer(
+    uint64_t buffer_state_bit) {
+  return {
+      buffer_,          metadata_buffer_,           buffer_id(),
+      buffer_state_bit, acquire_fence_fd_.Borrow(), release_fence_fd_.Borrow()};
+}
+
+Status<BufferDescription<BorrowedHandle>> ProducerChannel::OnGetBuffer(
     Message& /*message*/) {
   ATRACE_NAME("ProducerChannel::OnGetBuffer");
-  ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d", buffer_id());
-  return {NativeBufferHandle<BorrowedHandle>(buffer_, buffer_id())};
+  ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d, state=%" PRIx64 ".",
+           buffer_id(), buffer_state_->load());
+  return {GetBuffer(BufferHubDefs::kProducerStateBit)};
 }
 
 Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) {
   ATRACE_NAME("ProducerChannel::CreateConsumer");
-  ALOGD_IF(TRACE, "ProducerChannel::CreateConsumer: buffer_id=%d", buffer_id());
+  ALOGD_IF(TRACE,
+           "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d",
+           buffer_id(), producer_owns_);
 
   int channel_id;
   auto status = message.PushChannel(0, nullptr, &channel_id);
@@ -141,8 +228,21 @@
     return ErrorStatus(ENOMEM);
   }
 
-  auto consumer = std::make_shared<ConsumerChannel>(
-      service(), buffer_id(), channel_id, shared_from_this());
+  // Try find the next consumer state bit which has not been claimed by any
+  // consumer yet.
+  uint64_t consumer_state_bit = FindNextClearedBit(
+      active_consumer_bit_mask_ | orphaned_consumer_bit_mask_ |
+      BufferHubDefs::kProducerStateBit);
+  if (consumer_state_bit == 0ULL) {
+    ALOGE(
+        "ProducerChannel::CreateConsumer: reached the maximum mumber of "
+        "consumers per producer: 63.");
+    return ErrorStatus(E2BIG);
+  }
+
+  auto consumer =
+      std::make_shared<ConsumerChannel>(service(), buffer_id(), channel_id,
+                                        consumer_state_bit, shared_from_this());
   const auto channel_status = service()->SetChannel(channel_id, consumer);
   if (!channel_status) {
     ALOGE(
@@ -152,12 +252,14 @@
     return ErrorStatus(ENOMEM);
   }
 
-  if (!producer_owns_) {
+  if (!producer_owns_ &&
+      !BufferHubDefs::IsBufferReleased(buffer_state_->load())) {
     // Signal the new consumer when adding it to a posted producer.
     if (consumer->OnProducerPosted())
       pending_consumers_++;
   }
 
+  active_consumer_bit_mask_ |= consumer_state_bit;
   return {status.take()};
 }
 
@@ -168,8 +270,7 @@
 }
 
 Status<void> ProducerChannel::OnProducerPost(
-    Message&, LocalFence acquire_fence,
-    BufferWrapper<std::vector<std::uint8_t>> metadata) {
+    Message&, LocalFence acquire_fence) {
   ATRACE_NAME("ProducerChannel::OnProducerPost");
   ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
   if (!producer_owns_) {
@@ -177,27 +278,45 @@
     return ErrorStatus(EBUSY);
   }
 
-  if (meta_size_bytes_ != metadata.size()) {
-    ALOGD_IF(TRACE,
-             "ProducerChannel::OnProducerPost: Expected meta_size_bytes=%zu "
-             "got size=%zu",
-             meta_size_bytes_, metadata.size());
-    return ErrorStatus(EINVAL);
+  epoll_event event;
+  event.events = 0;
+  event.data.u64 = 0ULL;
+  int ret = epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
+                      dummy_fence_fd_.Get(), &event);
+  ALOGE_IF(ret < 0,
+      "ProducerChannel::OnProducerPost: Failed to modify the shared "
+      "release fence to include the dummy fence: %s",
+      strerror(errno));
+
+  eventfd_t dummy_fence_count = 0ULL;
+  if (eventfd_read(dummy_fence_fd_.Get(), &dummy_fence_count) < 0) {
+    const int error = errno;
+    if (error != EAGAIN) {
+      ALOGE(
+          "ProducerChannel::ProducerChannel: Failed to read dummy fence, "
+          "error: %s",
+          strerror(error));
+      return ErrorStatus(error);
+    }
   }
 
-  std::copy(metadata.begin(), metadata.end(), meta_.get());
+  ALOGW_IF(dummy_fence_count > 0,
+           "ProducerChannel::ProducerChannel: %" PRIu64
+           " dummy fence(s) was signaled during last release/gain cycle "
+           "buffer_id=%d.",
+           dummy_fence_count, buffer_id());
+
   post_fence_ = std::move(acquire_fence);
   producer_owns_ = false;
 
-  // Signal any interested consumers. If there are none, automatically release
-  // the buffer.
+  // Signal any interested consumers. If there are none, the buffer will stay
+  // in posted state until a consumer comes online. This behavior guarantees
+  // that no frame is silently dropped.
   pending_consumers_ = 0;
   for (auto consumer : consumer_channels_) {
     if (consumer->OnProducerPosted())
       pending_consumers_++;
   }
-  if (pending_consumers_ == 0)
-    SignalAvailable();
   ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
            pending_consumers_);
 
@@ -214,8 +333,13 @@
   }
 
   // There are still pending consumers, return busy.
-  if (pending_consumers_ > 0)
+  if (pending_consumers_ > 0) {
+    ALOGE(
+        "ProducerChannel::OnGain: Producer (id=%d) is gaining a buffer that "
+        "still has %d pending consumer(s).",
+        buffer_id(), pending_consumers_);
     return ErrorStatus(EBUSY);
+  }
 
   ClearAvailable();
   producer_owns_ = true;
@@ -223,9 +347,7 @@
   return {std::move(returned_fence_)};
 }
 
-Status<std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>>>
-ProducerChannel::OnConsumerAcquire(Message& /*message*/,
-                                   std::size_t metadata_size) {
+Status<LocalFence> ProducerChannel::OnConsumerAcquire(Message& /*message*/) {
   ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
   ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
            buffer_id());
@@ -236,12 +358,7 @@
 
   // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
   // Serialization just needs to read the handle.
-  if (metadata_size == 0)
-    return {std::make_pair(post_fence_.borrow(),
-                           WrapBuffer<std::uint8_t>(nullptr, 0))};
-  else
-    return {std::make_pair(post_fence_.borrow(),
-                           WrapBuffer(meta_.get(), meta_size_bytes_))};
+  return {std::move(post_fence_)};
 }
 
 Status<void> ProducerChannel::OnConsumerRelease(Message&,
@@ -273,17 +390,75 @@
   }
 
   OnConsumerIgnored();
+  if (pending_consumers_ == 0) {
+    // Clear the producer bit atomically to transit into released state. This
+    // has to done by BufferHub as it requries synchronization among all
+    // consumers.
+    BufferHubDefs::ModifyBufferState(buffer_state_,
+                                     BufferHubDefs::kProducerStateBit, 0ULL);
+    ALOGD_IF(TRACE,
+             "ProducerChannel::OnConsumerRelease: releasing last consumer: "
+             "buffer_id=%d state=%" PRIx64 ".",
+             buffer_id(), buffer_state_->load());
+
+    if (orphaned_consumer_bit_mask_) {
+      ALOGW(
+          "ProducerChannel::OnConsumerRelease: orphaned buffer detected "
+          "during the this acquire/release cycle: id=%d orphaned=0x%" PRIx64
+          " queue_index=%" PRIu64 ".",
+          buffer_id(), orphaned_consumer_bit_mask_,
+          metadata_header_->queue_index);
+      orphaned_consumer_bit_mask_ = 0;
+    }
+
+    SignalAvailable();
+  }
+
+  ALOGE_IF(pending_consumers_ &&
+               BufferHubDefs::IsBufferReleased(buffer_state_->load()),
+           "ProducerChannel::OnConsumerRelease: buffer state inconsistent: "
+           "pending_consumers=%d, buffer buffer is in releaed state.",
+           pending_consumers_);
   return {};
 }
 
 void ProducerChannel::OnConsumerIgnored() {
-  if (!--pending_consumers_)
-    SignalAvailable();
+  if (pending_consumers_ == 0) {
+    ALOGE("ProducerChannel::OnConsumerIgnored: no pending consumer.");
+    return;
+  }
+
+  --pending_consumers_;
   ALOGD_IF(TRACE,
            "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left",
            buffer_id(), pending_consumers_);
 }
 
+void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) {
+  // Ignore the orphaned consumer.
+  OnConsumerIgnored();
+
+  const uint64_t consumer_state_bit = channel->consumer_state_bit();
+  ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_bit,
+           "ProducerChannel::OnConsumerOrphaned: Consumer "
+           "(consumer_state_bit=%" PRIx64 ") is already orphaned.",
+           consumer_state_bit);
+  orphaned_consumer_bit_mask_ |= consumer_state_bit;
+
+  // Atomically clear the fence state bit as an orphaned consumer will never
+  // signal a release fence. Also clear the buffer state as it won't be released
+  // as well.
+  fence_state_->fetch_and(~consumer_state_bit);
+  BufferHubDefs::ModifyBufferState(buffer_state_, consumer_state_bit, 0ULL);
+
+  ALOGW(
+      "ProducerChannel::OnConsumerOrphaned: detected new orphaned consumer "
+      "buffer_id=%d consumer_state_bit=%" PRIx64 " queue_index=%" PRIu64
+      " buffer_state=%" PRIx64 " fence_state=%" PRIx64 ".",
+      buffer_id(), consumer_state_bit, metadata_header_->queue_index,
+      buffer_state_->load(), fence_state_->load());
+}
+
 Status<void> ProducerChannel::OnProducerMakePersistent(Message& message,
                                                        const std::string& name,
                                                        int user_id,
@@ -335,6 +510,40 @@
 void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) {
   consumer_channels_.erase(
       std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
+  active_consumer_bit_mask_ &= ~channel->consumer_state_bit();
+
+  const uint64_t buffer_state = buffer_state_->load();
+  if (BufferHubDefs::IsBufferPosted(buffer_state) ||
+      BufferHubDefs::IsBufferAcquired(buffer_state)) {
+    // The consumer client is being destoryed without releasing. This could
+    // happen in corner cases when the consumer crashes. Here we mark it
+    // orphaned before remove it from producer.
+    OnConsumerOrphaned(channel);
+  }
+
+  if (BufferHubDefs::IsBufferReleased(buffer_state) ||
+      BufferHubDefs::IsBufferGained(buffer_state)) {
+    // The consumer is being close while it is suppose to signal a release
+    // fence. Signal the dummy fence here.
+    if (fence_state_->load() & channel->consumer_state_bit()) {
+      epoll_event event;
+      event.events = EPOLLIN;
+      event.data.u64 = channel->consumer_state_bit();
+      if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
+                    dummy_fence_fd_.Get(), &event) < 0) {
+        ALOGE(
+            "ProducerChannel::RemoveConsumer: Failed to modify the shared "
+            "release fence to include the dummy fence: %s",
+            strerror(errno));
+        return;
+      }
+      ALOGW(
+          "ProducerChannel::RemoveConsumer: signal dummy release fence "
+          "buffer_id=%d",
+          buffer_id());
+      eventfd_write(dummy_fence_fd_.Get(), 1);
+    }
+  }
 }
 
 // Returns true if either the user or group ids match the owning ids or both
@@ -350,10 +559,12 @@
 // Returns true if the given parameters match the underlying buffer parameters.
 bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
                                       uint32_t layer_count, uint32_t format,
-                                      uint64_t usage, size_t meta_size_bytes) {
-  return meta_size_bytes == meta_size_bytes_ && buffer_.width() == width &&
-         buffer_.height() == height && buffer_.layer_count() == layer_count &&
-         buffer_.format() == format && buffer_.usage() == usage;
+                                      uint64_t usage,
+                                      size_t user_metadata_size) {
+  return user_metadata_size == user_metadata_size_ &&
+         buffer_.width() == width && buffer_.height() == height &&
+         buffer_.layer_count() == layer_count && buffer_.format() == format &&
+         buffer_.usage() == usage;
 }
 
 }  // namespace dvr
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/producer_channel.h
index 5ada478..e280f4d 100644
--- a/services/vr/bufferhubd/producer_channel.h
+++ b/services/vr/bufferhubd/producer_channel.h
@@ -33,7 +33,7 @@
   static pdx::Status<std::shared_ptr<ProducerChannel>> Create(
       BufferHubService* service, int channel_id, uint32_t width,
       uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage,
-      size_t meta_size_bytes);
+      size_t user_metadata_size);
 
   ~ProducerChannel() override;
 
@@ -42,24 +42,25 @@
 
   BufferInfo GetBufferInfo() const override;
 
-  pdx::Status<NativeBufferHandle<BorrowedHandle>> OnGetBuffer(Message& message);
+  BufferDescription<BorrowedHandle> GetBuffer(uint64_t buffer_state_bit);
 
   pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message);
   pdx::Status<RemoteChannelHandle> OnNewConsumer(Message& message);
 
-  pdx::Status<std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>>>
-  OnConsumerAcquire(Message& message, std::size_t metadata_size);
+  pdx::Status<LocalFence> OnConsumerAcquire(Message& message);
   pdx::Status<void> OnConsumerRelease(Message& message,
                                       LocalFence release_fence);
 
   void OnConsumerIgnored();
+  void OnConsumerOrphaned(ConsumerChannel* channel);
 
   void AddConsumer(ConsumerChannel* channel);
   void RemoveConsumer(ConsumerChannel* channel);
 
   bool CheckAccess(int euid, int egid);
   bool CheckParameters(uint32_t width, uint32_t height, uint32_t layer_count,
-                       uint32_t format, uint64_t usage, size_t meta_size_bytes);
+                       uint32_t format, uint64_t usage,
+                       size_t user_metadata_size);
 
   pdx::Status<void> OnProducerMakePersistent(Message& message,
                                              const std::string& name,
@@ -74,11 +75,28 @@
 
   IonBuffer buffer_;
 
+  // IonBuffer that is shared between bufferhubd, producer, and consumers.
+  IonBuffer metadata_buffer_;
+  BufferHubDefs::MetadataHeader* metadata_header_ = nullptr;
+  std::atomic<uint64_t>* buffer_state_ = nullptr;
+  std::atomic<uint64_t>* fence_state_ = nullptr;
+
+  // All active consumer bits. Valid bits are the lower 63 bits, while the
+  // highest bit is reserved for the producer and should not be set.
+  uint64_t active_consumer_bit_mask_{0ULL};
+  // All orphaned consumer bits. Valid bits are the lower 63 bits, while the
+  // highest bit is reserved for the producer and should not be set.
+  uint64_t orphaned_consumer_bit_mask_{0ULL};
+
   bool producer_owns_;
   LocalFence post_fence_;
   LocalFence returned_fence_;
-  size_t meta_size_bytes_;
-  std::unique_ptr<uint8_t[]> meta_;
+  size_t user_metadata_size_;  // size of user requested buffer buffer size.
+  size_t metadata_buf_size_;  // size of the ion buffer that holds metadata.
+
+  pdx::LocalHandle acquire_fence_fd_;
+  pdx::LocalHandle release_fence_fd_;
+  pdx::LocalHandle dummy_fence_fd_;
 
   static constexpr int kNoCheckId = -1;
   static constexpr int kUseCallerId = 0;
@@ -92,11 +110,10 @@
 
   ProducerChannel(BufferHubService* service, int channel, uint32_t width,
                   uint32_t height, uint32_t layer_count, uint32_t format,
-                  uint64_t usage, size_t meta_size_bytes, int* error);
+                  uint64_t usage, size_t user_metadata_size, int* error);
 
-  pdx::Status<void> OnProducerPost(
-      Message& message, LocalFence acquire_fence,
-      BufferWrapper<std::vector<std::uint8_t>> metadata);
+  pdx::Status<BufferDescription<BorrowedHandle>> OnGetBuffer(Message& message);
+  pdx::Status<void> OnProducerPost(Message& message, LocalFence acquire_fence);
   pdx::Status<LocalFence> OnProducerGain(Message& message);
 
   ProducerChannel(const ProducerChannel&) = delete;
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
index b8bb728..c0c48c2 100644
--- a/services/vr/bufferhubd/producer_queue_channel.cpp
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -7,8 +7,8 @@
 
 using android::pdx::ErrorStatus;
 using android::pdx::Message;
-using android::pdx::Status;
 using android::pdx::RemoteChannelHandle;
+using android::pdx::Status;
 using android::pdx::rpc::DispatchRemoteMethod;
 
 namespace android {
@@ -96,10 +96,12 @@
 }
 
 Status<RemoteChannelHandle> ProducerQueueChannel::OnCreateConsumerQueue(
-    Message& message) {
+    Message& message, bool silent) {
   ATRACE_NAME("ProducerQueueChannel::OnCreateConsumerQueue");
-  ALOGD_IF(TRACE, "ProducerQueueChannel::OnCreateConsumerQueue: channel_id=%d",
-           channel_id());
+  ALOGD_IF(
+      TRACE,
+      "ProducerQueueChannel::OnCreateConsumerQueue: channel_id=%d slient=%d",
+      channel_id(), silent);
 
   int channel_id;
   auto status = message.PushChannel(0, nullptr, &channel_id);
@@ -112,7 +114,7 @@
   }
 
   auto consumer_queue_channel = std::make_shared<ConsumerQueueChannel>(
-      service(), buffer_id(), channel_id, shared_from_this());
+      service(), buffer_id(), channel_id, shared_from_this(), silent);
 
   // Register the existing buffers with the new consumer queue.
   for (size_t slot = 0; slot < BufferHubRPC::kMaxQueueCapacity; slot++) {
@@ -222,7 +224,7 @@
 
   auto producer_channel_status =
       ProducerChannel::Create(service(), buffer_id, width, height, layer_count,
-                              format, usage, config_.meta_size_bytes);
+                              format, usage, config_.user_metadata_size);
   if (!producer_channel_status) {
     ALOGE(
         "ProducerQueueChannel::AllocateBuffer: Failed to create producer "
diff --git a/services/vr/bufferhubd/producer_queue_channel.h b/services/vr/bufferhubd/producer_queue_channel.h
index fd519c5..e825f47 100644
--- a/services/vr/bufferhubd/producer_queue_channel.h
+++ b/services/vr/bufferhubd/producer_queue_channel.h
@@ -26,7 +26,7 @@
   // Returns a handle for the service channel, as well as the size of the
   // metadata associated with the queue.
   pdx::Status<pdx::RemoteChannelHandle> OnCreateConsumerQueue(
-      pdx::Message& message);
+      pdx::Message& message, bool silent);
 
   pdx::Status<QueueInfo> OnGetQueueInfo(pdx::Message& message);
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index a346c0a..665a32b 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -640,11 +640,9 @@
         instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
 
     const VkSurfaceFormatKHR kWideColorFormats[] = {
-        {VK_FORMAT_R16G16B16A16_SFLOAT,
-         VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
-        {VK_FORMAT_R16G16B16A16_SFLOAT,
-         VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT},
-        {VK_FORMAT_A2R10G10B10_UNORM_PACK32,
+        {VK_FORMAT_R8G8B8A8_UNORM,
+         VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
+        {VK_FORMAT_R8G8B8A8_SRGB,
          VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
     };
     const uint32_t kNumWideColorFormats =