[automerger skipped] DO NOT MERGE ANYWHERE resolve merge conflicts of bcf5e2388e608e4ec4372d1eeb918bbe0e095b2e to oc-dr1-dev
am: 2ad06909c9 -s ours
am skip reason: subject contains skip directive

Change-Id: I08a81b0a363b7d31eb1bfbe1a64f24c2a78c8f67
diff --git a/Android.bp b/Android.bp
index cd05b21..de9ea86 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8,8 +8,15 @@
 
 subdirs = [
     "cmds/*",
+    "headers",
     "libs/*",
     "opengl",
     "services/*",
     "vulkan",
 ]
+
+cc_library_headers {
+    name: "libandroid_sensor_headers",
+    vendor: true,
+    export_include_dirs: ["include_sensor"],
+}
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/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 2d9a98c..9cdc9e9 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -124,6 +124,7 @@
         { OPT,      "events/sched/sched_waking/enable" },
         { OPT,      "events/sched/sched_blocked_reason/enable" },
         { OPT,      "events/sched/sched_cpu_hotplug/enable" },
+        { OPT,      "events/cgroup/enable" },
     } },
     { "irq",        "IRQ Events",   0, {
         { REQ,      "events/irq/enable" },
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 526fd19..3ea1d56 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -25,6 +25,8 @@
     chown root shell /sys/kernel/tracing/events/sched/sched_blocked_reason/enable
     chown root shell /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable
     chown root shell /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable
+    chown root shell /sys/kernel/debug/tracing/events/cgroup/enable
+    chown root shell /sys/kernel/tracing/events/cgroup/enable
     chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
     chown root shell /sys/kernel/tracing/events/power/cpu_frequency/enable
     chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable
@@ -77,6 +79,8 @@
     chmod 0664 /sys/kernel/tracing/events/sched/sched_blocked_reason/enable
     chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable
     chmod 0664 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/cgroup/enable
+    chmod 0664 /sys/kernel/tracing/events/cgroup/enable
     chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
     chmod 0664 /sys/kernel/tracing/events/power/cpu_frequency/enable
     chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index 7e05d72..014c995 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -35,12 +35,11 @@
 #include <fcntl.h>
 #include <sys/time.h>
 #include <errno.h>
+#include <memory>
 
 #include "selinux/selinux.h"
 #include "selinux/android.h"
 
-#include <UniquePtr.h>
-
 #define DEBUG 0
 
 using namespace android;
@@ -55,7 +54,7 @@
         freecon(p);
     }
 };
-typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext;
+typedef std::unique_ptr<char[], SecurityContext_Delete> Unique_SecurityContext;
 
 class MyShellCallback : public BnShellCallback
 {
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
new file mode 100644
index 0000000..f908cbf
--- /dev/null
+++ b/cmds/dumpstate/Android.bp
@@ -0,0 +1,130 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+    name: "dumpstate_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+}
+
+cc_library_headers {
+    name: "dumpstate_headers",
+    vendor_available: true,
+    export_include_dirs: ["."],
+    header_libs: [
+        "libbase_headers",
+        "libutils_headers",
+    ],
+    export_header_lib_headers: [
+        "libbase_headers",
+        "libutils_headers",
+    ],
+}
+
+cc_library_shared {
+    name: "libdumpstateutil",
+    defaults: ["dumpstate_defaults"],
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    header_libs: ["dumpstate_headers"],
+    export_header_lib_headers: ["dumpstate_headers"],
+    srcs: [
+        "DumpstateInternal.cpp",
+        "DumpstateUtil.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+}
+
+cc_library_shared {
+    name: "libdumpstateaidl",
+    defaults: ["dumpstate_defaults"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    aidl: {
+        local_include_dirs: ["binder"],
+        export_aidl_headers: true,
+    },
+    srcs: [
+        "binder/android/os/IDumpstate.aidl",
+        "binder/android/os/IDumpstateListener.aidl",
+        "binder/android/os/IDumpstateToken.aidl",
+    ],
+}
+
+cc_binary {
+    name: "dumpstate",
+    defaults: ["dumpstate_defaults"],
+    header_libs: ["dumpstate_headers"],
+    shared_libs: [
+        "android.hardware.dumpstate@1.0",
+        "libziparchive",
+        "libbase",
+        "libbinder",
+        "libcrypto",
+        "libcutils",
+        "libdebuggerd_client",
+        "libdumpstateaidl",
+        "libdumpstateutil",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+    ],
+    srcs: [
+        "DumpstateInternal.cpp",
+        "DumpstateService.cpp",
+        "utils.cpp",
+        "dumpstate.cpp",
+    ],
+    init_rc: ["dumpstate.rc"],
+}
+
+cc_test {
+    name: "dumpstate_test",
+    defaults: ["dumpstate_defaults"],
+    header_libs: ["dumpstate_headers"],
+    shared_libs: [
+        "libziparchive",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libdebuggerd_client",
+        "libdumpstateaidl",
+        "libdumpstateutil",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+    ],
+    srcs: [
+        "DumpstateInternal.cpp",
+        "DumpstateService.cpp",
+        "utils.cpp",
+        "tests/dumpstate_test.cpp",
+    ],
+    static_libs: ["libgmock"],
+}
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index a1b6a51..ea5fbf1 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,137 +1,5 @@
 LOCAL_PATH:= $(call my-dir)
 
-# ================#
-# Common settings #
-# ================#
-# ZipArchive support, the order matters here to get all symbols.
-COMMON_ZIP_LIBRARIES := libziparchive libz libcrypto
-
-# TODO: ideally the tests should depend on a shared dumpstate library, but currently libdumpstate
-# is used to define the device-specific HAL library. Instead, both dumpstate and dumpstate_test
-# shares a lot of common settings
-COMMON_LOCAL_CFLAGS := \
-       -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-COMMON_SRC_FILES := \
-        DumpstateInternal.cpp \
-        utils.cpp
-COMMON_SHARED_LIBRARIES := \
-        android.hardware.dumpstate@1.0 \
-        libhidlbase \
-        libhidltransport \
-        libbase \
-        libbinder \
-        libcutils \
-        libdebuggerd_client \
-        libdumpstateaidl \
-        libdumpstateutil \
-        liblog \
-        libselinux \
-        libutils \
-        $(COMMON_ZIP_LIBRARIES)
-
-# ====================#
-# libdumpstateutil #
-# ====================#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libdumpstateutil
-
-LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-LOCAL_SRC_FILES := \
-        DumpstateInternal.cpp \
-        DumpstateUtil.cpp
-LOCAL_SHARED_LIBRARIES := \
-        libbase \
-        liblog \
-
-include $(BUILD_SHARED_LIBRARY)
-
-# ====================#
-# libdumpstateheaders #
-# ====================#
-# TODO: this module is necessary so the device-specific libdumpstate implementations do not
-# need to add any other dependency (like libbase). Should go away once dumpstate HAL changes.
-include $(CLEAR_VARS)
-
-LOCAL_EXPORT_C_INCLUDE_DIRS = $(LOCAL_PATH)
-LOCAL_MODULE := libdumpstateheaders
-LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
-        $(COMMON_SHARED_LIBRARIES)
-LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \
-        $(COMMON_STATIC_LIBRARIES)
-# Soong requires that whats is on LOCAL_EXPORTED_ is also on LOCAL_
-LOCAL_SHARED_LIBRARIES := $(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS)
-LOCAL_STATIC_LIBRARIES := $(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS)
-
-include $(BUILD_STATIC_LIBRARY)
-
-# ================ #
-# libdumpstateaidl #
-# =================#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libdumpstateaidl
-
-LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
-
-LOCAL_SHARED_LIBRARIES := \
-        libbinder \
-        libutils
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/binder
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/binder
-LOCAL_SRC_FILES := \
-        binder/android/os/IDumpstate.aidl \
-        binder/android/os/IDumpstateListener.aidl \
-        binder/android/os/IDumpstateToken.aidl
-
-include $(BUILD_SHARED_LIBRARY)
-
-# ==========#
-# dumpstate #
-# ==========#
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
-        DumpstateService.cpp \
-        dumpstate.cpp
-
-LOCAL_MODULE := dumpstate
-
-LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
-
-LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES)
-
-LOCAL_CFLAGS += $(COMMON_LOCAL_CFLAGS)
-
-LOCAL_INIT_RC := dumpstate.rc
-
-include $(BUILD_EXECUTABLE)
-
-# ===============#
-# dumpstate_test #
-# ===============#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := dumpstate_test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
-
-LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
-        DumpstateService.cpp \
-        tests/dumpstate_test.cpp
-
-LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES) \
-        libgmock
-
-LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
-
-include $(BUILD_NATIVE_TEST)
-
 # =======================#
 # dumpstate_test_fixture #
 # =======================#
@@ -141,38 +9,14 @@
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+LOCAL_CFLAGS := \
+       -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
 
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_SRC_FILES := \
         tests/dumpstate_test_fixture.cpp
 
-LOCAL_MODULE_CLASS := NATIVE_TESTS
-
-dumpstate_tests_intermediates := $(local-intermediates-dir)/DATA
-dumpstate_tests_subpath_from_data := nativetest/dumpstate_test_fixture
-dumpstate_tests_root_in_device := /data/$(dumpstate_tests_subpath_from_data)
-dumpstate_tests_root_for_test_zip := $(dumpstate_tests_intermediates)/$(dumpstate_tests_subpath_from_data)
-testdata_files := $(call find-subdir-files, testdata/*)
-
-# Copy test data files to intermediates/DATA for use with LOCAL_PICKUP_FILES
-GEN := $(addprefix $(dumpstate_tests_root_for_test_zip)/, $(testdata_files))
-$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
-$(GEN): $(dumpstate_tests_root_for_test_zip)/testdata/% : $(LOCAL_PATH)/testdata/%
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-# Copy test data files again to $OUT/data so the tests can be run with adb sync
-# TODO: the build system should do this automatically
-GEN := $(addprefix $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/, $(testdata_files))
-$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
-$(GEN): $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/testdata/% : $(LOCAL_PATH)/testdata/%
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-LOCAL_PICKUP_FILES := $(dumpstate_tests_intermediates)
+LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, tests/testdata)
 
 include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 0343277..f0b6203 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -18,6 +18,8 @@
 
 #include "DumpstateInternal.h"
 
+#include <grp.h>
+#include <pwd.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -25,14 +27,14 @@
 #include <sys/prctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <cstdint>
 #include <string>
 #include <vector>
 
 #include <android-base/file.h>
-#include <cutils/log.h>
-#include <private/android_filesystem_config.h>
+#include <log/log.h>
 
 uint64_t Nanotime() {
     timespec ts;
@@ -42,7 +44,17 @@
 
 // Switches to non-root user and group.
 bool DropRootUser() {
-    if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
+    struct group* grp = getgrnam("shell");
+    gid_t shell_gid = grp != nullptr ? grp->gr_gid : 0;
+    struct passwd* pwd = getpwnam("shell");
+    uid_t shell_uid = pwd != nullptr ? pwd->pw_uid : 0;
+
+    if (!shell_gid || !shell_uid) {
+        MYLOGE("Unable to get AID_SHELL: %s\n", strerror(errno));
+        return false;
+    }
+
+    if (getgid() == shell_gid && getuid() == shell_uid) {
         MYLOGD("drop_root_user(): already running as Shell\n");
         return true;
     }
@@ -52,17 +64,28 @@
         return false;
     }
 
-    gid_t groups[] = {AID_LOG,  AID_SDCARD_R,     AID_SDCARD_RW, AID_MOUNT,
-                      AID_INET, AID_NET_BW_STATS, AID_READPROC,  AID_BLUETOOTH};
-    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
+    static const std::vector<std::string> group_names{
+        "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", "readproc", "bluetooth"};
+    std::vector<gid_t> groups(group_names.size(), 0);
+    for (size_t i = 0; i < group_names.size(); ++i) {
+        grp = getgrnam(group_names[i].c_str());
+        groups[i] = grp != nullptr ? grp->gr_gid : 0;
+        if (groups[i] == 0) {
+            MYLOGE("Unable to get required gid '%s': %s\n", group_names[i].c_str(),
+                   strerror(errno));
+            return false;
+        }
+    }
+
+    if (setgroups(groups.size(), groups.data()) != 0) {
         MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
         return false;
     }
-    if (setgid(AID_SHELL) != 0) {
+    if (setgid(shell_gid) != 0) {
         MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
         return false;
     }
-    if (setuid(AID_SHELL) != 0) {
+    if (setuid(shell_uid) != 0) {
         MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
         return false;
     }
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 26702c4..e866b8b 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -30,7 +30,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include "DumpstateInternal.h"
 
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 4161bd7..5f93e99 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -126,8 +126,7 @@
 static const std::string kDumpstateBoardPath = "/bugreports/";
 static const std::string kDumpstateBoardFiles[] = {
     "dumpstate_board.txt",
-    // TODO: rename to dumpstate_board.bin once vendors can handle it
-    "modem_log_all.tar"
+    "dumpstate_board.bin"
 };
 static const int NUM_OF_DUMPS = arraysize(kDumpstateBoardFiles);
 
@@ -145,10 +144,12 @@
  * Returns a vector of dump fds under |dir_path| with a given |file_prefix|.
  * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime|
  * is set, the vector only contains files that were written in the last 30 minutes.
+ * If |limit_by_count| is set, the vector only contains the ten latest files.
  */
 static std::vector<DumpData>* GetDumpFds(const std::string& dir_path,
                                          const std::string& file_prefix,
-                                         bool limit_by_mtime) {
+                                         bool limit_by_mtime,
+                                         bool limit_by_count = true) {
     const time_t thirty_minutes_ago = ds.now_ - 60 * 30;
 
     std::unique_ptr<std::vector<DumpData>> dump_data(new std::vector<DumpData>());
@@ -191,6 +192,10 @@
 
     std::sort(dump_data->begin(), dump_data->end());
 
+    if (limit_by_count && dump_data->size() > 10) {
+        dump_data->erase(dump_data->begin() + 10, dump_data->end());
+    }
+
     return dump_data.release();
 }
 
@@ -930,8 +935,10 @@
                  "VM TRACES AT LAST ANR", add_to_zip);
 
         if (anr_data->size() > 1) {
+            // NOTE: Historical ANRs are always added as separate entries in the
+            // bugreport zip file.
             AddDumps(anr_data->begin() + 1, anr_data->end(),
-                     "HISTORICAL ANR", add_to_zip);
+                     "HISTORICAL ANR", true /* add_to_zip */);
         }
     } else {
         printf("*** NO ANRs to dump in %s\n\n", ANR_DIR.c_str());
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index f02303b..7757c1e 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -26,10 +26,11 @@
 #include <vector>
 
 #include <android-base/macros.h>
+#include <android/os/IDumpstateListener.h>
+#include <utils/StrongPointer.h>
 #include <ziparchive/zip_writer.h>
 
 #include "DumpstateUtil.h"
-#include "android/os/BnDumpstate.h"
 
 // Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
 // std::vector<std::string>
@@ -49,6 +50,8 @@
 }  // namespace os
 }  // namespace android
 
+class ZipWriter;
+
 // TODO: remove once moved to HAL
 #ifdef __cplusplus
 extern "C" {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 1c19268..a94cf99 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -94,7 +94,7 @@
   protected:
     const std::string kTestPath = dirname(android::base::GetExecutablePath().c_str());
     const std::string kFixturesPath = kTestPath + "/../dumpstate_test_fixture/";
-    const std::string kTestDataPath = kFixturesPath + "/testdata/";
+    const std::string kTestDataPath = kFixturesPath + "tests/testdata/";
     const std::string kSimpleCommand = kFixturesPath + "dumpstate_test_fixture";
     const std::string kEchoCommand = "/system/bin/echo";
 
diff --git a/cmds/dumpstate/testdata/empty-file.txt b/cmds/dumpstate/tests/testdata/empty-file.txt
similarity index 100%
rename from cmds/dumpstate/testdata/empty-file.txt
rename to cmds/dumpstate/tests/testdata/empty-file.txt
diff --git a/cmds/dumpstate/testdata/multiple-lines-with-newline.txt b/cmds/dumpstate/tests/testdata/multiple-lines-with-newline.txt
similarity index 100%
rename from cmds/dumpstate/testdata/multiple-lines-with-newline.txt
rename to cmds/dumpstate/tests/testdata/multiple-lines-with-newline.txt
diff --git a/cmds/dumpstate/testdata/multiple-lines.txt b/cmds/dumpstate/tests/testdata/multiple-lines.txt
similarity index 100%
rename from cmds/dumpstate/testdata/multiple-lines.txt
rename to cmds/dumpstate/tests/testdata/multiple-lines.txt
diff --git a/cmds/dumpstate/testdata/single-line-with-newline.txt b/cmds/dumpstate/tests/testdata/single-line-with-newline.txt
similarity index 100%
rename from cmds/dumpstate/testdata/single-line-with-newline.txt
rename to cmds/dumpstate/tests/testdata/single-line-with-newline.txt
diff --git a/cmds/dumpstate/testdata/single-line.txt b/cmds/dumpstate/tests/testdata/single-line.txt
similarity index 100%
rename from cmds/dumpstate/testdata/single-line.txt
rename to cmds/dumpstate/tests/testdata/single-line.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt b/cmds/dumpstate/tests/testdata/stats-invalid-1st-NAN.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-1st-NAN.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt b/cmds/dumpstate/tests/testdata/stats-invalid-1st-negative.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-1st-negative.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt b/cmds/dumpstate/tests/testdata/stats-invalid-1st-too-big.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-1st-too-big.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-NAN.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-2nd-NAN.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-negative.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-2nd-negative.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-too-big.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-2nd-too-big.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt b/cmds/dumpstate/tests/testdata/stats-invalid-both-NAN.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-both-NAN.txt
diff --git a/cmds/dumpstate/testdata/stats-one-run-no-newline.txt b/cmds/dumpstate/tests/testdata/stats-one-run-no-newline.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-one-run-no-newline.txt
rename to cmds/dumpstate/tests/testdata/stats-one-run-no-newline.txt
diff --git a/cmds/dumpstate/testdata/stats-two-runs.txt b/cmds/dumpstate/tests/testdata/stats-two-runs.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-two-runs.txt
rename to cmds/dumpstate/tests/testdata/stats-two-runs.txt
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index f0e7200..fa6f6df 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -16,6 +16,7 @@
 
 #include <algorithm>
 #include <chrono>
+#include <iomanip>
 #include <thread>
 
 #include <android-base/file.h>
@@ -282,7 +283,14 @@
               std::chrono::duration<double> elapsed_seconds =
                   std::chrono::steady_clock::now() - start;
               aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
-                   << "was the duration of dumpsys " << service_name << endl;
+                   << "was the duration of dumpsys " << service_name;
+
+              using std::chrono::system_clock;
+              const auto finish = system_clock::to_time_t(system_clock::now());
+              std::tm finish_tm;
+              localtime_r(&finish, &finish_tm);
+              aout << ", ending at: " << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S")
+                   << endl;
             }
         } else {
             aerr << "Can't find service: " << service_name << endl;
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 66beb6d..cfd53e5 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -35,6 +35,7 @@
 using ::testing::Eq;
 using ::testing::HasSubstr;
 using ::testing::MakeAction;
+using ::testing::Mock;
 using ::testing::Not;
 using ::testing::Return;
 using ::testing::StrEq;
@@ -155,10 +156,11 @@
             .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
     }
 
-    void ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) {
+    sp<BinderMock> ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) {
         sp<BinderMock> binder_mock = ExpectCheckService(name);
         EXPECT_CALL(*binder_mock, dump(_, _))
             .WillRepeatedly(DoAll(Sleep(timeout_s), WithArg<0>(WriteOnFd(output)), Return(0)));
+        return binder_mock;
     }
 
     void CallMain(const std::vector<std::string>& args) {
@@ -245,15 +247,15 @@
 
 // Tests 'dumpsys -t 1 service_name' on a service that times out after 2s
 TEST_F(DumpsysTest, DumpRunningServiceTimeout) {
-    ExpectDumpAndHang("Valet", 2, "Here's your car");
+    sp<BinderMock> binder_mock = ExpectDumpAndHang("Valet", 2, "Here's your car");
 
     CallMain({"-t", "1", "Valet"});
 
     AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1s) EXPIRED");
     AssertNotDumped("Here's your car");
 
-    // Must wait so binder mock is deleted, otherwise test will fail with a leaked object
-    sleep(1);
+    // TODO(b/65056227): BinderMock is not destructed because thread is detached on dumpsys.cpp
+    Mock::AllowLeak(binder_mock.get());
 }
 
 // Tests 'dumpsys service_name Y U NO HAVE ARGS' on a service that is running
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp
index e293948..3eb39b9 100644
--- a/cmds/installd/CacheTracker.cpp
+++ b/cmds/installd/CacheTracker.cpp
@@ -148,6 +148,7 @@
         }
 
         // Bubble up modified time to parent
+        CHECK(p != nullptr);
         switch (p->fts_info) {
         case FTS_DP:
         case FTS_DEFAULT:
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e3021d8..8a79ee1 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -311,6 +311,7 @@
         return -1;
     }
 
+#if APPLY_HARD_QUOTAS
     if ((dq.dqb_bhardlimit == 0) || (dq.dqb_ihardlimit == 0)) {
         auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
         struct statvfs stat;
@@ -320,7 +321,8 @@
         }
 
         dq.dqb_valid = QIF_LIMITS;
-        dq.dqb_bhardlimit = (((stat.f_blocks * stat.f_frsize) / 10) * 9) / QIF_DQBLKSIZE;
+        dq.dqb_bhardlimit =
+            (((static_cast<uint64_t>(stat.f_blocks) * stat.f_frsize) / 10) * 9) / QIF_DQBLKSIZE;
         dq.dqb_ihardlimit = (stat.f_files / 2);
         if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), device.c_str(), uid,
                 reinterpret_cast<char*>(&dq)) != 0) {
@@ -334,6 +336,10 @@
         // Hard quota already set; assume it's reasonable
         return 0;
     }
+#else
+    // Hard quotas disabled
+    return 0;
+#endif
 }
 
 binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
@@ -1641,6 +1647,7 @@
     int64_t videoSize = 0;
     int64_t imageSize = 0;
     int64_t appSize = 0;
+    int64_t obbSize = 0;
 
     auto device = findQuotaDeviceForUuid(uuid);
     if (device.empty()) {
@@ -1688,6 +1695,13 @@
 #endif
             imageSize = dq.dqb_curspace;
         }
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), AID_MEDIA_OBB,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+            LOG(DEBUG) << "quotactl() for GID " << AID_MEDIA_OBB << " " << dq.dqb_curspace;
+#endif
+            obbSize = dq.dqb_curspace;
+        }
         ATRACE_END();
 
         ATRACE_BEGIN("apps");
@@ -1744,6 +1758,11 @@
         }
         fts_close(fts);
         ATRACE_END();
+
+        ATRACE_BEGIN("obb");
+        auto obbPath = create_data_media_obb_path(uuid_, "");
+        calculate_tree_size(obbPath, &obbSize);
+        ATRACE_END();
     }
 
     std::vector<int64_t> ret;
@@ -1752,6 +1771,7 @@
     ret.push_back(videoSize);
     ret.push_back(imageSize);
     ret.push_back(appSize);
+    ret.push_back(obbSize);
 #if MEASURE_DEBUG
     LOG(DEBUG) << "Final result " << toString(ret);
 #endif
@@ -1811,8 +1831,8 @@
         const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
         int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
         const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
-        const std::unique_ptr<std::string>& sharedLibraries,
-        const std::unique_ptr<std::string>& seInfo) {
+        const std::unique_ptr<std::string>& classLoaderContext,
+        const std::unique_ptr<std::string>& seInfo, bool downgrade) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     if (packageName && *packageName != "*") {
@@ -1826,10 +1846,11 @@
     const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
     const char* compiler_filter = compilerFilter.c_str();
     const char* volume_uuid = uuid ? uuid->c_str() : nullptr;
-    const char* shared_libraries = sharedLibraries ? sharedLibraries->c_str() : nullptr;
+    const char* class_loader_context = classLoaderContext ? classLoaderContext->c_str() : nullptr;
     const char* se_info = seInfo ? seInfo->c_str() : nullptr;
     int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded,
-            oat_dir, dexFlags, compiler_filter, volume_uuid, shared_libraries, se_info);
+            oat_dir, dexFlags, compiler_filter, volume_uuid, class_loader_context, se_info,
+            downgrade);
     return res ? error(res, "Failed to dexopt") : ok();
 }
 
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 5756b82..c8db3df 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -83,8 +83,8 @@
             const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
             int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
             const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
-            const std::unique_ptr<std::string>& sharedLibraries,
-            const std::unique_ptr<std::string>& seInfo);
+            const std::unique_ptr<std::string>& classLoaderContext,
+            const std::unique_ptr<std::string>& seInfo, bool downgrade);
 
     binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
 
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index c8e76b0..452a2b1 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -51,7 +51,7 @@
             @nullable @utf8InCpp String outputPath, int dexFlags,
             @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
             @nullable @utf8InCpp String sharedLibraries,
-            @nullable @utf8InCpp String seInfo);
+            @nullable @utf8InCpp String seInfo, boolean downgrade);
 
     void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
 
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 685fdd8..f29da17 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -191,7 +191,7 @@
 static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vdex_fd, int image_fd,
         const char* input_file_name, const char* output_file_name, int swap_fd,
         const char* instruction_set, const char* compiler_filter,
-        bool debuggable, bool post_bootcomplete, int profile_fd, const char* shared_libraries) {
+        bool debuggable, bool post_bootcomplete, int profile_fd, const char* class_loader_context) {
     static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
 
     if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
@@ -291,6 +291,12 @@
     char dex2oat_swap_fd[arraysize("--swap-fd=") + MAX_INT_LEN];
     bool have_dex2oat_image_fd = false;
     char dex2oat_image_fd[arraysize("--app-image-fd=") + MAX_INT_LEN];
+    size_t class_loader_context_size = arraysize("--class-loader-context=") + PKG_PATH_MAX;
+    char class_loader_context_arg[class_loader_context_size];
+    if (class_loader_context != nullptr) {
+        snprintf(class_loader_context_arg, class_loader_context_size, "--class-loader-context=%s",
+            class_loader_context);
+    }
 
     sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
     sprintf(zip_location_arg, "--zip-location=%s", relative_input_file_name);
@@ -387,7 +393,7 @@
                      + (have_app_image_format ? 1 : 0)
                      + dex2oat_flags_count
                      + (profile_fd == -1 ? 0 : 1)
-                     + (shared_libraries != nullptr ? 4 : 0)
+                     + (class_loader_context != nullptr ? 1 : 0)
                      + (has_base_dir ? 1 : 0)
                      + (have_dex2oat_large_app_threshold ? 1 : 0)];
     int i = 0;
@@ -447,15 +453,13 @@
     if (profile_fd != -1) {
         argv[i++] = profile_arg;
     }
-    if (shared_libraries != nullptr) {
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = "-classpath";
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = shared_libraries;
-    }
     if (has_base_dir) {
         argv[i++] = base_dir;
     }
+    if (class_loader_context != nullptr) {
+        argv[i++] = class_loader_context_arg;
+    }
+
     // Do not add after dex2oat_flags, they should override others for debugging.
     argv[i] = NULL;
 
@@ -1133,10 +1137,9 @@
 // (re)Creates the app image if needed.
 Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path, bool profile_guided,
         bool is_public, int uid, bool is_secondary_dex) {
-    // Use app images only if it is enabled (by a set image format) and we are compiling
-    // profile-guided (so the app image doesn't conservatively contain all classes).
-    // Note that we don't create an image for secondary dex files.
-    if (is_secondary_dex || !profile_guided) {
+
+    // We don't create an image for secondary dex files.
+    if (is_secondary_dex) {
         return Dex2oatFileWrapper();
     }
 
@@ -1145,6 +1148,14 @@
         // Happens when the out_oat_path has an unknown extension.
         return Dex2oatFileWrapper();
     }
+
+    // Use app images only if it is enabled (by a set image format) and we are compiling
+    // profile-guided (so the app image doesn't conservatively contain all classes).
+    if (!profile_guided) {
+        // In case there is a stale image, remove it now. Ignore any error.
+        unlink(image_path.c_str());
+        return Dex2oatFileWrapper();
+    }
     char app_image_format[kPropertyValueMax];
     bool have_app_image_format =
             get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
@@ -1359,7 +1370,7 @@
 // If this is for a profile guided compilation, profile_was_updated will tell whether or not
 // the profile has changed.
 static void exec_dexoptanalyzer(const std::string& dex_file, const std::string& instruction_set,
-        const std::string& compiler_filter, bool profile_was_updated) {
+        const std::string& compiler_filter, bool profile_was_updated, bool downgrade) {
     static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer";
     static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
 
@@ -1373,9 +1384,13 @@
     std::string isa_arg = "--isa=" + instruction_set;
     std::string compiler_filter_arg = "--compiler-filter=" + compiler_filter;
     const char* assume_profile_changed = "--assume-profile-changed";
+    const char* downgrade_flag = "--downgrade";
 
     // program name, dex file, isa, filter, the final NULL
-    const char* argv[5 + (profile_was_updated ? 1 : 0)];
+    const int argc = 5 +
+        (profile_was_updated ? 1 : 0) +
+        (downgrade ? 1 : 0);
+    const char* argv[argc];
     int i = 0;
     argv[i++] = DEXOPTANALYZER_BIN;
     argv[i++] = dex_file_arg.c_str();
@@ -1384,6 +1399,9 @@
     if (profile_was_updated) {
         argv[i++] = assume_profile_changed;
     }
+    if (downgrade) {
+        argv[i++] = downgrade_flag;
+    }
     argv[i] = NULL;
 
     execv(DEXOPTANALYZER_BIN, (char * const *)argv);
@@ -1464,7 +1482,7 @@
 static bool process_secondary_dex_dexopt(const char* original_dex_path, const char* pkgname,
         int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
         const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out,
-        std::string* oat_dir_out, std::string* dex_path_out) {
+        std::string* oat_dir_out, std::string* dex_path_out, bool downgrade) {
     int storage_flag;
 
     if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
@@ -1533,7 +1551,8 @@
         // child -- drop privileges before continuing.
         drop_capabilities(uid);
         // Run dexoptanalyzer to get dexopt_needed code.
-        exec_dexoptanalyzer(dex_path, instruction_set, compiler_filter, profile_was_updated);
+        exec_dexoptanalyzer(dex_path, instruction_set, compiler_filter, profile_was_updated,
+                            downgrade);
         exit(DEXOPTANALYZER_BIN_EXEC_ERROR);
     }
 
@@ -1560,7 +1579,8 @@
 
 int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set,
         int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
-        const char* volume_uuid, const char* shared_libraries, const char* se_info) {
+        const char* volume_uuid, const char* class_loader_context, const char* se_info,
+        bool downgrade) {
     CHECK(pkgname != nullptr);
     CHECK(pkgname[0] != 0);
     if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
@@ -1568,7 +1588,12 @@
     }
 
     if (!validate_dex_path_size(dex_path)) {
-        return false;
+        return -1;
+    }
+
+    if (class_loader_context != nullptr && strlen(class_loader_context) > PKG_PATH_MAX) {
+        LOG(ERROR) << "Class loader context exceeds the allowed size: " << class_loader_context;
+        return -1;
     }
 
     bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0;
@@ -1583,7 +1608,8 @@
     if (is_secondary_dex) {
         if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid,
                 instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str,
-                &dex_real_path)) {
+                &dex_real_path,
+                downgrade)) {
             oat_dir = oat_dir_str.c_str();
             dex_path = dex_real_path.c_str();
             if (dexopt_needed == NO_DEXOPT_NEEDED) {
@@ -1672,7 +1698,7 @@
                     debuggable,
                     boot_complete,
                     reference_profile_fd.get(),
-                    shared_libraries);
+                    class_loader_context);
         _exit(68);   /* only get here on exec failure */
     } else {
         int res = wait_child(pid);
@@ -1770,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;
     }
@@ -1785,37 +1817,56 @@
         return false;
     }
 
-    // The secondary dex does not exist anymore. Clear any generated files.
-    char oat_path[PKG_PATH_MAX];
-    char oat_dir[PKG_PATH_MAX];
-    char oat_isa_dir[PKG_PATH_MAX];
-    bool result = true;
-    for (size_t i = 0; i < isas.size(); i++) {
-        if (!create_secondary_dex_oat_layout(dex_path, isas[i], oat_dir, oat_isa_dir, oat_path)) {
-            LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
-            result = false;
-            continue;
-        }
+    // As a security measure we want to unlink art artifacts with the reduced capabilities
+    // of the package user id. So we fork and drop capabilities in the child.
+    pid_t pid = fork();
+    if (pid == 0) {
+        // The secondary dex does not exist anymore. Clear any generated files.
+        char oat_path[PKG_PATH_MAX];
+        char oat_dir[PKG_PATH_MAX];
+        char oat_isa_dir[PKG_PATH_MAX];
+        bool result = true;
+        /* child -- drop privileges before continuing */
+        drop_capabilities(uid);
+        for (size_t i = 0; i < isas.size(); i++) {
+            if (!create_secondary_dex_oat_layout(dex_path,
+                                                 isas[i],
+                                                 oat_dir,
+                                                 oat_isa_dir,
+                                                 oat_path)) {
+                LOG(ERROR) << "Could not create secondary odex layout: "
+                           << dex_path;
+                result = false;
+                continue;
+            }
 
-        // Delete oat/vdex/art files.
-        result = unlink_if_exists(oat_path) && result;
-        result = unlink_if_exists(create_vdex_filename(oat_path)) && result;
-        result = unlink_if_exists(create_image_filename(oat_path)) && result;
+            // Delete oat/vdex/art files.
+            result = unlink_if_exists(oat_path) && result;
+            result = unlink_if_exists(create_vdex_filename(oat_path)) && result;
+            result = unlink_if_exists(create_image_filename(oat_path)) && result;
 
-        // Delete profiles.
-        std::string current_profile = create_current_profile_path(
+            // Delete profiles.
+            std::string current_profile = create_current_profile_path(
                 multiuser_get_user_id(uid), dex_path, /*is_secondary*/true);
-        std::string reference_profile = create_reference_profile_path(
+            std::string reference_profile = create_reference_profile_path(
                 dex_path, /*is_secondary*/true);
-        result = unlink_if_exists(current_profile) && result;
-        result = unlink_if_exists(reference_profile) && result;
+            result = unlink_if_exists(current_profile) && result;
+            result = unlink_if_exists(reference_profile) && result;
 
-        // Try removing the directories as well, they might be empty.
-        result = rmdir_if_empty(oat_isa_dir) && result;
-        result = rmdir_if_empty(oat_dir) && result;
+            // We upgraded once the location of current profile for secondary dex files.
+            // Check for any previous left-overs and remove them as well.
+            std::string old_current_profile = dex_path + ".prof";
+            result = unlink_if_exists(old_current_profile);
+
+            // Try removing the directories as well, they might be empty.
+            result = rmdir_if_empty(oat_isa_dir) && result;
+            result = rmdir_if_empty(oat_dir) && result;
+        }
+        result ? _exit(0) : _exit(1);
     }
 
-    return result;
+    int return_code = wait_child(pid);
+    return return_code == 0;
 }
 
 // Helper for move_ab, so that we can have common failure-case cleanup.
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index d171ee5..23446da 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -63,7 +63,8 @@
 
 int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
         int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
-        const char* volume_uuid, const char* shared_libraries, const char* se_info);
+        const char* volume_uuid, const char* class_loader_context, const char* se_info,
+        bool downgrade);
 
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 68cb0d7..09e1a00 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -178,6 +178,7 @@
         const char* volume_uuid;
         const char* shared_libraries;
         const char* se_info;
+        bool downgrade;
     };
 
     bool ReadSystemProperties() {
@@ -281,6 +282,13 @@
         return true;
     }
 
+    bool ParseBool(const char* in) {
+        if (strcmp(in, "true") == 0) {
+            return true;
+        }
+        return false;
+    }
+
     bool ParseUInt(const char* in, uint32_t* out) {
         char* end;
         long long int result = strtoll(in, &end, 0);
@@ -349,6 +357,8 @@
         switch (version) {
             case 2:
                 return ReadArgumentsV2(argc, argv, true);
+            case 3:
+                return ReadArgumentsV3(argc, argv);
 
             default:
                 LOG(ERROR) << "Unsupported version " << version;
@@ -427,6 +437,10 @@
             }
         }
 
+        // Set downgrade to false. It is only relevant when downgrading compiler
+        // filter, which is not the case during ota.
+        package_parameters_.downgrade = false;
+
         if (param_index != 11) {
             LOG(ERROR) << "Not enough parameters";
             return false;
@@ -435,6 +449,89 @@
         return true;
     }
 
+    bool ReadArgumentsV3(int argc ATTRIBUTE_UNUSED, char** argv) {
+        size_t dexopt_index = 3;
+
+        // Check for "dexopt".
+        if (argv[dexopt_index] == nullptr) {
+            LOG(ERROR) << "Missing parameters";
+            return false;
+        }
+        if (std::string("dexopt").compare(argv[dexopt_index]) != 0) {
+            LOG(ERROR) << "Expected \"dexopt\"";
+            return false;
+        }
+
+        size_t param_index = 0;
+        for (;; ++param_index) {
+            const char* param = argv[dexopt_index + 1 + param_index];
+            if (param == nullptr) {
+                break;
+            }
+
+            switch (param_index) {
+                case 0:
+                    package_parameters_.apk_path = param;
+                    break;
+
+                case 1:
+                    package_parameters_.uid = atoi(param);
+                    break;
+
+                case 2:
+                    package_parameters_.pkgName = param;
+                    break;
+
+                case 3:
+                    package_parameters_.instruction_set = param;
+                    break;
+
+                case 4:
+                    package_parameters_.dexopt_needed = atoi(param);
+                    break;
+
+                case 5:
+                    package_parameters_.oat_dir = param;
+                    break;
+
+                case 6:
+                    package_parameters_.dexopt_flags = atoi(param);
+                    break;
+
+                case 7:
+                    package_parameters_.compiler_filter = param;
+                    break;
+
+                case 8:
+                    package_parameters_.volume_uuid = ParseNull(param);
+                    break;
+
+                case 9:
+                    package_parameters_.shared_libraries = ParseNull(param);
+                    break;
+
+                case 10:
+                    package_parameters_.se_info = ParseNull(param);
+                    break;
+
+                case 11:
+                    package_parameters_.downgrade = ParseBool(param);
+                    break;
+
+                default:
+                    LOG(ERROR) << "Too many arguments, got " << param;
+                    return false;
+            }
+        }
+
+        if (param_index != 12) {
+            LOG(ERROR) << "Not enough parameters";
+            return false;
+        }
+
+        return true;
+    }
+
     static int ReplaceMask(int input, int old_mask, int new_mask) {
         return (input & old_mask) != 0 ? new_mask : 0;
     }
@@ -534,6 +631,10 @@
         // receive from a v1 A side.
         package_parameters_.se_info = nullptr;
 
+        // Set downgrade to false. It is only relevant when downgrading compiler
+        // filter, which is not the case during ota.
+        package_parameters_.downgrade = false;
+
         return true;
     }
 
@@ -753,10 +854,6 @@
     }
 
     static const char* ParseNull(const char* arg) {
-        // b/38186355. Revert soon.
-        if (strcmp(arg, "!null") == 0) {
-            return nullptr;
-        }
         return (strcmp(arg, "!") == 0) ? nullptr : arg;
     }
 
@@ -823,7 +920,8 @@
                       package_parameters_.compiler_filter,
                       package_parameters_.volume_uuid,
                       package_parameters_.shared_libraries,
-                      package_parameters_.se_info);
+                      package_parameters_.se_info,
+                      package_parameters_.downgrade);
     }
 
     int RunPreopt() {
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index aed068c..2d58515 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -99,7 +99,7 @@
 static int64_t free() {
     struct statvfs buf;
     if (!statvfs("/data/local/tmp", &buf)) {
-        return buf.f_bavail * buf.f_frsize;
+        return static_cast<int64_t>(buf.f_bavail) * buf.f_frsize;
     } else {
         PLOG(ERROR) << "Failed to statvfs";
         return -1;
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index dab3236..46ed85f 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -38,16 +38,6 @@
 
 #define TEST_PROFILE_DIR "/data/misc/profiles"
 
-#define REALLY_LONG_APP_NAME "com.example." \
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
-        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-
-#define REALLY_LONG_LEAF_NAME "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_" \
-        "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_" \
-        "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_" \
-        "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_"
-
 namespace android {
 namespace installd {
 
@@ -88,6 +78,14 @@
     virtual void TearDown() {
         free(android_system_dirs.dirs);
     }
+
+    std::string create_too_long_path(const std::string& seed) {
+        std::string result = seed;
+        for (size_t i = seed.size(); i < PKG_PATH_MAX; i++) {
+            result += "a";
+        }
+        return result;
+    }
 };
 
 TEST_F(UtilsTest, IsValidApkPath_BadPrefix) {
@@ -388,17 +386,18 @@
             << "Primary user package directory should be created correctly";
 }
 
+
 TEST_F(UtilsTest, CreateMovePath_Fail_AppTooLong) {
     char path[PKG_PATH_MAX];
-
-    EXPECT_EQ(-1, create_move_path(path, REALLY_LONG_APP_NAME, "shared_prefs", 0))
+    std::string really_long_app_name = create_too_long_path("com.example");
+    EXPECT_EQ(-1, create_move_path(path, really_long_app_name.c_str(), "shared_prefs", 0))
             << "Should fail to create move path for primary user";
 }
 
 TEST_F(UtilsTest, CreateMovePath_Fail_LeafTooLong) {
     char path[PKG_PATH_MAX];
-
-    EXPECT_EQ(-1, create_move_path(path, "com.android.test", REALLY_LONG_LEAF_NAME, 0))
+    std::string really_long_leaf_name = create_too_long_path("leaf_");
+    EXPECT_EQ(-1, create_move_path(path, "com.android.test", really_long_leaf_name.c_str(), 0))
             << "Should fail to create move path for primary user";
 }
 
@@ -560,7 +559,7 @@
 }
 
 TEST_F(UtilsTest, CreateSecondaryCurrentProfile) {
-    EXPECT_EQ("/data/user/0/com.example/secondary.dex.prof",
+    EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.cur.prof",
             create_current_profile_path(/*user*/0,
                     "/data/user/0/com.example/secondary.dex", /*is_secondary*/true));
 }
@@ -571,5 +570,88 @@
                     "/data/user/0/com.example/secondary.dex", /*is_secondary*/true));
 }
 
+static void pass_secondary_dex_validation(const std::string& package_name,
+        const std::string& dex_path, int uid, int storage_flag) {
+    EXPECT_TRUE(validate_secondary_dex_path(package_name, dex_path, /*volume_uuid*/ nullptr, uid,
+            storage_flag))
+            << dex_path << " should be allowed as a valid secondary dex path";
+}
+
+static void fail_secondary_dex_validation(const std::string& package_name,
+        const std::string& dex_path, int uid, int storage_flag) {
+    EXPECT_FALSE(validate_secondary_dex_path(package_name, dex_path, /*volume_uuid*/ nullptr, uid,
+            storage_flag))
+            << dex_path << " should not be allowed as a valid secondary dex path";
+}
+
+TEST_F(UtilsTest, ValidateSecondaryDexFilesPath) {
+    std::string package_name = "com.test.app";
+    std::string app_dir_ce_user_0 = "/data/data/" + package_name;
+    std::string app_dir_ce_user_10 = "/data/user/10/" + package_name;
+
+    std::string app_dir_de_user_0 = "/data/user_de/0/" + package_name;
+    std::string app_dir_de_user_10 = "/data/user_de/10/" + package_name;
+
+    EXPECT_EQ(app_dir_ce_user_0,
+            create_data_user_ce_package_path(nullptr, 0, package_name.c_str()));
+    EXPECT_EQ(app_dir_ce_user_10,
+            create_data_user_ce_package_path(nullptr, 10, package_name.c_str()));
+
+    EXPECT_EQ(app_dir_de_user_0,
+            create_data_user_de_package_path(nullptr, 0, package_name.c_str()));
+    EXPECT_EQ(app_dir_de_user_10,
+            create_data_user_de_package_path(nullptr, 10, package_name.c_str()));
+
+    uid_t app_uid_for_user_0 = multiuser_get_uid(/*user_id*/0, /*app_id*/ 1234);
+    uid_t app_uid_for_user_10 = multiuser_get_uid(/*user_id*/10, /*app_id*/ 1234);
+
+    // Standard path for user 0 on CE storage.
+    pass_secondary_dex_validation(
+        package_name, app_dir_ce_user_0 + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
+    // Standard path for user 10 on CE storage.
+    pass_secondary_dex_validation(
+        package_name, app_dir_ce_user_10 + "/ce10.dex", app_uid_for_user_10, FLAG_STORAGE_CE);
+
+    // Standard path for user 0 on DE storage.
+    pass_secondary_dex_validation(
+        package_name, app_dir_de_user_0 + "/de0.dex", app_uid_for_user_0, FLAG_STORAGE_DE);
+    // Standard path for user 10 on DE storage.
+    pass_secondary_dex_validation(
+        package_name, app_dir_de_user_10 + "/de0.dex", app_uid_for_user_10, FLAG_STORAGE_DE);
+
+    // Dex path for user 0 accessed from user 10.
+    fail_secondary_dex_validation(
+        package_name, app_dir_ce_user_0 + "/path0_from10.dex",
+        app_uid_for_user_10, FLAG_STORAGE_CE);
+
+    // Dex path for CE storage accessed with DE.
+    fail_secondary_dex_validation(
+        package_name, app_dir_ce_user_0 + "/ce_from_de.dex", app_uid_for_user_0, FLAG_STORAGE_DE);
+
+    // Dex path for DE storage accessed with CE.
+    fail_secondary_dex_validation(
+        package_name, app_dir_de_user_0 + "/de_from_ce.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
+
+    // Location which does not start with '/'.
+    fail_secondary_dex_validation(
+        package_name, "without_slash.dex", app_uid_for_user_10, FLAG_STORAGE_DE);
+
+    // The dex file is not in the specified package directory.
+    fail_secondary_dex_validation(
+        "another.package", app_dir_ce_user_0 + "/for_another_package.dex",
+        app_uid_for_user_0, FLAG_STORAGE_DE);
+
+    // The dex path contains indirect directories.
+    fail_secondary_dex_validation(
+        package_name, app_dir_ce_user_0 + "/1/../foo.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
+    fail_secondary_dex_validation(
+        package_name, app_dir_ce_user_0 + "/1/./foo.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
+
+    // Super long path.
+    std::string too_long = create_too_long_path("too_long_");
+    fail_secondary_dex_validation(
+        package_name, app_dir_ce_user_10 + "/" + too_long, app_uid_for_user_10, FLAG_STORAGE_CE);
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 7c05417..dd32ac6 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -238,13 +238,38 @@
 
 // Keep profile paths in sync with ActivityThread and LoadedApk.
 const std::string PROFILE_EXT = ".prof";
+const std::string CURRENT_PROFILE_EXT = ".cur";
 const std::string PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT;
 
+// Gets the parent directory and the file name for the given secondary dex path.
+// Returns true on success, false on failure (if the dex_path does not have the expected
+// structure).
+static bool get_secondary_dex_location(const std::string& dex_path,
+        std::string* out_dir_name, std::string* out_file_name) {
+   size_t dirIndex = dex_path.rfind('/');
+   if (dirIndex == std::string::npos) {
+        return false;
+   }
+   if (dirIndex == dex_path.size() - 1) {
+        return false;
+   }
+   *out_dir_name = dex_path.substr(0, dirIndex);
+   *out_file_name = dex_path.substr(dirIndex + 1);
+
+   return true;
+}
+
 std::string create_current_profile_path(userid_t user, const std::string& location,
         bool is_secondary_dex) {
     if (is_secondary_dex) {
-        // Secondary dex profiles are stored next to the dex files using .prof extension.
-        return StringPrintf("%s%s", location.c_str(), PROFILE_EXT.c_str());
+        // Secondary dex current profiles are stored next to the dex files under the oat folder.
+        std::string dex_dir;
+        std::string dex_name;
+        CHECK(get_secondary_dex_location(location, &dex_dir, &dex_name))
+                << "Unexpected dir structure for secondary dex " << location;
+        return StringPrintf("%s/oat/%s%s%s",
+                dex_dir.c_str(), dex_name.c_str(), CURRENT_PROFILE_EXT.c_str(),
+                PROFILE_EXT.c_str());
     } else {
         // Profiles for primary apks are under /data/misc/profiles/cur.
         std::string profile_dir = create_primary_current_profile_package_dir_path(user, location);
@@ -255,12 +280,10 @@
 std::string create_reference_profile_path(const std::string& location, bool is_secondary_dex) {
     if (is_secondary_dex) {
         // Secondary dex reference profiles are stored next to the dex files under the oat folder.
-        size_t dirIndex = location.rfind('/');
-        CHECK(dirIndex != std::string::npos)
+        std::string dex_dir;
+        std::string dex_name;
+        CHECK(get_secondary_dex_location(location, &dex_dir, &dex_name))
                 << "Unexpected dir structure for secondary dex " << location;
-
-        std::string dex_dir = location.substr(0, dirIndex);
-        std::string dex_name = location.substr(dirIndex +1);
         return StringPrintf("%s/oat/%s%s",
                 dex_dir.c_str(), dex_name.c_str(), PROFILE_EXT.c_str());
     } else {
@@ -633,7 +656,7 @@
 int64_t data_disk_free(const std::string& data_path) {
     struct statvfs sfs;
     if (statvfs(data_path.c_str(), &sfs) == 0) {
-        return sfs.f_bavail * sfs.f_frsize;
+        return static_cast<int64_t>(sfs.f_bavail) * sfs.f_frsize;
     } else {
         PLOG(ERROR) << "Couldn't statvfs " << data_path;
         return -1;
@@ -778,24 +801,36 @@
 }
 
 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);
 
-    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());
-    dir_rec_t dir;
-    if (get_path_from_string(&dir, app_private_dir.c_str()) != 0) {
-        LOG(WARNING) << "Could not get dir rec for " << app_private_dir;
-        return false;
+    // Empty paths are not allowed.
+    if (dex_path.empty()) { return false; }
+    // First character should always be '/'. No relative paths.
+    if (dex_path[0] != '/') { return false; }
+    // The last character should not be '/'.
+    if (dex_path[dex_path.size() - 1] == '/') { return false; }
+    // There should be no '.' after the directory marker.
+    if (dex_path.find("/.") != std::string::npos) { return false; }
+    // The path should be at most PKG_PATH_MAX long.
+    if (dex_path.size() > PKG_PATH_MAX) { return false; }
+
+    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;
+        }
     }
-    // Usually secondary dex files have a nested directory structure.
-    // Pick at most 10 subdirectories when validating (arbitrary value).
-    // If the secondary dex file is >10 directory nested then validation will
-    // fail and the file will not be compiled.
-    return validate_path(&dir, dex_path.c_str(), /*max_subdirs*/ 10) == 0;
+
+    // If we got here we have a valid path.
+    return true;
 }
 
 /**
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 070da84..e938042 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -36,6 +36,8 @@
 #define BYPASS_QUOTA 0
 #define BYPASS_SDCARDFS 0
 
+#define APPLY_HARD_QUOTAS 1
+
 namespace android {
 namespace installd {
 
@@ -123,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/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index d5cfcaf..31cd0cb 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -8,10 +8,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <cutils/android_filesystem_config.h>
 #include <cutils/multiuser.h>
 
-#include <private/android_filesystem_config.h>
-
 #include <selinux/android.h>
 #include <selinux/avc.h>
 
diff --git a/data/etc/android.hardware.radio.xml b/data/etc/android.hardware.broadcastradio.xml
similarity index 93%
rename from data/etc/android.hardware.radio.xml
rename to data/etc/android.hardware.broadcastradio.xml
index f718c47..c669518 100644
--- a/data/etc/android.hardware.radio.xml
+++ b/data/etc/android.hardware.broadcastradio.xml
@@ -16,5 +16,5 @@
 
 <!-- This is the standard set of features for a broadcast radio. -->
 <permissions>
-    <feature name="android.hardware.radio" />
+    <feature name="android.hardware.broadcastradio" />
 </permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 835504f..561f5ba 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -41,7 +41,6 @@
     <feature name="android.software.voice_recognizers" notLowRam="true" />
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
-    <feature name="android.software.input_methods" />
     <feature name="android.software.print" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
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/headers/Android.bp b/headers/Android.bp
new file mode 100644
index 0000000..82bc8a1
--- /dev/null
+++ b/headers/Android.bp
@@ -0,0 +1,20 @@
+cc_library_headers {
+    name: "media_plugin_headers",
+    vendor_available: true,
+    export_include_dirs: [
+        "media_plugin",
+        "media_plugin/media/openmax",
+    ],
+    header_libs: [
+        "libstagefright_headers",
+        "libcutils_headers",
+        "libutils_headers",
+        "libstagefright_foundation_headers",
+    ],
+    export_header_lib_headers: [
+        "libstagefright_headers",
+        "libcutils_headers",
+        "libutils_headers",
+        "libstagefright_foundation_headers",
+    ],
+}
diff --git a/include/media/cas/CasAPI.h b/headers/media_plugin/media/cas/CasAPI.h
similarity index 100%
rename from include/media/cas/CasAPI.h
rename to headers/media_plugin/media/cas/CasAPI.h
diff --git a/include/media/cas/DescramblerAPI.h b/headers/media_plugin/media/cas/DescramblerAPI.h
similarity index 100%
rename from include/media/cas/DescramblerAPI.h
rename to headers/media_plugin/media/cas/DescramblerAPI.h
diff --git a/include/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h
similarity index 100%
rename from include/media/drm/DrmAPI.h
rename to headers/media_plugin/media/drm/DrmAPI.h
diff --git a/include/media/editor/II420ColorConverter.h b/headers/media_plugin/media/editor/II420ColorConverter.h
similarity index 100%
rename from include/media/editor/II420ColorConverter.h
rename to headers/media_plugin/media/editor/II420ColorConverter.h
diff --git a/include/media/hardware/CryptoAPI.h b/headers/media_plugin/media/hardware/CryptoAPI.h
similarity index 100%
rename from include/media/hardware/CryptoAPI.h
rename to headers/media_plugin/media/hardware/CryptoAPI.h
diff --git a/include/media/hardware/HDCPAPI.h b/headers/media_plugin/media/hardware/HDCPAPI.h
similarity index 100%
rename from include/media/hardware/HDCPAPI.h
rename to headers/media_plugin/media/hardware/HDCPAPI.h
diff --git a/include/media/hardware/HardwareAPI.h b/headers/media_plugin/media/hardware/HardwareAPI.h
similarity index 100%
rename from include/media/hardware/HardwareAPI.h
rename to headers/media_plugin/media/hardware/HardwareAPI.h
diff --git a/include/media/hardware/MetadataBufferType.h b/headers/media_plugin/media/hardware/MetadataBufferType.h
similarity index 100%
rename from include/media/hardware/MetadataBufferType.h
rename to headers/media_plugin/media/hardware/MetadataBufferType.h
diff --git a/include/media/hardware/OMXPluginBase.h b/headers/media_plugin/media/hardware/OMXPluginBase.h
similarity index 100%
rename from include/media/hardware/OMXPluginBase.h
rename to headers/media_plugin/media/hardware/OMXPluginBase.h
diff --git a/include/media/hardware/VideoAPI.h b/headers/media_plugin/media/hardware/VideoAPI.h
similarity index 100%
rename from include/media/hardware/VideoAPI.h
rename to headers/media_plugin/media/hardware/VideoAPI.h
diff --git a/include/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h
similarity index 98%
rename from include/media/openmax/OMX_AsString.h
rename to headers/media_plugin/media/openmax/OMX_AsString.h
index 56d7cc8..dc25ded 100644
--- a/include/media/openmax/OMX_AsString.h
+++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -930,6 +930,14 @@
 #ifndef AS_STRING_FOR_OMX_VIDEOEXT_H
 #define AS_STRING_FOR_OMX_VIDEOEXT_H
 
+inline static const char *asString(OMX_VIDEO_AVCPROFILEEXTTYPE i, const char *def = "??") {
+    switch (i) {
+        case OMX_VIDEO_AVCProfileConstrainedBaseline:   return "ConstrainedBaseline";
+        case OMX_VIDEO_AVCProfileConstrainedHigh:       return "ConstrainedHigh";
+        default:                                return asString((OMX_VIDEO_AVCPROFILETYPE)i, def);
+    }
+}
+
 inline static const char *asString(OMX_VIDEO_VP8PROFILETYPE i, const char *def = "??") {
     switch (i) {
         case OMX_VIDEO_VP8ProfileMain:    return "Main";
diff --git a/include/media/openmax/OMX_Audio.h b/headers/media_plugin/media/openmax/OMX_Audio.h
similarity index 100%
rename from include/media/openmax/OMX_Audio.h
rename to headers/media_plugin/media/openmax/OMX_Audio.h
diff --git a/include/media/openmax/OMX_AudioExt.h b/headers/media_plugin/media/openmax/OMX_AudioExt.h
similarity index 100%
rename from include/media/openmax/OMX_AudioExt.h
rename to headers/media_plugin/media/openmax/OMX_AudioExt.h
diff --git a/include/media/openmax/OMX_Component.h b/headers/media_plugin/media/openmax/OMX_Component.h
similarity index 100%
rename from include/media/openmax/OMX_Component.h
rename to headers/media_plugin/media/openmax/OMX_Component.h
diff --git a/include/media/openmax/OMX_ContentPipe.h b/headers/media_plugin/media/openmax/OMX_ContentPipe.h
similarity index 100%
rename from include/media/openmax/OMX_ContentPipe.h
rename to headers/media_plugin/media/openmax/OMX_ContentPipe.h
diff --git a/include/media/openmax/OMX_Core.h b/headers/media_plugin/media/openmax/OMX_Core.h
similarity index 100%
rename from include/media/openmax/OMX_Core.h
rename to headers/media_plugin/media/openmax/OMX_Core.h
diff --git a/include/media/openmax/OMX_IVCommon.h b/headers/media_plugin/media/openmax/OMX_IVCommon.h
similarity index 100%
rename from include/media/openmax/OMX_IVCommon.h
rename to headers/media_plugin/media/openmax/OMX_IVCommon.h
diff --git a/include/media/openmax/OMX_Image.h b/headers/media_plugin/media/openmax/OMX_Image.h
similarity index 100%
rename from include/media/openmax/OMX_Image.h
rename to headers/media_plugin/media/openmax/OMX_Image.h
diff --git a/include/media/openmax/OMX_Index.h b/headers/media_plugin/media/openmax/OMX_Index.h
similarity index 100%
rename from include/media/openmax/OMX_Index.h
rename to headers/media_plugin/media/openmax/OMX_Index.h
diff --git a/include/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
similarity index 100%
rename from include/media/openmax/OMX_IndexExt.h
rename to headers/media_plugin/media/openmax/OMX_IndexExt.h
diff --git a/include/media/openmax/OMX_Other.h b/headers/media_plugin/media/openmax/OMX_Other.h
similarity index 100%
rename from include/media/openmax/OMX_Other.h
rename to headers/media_plugin/media/openmax/OMX_Other.h
diff --git a/include/media/openmax/OMX_Types.h b/headers/media_plugin/media/openmax/OMX_Types.h
similarity index 100%
rename from include/media/openmax/OMX_Types.h
rename to headers/media_plugin/media/openmax/OMX_Types.h
diff --git a/include/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h
similarity index 100%
rename from include/media/openmax/OMX_Video.h
rename to headers/media_plugin/media/openmax/OMX_Video.h
diff --git a/include/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h
similarity index 94%
rename from include/media/openmax/OMX_VideoExt.h
rename to headers/media_plugin/media/openmax/OMX_VideoExt.h
index 128dd2d..c102564 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -58,6 +58,12 @@
     OMX_NALUFORMATSTYPE eNaluFormat;
 } OMX_NALSTREAMFORMATTYPE;
 
+/** AVC additional profiles */
+typedef enum OMX_VIDEO_AVCPROFILEEXTTYPE {
+    OMX_VIDEO_AVCProfileConstrainedBaseline = 0x10000,   /**< Constrained baseline profile */
+    OMX_VIDEO_AVCProfileConstrainedHigh     = 0x80000,   /**< Constrained high profile */
+} OMX_VIDEO_AVCPROFILEEXTTYPE;
+
 /** VP8 profiles */
 typedef enum OMX_VIDEO_VP8PROFILETYPE {
     OMX_VIDEO_VP8ProfileMain = 0x01,
@@ -164,20 +170,20 @@
 
 /** VP9 levels */
 typedef enum OMX_VIDEO_VP9LEVELTYPE {
-    OMX_VIDEO_VP9Level1  = 0x0,
-    OMX_VIDEO_VP9Level11 = 0x1,
-    OMX_VIDEO_VP9Level2  = 0x2,
-    OMX_VIDEO_VP9Level21 = 0x4,
-    OMX_VIDEO_VP9Level3  = 0x8,
-    OMX_VIDEO_VP9Level31 = 0x10,
-    OMX_VIDEO_VP9Level4  = 0x20,
-    OMX_VIDEO_VP9Level41 = 0x40,
-    OMX_VIDEO_VP9Level5  = 0x80,
-    OMX_VIDEO_VP9Level51 = 0x100,
-    OMX_VIDEO_VP9Level52 = 0x200,
-    OMX_VIDEO_VP9Level6  = 0x400,
-    OMX_VIDEO_VP9Level61 = 0x800,
-    OMX_VIDEO_VP9Level62 = 0x1000,
+    OMX_VIDEO_VP9Level1  = 0x1,
+    OMX_VIDEO_VP9Level11 = 0x2,
+    OMX_VIDEO_VP9Level2  = 0x4,
+    OMX_VIDEO_VP9Level21 = 0x8,
+    OMX_VIDEO_VP9Level3  = 0x10,
+    OMX_VIDEO_VP9Level31 = 0x20,
+    OMX_VIDEO_VP9Level4  = 0x40,
+    OMX_VIDEO_VP9Level41 = 0x80,
+    OMX_VIDEO_VP9Level5  = 0x100,
+    OMX_VIDEO_VP9Level51 = 0x200,
+    OMX_VIDEO_VP9Level52 = 0x400,
+    OMX_VIDEO_VP9Level6  = 0x800,
+    OMX_VIDEO_VP9Level61 = 0x1000,
+    OMX_VIDEO_VP9Level62 = 0x2000,
     OMX_VIDEO_VP9LevelUnknown = 0x6EFFFFFF,
     OMX_VIDEO_VP9LevelMax = 0x7FFFFFFF
 } OMX_VIDEO_VP9LEVELTYPE;
@@ -290,6 +296,8 @@
     OMX_VIDEO_DolbyVisionProfileDvheStn = 0x20,
     OMX_VIDEO_DolbyVisionProfileDvheDth = 0x40,
     OMX_VIDEO_DolbyVisionProfileDvheDtb = 0x80,
+    OMX_VIDEO_DolbyVisionProfileDvheSt  = 0x100,
+    OMX_VIDEO_DolbyVisionProfileDvavSe  = 0x200,
     OMX_VIDEO_DolbyVisionProfileMax     = 0x7FFFFFFF
 } OMX_VIDEO_DOLBYVISIONPROFILETYPE;
 
diff --git a/include/android/asset_manager.h b/include/android/asset_manager.h
index 7ef3ecb..2ac7d4d 100644
--- a/include/android/asset_manager.h
+++ b/include/android/asset_manager.h
@@ -33,6 +33,10 @@
 extern "C" {
 #endif
 
+#if !defined(__ANDROID__) && !defined(__RENAME_IF_FILE_OFFSET64)
+#define __RENAME_IF_FILE_OFFSET64(x)
+#endif
+
 struct AAssetManager;
 /**
  * {@link AAssetManager} provides access to an application's raw assets by
@@ -132,9 +136,9 @@
  *
  * Returns the new position on success, or (off_t) -1 on error.
  */
-off_t AAsset_seek(AAsset* asset, off_t offset, int whence);
+off_t AAsset_seek(AAsset* asset, off_t offset, int whence)
+    __RENAME_IF_FILE_OFFSET64(AAsset_seek64);
 
-#if __ANDROID_API__ >= 13
 /**
  * Seek to the specified offset within the asset data.  'whence' uses the
  * same constants as lseek()/fseek().
@@ -145,7 +149,6 @@
  * Returns the new position on success, or (off64_t) -1 on error.
  */
 off64_t AAsset_seek64(AAsset* asset, off64_t offset, int whence);
-#endif
 
 /**
  * Close the asset, freeing all associated resources.
@@ -162,29 +165,27 @@
 /**
  * Report the total size of the asset data.
  */
-off_t AAsset_getLength(AAsset* asset);
+off_t AAsset_getLength(AAsset* asset)
+    __RENAME_IF_FILE_OFFSET64(AAsset_getLength64);
 
-#if __ANDROID_API__ >= 13
 /**
  * Report the total size of the asset data. Reports the size using a 64-bit
  * number insted of 32-bit as AAsset_getLength.
  */
 off64_t AAsset_getLength64(AAsset* asset);
-#endif
 
 /**
  * Report the total amount of asset data that can be read from the current position.
  */
-off_t AAsset_getRemainingLength(AAsset* asset);
+off_t AAsset_getRemainingLength(AAsset* asset)
+    __RENAME_IF_FILE_OFFSET64(AAsset_getRemainingLength64);
 
-#if __ANDROID_API__ >= 13
 /**
  * Report the total amount of asset data that can be read from the current position.
  *
  * Uses a 64-bit number instead of a 32-bit number as AAsset_getRemainingLength does.
  */
 off64_t AAsset_getRemainingLength64(AAsset* asset);
-#endif
 
 /**
  * Open a new file descriptor that can be used to read the asset data. If the
@@ -194,9 +195,9 @@
  * Returns < 0 if direct fd access is not possible (for example, if the asset is
  * compressed).
  */
-int AAsset_openFileDescriptor(AAsset* asset, off_t* outStart, off_t* outLength);
+int AAsset_openFileDescriptor(AAsset* asset, off_t* outStart, off_t* outLength)
+    __RENAME_IF_FILE_OFFSET64(AAsset_openFileDescriptor64);
 
-#if __ANDROID_API__ >= 13
 /**
  * Open a new file descriptor that can be used to read the asset data.
  *
@@ -207,7 +208,6 @@
  * compressed).
  */
 int AAsset_openFileDescriptor64(AAsset* asset, off64_t* outStart, off64_t* outLength);
-#endif
 
 /**
  * Returns whether this asset's internal buffer is allocated in ordinary RAM (i.e. not
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 97b4a2a..7f46087 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -381,8 +381,13 @@
  *     ASensorManager* sensorManager = ASensorManager_getInstance();
  *
  */
+#if __ANDROID_API__ >= __ANDROID_API_O__
 __attribute__ ((deprecated)) ASensorManager* ASensorManager_getInstance();
+#else
+ASensorManager* ASensorManager_getInstance();
+#endif
 
+#if __ANDROID_API__ >= __ANDROID_API_O__
 /*
  * Get a reference to the sensor manager. ASensorManager is a singleton
  * per package as different packages may have access to different sensors.
@@ -393,6 +398,7 @@
  *
  */
 ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName);
+#endif
 
 /**
  * Returns the list of available sensors.
diff --git a/include/android/sharedmem_jni.h b/include/android/sharedmem_jni.h
new file mode 100644
index 0000000..85ac78f
--- /dev/null
+++ b/include/android/sharedmem_jni.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup Memory
+ * @{
+ */
+
+/**
+ * @file sharedmem_jni.h
+ */
+
+#ifndef ANDROID_SHARED_MEMORY_JNI_H
+#define ANDROID_SHARED_MEMORY_JNI_H
+
+#include <jni.h>
+#include <android/sharedmem.h>
+#include <stddef.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ *   This file is part of Android's set of stable system headers
+ *   exposed by the Android NDK (Native Development Kit).
+ *
+ *   Third-party source AND binary code relies on the definitions
+ *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/**
+ * Structures and functions for a shared memory buffer that can be shared across process.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __ANDROID_API__ >= __ANDROID_API_O_MR1__
+
+/**
+ * Returns a dup'd FD from the given Java android.os.SharedMemory object. The returned file
+ * descriptor has all the same properties & capabilities as the FD returned from
+ * ASharedMemory_create(), however the protection flags will be the same as those of the
+ * android.os.SharedMemory object.
+ *
+ * Use close() to release the shared memory region.
+ *
+ * \param env The JNIEnv* pointer
+ * \param sharedMemory The Java android.os.SharedMemory object
+ * \return file descriptor that denotes the shared memory; -1 if the shared memory object is
+ *      already closed, if the JNIEnv or jobject is NULL, or if there are too many open file
+ *      descriptors (errno=EMFILE)
+ */
+int ASharedMemory_dupFromJava(JNIEnv* env, jobject sharedMemory);
+
+#endif
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_SHARED_MEMORY_JNI_H
+
+/** @} */
diff --git a/include/batteryservice b/include/batteryservice
new file mode 120000
index 0000000..2178c32
--- /dev/null
+++ b/include/batteryservice
@@ -0,0 +1 @@
+../services/batteryservice/include/batteryservice/
\ No newline at end of file
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 949781c..9f3d45a 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -80,6 +80,7 @@
             int32_t scanCode;
             int32_t metaState;
             int32_t repeatCount;
+            uint32_t empty2;
             nsecs_t downTime __attribute__((aligned(8)));
 
             inline size_t size() const {
@@ -100,13 +101,14 @@
             int32_t metaState;
             int32_t buttonState;
             int32_t edgeFlags;
+            uint32_t empty2;
             nsecs_t downTime __attribute__((aligned(8)));
             float xOffset;
             float yOffset;
             float xPrecision;
             float yPrecision;
             uint32_t pointerCount;
-            uint32_t empty2;
+            uint32_t empty3;
             // Note that PointerCoords requires 8 byte alignment.
             struct Pointer {
                 PointerProperties properties;
@@ -381,20 +383,36 @@
         int32_t idToIndex[MAX_POINTER_ID + 1];
         PointerCoords pointers[MAX_POINTERS];
 
-        void initializeFrom(const InputMessage* msg) {
-            eventTime = msg->body.motion.eventTime;
+        void initializeFrom(const InputMessage& msg) {
+            eventTime = msg.body.motion.eventTime;
             idBits.clear();
-            for (uint32_t i = 0; i < msg->body.motion.pointerCount; i++) {
-                uint32_t id = msg->body.motion.pointers[i].properties.id;
+            for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+                uint32_t id = msg.body.motion.pointers[i].properties.id;
                 idBits.markBit(id);
                 idToIndex[id] = i;
-                pointers[i].copyFrom(msg->body.motion.pointers[i].coords);
+                pointers[i].copyFrom(msg.body.motion.pointers[i].coords);
             }
         }
 
+        void initializeFrom(const History& other) {
+            eventTime = other.eventTime;
+            idBits = other.idBits; // temporary copy
+            for (size_t i = 0; i < other.idBits.count(); i++) {
+                uint32_t id = idBits.clearFirstMarkedBit();
+                int32_t index = other.idToIndex[id];
+                idToIndex[id] = index;
+                pointers[index].copyFrom(other.pointers[index]);
+            }
+            idBits = other.idBits; // final copy
+        }
+
         const PointerCoords& getPointerById(uint32_t id) const {
             return pointers[idToIndex[id]];
         }
+
+        bool hasPointerId(uint32_t id) const {
+            return idBits.hasBit(id);
+        }
     };
     struct TouchState {
         int32_t deviceId;
@@ -413,7 +431,7 @@
             lastResample.idBits.clear();
         }
 
-        void addHistory(const InputMessage* msg) {
+        void addHistory(const InputMessage& msg) {
             historyCurrent ^= 1;
             if (historySize < 2) {
                 historySize += 1;
@@ -424,6 +442,24 @@
         const History* getHistory(size_t index) const {
             return &history[(historyCurrent + index) & 1];
         }
+
+        bool recentCoordinatesAreIdentical(uint32_t id) const {
+            // Return true if the two most recently received "raw" coordinates are identical
+            if (historySize < 2) {
+                return false;
+            }
+            if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) {
+                return false;
+            }
+            float currentX = getHistory(0)->getPointerById(id).getX();
+            float currentY = getHistory(0)->getPointerById(id).getY();
+            float previousX = getHistory(1)->getPointerById(id).getX();
+            float previousY = getHistory(1)->getPointerById(id).getY();
+            if (currentX == previousX && currentY == previousY) {
+                return true;
+            }
+            return false;
+        }
     };
     Vector<TouchState> mTouchStates;
 
@@ -443,8 +479,7 @@
             Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent,
             int32_t* displayId);
 
-    void updateTouchState(InputMessage* msg);
-    void rewriteMessage(const TouchState& state, InputMessage* msg);
+    void updateTouchState(InputMessage& msg);
     void resampleTouchState(nsecs_t frameTime, MotionEvent* event,
             const InputMessage *next);
 
@@ -453,6 +488,7 @@
 
     status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
 
+    static void rewriteMessage(TouchState& state, InputMessage& msg);
     static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
     static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
     static void addSample(MotionEvent* event, const InputMessage* msg);
diff --git a/include/media b/include/media
new file mode 120000
index 0000000..3e7da1c
--- /dev/null
+++ b/include/media
@@ -0,0 +1 @@
+../headers/media_plugin/media
\ No newline at end of file
diff --git a/include/private/ui/RegionHelper.h b/include/private/ui/RegionHelper.h
index a86c586..380e745 100644
--- a/include/private/ui/RegionHelper.h
+++ b/include/private/ui/RegionHelper.h
@@ -21,6 +21,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <limits>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
diff --git a/include_sensor/android/looper.h b/include_sensor/android/looper.h
new file mode 120000
index 0000000..0cf51b8
--- /dev/null
+++ b/include_sensor/android/looper.h
@@ -0,0 +1 @@
+../../include/android/looper.h
\ No newline at end of file
diff --git a/include_sensor/android/sensor.h b/include_sensor/android/sensor.h
new file mode 120000
index 0000000..0626f4f
--- /dev/null
+++ b/include_sensor/android/sensor.h
@@ -0,0 +1 @@
+../../include/android/sensor.h
\ No newline at end of file
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 3a353c2..83b8021 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -16,6 +16,16 @@
     name: "libbinder_headers",
     export_include_dirs: ["include"],
     vendor_available: true,
+    header_libs: [
+        "libbase_headers",
+        "libcutils_headers",
+        "libutils_headers",
+    ],
+    export_header_lib_headers: [
+        "libbase_headers",
+        "libcutils_headers",
+        "libutils_headers",
+    ],
 }
 
 cc_library {
@@ -23,6 +33,9 @@
 
     // for vndbinder
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     srcs: [
         "AppOpsManager.cpp",
@@ -56,8 +69,13 @@
         "TextOutput.cpp",
         "IpPrefix.cpp",
         "Value.cpp",
+        "aidl/android/content/pm/IPackageManagerNative.aidl",
     ],
 
+    aidl: {
+        export_aidl_headers: true,
+    },
+
     cflags: [
         "-Wall",
         "-Wextra",
@@ -80,11 +98,6 @@
         "libbinder_headers",
     ],
 
-    export_shared_lib_headers: [
-        "libbase",
-        "libutils",
-    ],
-
     export_header_lib_headers: [
         "libbinder_headers",
     ],
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index a8f2da5..4ac61a3 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <binder/Debug.h>
+#include <binder/ProcessState.h>
 
 #include <utils/misc.h>
 
@@ -294,5 +295,14 @@
     }
 }
 
+ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) {
+    sp<ProcessState> proc = ProcessState::selfOrNull();
+    if (proc.get() == NULL) {
+        return 0;
+    }
+
+    return proc->getKernelReferences(count, buf);
+}
+
 }; // namespace android
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 9d96dd6..460bbe2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1933,7 +1933,7 @@
 
 status_t Parcel::readBool(bool *pArg) const
 {
-    int32_t tmp;
+    int32_t tmp = 0;
     status_t ret = readInt32(&tmp);
     *pArg = (tmp != 0);
     return ret;
@@ -1946,7 +1946,7 @@
 
 status_t Parcel::readChar(char16_t *pArg) const
 {
-    int32_t tmp;
+    int32_t tmp = 0;
     status_t ret = readInt32(&tmp);
     *pArg = char16_t(tmp);
     return ret;
@@ -1959,7 +1959,7 @@
 
 status_t Parcel::readByte(int8_t *pArg) const
 {
-    int32_t tmp;
+    int32_t tmp = 0;
     status_t ret = readInt32(&tmp);
     *pArg = int8_t(tmp);
     return ret;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index add5e74..11dd525 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -90,6 +90,12 @@
     return gProcess;
 }
 
+sp<ProcessState> ProcessState::selfOrNull()
+{
+    Mutex::Autolock _l(gProcessMutex);
+    return gProcess;
+}
+
 void ProcessState::setContextObject(const sp<IBinder>& object)
 {
     setContextObject(object, String16("default"));
@@ -176,6 +182,46 @@
     return mManagesContexts;
 }
 
+// Get references to userspace objects held by the kernel binder driver
+// Writes up to count elements into buf, and returns the total number
+// of references the kernel has, which may be larger than count.
+// buf may be NULL if count is 0.  The pointers returned by this method
+// should only be used for debugging and not dereferenced, they may
+// already be invalid.
+ssize_t ProcessState::getKernelReferences(size_t buf_count, uintptr_t* buf)
+{
+    // TODO: remove these when they are defined by bionic's binder.h
+    struct binder_node_debug_info {
+        binder_uintptr_t ptr;
+        binder_uintptr_t cookie;
+        __u32 has_strong_ref;
+        __u32 has_weak_ref;
+    };
+#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info)
+
+    binder_node_debug_info info = {};
+
+    uintptr_t* end = buf ? buf + buf_count : NULL;
+    size_t count = 0;
+
+    do {
+        status_t result = ioctl(mDriverFD, BINDER_GET_NODE_DEBUG_INFO, &info);
+        if (result < 0) {
+            return -1;
+        }
+        if (info.ptr != 0) {
+            if (buf && buf < end)
+                *buf++ = info.ptr;
+            count++;
+            if (buf && buf < end)
+                *buf++ = info.cookie;
+            count++;
+        }
+    } while (info.ptr != 0);
+
+    return count;
+}
+
 ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
 {
     const size_t N=mHandleToObject.size();
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
new file mode 100644
index 0000000..3264666
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -0,0 +1,57 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content.pm;
+
+/**
+ * Parallel implementation of certain {@link PackageManager} APIs that need to
+ * be exposed to native code.
+ * <p>These APIs are a parallel definition to the APIs in PackageManager, so,
+ * they can technically diverge. However, it's good practice to keep these
+ * APIs in sync with each other.
+ * <p>Because these APIs are exposed to native code, it's possible they will
+ * be exposed to privileged components [such as UID 0]. Care should be taken
+ * to avoid exposing potential security holes for methods where permission
+ * checks are bypassed based upon UID alone.
+ *
+ * @hide
+ */
+interface IPackageManagerNative {
+    /**
+     * Returns a set of names for the given UIDs.
+     * IMPORTANT: Unlike the Java version of this API, unknown UIDs are
+     * not represented by 'null's. Instead, they are represented by empty
+     * 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/binder/include/binder/Debug.h b/libs/binder/include/binder/Debug.h
index f6a3355..be0266c 100644
--- a/libs/binder/include/binder/Debug.h
+++ b/libs/binder/include/binder/Debug.h
@@ -18,14 +18,13 @@
 #define ANDROID_BINDER_DEBUG_H
 
 #include <stdint.h>
+#include <sys/cdefs.h>
 #include <sys/types.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
 
 const char* stringForIndent(int32_t indentLevel);
 
@@ -39,9 +38,10 @@
     size_t alignment=0, bool cArrayStyle=false,
     debugPrintFunc func = 0, void* cookie = 0);
 
-#ifdef __cplusplus
-}
-#endif
+
+ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
+
+__END_DECLS
 
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 1ef045d..f85c261 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -35,6 +35,7 @@
 {
 public:
     static  sp<ProcessState>    self();
+    static  sp<ProcessState>    selfOrNull();
     /* initWithDriver() can be used to configure libbinder to use
      * a different binder driver dev node. It must be called *before*
      * any call to ProcessState::self(). /dev/binder remains the default.
@@ -71,6 +72,8 @@
 
             String8             getDriverName();
 
+            ssize_t             getKernelReferences(size_t count, uintptr_t* buf);
+
 private:
     friend class IPCThreadState;
     
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index ff5912f..9b289c0 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -77,6 +77,16 @@
         virtual void TearDown() {
         }
     protected:
+        /* The ioctl must either return 0, or if it doesn't errno should be accepted_errno */
+        void binderTestIoctlSuccessOrError(int cmd, void *arg, int accepted_errno) {
+            int ret;
+
+            ret = ioctl(m_binderFd, cmd, arg);
+            if (ret != 0) {
+                EXPECT_EQ(errno, accepted_errno);
+            }
+        }
+
         void binderTestIoctlRetErr2(int cmd, void *arg, int expect_ret, int expect_errno, int accept_errno) {
             int ret;
 
@@ -250,7 +260,7 @@
 
     {
         SCOPED_TRACE("1st WriteRead");
-        binderTestIoctl(BINDER_WRITE_READ, &bwr);
+        binderTestIoctlSuccessOrError(BINDER_WRITE_READ, &bwr, EAGAIN);
     }
     EXPECT_EQ(sizeof(bc1), bwr.write_consumed);
     if (bwr.read_consumed < offsetof(typeof(br), pad)) {
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 6e8f7df..455f2c4 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -101,18 +101,21 @@
 };
 
 static const uint32_t num_buckets = 128;
-static const uint64_t max_time_bucket = 50ull * 1000000;
-static const uint64_t time_per_bucket = max_time_bucket / num_buckets;
-static constexpr float time_per_bucket_ms = time_per_bucket / 1.0E6;
+static uint64_t max_time_bucket = 50ull * 1000000;
+static uint64_t time_per_bucket = max_time_bucket / num_buckets;
 
 struct ProcResults {
-    uint64_t m_best = max_time_bucket;
     uint64_t m_worst = 0;
     uint32_t m_buckets[num_buckets] = {0};
     uint64_t m_transactions = 0;
+    uint64_t m_long_transactions = 0;
     uint64_t m_total_time = 0;
+    uint64_t m_best = max_time_bucket;
 
     void add_time(uint64_t time) {
+        if (time > max_time_bucket) {
+            m_long_transactions++;
+        }
         m_buckets[min(time, max_time_bucket-1) / time_per_bucket] += 1;
         m_best = min(time, m_best);
         m_worst = max(time, m_worst);
@@ -127,16 +130,24 @@
         ret.m_worst = max(a.m_worst, b.m_worst);
         ret.m_best = min(a.m_best, b.m_best);
         ret.m_transactions = a.m_transactions + b.m_transactions;
+        ret.m_long_transactions = a.m_long_transactions + b.m_long_transactions;
         ret.m_total_time = a.m_total_time + b.m_total_time;
         return ret;
     }
     void dump() {
+        if (m_long_transactions > 0) {
+            cout << (double)m_long_transactions / m_transactions << "% of transactions took longer "
+                "than estimated max latency. Consider setting -m to be higher than "
+                 << m_worst / 1000 << " microseconds" << endl;
+        }
+
         double best = (double)m_best / 1.0E6;
         double worst = (double)m_worst / 1.0E6;
         double average = (double)m_total_time / m_transactions / 1.0E6;
         cout << "average:" << average << "ms worst:" << worst << "ms best:" << best << "ms" << endl;
 
         uint64_t cur_total = 0;
+        float time_per_bucket_ms = time_per_bucket / 1.0E6;
         for (int i = 0; i < num_buckets; i++) {
             float cur_time = time_per_bucket_ms * i + 0.5f * time_per_bucket_ms;
             if ((cur_total < 0.5f * m_transactions) && (cur_total + m_buckets[i] >= 0.5f * m_transactions)) {
@@ -154,7 +165,6 @@
             cur_total += m_buckets[i];
         }
         cout << endl;
-
     }
 };
 
@@ -166,13 +176,12 @@
     return serviceName;
 }
 
-void worker_fx(
-    int num,
-    int worker_count,
-    int iterations,
-    int payload_size,
-    bool cs_pair,
-    Pipe p)
+void worker_fx(int num,
+               int worker_count,
+               int iterations,
+               int payload_size,
+               bool cs_pair,
+               Pipe p)
 {
     // Create BinderWorkerService and for go.
     ProcessState::self()->startThreadPool();
@@ -204,12 +213,12 @@
     for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) {
         Parcel data, reply;
         int target = cs_pair ? num % server_count : rand() % workers.size();
-	int sz = payload_size;
+        int sz = payload_size;
 
-	while (sz > sizeof(uint32_t)) {
-		data.writeInt32(0);
-		sz -= sizeof(uint32_t);
-	}
+        while (sz > sizeof(uint32_t)) {
+            data.writeInt32(0);
+            sz -= sizeof(uint32_t);
+        }
         start = chrono::high_resolution_clock::now();
         status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
         end = chrono::high_resolution_clock::now();
@@ -264,47 +273,19 @@
     }
 }
 
-int main(int argc, char *argv[])
+void run_main(int iterations,
+              int workers,
+              int payload_size,
+              int cs_pair,
+              bool training_round=false)
 {
-    int workers = 2;
-    int iterations = 10000;
-    int payload_size = 0;
-    bool cs_pair = false;
-    (void)argc;
-    (void)argv;
     vector<Pipe> pipes;
-
-    // Parse arguments.
-    for (int i = 1; i < argc; i++) {
-        if (string(argv[i]) == "-w") {
-            workers = atoi(argv[i+1]);
-            i++;
-            continue;
-        }
-        if (string(argv[i]) == "-i") {
-            iterations = atoi(argv[i+1]);
-            i++;
-            continue;
-        }
-        if (string(argv[i]) == "-s") {
-            payload_size = atoi(argv[i+1]);
-	    i++;
-	}
-        if (string(argv[i]) == "-p") {
-		// client/server pairs instead of spreading
-		// requests to all workers. If true, half
-		// the workers become clients and half servers
-		cs_pair = true;
-	}
-    }
-
     // Create all the workers and wait for them to spawn.
     for (int i = 0; i < workers; i++) {
         pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair));
     }
     wait_all(pipes);
 
-
     // Run the workers and wait for completion.
     chrono::time_point<chrono::high_resolution_clock> start, end;
     cout << "waiting for workers to complete" << endl;
@@ -326,7 +307,6 @@
         pipes[i].recv(tmp_results);
         tot_results = ProcResults::combine(tot_results, tmp_results);
     }
-    tot_results.dump();
 
     // Kill all the workers.
     cout << "killing workers" << endl;
@@ -338,5 +318,83 @@
             cout << "nonzero child status" << status << endl;
         }
     }
+    if (training_round) {
+        // sets max_time_bucket to 2 * m_worst from the training round.
+        // Also needs to adjust time_per_bucket accordingly.
+        max_time_bucket = 2 * tot_results.m_worst;
+        time_per_bucket = max_time_bucket / num_buckets;
+        cout << "Max latency during training: " << tot_results.m_worst / 1.0E6 << "ms" << endl;
+    } else {
+            tot_results.dump();
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    int workers = 2;
+    int iterations = 10000;
+    int payload_size = 0;
+    bool cs_pair = false;
+    bool training_round = false;
+    (void)argc;
+    (void)argv;
+
+    // Parse arguments.
+    for (int i = 1; i < argc; i++) {
+        if (string(argv[i]) == "--help") {
+            cout << "Usage: binderThroughputTest [OPTIONS]" << endl;
+            cout << "\t-i N    : Specify number of iterations." << endl;
+            cout << "\t-m N    : Specify expected max latency in microseconds." << endl;
+            cout << "\t-p      : Split workers into client/server pairs." << endl;
+            cout << "\t-s N    : Specify payload size." << endl;
+            cout << "\t-t N    : Run training round." << endl;
+            cout << "\t-w N    : Specify total number of workers." << endl;
+            return 0;
+        }
+        if (string(argv[i]) == "-w") {
+            workers = atoi(argv[i+1]);
+            i++;
+            continue;
+        }
+        if (string(argv[i]) == "-i") {
+            iterations = atoi(argv[i+1]);
+            i++;
+            continue;
+        }
+        if (string(argv[i]) == "-s") {
+            payload_size = atoi(argv[i+1]);
+            i++;
+        }
+        if (string(argv[i]) == "-p") {
+            // client/server pairs instead of spreading
+            // requests to all workers. If true, half
+            // the workers become clients and half servers
+            cs_pair = true;
+        }
+        if (string(argv[i]) == "-t") {
+            // Run one training round before actually collecting data
+            // to get an approximation of max latency.
+            training_round = true;
+        }
+        if (string(argv[i]) == "-m") {
+            // Caller specified the max latency in microseconds.
+            // No need to run training round in this case.
+            if (atoi(argv[i+1]) > 0) {
+                max_time_bucket = strtoull(argv[i+1], (char **)NULL, 10) * 1000;
+                i++;
+            } else {
+                cout << "Max latency -m must be positive." << endl;
+                exit(EXIT_FAILURE);
+            }
+        }
+    }
+
+    if (training_round) {
+        cout << "Start training round" << endl;
+        run_main(iterations, workers, payload_size, cs_pair, training_round=true);
+        cout << "Completed training round" << endl << endl;
+    }
+
+    run_main(iterations, workers, payload_size, cs_pair);
     return 0;
 }
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 4558fe8..3996305 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -20,6 +20,9 @@
 cc_library_shared {
     name: "libgui",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     clang: true,
     cppflags: [
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index d9d50db..da42956 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -19,6 +19,8 @@
 //#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #include <utils/Log.h>
 
+#include <inttypes.h>
+
 #include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
 
@@ -31,13 +33,13 @@
 namespace android {
 
 BufferItemConsumer::BufferItemConsumer(
-        const sp<IGraphicBufferConsumer>& consumer, uint32_t consumerUsage,
+        const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
         int bufferCount, bool controlledByApp) :
     ConsumerBase(consumer, controlledByApp)
 {
     status_t err = mConsumer->setConsumerUsageBits(consumerUsage);
     LOG_ALWAYS_FATAL_IF(err != OK,
-            "Failed to set consumer usage bits to %#x", consumerUsage);
+            "Failed to set consumer usage bits to %#" PRIx64, consumerUsage);
     if (bufferCount != DEFAULT_MAX_BUFFERS) {
         err = mConsumer->setMaxAcquiredBufferCount(bufferCount);
         LOG_ALWAYS_FATAL_IF(err != OK,
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 168d355..17cf677 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <inttypes.h>
+#include <pwd.h>
+#include <sys/types.h>
 
 #define LOG_TAG "BufferQueueConsumer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -34,7 +36,6 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/PermissionCache.h>
-#include <private/android_filesystem_config.h>
 
 #include <system/window.h>
 
@@ -747,12 +748,19 @@
 }
 
 status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResult) const {
+    struct passwd* pwd = getpwnam("shell");
+    uid_t shellUid = pwd ? pwd->pw_uid : 0;
+    if (!shellUid) {
+        int savedErrno = errno;
+        BQ_LOGE("Cannot get AID_SHELL");
+        return savedErrno ? -savedErrno : UNKNOWN_ERROR;
+    }
+
     const IPCThreadState* ipc = IPCThreadState::self();
     const pid_t pid = ipc->getCallingPid();
     const uid_t uid = ipc->getCallingUid();
-    if ((uid != AID_SHELL)
-            && !PermissionCache::checkPermission(String16(
-            "android.permission.DUMP"), pid, uid)) {
+    if ((uid != shellUid) &&
+        !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
         outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
                 "from pid=%d, uid=%d\n", pid, uid);
         android_errorWriteWithInfoLog(0x534e4554, "27046057",
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 3d94a02..625dc5b 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -347,10 +347,10 @@
     return NO_ERROR;
 }
 
-status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
-        sp<android::Fence> *outFence, uint32_t width, uint32_t height,
-        PixelFormat format, uint64_t usage,
-        FrameEventHistoryDelta* outTimestamps) {
+status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
+                                            uint32_t width, uint32_t height, PixelFormat format,
+                                            uint64_t usage, uint64_t* outBufferAge,
+                                            FrameEventHistoryDelta* outTimestamps) {
     ATRACE_CALL();
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
@@ -558,6 +558,9 @@
             mSlots[*outSlot].mFrameNumber,
             mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
 
+    if (outBufferAge) {
+        *outBufferAge = mCore->mBufferAge;
+    }
     addAndGetFrameTimestamps(nullptr, outTimestamps);
 
     return returnFlags;
@@ -1099,6 +1102,7 @@
             value = (mCore->mQueue.size() > 1);
             break;
         case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
+            // deprecated; higher 32 bits are truncated
             value = static_cast<int32_t>(mCore->mConsumerUsageBits);
             break;
         case NATIVE_WINDOW_DEFAULT_DATASPACE:
@@ -1544,4 +1548,12 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::getConsumerUsage(uint64_t* outUsage) const {
+    BQ_LOGV("getConsumerUsage");
+
+    Mutex::Autolock lock(mCore->mMutex);
+    *outUsage = mCore->mConsumerUsageBits;
+    return NO_ERROR;
+}
+
 } // namespace android
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 3d36376..7aa7872 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -335,16 +335,25 @@
         return OK;
     }
 
-    auto status = mSlots[slot].mFence->getStatus();
-
-    if (status == Fence::Status::Invalid) {
-        CB_LOGE("fence has invalid state");
+    // Check status of fences first because merging is expensive.
+    // Merging an invalid fence with any other fence results in an
+    // invalid fence.
+    auto currentStatus = mSlots[slot].mFence->getStatus();
+    if (currentStatus == Fence::Status::Invalid) {
+        CB_LOGE("Existing fence has invalid state");
         return BAD_VALUE;
     }
 
-    if (status == Fence::Status::Signaled) {
+    auto incomingStatus = fence->getStatus();
+    if (incomingStatus == Fence::Status::Invalid) {
+        CB_LOGE("New fence has invalid state");
         mSlots[slot].mFence = fence;
-    } else {  // status == Fence::Status::Unsignaled
+        return BAD_VALUE;
+    }
+
+    // If both fences are signaled or both are unsignaled, we need to merge
+    // them to get an accurate timestamp.
+    if (currentStatus == incomingStatus) {
         char fenceName[32] = {};
         snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot);
         sp<Fence> mergedFence = Fence::merge(
@@ -357,7 +366,17 @@
             return BAD_VALUE;
         }
         mSlots[slot].mFence = mergedFence;
+    } else if (incomingStatus == Fence::Status::Unsignaled) {
+        // If one fence has signaled and the other hasn't, the unsignaled
+        // fence will approximately correspond with the correct timestamp.
+        // There's a small race if both fences signal at about the same time
+        // and their statuses are retrieved with unfortunate timing. However,
+        // by this point, they will have both signaled and only the timestamp
+        // will be slightly off; any dependencies after this point will
+        // already have been met.
+        mSlots[slot].mFence = fence;
     }
+    // else if (currentStatus == Fence::Status::Unsignaled) is a no-op.
 
     return OK;
 }
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 34c9d78..14d9937 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -43,7 +43,7 @@
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
-EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 #define CROP_EXT_STR "EGL_ANDROID_image_crop"
 #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
 #define EGL_PROTECTED_CONTENT_EXT 0x32C0
@@ -1115,7 +1115,7 @@
     return mConsumer->setDefaultBufferDataSpace(defaultDataSpace);
 }
 
-status_t GLConsumer::setConsumerUsageBits(uint32_t usage) {
+status_t GLConsumer::setConsumerUsageBits(uint64_t usage) {
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         GLC_LOGE("setConsumerUsageBits: GLConsumer is abandoned!");
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 1b0fe06..71e22ce 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -62,7 +62,8 @@
     SET_DEQUEUE_TIMEOUT,
     GET_LAST_QUEUED_BUFFER,
     GET_FRAME_TIMESTAMPS,
-    GET_UNIQUE_ID
+    GET_UNIQUE_ID,
+    GET_CONSUMER_USAGE,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -124,9 +125,9 @@
         return result;
     }
 
-    virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width,
-            uint32_t height, PixelFormat format, uint64_t usage,
-            FrameEventHistoryDelta* outTimestamps) {
+    virtual status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height,
+                                   PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+                                   FrameEventHistoryDelta* outTimestamps) {
         Parcel data, reply;
         bool getFrameTimestamps = (outTimestamps != nullptr);
 
@@ -149,6 +150,17 @@
             fence->clear();
             return result;
         }
+        if (outBufferAge) {
+            result = reply.readUint64(outBufferAge);
+        } else {
+            // Read the value even if outBufferAge is nullptr:
+            uint64_t bufferAge;
+            result = reply.readUint64(&bufferAge);
+        }
+        if (result != NO_ERROR) {
+            ALOGE("IGBP::dequeueBuffer failed to read buffer age: %d", result);
+            return result;
+        }
         if (getFrameTimestamps) {
             result = reply.read(*outTimestamps);
             if (result != NO_ERROR) {
@@ -493,6 +505,25 @@
         }
         return actualResult;
     }
+
+    virtual status_t getConsumerUsage(uint64_t* outUsage) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        status_t result = remote()->transact(GET_CONSUMER_USAGE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getConsumerUsage failed to transact: %d", result);
+        }
+        status_t actualResult = NO_ERROR;
+        result = reply.readInt32(&actualResult);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readUint64(outUsage);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        return actualResult;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -516,11 +547,10 @@
         return mBase->setAsyncMode(async);
     }
 
-    status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
-            PixelFormat format, uint64_t usage,
-            FrameEventHistoryDelta* outTimestamps) override {
-        return mBase->dequeueBuffer(
-                slot, fence, w, h, format, usage, outTimestamps);
+    status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, PixelFormat format,
+                           uint64_t usage, uint64_t* outBufferAge,
+                           FrameEventHistoryDelta* outTimestamps) override {
+        return mBase->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps);
     }
 
     status_t detachBuffer(int slot) override {
@@ -612,6 +642,10 @@
     status_t getUniqueId(uint64_t* outId) const override {
         return mBase->getUniqueId(outId);
     }
+
+    status_t getConsumerUsage(uint64_t* outUsage) const override {
+        return mBase->getConsumerUsage(outUsage);
+    }
 };
 
 IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer,
@@ -655,16 +689,18 @@
             uint32_t height = data.readUint32();
             PixelFormat format = static_cast<PixelFormat>(data.readInt32());
             uint64_t usage = data.readUint64();
+            uint64_t bufferAge = 0;
             bool getTimestamps = data.readBool();
 
             int buf = 0;
             sp<Fence> fence = Fence::NO_FENCE;
             FrameEventHistoryDelta frameTimestamps;
-            int result = dequeueBuffer(&buf, &fence, width, height, format,
-                    usage, getTimestamps ? &frameTimestamps : nullptr);
+            int result = dequeueBuffer(&buf, &fence, width, height, format, usage, &bufferAge,
+                                       getTimestamps ? &frameTimestamps : nullptr);
 
             reply->writeInt32(buf);
             reply->write(*fence);
+            reply->writeUint64(bufferAge);
             if (getTimestamps) {
                 reply->write(frameTimestamps);
             }
@@ -877,6 +913,20 @@
             }
             return NO_ERROR;
         }
+        case GET_CONSUMER_USAGE: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            uint64_t outUsage = 0;
+            status_t actualResult = getConsumerUsage(&outUsage);
+            status_t result = reply->writeInt32(actualResult);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply->writeUint64(outUsage);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 409a3cb..5b1c599 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -44,21 +44,19 @@
 
 namespace android {
 
-Surface::Surface(
-        const sp<IGraphicBufferProducer>& bufferProducer,
-        bool controlledByApp)
-    : mGraphicBufferProducer(bufferProducer),
-      mCrop(Rect::EMPTY_RECT),
-      mGenerationNumber(0),
-      mSharedBufferMode(false),
-      mAutoRefresh(false),
-      mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
-      mSharedBufferHasBeenQueued(false),
-      mQueriedSupportedTimestamps(false),
-      mFrameTimestampsSupportsPresent(false),
-      mEnableFrameTimestamps(false),
-      mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>())
-{
+Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
+      : mGraphicBufferProducer(bufferProducer),
+        mCrop(Rect::EMPTY_RECT),
+        mBufferAge(0),
+        mGenerationNumber(0),
+        mSharedBufferMode(false),
+        mAutoRefresh(false),
+        mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
+        mSharedBufferHasBeenQueued(false),
+        mQueriedSupportedTimestamps(false),
+        mFrameTimestampsSupportsPresent(false),
+        mEnableFrameTimestamps(false),
+        mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
     ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
@@ -506,9 +504,10 @@
     nsecs_t startTime = systemTime();
 
     FrameEventHistoryDelta frameTimestamps;
-    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
-            reqWidth, reqHeight, reqFormat, reqUsage,
-            enableFrameTimestamps ? &frameTimestamps : nullptr);
+    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
+                                                            reqFormat, reqUsage, &mBufferAge,
+                                                            enableFrameTimestamps ? &frameTimestamps
+                                                                                  : nullptr);
     mLastDequeueDuration = systemTime() - startTime;
 
     if (result < 0) {
@@ -845,6 +844,14 @@
                 }
                 return err;
             }
+            case NATIVE_WINDOW_BUFFER_AGE: {
+                if (mBufferAge > INT32_MAX) {
+                    *value = 0;
+                } else {
+                    *value = static_cast<int32_t>(mBufferAge);
+                }
+                return NO_ERROR;
+            }
             case NATIVE_WINDOW_LAST_DEQUEUE_DURATION: {
                 int64_t durationUs = mLastDequeueDuration / 1000;
                 *value = durationUs > std::numeric_limits<int>::max() ?
@@ -967,6 +974,9 @@
     case NATIVE_WINDOW_SET_USAGE64:
         res = dispatchSetUsage64(args);
         break;
+    case NATIVE_WINDOW_GET_CONSUMER_USAGE64:
+        res = dispatchGetConsumerUsage64(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1145,6 +1155,11 @@
     return getHdrSupport(outSupport);
 }
 
+int Surface::dispatchGetConsumerUsage64(va_list args) {
+    uint64_t* usage = va_arg(args, uint64_t*);
+    return getConsumerUsage(usage);
+}
+
 int Surface::connect(int api) {
     static sp<IProducerListener> listener = new DummyProducerListener();
     return connect(api, listener);
@@ -1714,6 +1729,11 @@
     return mGraphicBufferProducer->getUniqueId(outId);
 }
 
+int Surface::getConsumerUsage(uint64_t* outUsage) const {
+    Mutex::Autolock lock(mMutex);
+    return mGraphicBufferProducer->getConsumerUsage(outUsage);
+}
+
 nsecs_t Surface::getLastDequeueStartTime() const {
     Mutex::Autolock lock(mMutex);
     return mLastDequeueStartTime;
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
index 187b211..afa15c5 100644
--- a/libs/gui/SyncFeatures.cpp
+++ b/libs/gui/SyncFeatures.cpp
@@ -27,7 +27,7 @@
 
 #include <private/gui/SyncFeatures.h>
 
-EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
 namespace android {
 
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index 7c0552e..3b89291 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -21,6 +21,8 @@
 #include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
 #include <gui/bufferqueue/1.0/B2HProducerListener.h>
 
+#include <system/window.h>
+
 namespace android {
 namespace hardware {
 namespace graphics {
@@ -989,10 +991,10 @@
 }
 
 // FIXME: usage bits truncated -- needs a 64-bits usage version
-status_t H2BGraphicBufferProducer::dequeueBuffer(
-        int* slot, sp<Fence>* fence,
-        uint32_t w, uint32_t h, ::android::PixelFormat format,
-        uint64_t usage, FrameEventHistoryDelta* outTimestamps) {
+status_t H2BGraphicBufferProducer::dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
+                                                 uint32_t h, ::android::PixelFormat format,
+                                                 uint64_t usage, uint64_t* outBufferAge,
+                                                 FrameEventHistoryDelta* outTimestamps) {
     *fence = new Fence();
     status_t fnStatus;
     status_t transStatus = toStatusT(mBase->dequeueBuffer(
@@ -1016,6 +1018,10 @@
                     fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
                 }
             }));
+    if (outBufferAge) {
+        // Since the HAL version doesn't return the buffer age, set it to 0:
+        *outBufferAge = 0;
+    }
     return transStatus == NO_ERROR ? fnStatus : transStatus;
 }
 
@@ -1228,6 +1234,18 @@
     return transStatus == NO_ERROR ? fnStatus : transStatus;
 }
 
+status_t H2BGraphicBufferProducer::getConsumerUsage(uint64_t* outUsage) const {
+    ALOGW("getConsumerUsage is not fully supported");
+    int result;
+    status_t transStatus = toStatusT(mBase->query(
+            NATIVE_WINDOW_CONSUMER_USAGE_BITS,
+            [&result, outUsage] (int32_t tResult, int32_t tValue) {
+                result = static_cast<int>(tResult);
+                *outUsage = static_cast<uint64_t>(tValue);
+            }));
+    return transStatus == NO_ERROR ? result : static_cast<int>(transStatus);
+}
+
 }  // namespace utils
 }  // namespace V1_0
 }  // namespace bufferqueue
diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h
index 217fe6a..d9c5775 100644
--- a/libs/gui/include/gui/BufferItemConsumer.h
+++ b/libs/gui/include/gui/BufferItemConsumer.h
@@ -52,7 +52,7 @@
     // controlledByApp tells whether this consumer is controlled by the
     // application.
     BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer,
-            uint32_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+            uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
             bool controlledByApp = false);
 
     ~BufferItemConsumer() override;
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 0f8917a..5c7ffb4 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -80,9 +80,10 @@
     //
     // In both cases, the producer will need to call requestBuffer to get a
     // GraphicBuffer handle for the returned slot.
-    virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence,
-            uint32_t width, uint32_t height, PixelFormat format,
-            uint64_t usage, FrameEventHistoryDelta* outTimestamps) override;
+    virtual status_t dequeueBuffer(int* outSlot, sp<Fence>* outFence, uint32_t width,
+                                   uint32_t height, PixelFormat format, uint64_t usage,
+                                   uint64_t* outBufferAge,
+                                   FrameEventHistoryDelta* outTimestamps) override;
 
     // See IGraphicBufferProducer::detachBuffer
     virtual status_t detachBuffer(int slot);
@@ -182,6 +183,9 @@
     // See IGraphicBufferProducer::getUniqueId
     virtual status_t getUniqueId(uint64_t* outId) const override;
 
+    // See IGraphicBufferProducer::getConsumerUsage
+    virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
+
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index 2cf6162..75f2cca 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -210,7 +210,7 @@
     // so the refactoring can proceed smoothly
     status_t setDefaultBufferFormat(PixelFormat defaultFormat);
     status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace);
-    status_t setConsumerUsageBits(uint32_t usage);
+    status_t setConsumerUsageBits(uint64_t usage);
     status_t setTransformHint(uint32_t hint);
     status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
 
@@ -386,7 +386,7 @@
     // BufferQueue instance; these will be OR:d with any additional flags passed
     // from the GLConsumer user. In particular, GLConsumer will always
     // consume buffers as hardware textures.
-    static const uint32_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
 
     // mCurrentTextureImage is the EglImage/buffer of the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 6d16e74..039dc0d 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -194,9 +194,9 @@
     //
     // All other negative values are an unknown error returned downstream
     // from the graphics allocator (typically errno).
-    virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
-            uint32_t h, PixelFormat format, uint64_t usage,
-            FrameEventHistoryDelta* outTimestamps) = 0;
+    virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
+                                   PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+                                   FrameEventHistoryDelta* outTimestamps) = 0;
 
     // detachBuffer attempts to remove all ownership of the buffer in the given
     // slot from the buffer queue. If this call succeeds, the slot will be
@@ -593,6 +593,12 @@
 
     // Returns a unique id for this BufferQueue
     virtual status_t getUniqueId(uint64_t* outId) const = 0;
+
+    // Returns the consumer usage flags for this BufferQueue. This returns the
+    // full 64-bit usage flags, rather than the truncated 32-bit usage flags
+    // returned by querying the now deprecated
+    // NATIVE_WINDOW_CONSUMER_USAGE_BITS attribute.
+    virtual status_t getConsumerUsage(uint64_t* outUsage) const = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 0f7e12a..55dd6bf 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -159,6 +159,7 @@
     status_t getHdrSupport(bool* supported);
 
     status_t getUniqueId(uint64_t* outId) const;
+    status_t getConsumerUsage(uint64_t* outUsage) const;
 
     // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call
     nsecs_t getLastDequeueStartTime() const;
@@ -223,6 +224,7 @@
     int dispatchGetFrameTimestamps(va_list args);
     int dispatchGetWideColorSupport(va_list args);
     int dispatchGetHdrSupport(va_list args);
+    int dispatchGetConsumerUsage64(va_list args);
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -403,6 +405,10 @@
     // (the change since the previous frame) passed in by the producer.
     Region mDirtyRegion;
 
+    // mBufferAge tracks the age of the contents of the most recently dequeued
+    // buffer as the number of frames that have elapsed since it was last queued
+    uint64_t mBufferAge;
+
     // Stores the current generation number. See setGenerationNumber and
     // IGraphicBufferProducer::setGenerationNumber for more information.
     uint32_t mGenerationNumber;
diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
index c3a9d44..74850b4 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
@@ -64,9 +64,9 @@
     status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
     status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override;
     status_t setAsyncMode(bool async) override;
-    status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
-            uint32_t h, ::android::PixelFormat format, uint64_t usage,
-            FrameEventHistoryDelta* outTimestamps) override;
+    status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
+                           ::android::PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+                           FrameEventHistoryDelta* outTimestamps) override;
     status_t detachBuffer(int slot) override;
     status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence)
             override;
@@ -94,6 +94,7 @@
           sp<Fence>* outFence, float outTransformMatrix[16]) override;
     void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
     status_t getUniqueId(uint64_t* outId) const override;
+    status_t getConsumerUsage(uint64_t* outUsage) const override;
 };
 
 }  // namespace utils
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index d64e530..b87cbbd 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -76,8 +76,8 @@
 
         int slot;
         sp<Fence> outFence;
-        status_t ret = mProducer->dequeueBuffer(&slot, &outFence, kWidth,
-                                                kHeight, 0, 0, nullptr);
+        status_t ret = mProducer->dequeueBuffer(&slot, &outFence, kWidth, kHeight, 0, 0,
+                                                nullptr, nullptr);
         ASSERT_GE(ret, 0);
 
         ALOGV("dequeueBuffer: slot=%d", slot);
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 4220aaf..9a20859 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -144,8 +144,8 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                       nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -188,16 +188,16 @@
 
     for (int i = 0; i < 2; i++) {
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-                mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
-                    GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
+                  mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+                                           nullptr, nullptr));
         ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
         ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
     }
 
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
-                GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+                                       nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
 
@@ -239,8 +239,8 @@
     EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(3));
     for (int i = 0; i < 3; i++) {
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-                mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
-                    GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
+                  mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+                                           nullptr, nullptr));
         ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
         ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -275,8 +275,8 @@
     BufferItem item;
 
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
-            GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+                                       nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
     ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -285,8 +285,8 @@
 
     for (int i = 0; i < 2; i++) {
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-                mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
-                GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
+                  mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+                                           nullptr, nullptr));
         ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
         ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -335,8 +335,8 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                       nullptr, nullptr));
     ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
     ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -384,8 +384,8 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                       nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
     IGraphicBufferProducer::QueueBufferInput input(0, false,
             HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
@@ -420,8 +420,8 @@
             EGL_NO_SYNC_KHR, Fence::NO_FENCE));
 
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                       nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataOut;
@@ -443,8 +443,8 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                       nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -492,22 +492,24 @@
     sp<GraphicBuffer> buffer;
     // This should return an error since it would require an allocation
     ASSERT_EQ(OK, mProducer->allowAllocation(false));
-    ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, 0, 0,
-            0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+    ASSERT_EQ(WOULD_BLOCK,
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                       nullptr, nullptr));
 
     // This should succeed, now that we've lifted the prohibition
     ASSERT_EQ(OK, mProducer->allowAllocation(true));
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-            GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                       nullptr, nullptr));
 
     // Release the previous buffer back to the BufferQueue
     mProducer->cancelBuffer(slot, fence);
 
     // This should fail since we're requesting a different size
     ASSERT_EQ(OK, mProducer->allowAllocation(false));
-    ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence,
-            WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+    ASSERT_EQ(WOULD_BLOCK,
+              mProducer->dequeueBuffer(&slot, &fence, WIDTH * 2, HEIGHT * 2, 0,
+                                       GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr));
 }
 
 TEST_F(BufferQueueTest, TestGenerationNumbers) {
@@ -524,7 +526,7 @@
     int slot;
     sp<Fence> fence;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
 
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
@@ -567,7 +569,7 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
+              mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
 
     // Queue the buffer
@@ -581,8 +583,7 @@
     // always the same one and because async mode gets enabled.
     int slot;
     for (int i = 0; i < 5; i++) {
-        ASSERT_EQ(OK, mProducer->dequeueBuffer(
-                &slot, &fence, 0, 0, 0, 0, nullptr));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
         ASSERT_EQ(sharedSlot, slot);
         ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
     }
@@ -619,7 +620,7 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
+              mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
 
     // Queue the buffer
@@ -646,8 +647,7 @@
     // always return the same one.
     int slot;
     for (int i = 0; i < 5; i++) {
-        ASSERT_EQ(OK, mProducer->dequeueBuffer(
-                &slot, &fence, 0, 0, 0, 0, nullptr));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
         ASSERT_EQ(sharedSlot, slot);
         ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
     }
@@ -686,7 +686,7 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
+              mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
 
     // Enable shared buffer mode
@@ -703,8 +703,7 @@
     // always the same one and because async mode gets enabled.
     int slot;
     for (int i = 0; i < 5; i++) {
-        ASSERT_EQ(OK, mProducer->dequeueBuffer(
-                &slot, &fence, 0, 0, 0, 0, nullptr));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
         ASSERT_EQ(sharedSlot, slot);
         ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
     }
@@ -739,8 +738,7 @@
     for (int i = 0; i < 5; ++i) {
         int slot = BufferQueue::INVALID_BUFFER_SLOT;
         sp<Fence> fence = Fence::NO_FENCE;
-        auto result = mProducer->dequeueBuffer(
-                &slot, &fence, 0, 0, 0, 0, nullptr);
+        auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr);
         if (i < 2) {
             ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
                     result);
@@ -767,8 +765,7 @@
     for (int i = 0; i < 2; ++i) {
         int slot = BufferQueue::INVALID_BUFFER_SLOT;
         sp<Fence> fence = Fence::NO_FENCE;
-        ASSERT_EQ(OK, mProducer->dequeueBuffer(
-                &slot, &fence, 0, 0, 0, 0, nullptr));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
         ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
         IGraphicBufferProducer::QueueBufferInput input(0ull, true,
                 HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
@@ -779,8 +776,7 @@
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
     sp<Fence> fence = Fence::NO_FENCE;
     auto startTime = systemTime();
-    ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(
-            &slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_GE(systemTime() - startTime, TIMEOUT);
 
     // We're technically attaching the same buffer multiple times (since we
@@ -801,7 +797,7 @@
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
     sp<Fence> sourceFence;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr));
+              mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr, nullptr));
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
     ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -824,7 +820,7 @@
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
     sp<Fence> fence;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     sp<GraphicBuffer> firstBuffer;
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
 
@@ -836,7 +832,7 @@
     // Dequeue a second buffer
     slot = BufferQueue::INVALID_BUFFER_SLOT;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+              mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     sp<GraphicBuffer> secondBuffer;
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
 
@@ -887,8 +883,8 @@
     int slots[3] = {};
     mProducer->setMaxDequeuedBufferCount(3);
     for (size_t i = 0; i < 3; ++i) {
-        status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
-                0, 0, 0, 0, nullptr);
+        status_t result =
+                mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr);
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
         ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
     }
@@ -901,8 +897,7 @@
     // The first segment is a two-buffer segment, so we only put one buffer into
     // the queue at a time
     for (size_t i = 0; i < 5; ++i) {
-        ASSERT_EQ(OK, mProducer->dequeueBuffer(
-                &slot, &fence, 0, 0, 0, 0, nullptr));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
         ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
         ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -917,17 +912,16 @@
     // two-buffer segment, but then at the end, we put two buffers in the queue
     // at the same time before draining it.
     for (size_t i = 0; i < 5; ++i) {
-        ASSERT_EQ(OK, mProducer->dequeueBuffer(
-                &slot, &fence, 0, 0, 0, 0, nullptr));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
         ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
         ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
                 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
         std::this_thread::sleep_for(16ms);
     }
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
     ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
     ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -942,11 +936,10 @@
 
     // The third segment is a triple-buffer segment, so the queue is switching
     // between one buffer and two buffers deep.
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
     for (size_t i = 0; i < 5; ++i) {
-        ASSERT_EQ(OK, mProducer->dequeueBuffer(
-                &slot, &fence, 0, 0, 0, 0, nullptr));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
         ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
         ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -1026,8 +1019,8 @@
     int slots[4] = {};
     mProducer->setMaxDequeuedBufferCount(4);
     for (size_t i = 0; i < 4; ++i) {
-        status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
-                0, 0, 0, 0, nullptr);
+        status_t result =
+                mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr);
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
         ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
     }
@@ -1038,14 +1031,14 @@
     // Get buffers in all states: dequeued, filled, acquired, free
 
     // Fill 3 buffers
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
     // Dequeue 1 buffer
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
 
     // Acquire and free 1 buffer
     ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -1104,8 +1097,8 @@
     int slots[2] = {};
     ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
     for (size_t i = 0; i < 2; ++i) {
-        status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
-                0, 0, 0, 0, nullptr);
+        status_t result =
+                mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr);
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
         ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
     }
@@ -1115,10 +1108,10 @@
 
     // Fill 2 buffers without consumer consuming them. Verify that all
     // queued buffer returns proper bufferReplaced flag
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
     ASSERT_EQ(false, output.bufferReplaced);
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
     ASSERT_EQ(true, output.bufferReplaced);
 }
@@ -1140,8 +1133,7 @@
             NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
 
     // Dequeue, request, and queue one buffer
-    status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0,
-            nullptr);
+    status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr);
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
@@ -1156,7 +1148,7 @@
             EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
 
     // Dequeue and queue the buffer again
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
 
     // Acquire and release the buffer again. Upon acquiring, the buffer handle
@@ -1168,7 +1160,7 @@
             EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
 
     // Dequeue and queue the buffer again
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
 
     // Disconnect the producer end. This should clear all of the slots and mark
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 5848c74..0982d7e 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -33,6 +33,7 @@
 #include <utils/Mutex.h>
 #include <utils/Condition.h>
 
+#include <vector>
 #define CPU_CONSUMER_TEST_FORMAT_RAW 0
 #define CPU_CONSUMER_TEST_FORMAT_Y8 0
 #define CPU_CONSUMER_TEST_FORMAT_Y16 0
@@ -635,7 +636,7 @@
 
     // Consume
 
-    CpuConsumer::LockedBuffer *b = new CpuConsumer::LockedBuffer[params.maxLockedBuffers];
+    std::vector<CpuConsumer::LockedBuffer> b(params.maxLockedBuffers);
     for (int i = 0; i < params.maxLockedBuffers; i++) {
         ALOGV("Locking frame %d", i);
         err = mCC->lockNextBuffer(&b[i]);
@@ -684,9 +685,6 @@
     for (int i = 1; i < params.maxLockedBuffers; i++) {
         mCC->unlockBuffer(b[i]);
     }
-
-    delete[] b;
-
 }
 
 CpuConsumerTestParams y8TestSets[] = {
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index bcfc91c..dd23bd4 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -194,7 +194,8 @@
     };
 
     status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
-        return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr);
+        return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage,
+                                        nullptr, nullptr);
     }
 
     void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -206,9 +207,12 @@
 
         ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
-        ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-                (mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH,
-                DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr)));
+
+        ASSERT_EQ(OK,
+                  ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                          (mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+                                                    DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS,
+                                                    nullptr, nullptr)));
 
         EXPECT_LE(0, *slot);
         EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot);
@@ -343,11 +347,11 @@
     int dequeuedSlot = -1;
     sp<Fence> dequeuedFence;
 
-
-    ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-            (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
-                                     DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS, nullptr)));
+    ASSERT_EQ(OK,
+              ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                      (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH,
+                                                DEFAULT_HEIGHT, DEFAULT_FORMAT,
+                                                TEST_PRODUCER_USAGE_BITS, nullptr, nullptr)));
 
     EXPECT_LE(0, dequeuedSlot);
     EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
@@ -403,10 +407,11 @@
     int dequeuedSlot = -1;
     sp<Fence> dequeuedFence;
 
-    ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-            (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
-                                     DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS, nullptr)));
+    ASSERT_EQ(OK,
+              ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                      (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH,
+                                                DEFAULT_HEIGHT, DEFAULT_FORMAT,
+                                                TEST_PRODUCER_USAGE_BITS, nullptr, nullptr)));
 
     // Slot was enqueued without requesting a buffer
     {
@@ -472,10 +477,11 @@
     int dequeuedSlot = -1;
     sp<Fence> dequeuedFence;
 
-    ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-            (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
-                                     DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS, nullptr)));
+    ASSERT_EQ(OK,
+              ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                      (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH,
+                                                DEFAULT_HEIGHT, DEFAULT_FORMAT,
+                                                TEST_PRODUCER_USAGE_BITS, nullptr, nullptr)));
 
     // No return code, but at least test that it doesn't blow up...
     // TODO: add a return code
@@ -519,12 +525,11 @@
     int dequeuedSlot = -1;
     sp<Fence> dequeuedFence;
     for (int i = 0; i < maxBuffers; ++i) {
-
-        EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-                (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
-                                         DEFAULT_WIDTH, DEFAULT_HEIGHT,
-                                         DEFAULT_FORMAT,
-                                         TEST_PRODUCER_USAGE_BITS, nullptr)))
+        EXPECT_EQ(OK,
+                  ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                          (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH,
+                                                    DEFAULT_HEIGHT, DEFAULT_FORMAT,
+                                                    TEST_PRODUCER_USAGE_BITS, nullptr, nullptr)))
                 << "iteration: " << i << ", slot: " << dequeuedSlot;
     }
 
@@ -557,11 +562,11 @@
     int dequeuedSlot = -1;
     sp<Fence> dequeuedFence;
     for (int i = 0; i < 2; i++) {
-        ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-                (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
-                                         DEFAULT_WIDTH, DEFAULT_HEIGHT,
-                                         DEFAULT_FORMAT,
-                                         TEST_PRODUCER_USAGE_BITS, nullptr)))
+        ASSERT_EQ(OK,
+                  ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                          (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH,
+                                                    DEFAULT_HEIGHT, DEFAULT_FORMAT,
+                                                    TEST_PRODUCER_USAGE_BITS, nullptr, nullptr)))
                 << "slot: " << dequeuedSlot;
     }
 
@@ -593,10 +598,11 @@
     // Should now be able to queue/dequeue as many buffers as we want without
     // blocking
     for (int i = 0; i < 5; ++i) {
-        ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-                (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
-                DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                TEST_PRODUCER_USAGE_BITS, nullptr)))
+        ASSERT_EQ(OK,
+                  ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                          (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH,
+                                                    DEFAULT_HEIGHT, DEFAULT_FORMAT,
+                                                    TEST_PRODUCER_USAGE_BITS, nullptr, nullptr)))
                 << "slot : " << dequeuedSlot;
         ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
         ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
@@ -610,10 +616,11 @@
         int dequeuedSlot = -1;
         sp<Fence> dequeuedFence;
 
-        ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-                (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
-                DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                TEST_PRODUCER_USAGE_BITS, nullptr)))
+        ASSERT_EQ(OK,
+                  ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                          (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH,
+                                                    DEFAULT_HEIGHT, DEFAULT_FORMAT,
+                                                    TEST_PRODUCER_USAGE_BITS, nullptr, nullptr)))
                 << "slot: " << dequeuedSlot;
     }
 
@@ -630,8 +637,9 @@
     int slot = -1;
     sp<Fence> fence;
 
-    ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
-            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr));
+    ASSERT_EQ(NO_INIT,
+              mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
+                                       TEST_PRODUCER_USAGE_BITS, nullptr, nullptr));
 }
 
 TEST_F(IGraphicBufferProducerTest,
@@ -649,10 +657,11 @@
     int slot = -1;
     sp<Fence> fence;
 
-    ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-            (mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
-            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS,
-            nullptr)));
+    ASSERT_EQ(OK,
+              ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                      (mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+                                                DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS,
+                                                nullptr, nullptr)));
 
     EXPECT_LE(0, slot);
     EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot);
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
index 6bc3ccf..bb6b8a5 100644
--- a/libs/gui/tests/Malicious.cpp
+++ b/libs/gui/tests/Malicious.cpp
@@ -38,8 +38,10 @@
     }
     status_t setAsyncMode(bool async) override { return mProducer->setAsyncMode(async); }
     status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, PixelFormat format,
-                           uint64_t usage, FrameEventHistoryDelta* outTimestamps) override {
-        return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outTimestamps);
+                           uint64_t usage, uint64_t* outBufferAge,
+                           FrameEventHistoryDelta* outTimestamps) override {
+        return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge,
+                                        outTimestamps);
     }
     status_t detachBuffer(int slot) override { return mProducer->detachBuffer(slot); }
     status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
@@ -90,6 +92,9 @@
     }
     void getFrameTimestamps(FrameEventHistoryDelta*) override {}
     status_t getUniqueId(uint64_t* outId) const override { return mProducer->getUniqueId(outId); }
+    status_t getConsumerUsage(uint64_t* outUsage) const override {
+        return mProducer->getConsumerUsage(outUsage);
+    }
 
 protected:
     sp<IGraphicBufferProducer> mProducer;
@@ -105,10 +110,10 @@
 
     // Override dequeueBuffer, optionally corrupting the returned slot number
     status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height,
-                           PixelFormat format, uint64_t usage,
+                           PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
                            FrameEventHistoryDelta* outTimestamps) override {
         EXPECT_EQ(BUFFER_NEEDS_REALLOCATION,
-                  mProducer->dequeueBuffer(buf, fence, width, height, format, usage,
+                  mProducer->dequeueBuffer(buf, fence, width, height, format, usage, outBufferAge,
                                            outTimestamps));
         EXPECT_EQ(mExpectedSlot, *buf);
         if (mMaliciousValue != 0) {
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index e2f4948..ad6e051 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -82,8 +82,8 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                           nullptr, nullptr));
     ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -116,8 +116,8 @@
     // This should succeed even with allocation disabled since it will have
     // received the buffer back from the output BufferQueue
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                           nullptr, nullptr));
 }
 
 TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
@@ -154,8 +154,8 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                           nullptr, nullptr));
     ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -191,8 +191,8 @@
     // This should succeed even with allocation disabled since it will have
     // received the buffer back from the output BufferQueues
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                           nullptr, nullptr));
 }
 
 TEST_F(StreamSplitterTest, OutputAbandonment) {
@@ -218,8 +218,8 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+              inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                           nullptr, nullptr));
     ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
 
     // Abandon the output
@@ -231,8 +231,9 @@
     ASSERT_EQ(OK, inputProducer->queueBuffer(slot, qbInput, &qbOutput));
 
     // Input should be abandoned
-    ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-            GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
+    ASSERT_EQ(NO_INIT,
+              inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                           nullptr, nullptr));
 }
 
 } // namespace android
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index bd598e4..d5b2f00 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -28,7 +28,7 @@
 #include <utils/Log.h>
 #include <utils/Thread.h>
 
-EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 #define CROP_EXT_STR "EGL_ANDROID_image_crop"
 
 namespace android {
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/input/Android.bp b/libs/input/Android.bp
index 9294419..2f39976 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -34,6 +34,7 @@
     clang: true,
 
     shared_libs: [
+        "libbase",
         "liblog",
         "libcutils",
     ],
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index de06292..1918379 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -117,6 +117,8 @@
             msg->body.key.deviceId = body.key.deviceId;
             // int32_t source
             msg->body.key.source = body.key.source;
+            // int32_t displayId
+            msg->body.key.displayId = body.key.displayId;
             // int32_t action
             msg->body.key.action = body.key.action;
             // int32_t flags
@@ -142,6 +144,8 @@
             msg->body.motion.deviceId = body.motion.deviceId;
             // int32_t source
             msg->body.motion.source = body.motion.source;
+            // int32_t displayId
+            msg->body.motion.displayId = body.motion.displayId;
             // int32_t action
             msg->body.motion.action = body.motion.action;
             // int32_t actionButton
@@ -594,7 +598,7 @@
             MotionEvent* motionEvent = factory->createMotionEvent();
             if (! motionEvent) return NO_MEMORY;
 
-            updateTouchState(&mMsg);
+            updateTouchState(mMsg);
             initializeMotionEvent(motionEvent, &mMsg);
             *outSeq = mMsg.body.motion.seq;
             *outEvent = motionEvent;
@@ -662,7 +666,7 @@
     uint32_t chain = 0;
     for (size_t i = 0; i < count; i++) {
         InputMessage& msg = batch.samples.editItemAt(i);
-        updateTouchState(&msg);
+        updateTouchState(msg);
         if (i) {
             SeqChain seqChain;
             seqChain.seq = msg.body.motion.seq;
@@ -682,20 +686,19 @@
     return OK;
 }
 
-void InputConsumer::updateTouchState(InputMessage* msg) {
+void InputConsumer::updateTouchState(InputMessage& msg) {
     if (!mResampleTouch ||
-            !(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) {
+            !(msg.body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) {
         return;
     }
 
-    int32_t deviceId = msg->body.motion.deviceId;
-    int32_t source = msg->body.motion.source;
-    nsecs_t eventTime = msg->body.motion.eventTime;
+    int32_t deviceId = msg.body.motion.deviceId;
+    int32_t source = msg.body.motion.source;
 
     // Update the touch state history to incorporate the new input message.
     // If the message is in the past relative to the most recently produced resampled
     // touch, then use the resampled time and coordinates instead.
-    switch (msg->body.motion.action & AMOTION_EVENT_ACTION_MASK) {
+    switch (msg.body.motion.action & AMOTION_EVENT_ACTION_MASK) {
     case AMOTION_EVENT_ACTION_DOWN: {
         ssize_t index = findTouchState(deviceId, source);
         if (index < 0) {
@@ -713,11 +716,7 @@
         if (index >= 0) {
             TouchState& touchState = mTouchStates.editItemAt(index);
             touchState.addHistory(msg);
-            if (eventTime < touchState.lastResample.eventTime) {
-                rewriteMessage(touchState, msg);
-            } else {
-                touchState.lastResample.idBits.clear();
-            }
+            rewriteMessage(touchState, msg);
         }
         break;
     }
@@ -726,7 +725,7 @@
         ssize_t index = findTouchState(deviceId, source);
         if (index >= 0) {
             TouchState& touchState = mTouchStates.editItemAt(index);
-            touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId());
+            touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
             rewriteMessage(touchState, msg);
         }
         break;
@@ -737,7 +736,7 @@
         if (index >= 0) {
             TouchState& touchState = mTouchStates.editItemAt(index);
             rewriteMessage(touchState, msg);
-            touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId());
+            touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
         }
         break;
     }
@@ -745,7 +744,7 @@
     case AMOTION_EVENT_ACTION_SCROLL: {
         ssize_t index = findTouchState(deviceId, source);
         if (index >= 0) {
-            const TouchState& touchState = mTouchStates.itemAt(index);
+            TouchState& touchState = mTouchStates.editItemAt(index);
             rewriteMessage(touchState, msg);
         }
         break;
@@ -755,7 +754,7 @@
     case AMOTION_EVENT_ACTION_CANCEL: {
         ssize_t index = findTouchState(deviceId, source);
         if (index >= 0) {
-            const TouchState& touchState = mTouchStates.itemAt(index);
+            TouchState& touchState = mTouchStates.editItemAt(index);
             rewriteMessage(touchState, msg);
             mTouchStates.removeAt(index);
         }
@@ -764,21 +763,36 @@
     }
 }
 
-void InputConsumer::rewriteMessage(const TouchState& state, InputMessage* msg) {
-    for (uint32_t i = 0; i < msg->body.motion.pointerCount; i++) {
-        uint32_t id = msg->body.motion.pointers[i].properties.id;
+/**
+ * Replace the coordinates in msg with the coordinates in lastResample, if necessary.
+ *
+ * If lastResample is no longer valid for a specific pointer (i.e. the lastResample time
+ * is in the past relative to msg and the past two events do not contain identical coordinates),
+ * then invalidate the lastResample data for that pointer.
+ * If the two past events have identical coordinates, then lastResample data for that pointer will
+ * remain valid, and will be used to replace these coordinates. Thus, if a certain coordinate x0 is
+ * resampled to the new value x1, then x1 will always be used to replace x0 until some new value
+ * not equal to x0 is received.
+ */
+void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) {
+    nsecs_t eventTime = msg.body.motion.eventTime;
+    for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+        uint32_t id = msg.body.motion.pointers[i].properties.id;
         if (state.lastResample.idBits.hasBit(id)) {
-            PointerCoords& msgCoords = msg->body.motion.pointers[i].coords;
-            const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
+            if (eventTime < state.lastResample.eventTime ||
+                    state.recentCoordinatesAreIdentical(id)) {
+                PointerCoords& msgCoords = msg.body.motion.pointers[i].coords;
+                const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
 #if DEBUG_RESAMPLING
-            ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
-                    resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
-                    resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_Y),
-                    msgCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
-                    msgCoords.getAxisValue(AMOTION_EVENT_AXIS_Y));
+                ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
+                        resampleCoords.getX(), resampleCoords.getY(),
+                        msgCoords.getX(), msgCoords.getY());
 #endif
-            msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
-            msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY());
+                msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
+                msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY());
+            } else {
+                state.lastResample.idBits.clearBit(id);
+            }
         }
     }
 }
@@ -827,12 +841,12 @@
     if (next) {
         // Interpolate between current sample and future sample.
         // So current->eventTime <= sampleTime <= future.eventTime.
-        future.initializeFrom(next);
+        future.initializeFrom(*next);
         other = &future;
         nsecs_t delta = future.eventTime - current->eventTime;
         if (delta < RESAMPLE_MIN_DELTA) {
 #if DEBUG_RESAMPLING
-            ALOGD("Not resampled, delta time is too small: %lld ns.", delta);
+            ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta);
 #endif
             return;
         }
@@ -844,12 +858,12 @@
         nsecs_t delta = current->eventTime - other->eventTime;
         if (delta < RESAMPLE_MIN_DELTA) {
 #if DEBUG_RESAMPLING
-            ALOGD("Not resampled, delta time is too small: %lld ns.", delta);
+            ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta);
 #endif
             return;
         } else if (delta > RESAMPLE_MAX_DELTA) {
 #if DEBUG_RESAMPLING
-            ALOGD("Not resampled, delta time is too large: %lld ns.", delta);
+            ALOGD("Not resampled, delta time is too large: %" PRId64 " ns.", delta);
 #endif
             return;
         }
@@ -857,7 +871,7 @@
         if (sampleTime > maxPredict) {
 #if DEBUG_RESAMPLING
             ALOGD("Sample time is too far in the future, adjusting prediction "
-                    "from %lld to %lld ns.",
+                    "from %" PRId64 " to %" PRId64 " ns.",
                     sampleTime - current->eventTime, maxPredict - current->eventTime);
 #endif
             sampleTime = maxPredict;
@@ -871,18 +885,32 @@
     }
 
     // Resample touch coordinates.
+    History oldLastResample;
+    oldLastResample.initializeFrom(touchState.lastResample);
     touchState.lastResample.eventTime = sampleTime;
     touchState.lastResample.idBits.clear();
     for (size_t i = 0; i < pointerCount; i++) {
         uint32_t id = event->getPointerId(i);
         touchState.lastResample.idToIndex[id] = i;
         touchState.lastResample.idBits.markBit(id);
+        if (oldLastResample.hasPointerId(id) && touchState.recentCoordinatesAreIdentical(id)) {
+            // We maintain the previously resampled value for this pointer (stored in
+            // oldLastResample) when the coordinates for this pointer haven't changed since then.
+            // This way we don't introduce artificial jitter when pointers haven't actually moved.
+
+            // We know here that the coordinates for the pointer haven't changed because we
+            // would've cleared the resampled bit in rewriteMessage if they had. We can't modify
+            // lastResample in place becasue the mapping from pointer ID to index may have changed.
+            touchState.lastResample.pointers[i].copyFrom(oldLastResample.getPointerById(id));
+            continue;
+        }
+
         PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
         const PointerCoords& currentCoords = current->getPointerById(id);
+        resampledCoords.copyFrom(currentCoords);
         if (other->idBits.hasBit(id)
                 && shouldResampleTool(event->getToolType(i))) {
             const PointerCoords& otherCoords = other->getPointerById(id);
-            resampledCoords.copyFrom(currentCoords);
             resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
                     lerp(currentCoords.getX(), otherCoords.getX(), alpha));
             resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
@@ -896,7 +924,6 @@
                     alpha);
 #endif
         } else {
-            resampledCoords.copyFrom(currentCoords);
 #if DEBUG_RESAMPLING
             ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)",
                     id, resampledCoords.getX(), resampledCoords.getY(),
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 7f6b157..b174fa8 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -23,13 +23,14 @@
 // Log debug messages about the progress of the algorithm itself.
 #define DEBUG_STRATEGY 0
 
-#include <math.h>
+#include <inttypes.h>
 #include <limits.h>
+#include <math.h>
 
+#include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <input/VelocityTracker.h>
 #include <utils/BitSet.h>
-#include <utils/String8.h>
 #include <utils/Timers.h>
 
 namespace android {
@@ -46,8 +47,7 @@
 
 static float vectorDot(const float* a, const float* b, uint32_t m) {
     float r = 0;
-    while (m) {
-        m--;
+    for (size_t i = 0; i < m; i++) {
         r += *(a++) * *(b++);
     }
     return r;
@@ -55,8 +55,7 @@
 
 static float vectorNorm(const float* a, uint32_t m) {
     float r = 0;
-    while (m) {
-        m--;
+    for (size_t i = 0; i < m; i++) {
         float t = *(a++);
         r += t * t;
     }
@@ -64,36 +63,36 @@
 }
 
 #if DEBUG_STRATEGY || DEBUG_VELOCITY
-static String8 vectorToString(const float* a, uint32_t m) {
-    String8 str;
-    str.append("[");
-    while (m--) {
-        str.appendFormat(" %f", *(a++));
-        if (m) {
-            str.append(",");
+static std::string vectorToString(const float* a, uint32_t m) {
+    std::string str;
+    str += "[";
+    for (size_t i = 0; i < m; i++) {
+        if (i) {
+            str += ",";
         }
+        str += android::base::StringPrintf(" %f", *(a++));
     }
-    str.append(" ]");
+    str += " ]";
     return str;
 }
 
-static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
-    String8 str;
-    str.append("[");
+static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
+    std::string str;
+    str = "[";
     for (size_t i = 0; i < m; i++) {
         if (i) {
-            str.append(",");
+            str += ",";
         }
-        str.append(" [");
+        str += " [";
         for (size_t j = 0; j < n; j++) {
             if (j) {
-                str.append(",");
+                str += ",";
             }
-            str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]);
+            str += android::base::StringPrintf(" %f", a[rowMajor ? i * n + j : j * m + i]);
         }
-        str.append(" ]");
+        str += " ]";
     }
-    str.append(" ]");
+    str += " ]";
     return str;
 }
 #endif
@@ -244,7 +243,7 @@
     mStrategy->addMovement(eventTime, idBits, positions);
 
 #if DEBUG_VELOCITY
-    ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d",
+    ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d",
             eventTime, idBits.value, mActivePointerId);
     for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
         uint32_t id = iterBits.firstMarkedBit();
@@ -256,8 +255,8 @@
                 "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
                 id, positions[index].x, positions[index].y,
                 int(estimator.degree),
-                vectorToString(estimator.xCoeff, estimator.degree + 1).string(),
-                vectorToString(estimator.yCoeff, estimator.degree + 1).string(),
+                vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
+                vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
                 estimator.confidence);
     }
 #endif
@@ -443,8 +442,8 @@
         const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) {
 #if DEBUG_STRATEGY
     ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
-            vectorToString(x, m).string(), vectorToString(y, m).string(),
-            vectorToString(w, m).string());
+            vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
+            vectorToString(w, m).c_str());
 #endif
 
     // Expand the X vector to a matrix A, pre-multiplied by the weights.
@@ -456,7 +455,7 @@
         }
     }
 #if DEBUG_STRATEGY
-    ALOGD("  - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string());
+    ALOGD("  - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
 #endif
 
     // Apply the Gram-Schmidt process to A to obtain its QR decomposition.
@@ -491,8 +490,8 @@
         }
     }
 #if DEBUG_STRATEGY
-    ALOGD("  - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string());
-    ALOGD("  - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string());
+    ALOGD("  - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
+    ALOGD("  - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
 
     // calculate QR, if we factored A correctly then QR should equal A
     float qr[n][m];
@@ -504,7 +503,7 @@
             }
         }
     }
-    ALOGD("  - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string());
+    ALOGD("  - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
 #endif
 
     // Solve R B = Qt W Y to find B.  This is easy because R is upper triangular.
@@ -522,7 +521,7 @@
         outB[i] /= r[i][i];
     }
 #if DEBUG_STRATEGY
-    ALOGD("  - b=%s", vectorToString(outB, n).string());
+    ALOGD("  - b=%s", vectorToString(outB, n).c_str());
 #endif
 
     // Calculate the coefficient of determination as 1 - (SSerr / SStot) where
@@ -608,8 +607,8 @@
 #if DEBUG_STRATEGY
             ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
                     int(outEstimator->degree),
-                    vectorToString(outEstimator->xCoeff, n).string(),
-                    vectorToString(outEstimator->yCoeff, n).string(),
+                    vectorToString(outEstimator->xCoeff, n).c_str(),
+                    vectorToString(outEstimator->yCoeff, n).c_str(),
                     outEstimator->confidence);
 #endif
             return true;
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 3df97a1..6490804 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -115,7 +115,7 @@
      * The consumer gralloc usage bits currently set by the consumer.
      * The values are defined in hardware/libhardware/include/gralloc.h.
      */
-    NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10,
+    NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10, /* deprecated */
 
     /**
      * Transformation that will by applied to buffers by the hwcomposer.
@@ -224,6 +224,7 @@
     NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT        = 28,
     NATIVE_WINDOW_GET_HDR_SUPPORT               = 29,
     NATIVE_WINDOW_SET_USAGE64                   = 30,
+    NATIVE_WINDOW_GET_CONSUMER_USAGE64          = 31,
 // clang-format on
 };
 
@@ -900,13 +901,18 @@
 
 static inline int native_window_get_wide_color_support(
     struct ANativeWindow* window, bool* outSupport) {
-  return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT,
-                         outSupport);
+    return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT,
+            outSupport);
 }
 
 static inline int native_window_get_hdr_support(struct ANativeWindow* window,
                                                 bool* outSupport) {
-  return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport);
+    return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport);
+}
+
+static inline int native_window_get_consumer_usage(struct ANativeWindow* window,
+                                                   uint64_t* outUsage) {
+    return window->perform(window, NATIVE_WINDOW_GET_CONSUMER_USAGE64, outUsage);
 }
 
 __END_DECLS
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 6630d90..59173cb 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -15,6 +15,9 @@
 cc_library_shared {
     name: "libui",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     clang: true,
     cppflags: [
@@ -105,4 +108,10 @@
     ],
 }
 
+cc_library_headers {
+    name: "libui_headers",
+    export_include_dirs: ["include"],
+    vendor_available: true,
+}
+
 subdirs = ["tests"]
diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp
index 39adc5e..755e60c 100644
--- a/libs/ui/HdrCapabilities.cpp
+++ b/libs/ui/HdrCapabilities.cpp
@@ -76,7 +76,7 @@
     mMaxAverageLuminance = reinterpret_cast<float const&>(buf[1]);
     mMinLuminance        = reinterpret_cast<float const&>(buf[2]);
     if (itemCount) {
-        mSupportedHdrTypes.reserve(itemCount);
+        mSupportedHdrTypes.resize(itemCount);
         for (size_t i = 0; i < itemCount; ++i) {
             mSupportedHdrTypes[i] = buf[4 + i];
         }
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 bfb9a55..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)};
 }
@@ -417,6 +381,29 @@
   on_buffer_removed_ = callback;
 }
 
+pdx::Status<void> BufferHubQueue::FreeAllBuffers() {
+  // Clear all available buffers.
+  while (!available_buffers_.empty())
+    available_buffers_.pop();
+
+  pdx::Status<void> last_error;  // No error.
+  // Clear all buffers this producer queue is tracking.
+  for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
+    if (buffers_[slot] != nullptr) {
+      auto status = RemoveBuffer(slot);
+      if (!status) {
+        ALOGE(
+            "ProducerQueue::FreeAllBuffers: Failed to remove buffer at "
+            "slot=%zu.",
+            slot);
+        last_error = status.error_status();
+      }
+    }
+  }
+
+  return last_error;
+}
+
 ProducerQueue::ProducerQueue(LocalChannelHandle handle)
     : BASE(std::move(handle)) {
   auto status = ImportQueue();
@@ -526,7 +513,7 @@
   if (!status)
     return status;
 
-  return Enqueue(buffer, slot);
+  return BufferHubQueue::Enqueue({buffer, slot, 0ULL});
 }
 
 Status<void> ProducerQueue::RemoveBuffer(size_t slot) {
@@ -543,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",
@@ -597,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;
@@ -620,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) {
@@ -663,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);
@@ -681,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 0f75a5c..221bc4f 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -131,9 +131,9 @@
 
 status_t BufferHubQueueProducer::dequeueBuffer(
     int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height,
-    PixelFormat format, uint64_t usage,
+    PixelFormat format, uint64_t usage, uint64_t* /*outBufferAge*/,
     FrameEventHistoryDelta* /* out_timestamps */) {
-  ALOGD_IF(TRACE, "dequeueBuffer: w=%u, h=%u, format=%d, usage=%llu", width,
+  ALOGD_IF(TRACE, "dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width,
            height, format, usage);
 
   status_t ret;
@@ -206,11 +206,11 @@
   // It's either in free state (if the buffer has never been used before) or
   // in queued state (if the buffer has been dequeued and queued back to
   // BufferHubQueue).
-  // TODO(jwcai) Clean this up, make mBufferState compatible with BufferHub's
-  // model.
-  LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() &&
-                       !buffers_[slot].mBufferState.isQueued()),
-                      "dequeueBuffer: slot %zu is not free or queued.", slot);
+  LOG_ALWAYS_FATAL_IF(
+      (!buffers_[slot].mBufferState.isFree() &&
+       !buffers_[slot].mBufferState.isQueued()),
+      "dequeueBuffer: slot %zu is not free or queued, actual state: %s.", slot,
+      buffers_[slot].mBufferState.string());
 
   buffers_[slot].mBufferState.freeQueued();
   buffers_[slot].mBufferState.dequeue();
@@ -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);
@@ -514,6 +514,7 @@
     return BAD_VALUE;
   }
 
+  FreeAllBuffers();
   connected_api_ = kNoConnectedApi;
   return NO_ERROR;
 }
@@ -608,6 +609,14 @@
   return NO_ERROR;
 }
 
+status_t BufferHubQueueProducer::getConsumerUsage(uint64_t* out_usage) const {
+  ALOGD_IF(TRACE, __FUNCTION__);
+
+  // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS
+  *out_usage = 0;
+  return NO_ERROR;
+}
+
 status_t BufferHubQueueProducer::AllocateBuffer(uint32_t width, uint32_t height,
                                                 uint32_t layer_count,
                                                 PixelFormat format,
@@ -647,5 +656,31 @@
   return NO_ERROR;
 }
 
+status_t BufferHubQueueProducer::FreeAllBuffers() {
+  for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
+    // Reset in memory objects related the the buffer.
+    buffers_[slot].mGraphicBuffer = nullptr;
+    buffers_[slot].mBufferState.reset();
+    buffers_[slot].mRequestBufferCalled = false;
+    buffers_[slot].mBufferProducer = nullptr;
+    buffers_[slot].mFence = Fence::NO_FENCE;
+  }
+
+  auto status = queue_->FreeAllBuffers();
+  if (!status) {
+    ALOGE(
+        "BufferHubQueueProducer::FreeAllBuffers: Failed to free all buffers on "
+        "the queue: %s",
+        status.GetErrorMessage().c_str());
+  }
+
+  if (queue_->capacity() != 0 || queue_->count() != 0) {
+    LOG_ALWAYS_FATAL(
+        "BufferHubQueueProducer::FreeAllBuffers: Not all buffers are freed.");
+  }
+
+  return NO_ERROR;
+}
+
 }  // namespace dvr
 }  // namespace android
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 d57c7af..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];
   }
@@ -122,13 +131,17 @@
   // to deregister a buffer for epoll and internal bookkeeping.
   virtual pdx::Status<void> RemoveBuffer(size_t slot);
 
+  // Free all buffers that belongs to this queue. Can only be called from
+  // producer side.
+  virtual pdx::Status<void> FreeAllBuffers();
+
   // Dequeue a buffer from the free queue, blocking until one is available. The
   // timeout argument specifies the number of milliseconds that |Dequeue()| will
   // 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);
@@ -141,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)
@@ -157,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();
@@ -214,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};
@@ -297,16 +317,24 @@
   // Remove producer buffer from the queue.
   pdx::Status<void> RemoveBuffer(size_t slot) override;
 
+  // Free all buffers on this producer queue.
+  pdx::Status<void> FreeAllBuffers() override {
+    return BufferHubQueue::FreeAllBuffers();
+  }
+
   // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
   // and caller should call Post() once it's done writing to release the buffer
   // 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:
@@ -317,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 {
@@ -338,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.
@@ -365,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|
@@ -380,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/include/private/dvr/buffer_hub_queue_producer.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
index 638a56c..7ed55fb 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
@@ -43,6 +43,7 @@
   // See |IGraphicBufferProducer::dequeueBuffer|
   status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width,
                          uint32_t height, PixelFormat format, uint64_t usage,
+                         uint64_t* outBufferAge,
                          FrameEventHistoryDelta* outTimestamps) override;
 
   // See |IGraphicBufferProducer::detachBuffer|
@@ -111,6 +112,9 @@
   // See |IGraphicBufferProducer::getUniqueId|
   status_t getUniqueId(uint64_t* out_id) const override;
 
+  // See |IGraphicBufferProducer::getConsumerUsage|
+  status_t getConsumerUsage(uint64_t* out_usage) const override;
+
  private:
   using LocalHandle = pdx::LocalHandle;
 
@@ -131,6 +135,10 @@
   // Remove a buffer via BufferHubRPC.
   status_t RemoveBuffer(size_t slot);
 
+  // Free all buffers which are owned by the prodcuer. Note that if graphic
+  // buffers are acquired by the consumer, we can't .
+  status_t FreeAllBuffers();
+
   // Concreate implementation backed by BufferHubBuffer.
   std::shared_ptr<ProducerQueue> queue_;
 
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 e0a4052..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());
 }
@@ -570,6 +583,103 @@
   EXPECT_EQ(consumer_queue_->is_async(), kIsAsync);
 }
 
+TEST_F(BufferHubQueueTest, TestFreeAllBuffers) {
+  constexpr size_t kBufferCount = 2;
+
+#define CHECK_NO_BUFFER_THEN_ALLOCATE(num_buffers)  \
+  EXPECT_EQ(consumer_queue_->count(), 0U);          \
+  EXPECT_EQ(consumer_queue_->capacity(), 0U);       \
+  EXPECT_EQ(producer_queue_->count(), 0U);          \
+  EXPECT_EQ(producer_queue_->capacity(), 0U);       \
+  for (size_t i = 0; i < num_buffers; i++) {        \
+    AllocateBuffer();                               \
+  }                                                 \
+  EXPECT_EQ(producer_queue_->count(), num_buffers); \
+  EXPECT_EQ(producer_queue_->capacity(), num_buffers);
+
+  size_t slot;
+  uint64_t seq;
+  LocalHandle fence;
+  pdx::Status<void> status;
+  pdx::Status<std::shared_ptr<BufferConsumer>> consumer_status;
+  pdx::Status<std::shared_ptr<BufferProducer>> producer_status;
+  std::shared_ptr<BufferConsumer> consumer_buffer;
+  std::shared_ptr<BufferProducer> producer_buffer;
+
+  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<uint64_t>().Build(),
+                           UsagePolicy{}));
+
+  // Free all buffers when buffers are avaible for dequeue.
+  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
+  status = producer_queue_->FreeAllBuffers();
+  EXPECT_TRUE(status.ok());
+
+  // Free all buffers when one buffer is dequeued.
+  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
+  producer_status = producer_queue_->Dequeue(100, &slot, &fence);
+  ASSERT_TRUE(producer_status.ok());
+  status = producer_queue_->FreeAllBuffers();
+  EXPECT_TRUE(status.ok());
+
+  // 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(100, &slot, &fence);
+    ASSERT_TRUE(producer_status.ok());
+  }
+  status = producer_queue_->FreeAllBuffers();
+  EXPECT_TRUE(status.ok());
+
+  // Free all buffers when one buffer is posted.
+  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
+  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)));
+  status = producer_queue_->FreeAllBuffers();
+  EXPECT_TRUE(status.ok());
+
+  // 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(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)));
+  }
+  status = producer_queue_->FreeAllBuffers();
+  EXPECT_TRUE(status.ok());
+
+  // 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(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(100, &slot, &seq, &fence);
+    ASSERT_TRUE(consumer_status.ok());
+  }
+
+  status = producer_queue_->FreeAllBuffers();
+  EXPECT_TRUE(status.ok());
+
+  // In addition to FreeAllBuffers() from the queue, it is also required to
+  // delete all references to the ProducerBuffer (i.e. the PDX client).
+  producer_buffer = nullptr;
+
+  // Crank consumer queue events to pickup EPOLLHUP events on the queue.
+  consumer_queue_->HandleQueueEvents();
+
+  // One last check.
+  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
+
+#undef CHECK_NO_BUFFER_THEN_ALLOCATE
+}
+
 }  // namespace
 
 }  // namespace dvr
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
index c7692d0..28cd63a 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -117,9 +117,9 @@
     ASSERT_NE(nullptr, outSlot);
     ASSERT_NE(nullptr, outFence);
 
-    int ret = mProducer->dequeueBuffer(outSlot, outFence, kDefaultWidth,
-                                       kDefaultHeight, kDefaultFormat,
-                                       kTestProducerUsageBits, nullptr);
+    int ret = mProducer->dequeueBuffer(
+        outSlot, outFence, kDefaultWidth, kDefaultHeight, kDefaultFormat,
+        kTestProducerUsageBits, nullptr, nullptr);
     // BUFFER_NEEDS_REALLOCATION can be either on or off.
     ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret);
 
@@ -440,9 +440,10 @@
   sp<Fence> fence;
   for (int i = 0; i < 2; i++) {
     ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
-                      (mProducer->dequeueBuffer(
-                          &slot, &fence, kDefaultWidth, kDefaultHeight,
-                          kDefaultFormat, kTestProducerUsageBits, nullptr)))
+                      (mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth,
+                                                kDefaultHeight, kDefaultFormat,
+                                                kTestProducerUsageBits,
+                                                nullptr, nullptr)))
         << "slot: " << slot;
   }
 
@@ -458,7 +459,8 @@
 
   ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth,
                                               kDefaultHeight, kDefaultFormat,
-                                              kTestProducerUsageBits, nullptr));
+                                              kTestProducerUsageBits,
+                                              nullptr, nullptr));
 }
 
 TEST_F(BufferHubQueueProducerTest,
@@ -506,6 +508,44 @@
   ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
 }
 
+TEST_F(BufferHubQueueProducerTest, ConnectDisconnectReconnect) {
+  int slot = -1;
+  sp<GraphicBuffer> buffer;
+  IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  EXPECT_NO_FATAL_FAILURE(ConnectProducer());
+
+  constexpr int maxDequeuedBuffers = 1;
+  int minUndequeuedBuffers;
+  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                       &minUndequeuedBuffers));
+  EXPECT_EQ(NO_ERROR, mProducer->setAsyncMode(false));
+  EXPECT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
+
+  int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers;
+
+  // Dequeue, request, and queue all buffers.
+  for (int i = 0; i < maxCapacity; i++) {
+    EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+    EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+    EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+  }
+
+  // Disconnect then reconnect.
+  EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  EXPECT_NO_FATAL_FAILURE(ConnectProducer());
+
+  // Dequeue, request, and queue all buffers.
+  for (int i = 0; i < maxCapacity; i++) {
+    EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+    EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+    EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+  }
+
+  EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+}
+
 }  // namespace
 
 }  // namespace dvr
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 7fe9825..9fe161d 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -32,6 +32,7 @@
     "dvr_display_manager.cpp",
     "dvr_hardware_composer_client.cpp",
     "dvr_performance.cpp",
+    "dvr_pose.cpp",
     "dvr_surface.cpp",
     "dvr_vsync.cpp",
 ]
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 018abbb..09a49dd 100644
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -14,6 +14,8 @@
 using android::dvr::BufferProducer;
 using android::dvr::ConsumerQueue;
 using android::dvr::ProducerQueue;
+using android::dvr::ProducerQueueConfigBuilder;
+using android::dvr::UsagePolicy;
 
 extern "C" {
 
@@ -25,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.
@@ -62,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
@@ -73,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;
 
@@ -120,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);
@@ -137,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;
 }
 
@@ -156,6 +227,36 @@
   return 0;
 }
 
+int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format,
+                              uint32_t layer_count, uint64_t usage,
+                              size_t capacity, size_t metadata_size,
+                              DvrWriteBufferQueue** out_write_queue) {
+  if (!out_write_queue)
+    return -EINVAL;
+
+  auto config_builder = ProducerQueueConfigBuilder()
+                            .SetDefaultWidth(width)
+                            .SetDefaultHeight(height)
+                            .SetDefaultFormat(format)
+                            .SetMetadataSize(metadata_size);
+  std::unique_ptr<ProducerQueue> producer_queue =
+      ProducerQueue::Create(config_builder.Build(), UsagePolicy{});
+  if (!producer_queue) {
+    ALOGE("dvrWriteBufferQueueCreate: Failed to create producer queue.");
+    return -ENOMEM;
+  }
+
+  auto status = producer_queue->AllocateBuffers(width, height, layer_count,
+                                                format, usage, capacity);
+  if (!status.ok()) {
+    ALOGE("dvrWriteBufferQueueCreate: Failed to allocate buffers.");
+    return -ENOMEM;
+  }
+
+  *out_write_queue = new DvrWriteBufferQueue(std::move(producer_queue));
+  return 0;
+}
+
 void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) {
   delete write_queue;
 }
@@ -176,6 +277,14 @@
 
 int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
                                           ANativeWindow** out_window) {
+  ALOGW(
+      "dvrWriteBufferQueueGetExternalSurface: This API has been deprecated and "
+      "renamed to dvrWriteBufferQueueGetANativeWindow.");
+  return dvrWriteBufferQueueGetANativeWindow(write_queue, out_window);
+}
+
+int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue,
+                                        ANativeWindow** out_window) {
   if (!write_queue || !out_window)
     return -EINVAL;
 
@@ -199,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)
@@ -251,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;
 }
 
@@ -271,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);
         });
   }
 }
@@ -330,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 ffbe7a5..e53a686 100644
--- a/libs/vr/libdvr/dvr_buffer_queue_internal.h
+++ b/libs/vr/libdvr/dvr_buffer_queue_internal.h
@@ -5,11 +5,23 @@
 #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;
+typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context);
+typedef void (*DvrReadBufferQueueBufferRemovedCallback)(DvrReadBuffer* buffer,
+                                                        void* context);
+
 struct DvrWriteBufferQueue {
+  using BufferHubQueue = android::dvr::BufferHubQueue;
   using ProducerQueue = android::dvr::ProducerQueue;
 
   // Create a concrete object for DvrWriteBufferQueue.
@@ -31,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(
@@ -54,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(
@@ -63,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
new file mode 100644
index 0000000..c379ef5
--- /dev/null
+++ b/libs/vr/libdvr/dvr_pose.cpp
@@ -0,0 +1,29 @@
+#include "include/dvr/dvr_pose.h"
+
+#include <memory>
+
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/pose_client_internal.h>
+
+#include "dvr_buffer_queue_internal.h"
+
+using android::dvr::ConsumerQueue;
+
+int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type,
+                               DvrReadBufferQueue** queue_out) {
+  if (!client || !queue_out)
+    return -EINVAL;
+
+  ConsumerQueue* consumer_queue;
+  int status = android::dvr::dvrPoseClientGetDataReaderHandle(client,
+                                                              data_type,
+                                                              &consumer_queue);
+  if (status != 0) {
+    ALOGE("dvrPoseClientGetDataReader: Failed to get queue: %d", status);
+    return status;
+  }
+
+  std::shared_ptr<ConsumerQueue> consumer_queue_ptr{consumer_queue};
+  *queue_out = new DvrReadBufferQueue(consumer_queue_ptr);
+  return 0;
+}
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index d0dbd8d..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;
@@ -23,6 +29,7 @@
 typedef struct DvrDisplayManager DvrDisplayManager;
 typedef struct DvrSurfaceState DvrSurfaceState;
 typedef struct DvrPoseClient DvrPoseClient;
+typedef struct DvrPoseDataCaptureRequest DvrPoseDataCaptureRequest;
 typedef struct DvrVSyncClient DvrVSyncClient;
 typedef struct DvrVirtualTouchpad DvrVirtualTouchpad;
 
@@ -33,6 +40,7 @@
 
 typedef struct DvrReadBufferQueue DvrReadBufferQueue;
 typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata;
 
 typedef struct DvrSurface DvrSurface;
 typedef uint64_t DvrSurfaceAttributeType;
@@ -159,18 +167,33 @@
     DvrBuffer* buffer);
 
 // dvr_buffer_queue.h
+typedef int (*DvrWriteBufferQueueCreatePtr)(uint32_t width, uint32_t height,
+                                            uint32_t format,
+                                            uint32_t layer_count,
+                                            uint64_t usage, size_t capacity,
+                                            size_t metadata_size,
+                                            DvrWriteBufferQueue** queue_out);
 typedef void (*DvrWriteBufferQueueDestroyPtr)(DvrWriteBufferQueue* write_queue);
 typedef ssize_t (*DvrWriteBufferQueueGetCapacityPtr)(
     DvrWriteBufferQueue* write_queue);
 typedef int (*DvrWriteBufferQueueGetIdPtr)(DvrWriteBufferQueue* write_queue);
 typedef int (*DvrWriteBufferQueueGetExternalSurfacePtr)(
     DvrWriteBufferQueue* write_queue, ANativeWindow** out_window);
+typedef int (*DvrWriteBufferQueueGetANativeWindowPtr)(
+    DvrWriteBufferQueue* write_queue, ANativeWindow** out_window);
 typedef int (*DvrWriteBufferQueueCreateReadQueuePtr)(
     DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue);
 typedef int (*DvrWriteBufferQueueDequeuePtr)(DvrWriteBufferQueue* write_queue,
                                              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);
@@ -185,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,
@@ -238,6 +268,15 @@
                                              DvrPoseAsync* out_pose);
 typedef int (*DvrPoseClientSensorsEnablePtr)(DvrPoseClient* client,
                                              bool enabled);
+typedef int (*DvrPoseClientDataCapturePtr)(DvrPoseClient* client,
+    const DvrPoseDataCaptureRequest* request);
+typedef int (*DvrPoseClientDataReaderDestroyPtr)(DvrPoseClient* client,
+                                                 uint64_t data_type);
+
+// dvr_pose.h
+typedef int (*DvrPoseClientGetDataReaderPtr)(DvrPoseClient* client,
+                                             uint64_t data_type,
+                                             DvrReadBufferQueue** read_queue);
 
 // services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h
 
@@ -334,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;
 
@@ -358,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 72e0f67..cce8c7e 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -65,7 +65,7 @@
 DVR_V1_API_ENTRY(WriteBufferQueueDestroy);
 DVR_V1_API_ENTRY(WriteBufferQueueGetCapacity);
 DVR_V1_API_ENTRY(WriteBufferQueueGetId);
-DVR_V1_API_ENTRY(WriteBufferQueueGetExternalSurface);
+DVR_V1_API_ENTRY(WriteBufferQueueGetExternalSurface);  // deprecated
 DVR_V1_API_ENTRY(WriteBufferQueueCreateReadQueue);
 DVR_V1_API_ENTRY(WriteBufferQueueDequeue);
 DVR_V1_API_ENTRY(WriteBufferQueueResizeBuffer);
@@ -160,3 +160,20 @@
 
 // Read buffer queue
 DVR_V1_API_ENTRY(ReadBufferQueueGetEventFd);
+
+// Create write buffer queue locally
+DVR_V1_API_ENTRY(WriteBufferQueueCreate);
+
+// 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);
+DVR_V1_API_ENTRY(PoseClientDataReaderDestroy);
diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
index e2127f8..bf695c7 100644
--- a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
@@ -12,6 +12,36 @@
 typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
 typedef struct DvrReadBufferQueue DvrReadBufferQueue;
 
+// Creates a write buffer queue to be used locally.
+//
+// Note that this API is mostly for testing purpose. For now there is no
+// mechanism to send a DvrWriteBufferQueue cross process. Use
+// dvrSurfaceCreateWriteBufferQueue if cross-process buffer transport is
+// intended.
+//
+// @param width The width of the buffers that this queue will produce.
+// @param height The height of buffers that this queue will produce.
+// @param format The format of the buffers that this queue will produce. This
+//     must be one of the AHARDWAREBUFFER_FORMAT_XXX enums.
+// @param layer_count The number of layers of the buffers that this queue will
+//     produce.
+// @param usage The usage of the buffers that this queue will produce. This
+//     must a combination of the AHARDWAREBUFFER_USAGE_XXX flags.
+// @param capacity The number of buffer that this queue will allocate. Note that
+//     all buffers will be allocated on create. Currently, the number of buffers
+//     is the queue cannot be changed after creation though DVR API. However,
+//     ANativeWindow can choose to reallocate, attach, or detach buffers from
+//     a DvrWriteBufferQueue through Android platform logic.
+// @param metadata_size The size of metadata in bytes.
+// @param out_write_queue The pointer of a DvrWriteBufferQueue will be filled
+//      here if the method call succeeds. The metadata size must match
+//      the metadata size in dvrWriteBufferPost/dvrReadBufferAcquire.
+// @return Zero on success, or negative error code.
+int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format,
+                              uint32_t layer_count, uint64_t usage,
+                              size_t capacity, size_t metadata_size,
+                              DvrWriteBufferQueue** out_write_queue);
+
 // Destroy a write buffer queue.
 //
 // @param write_queue The DvrWriteBufferQueue of interest.
@@ -43,6 +73,10 @@
 //     the method call succeeds.
 // @return Zero on success; or -EINVAL if this DvrWriteBufferQueue does not
 //     support being used as an android Surface.
+int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue,
+                                        ANativeWindow** out_window);
+
+// @deprecated Please use dvrWriteBufferQueueGetANativeWindow instead.
 int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
                                           ANativeWindow** out_window);
 
@@ -55,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.
 //
@@ -119,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 b3df028..8752751 100644
--- a/libs/vr/libdvr/include/dvr/dvr_pose.h
+++ b/libs/vr/libdvr/include/dvr/dvr_pose.h
@@ -15,6 +15,9 @@
 #endif
 #endif
 
+typedef struct DvrPoseClient DvrPoseClient;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
 // Represents an estimated pose, accessed asynchronously through a shared ring
 // buffer. No assumptions should be made about the data in padding space.
 // The size of this struct is 128 bytes.
@@ -95,6 +98,57 @@
   uint8_t padding[12];
 } DvrPose;
 
+// Represents a data type that can be streamed from pose service.
+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 = (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 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.
+  uint32_t sample_interval;
+  // The length of time to capture samples in milliseconds. Set to 0 to capture
+  // indefinitely.
+  uint32_t capture_time_ms;
+  // Reserved fields.
+  uint32_t reserved0;
+  uint32_t reserved1;
+  uint32_t reserved2;
+  uint32_t reserved3;
+  uint32_t reserved4;
+} DvrPoseDataCaptureRequest;
+
+// Gets a read buffer queue for the data type |data_type|. Each call returns a
+// different read buffer queue connected to the same write buffer queue. A
+// separate write buffer queue exists for each |data_type|.
+//
+// PoseService supports a single consumer per write buffer queue. The consumer
+// is expected to hold a single DvrReadBufferQueue at a time. Callers should
+// cache these instead of requesting new ones when possible. If the consumer
+// disconnects from the queue, it can regain a read buffer queue for the same
+// producer by calling this function.
+//
+// For data_type DVR_POSE_RAW_DATA_STEREO_IMAGE, each buffer consists of two
+// images formatted as a AHARDWAREBUFFER_FORMAT_BLOB, where height is 1 and
+// width is the total size of both images. The size of an individual image can
+// 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, uint64_t data_type,
+                               DvrReadBufferQueue** queue_out);
+
+// TODO(b/65067592): Move pose api's from pose_client.h to here.
+
 __END_DECLS
 
 #endif  // ANDROID_DVR_PUBLIC_POSE_H_
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 16da1d9..62cd8d4 100644
--- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -1,28 +1,32 @@
+#include <android/log.h>
+#include <android/native_window.h>
+#include <android-base/unique_fd.h>
 #include <dvr/dvr_api.h>
 #include <dvr/dvr_buffer_queue.h>
-#include <gui/Surface.h>
-#include <private/dvr/buffer_hub_queue_client.h>
 
-#include <base/logging.h>
 #include <gtest/gtest.h>
 
-#include "../dvr_internal.h"
-#include "../dvr_buffer_queue_internal.h"
+#include <array>
+#include <unordered_map>
 
-namespace android {
-namespace dvr {
+#ifndef ALOGD
+#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#endif
+
+#ifndef ALOGD_IF
+#define ALOGD_IF(cond, ...) \
+  ((__predict_false(cond)) ? ((void)ALOGD(__VA_ARGS__)) : (void)0)
+#endif
 
 namespace {
 
 static constexpr uint32_t kBufferWidth = 100;
 static constexpr uint32_t kBufferHeight = 1;
 static constexpr uint32_t kLayerCount = 1;
-static constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
-static constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
+static constexpr uint32_t kBufferFormat = AHARDWAREBUFFER_FORMAT_BLOB;
+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) {
@@ -36,14 +40,6 @@
   }
 
  protected:
-  void SetUp() override {
-    config_builder_ = ProducerQueueConfigBuilder()
-                          .SetDefaultWidth(kBufferWidth)
-                          .SetDefaultHeight(kBufferHeight)
-                          .SetDefaultFormat(kBufferFormat)
-                          .SetMetadata<TestMeta>();
-  }
-
   void TearDown() override {
     if (write_queue_ != nullptr) {
       dvrWriteBufferQueueDestroy(write_queue_);
@@ -51,19 +47,6 @@
     }
   }
 
-  void CreateWriteBufferQueue() {
-    write_queue_ = new DvrWriteBufferQueue(
-        ProducerQueue::Create(config_builder_.Build(), UsagePolicy{}));
-    ASSERT_NE(nullptr, write_queue_);
-  }
-
-  void AllocateBuffers(size_t buffer_count) {
-    auto status = write_queue_->producer_queue()->AllocateBuffers(
-        kBufferWidth, kBufferHeight, kLayerCount, kBufferFormat, kBufferUsage,
-        buffer_count);
-    ASSERT_TRUE(status.ok());
-  }
-
   void HandleBufferAvailable() {
     buffer_available_count_ += 1;
     ALOGD_IF(TRACE, "Buffer avaiable, count=%d", buffer_available_count_);
@@ -75,22 +58,26 @@
              buffer_removed_count_);
   }
 
-  ProducerQueueConfigBuilder config_builder_;
   DvrWriteBufferQueue* write_queue_{nullptr};
   int buffer_available_count_{0};
   int buffer_removed_count_{0};
 };
 
-TEST_F(DvrBufferQueueTest, TestWrite_QueueCreateDestroy) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+TEST_F(DvrBufferQueueTest, WriteQueueCreateDestroy) {
+  int ret = dvrWriteBufferQueueCreate(
+      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
+      /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
+  ASSERT_EQ(0, ret);
 
   dvrWriteBufferQueueDestroy(write_queue_);
   write_queue_ = nullptr;
 }
 
-TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
-  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity));
+TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) {
+  int ret = dvrWriteBufferQueueCreate(
+      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
+      kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
+  ASSERT_EQ(0, ret);
 
   size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_);
 
@@ -98,11 +85,14 @@
   ASSERT_EQ(kQueueCapacity, capacity);
 }
 
-TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) {
+  int ret = dvrWriteBufferQueueCreate(
+      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
+      /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
+  ASSERT_EQ(0, ret);
 
   DvrReadBufferQueue* read_queue = nullptr;
-  int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
+  ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
 
   ASSERT_EQ(0, ret);
   ASSERT_NE(nullptr, read_queue);
@@ -110,12 +100,15 @@
   dvrReadBufferQueueDestroy(read_queue);
 }
 
-TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) {
+  int ret = dvrWriteBufferQueueCreate(
+      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
+      /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
+  ASSERT_EQ(0, ret);
 
   DvrReadBufferQueue* read_queue1 = nullptr;
   DvrReadBufferQueue* read_queue2 = nullptr;
-  int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1);
+  ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1);
 
   ASSERT_EQ(0, ret);
   ASSERT_NE(nullptr, read_queue1);
@@ -129,98 +122,86 @@
   dvrReadBufferQueueDestroy(read_queue2);
 }
 
-TEST_F(DvrBufferQueueTest, CreateEmptyBuffer) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
-  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(3));
+TEST_F(DvrBufferQueueTest, GainBuffer) {
+  int ret = dvrWriteBufferQueueCreate(
+      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
+      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) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
-  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity));
+TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) {
+  int ret = dvrWriteBufferQueueCreate(
+      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
+      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;
 
-  int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
+  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);
-  pdx::LocalHandle release_fence(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);
-  pdx::LocalHandle acquire_fence(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
@@ -233,45 +214,38 @@
   dvrReadBufferQueueDestroy(read_queue);
 }
 
-TEST_F(DvrBufferQueueTest, TestGetExternalSurface) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+TEST_F(DvrBufferQueueTest, GetANativeWindow) {
+  int 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;
-
-  // The |write_queue_| doesn't have proper metadata (must be
-  // DvrNativeBufferMetadata) configured during creation.
-  int ret = dvrWriteBufferQueueGetExternalSurface(write_queue_, &window);
-  ASSERT_EQ(-EINVAL, ret);
-  ASSERT_EQ(nullptr, window);
-
-  // A write queue with DvrNativeBufferMetadata should work fine.
-  auto config = ProducerQueueConfigBuilder()
-                    .SetMetadata<DvrNativeBufferMetadata>()
-                    .Build();
-  std::unique_ptr<DvrWriteBufferQueue, decltype(&dvrWriteBufferQueueDestroy)>
-      write_queue(
-          new DvrWriteBufferQueue(ProducerQueue::Create(config, UsagePolicy{})),
-          dvrWriteBufferQueueDestroy);
-  ASSERT_NE(nullptr, write_queue.get());
-
-  ret = dvrWriteBufferQueueGetExternalSurface(write_queue.get(), &window);
+  ret = dvrWriteBufferQueueGetANativeWindow(write_queue_, &window);
   ASSERT_EQ(0, ret);
   ASSERT_NE(nullptr, window);
 
-  sp<Surface> surface = static_cast<Surface*>(window);
-  ASSERT_TRUE(Surface::isValid(surface));
+  uint32_t width = ANativeWindow_getWidth(window);
+  uint32_t height = ANativeWindow_getHeight(window);
+  uint32_t format = ANativeWindow_getFormat(window);
+  ASSERT_EQ(kBufferWidth, width);
+  ASSERT_EQ(kBufferHeight, height);
+  ASSERT_EQ(kBufferFormat, format);
 }
 
 // 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) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
-  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity));
+TEST_F(DvrBufferQueueTest, ResizeBuffer) {
+  int ret = dvrWriteBufferQueueCreate(
+      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
+      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;
@@ -281,7 +255,7 @@
   AHardwareBuffer* ahb3 = nullptr;
   AHardwareBuffer_Desc buffer_desc;
 
-  int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
+  ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
 
   ASSERT_EQ(0, ret);
   ASSERT_NE(nullptr, read_queue);
@@ -289,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);
@@ -310,11 +277,12 @@
   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);
-  pdx::LocalHandle release_fence1(fence_fd);
+  android::base::unique_fd release_fence1(fence_fd);
 
   // Check the buffer dimension.
   ret = dvrWriteBufferGetAHardwareBuffer(wb1, &ahb1);
@@ -336,12 +304,13 @@
   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,
            fence_fd);
-  pdx::LocalHandle release_fence2(fence_fd);
+  android::base::unique_fd release_fence2(fence_fd);
 
   // Check the buffer dimension, should be new width
   ret = dvrWriteBufferGetAHardwareBuffer(wb2, &ahb2);
@@ -362,12 +331,13 @@
   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,
            fence_fd);
-  pdx::LocalHandle release_fence3(fence_fd);
+  android::base::unique_fd release_fence3(fence_fd);
 
   // Check the buffer dimension, should be new width
   ret = dvrWriteBufferGetAHardwareBuffer(wb3, &ahb3);
@@ -385,77 +355,14 @@
   dvrReadBufferQueueDestroy(read_queue);
 }
 
-TEST_F(DvrBufferQueueTest, DequeueEmptyMetadata) {
-  // Overrides default queue parameters: Empty metadata.
-  config_builder_.SetMetadata<void>();
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
-  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(1));
-
-  DvrReadBuffer* rb = nullptr;
-  DvrWriteBuffer* wb = nullptr;
-  dvrReadBufferCreateEmpty(&rb);
-  dvrWriteBufferCreateEmpty(&wb);
+TEST_F(DvrBufferQueueTest, ReadQueueEventFd) {
+  int ret = dvrWriteBufferQueueCreate(
+      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
+      kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
+  ASSERT_EQ(0, ret);
 
   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) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
-  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(1));
-
-  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) {
-  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
-  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity));
-
-  DvrReadBufferQueue* read_queue = nullptr;
-  int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
+  ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
 
   ASSERT_EQ(0, ret);
   ASSERT_NE(nullptr, read_queue);
@@ -464,7 +371,154 @@
   ASSERT_GT(event_fd, 0);
 }
 
-}  // namespace
+// 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, StableBufferIdAndHardwareBuffer) {
+  int ret = dvrWriteBufferQueueCreate(
+      kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
+      kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
+  ASSERT_EQ(0, ret);
 
-}  // namespace dvr
-}  // namespace android
+  int fence_fd = -1;
+  DvrReadBufferQueue* read_queue = nullptr;
+  EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
+
+  // Read buffers.
+  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;
+
+  constexpr int kNumTests = 100;
+
+  // 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) {
+    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]);
+    ASSERT_GT(buffer_id, 0);
+
+    AHardwareBuffer* hb = nullptr;
+    ASSERT_EQ(0, dvrWriteBufferGetAHardwareBuffer(wbs[i], &hb));
+
+    auto whb_it = whbs.find(buffer_id);
+    if (whb_it == whbs.end()) {
+      // If this is a new buffer id, check that total number of unique
+      // hardware buffers won't exceed queue capacity.
+      ASSERT_LT(whbs.size(), kQueueCapacity);
+      whbs.emplace(buffer_id, hb);
+    } else {
+      // If this is a buffer id we have seen before, check that the
+      // buffer_id maps to the same AHardwareBuffer handle.
+      ASSERT_EQ(hb, whb_it->second);
+    }
+  };
+
+  std::function<void(size_t i)> Post = [&](size_t i) {
+    ASSERT_TRUE(dvrWriteBufferIsValid(wbs[i]));
+
+    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) {
+    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]));
+
+    int buffer_id = dvrReadBufferGetId(rbs[i]);
+    ASSERT_GT(buffer_id, 0);
+
+    AHardwareBuffer* hb = nullptr;
+    ASSERT_EQ(0, dvrReadBufferGetAHardwareBuffer(rbs[i], &hb));
+
+    auto rhb_it = rhbs.find(buffer_id);
+    if (rhb_it == rhbs.end()) {
+      // If this is a new buffer id, check that total number of unique hardware
+      // buffers won't exceed queue capacity.
+      ASSERT_LT(rhbs.size(), kQueueCapacity);
+      rhbs.emplace(buffer_id, hb);
+    } else {
+      // If this is a buffer id we have seen before, check that the buffer_id
+      // maps to the same AHardwareBuffer handle.
+      ASSERT_EQ(hb, rhb_it->second);
+    }
+  };
+
+  std::function<void(size_t i)> Release = [&](size_t i) {
+    ASSERT_TRUE(dvrReadBufferIsValid(rbs[i]));
+
+    int ret = dvrReadBufferQueueReleaseBuffer(read_queue, rbs[i], &metas[i],
+                                              /*release_fence_fd=*/-1);
+    ASSERT_EQ(ret, 0);
+  };
+
+  // Scenario one:
+  for (int i = 0; i < kNumTests; i++) {
+    // Gain all write buffers.
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Gain(i));
+    }
+    // Post all write buffers.
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Post(i));
+    }
+    // Acquire all read buffers.
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Acquire(i));
+    }
+    // Release all read buffers.
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Release(i));
+    }
+  }
+
+  // Scenario two:
+  for (int i = 0; i < kNumTests; i++) {
+    // Gain and post all write buffers.
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Gain(i));
+      ASSERT_NO_FATAL_FAILURE(Post(i));
+    }
+    // Acquire and release all read buffers.
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Acquire(i));
+      ASSERT_NO_FATAL_FAILURE(Release(i));
+    }
+  }
+
+  // Scenario three:
+  for (int i = 0; i < kNumTests; i++) {
+    // Gain all write buffers then post them in reversed order.
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Gain(i));
+    }
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Post(kQueueCapacity - 1 - i));
+    }
+
+    // Acquire all write buffers then release them in reversed order.
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Acquire(i));
+    }
+    for (size_t i = 0; i < kQueueCapacity; i++) {
+      ASSERT_NO_FATAL_FAILURE(Release(kQueueCapacity - 1 - i));
+    }
+  }
+}
+
+}  // namespace
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index f55e994..10c0b31 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -5,12 +5,15 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-DLOG_TAG=\"libpdx\"",
+        "-DTRACE=0",
     ],
     export_include_dirs: ["private"],
     local_include_dirs: ["private"],
     srcs: [
         "client.cpp",
         "service.cpp",
+        "service_dispatcher.cpp",
         "status.cpp",
     ],
 }
@@ -33,6 +36,7 @@
         "variant_tests.cpp",
     ],
     static_libs: [
+        "libcutils",
         "libgmock",
         "libpdx",
         "liblog",
diff --git a/libs/vr/libpdx/client.cpp b/libs/vr/libpdx/client.cpp
index bfa2d87..a01c4d6 100644
--- a/libs/vr/libpdx/client.cpp
+++ b/libs/vr/libpdx/client.cpp
@@ -1,6 +1,5 @@
 #include "pdx/client.h"
 
-#define LOG_TAG "ServiceFramework"
 #include <log/log.h>
 
 #include <pdx/trace.h>
diff --git a/libs/vr/libpdx/mock_tests.cpp b/libs/vr/libpdx/mock_tests.cpp
index 76fd154..4143837 100644
--- a/libs/vr/libpdx/mock_tests.cpp
+++ b/libs/vr/libpdx/mock_tests.cpp
@@ -3,7 +3,6 @@
 #include <pdx/mock_client_channel_factory.h>
 #include <pdx/mock_message_reader.h>
 #include <pdx/mock_message_writer.h>
-#include <pdx/mock_service_dispatcher.h>
 #include <pdx/mock_service_endpoint.h>
 
 TEST(MockTypes, Instantiation) {
@@ -15,6 +14,5 @@
   android::pdx::MockMessageReader message_reader;
   android::pdx::MockOutputResourceMapper output_resource_mapper;
   android::pdx::MockMessageWriter message_writer;
-  android::pdx::MockServiceDispatcher service_dispatcher;
   android::pdx::MockEndpoint endpoint;
 }
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/mock_service_dispatcher.h b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
deleted file mode 100644
index 9b51d30..0000000
--- a/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
-#define ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
-
-#include <gmock/gmock.h>
-#include <pdx/service_dispatcher.h>
-
-namespace android {
-namespace pdx {
-
-class MockServiceDispatcher : public ServiceDispatcher {
- public:
-  MOCK_METHOD1(AddService, int(const std::shared_ptr<Service>& service));
-  MOCK_METHOD1(RemoveService, int(const std::shared_ptr<Service>& service));
-  MOCK_METHOD0(ReceiveAndDispatch, int());
-  MOCK_METHOD1(ReceiveAndDispatch, int(int timeout));
-  MOCK_METHOD0(EnterDispatchLoop, int());
-  MOCK_METHOD1(SetCanceled, void(bool cancel));
-  MOCK_CONST_METHOD0(IsCanceled, bool());
-};
-
-}  // namespace pdx
-}  // namespace android
-
-#endif  // ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_service_endpoint.h b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
index e741d4a..7f829e7 100644
--- a/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
+++ b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
@@ -66,6 +66,7 @@
   MOCK_METHOD0(AllocateMessageState, void*());
   MOCK_METHOD1(FreeMessageState, void(void* state));
   MOCK_METHOD0(Cancel, Status<void>());
+  MOCK_CONST_METHOD0(epoll_fd, int());
 };
 
 }  // namespace pdx
diff --git a/libs/vr/libpdx/private/pdx/service_dispatcher.h b/libs/vr/libpdx/private/pdx/service_dispatcher.h
index c5e342a..bd27000 100644
--- a/libs/vr/libpdx/private/pdx/service_dispatcher.h
+++ b/libs/vr/libpdx/private/pdx/service_dispatcher.h
@@ -2,6 +2,11 @@
 #define ANDROID_PDX_SERVICE_DISPATCHER_H_
 
 #include <memory>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/file_handle.h>
 
 namespace android {
 namespace pdx {
@@ -15,7 +20,10 @@
  */
 class ServiceDispatcher {
  public:
-  virtual ~ServiceDispatcher() = default;
+  // Get a new instance of ServiceDispatcher, or return nullptr if init failed.
+  static std::unique_ptr<ServiceDispatcher> Create();
+
+  ~ServiceDispatcher();
 
   /*
    * Adds a service to the list of services handled by this dispatcher. This
@@ -24,7 +32,7 @@
    *
    * Returns 0 on success; -EEXIST if the service was already added.
    */
-  virtual int AddService(const std::shared_ptr<Service>& service) = 0;
+  int AddService(const std::shared_ptr<Service>& service);
 
   /*
    * Removes a service from this dispatcher. This will fail if any threads are
@@ -33,7 +41,7 @@
    * Returns 0 on success; -ENOENT if the service was not previously added;
    * -EBUSY if there are threads in the dispatcher.
    */
-  virtual int RemoveService(const std::shared_ptr<Service>& service) = 0;
+  int RemoveService(const std::shared_ptr<Service>& service);
 
   /*
    * Receive and dispatch one set of messages. Multiple threads may enter this
@@ -42,14 +50,14 @@
    * cycle, requiring an external loop. This is useful when other work needs
    * to be done in the service dispatch loop.
    */
-  virtual int ReceiveAndDispatch() = 0;
+  int ReceiveAndDispatch();
 
   /*
    * Same as above with timeout in milliseconds. A negative value means
    * infinite timeout, while a value of 0 means return immediately if no
    * messages are available to receive.
    */
-  virtual int ReceiveAndDispatch(int timeout) = 0;
+  int ReceiveAndDispatch(int timeout);
 
   /*
    * Receive and dispatch messages until canceled. When more than one thread
@@ -58,19 +66,39 @@
    * hands Message instances (via move assignment) over to a queue of threads
    * (or perhaps one of several) to handle.
    */
-  virtual int EnterDispatchLoop() = 0;
+  int EnterDispatchLoop();
 
   /*
    * Sets the canceled state of the dispatcher. When canceled is true, any
    * threads blocked waiting for messages will return. This method waits until
    * all dispatch threads have exited the dispatcher.
    */
-  virtual void SetCanceled(bool cancel) = 0;
+  void SetCanceled(bool cancel);
 
   /*
    * Gets the canceled state of the dispatcher.
    */
-  virtual bool IsCanceled() const = 0;
+  bool IsCanceled() const;
+
+ private:
+  ServiceDispatcher();
+
+  // Internal thread accounting.
+  int ThreadEnter();
+  void ThreadExit();
+
+  std::mutex mutex_;
+  std::condition_variable condition_;
+  std::atomic<bool> canceled_{false};
+
+  std::vector<std::shared_ptr<Service>> services_;
+
+  int thread_count_ = 0;
+  LocalHandle event_fd_;
+  LocalHandle epoll_fd_;
+
+  ServiceDispatcher(const ServiceDispatcher&) = delete;
+  void operator=(const ServiceDispatcher&) = delete;
 };
 
 }  // namespace pdx
diff --git a/libs/vr/libpdx/private/pdx/service_endpoint.h b/libs/vr/libpdx/private/pdx/service_endpoint.h
index 28bd6bc..d581894 100644
--- a/libs/vr/libpdx/private/pdx/service_endpoint.h
+++ b/libs/vr/libpdx/private/pdx/service_endpoint.h
@@ -136,6 +136,10 @@
   // Cancels the endpoint, unblocking any receiver threads waiting for a
   // message.
   virtual Status<void> Cancel() = 0;
+
+  // Returns an fd that can be used with epoll() to wait for incoming messages
+  // from this endpoint.
+  virtual int epoll_fd() const = 0;
 };
 
 }  // namespace pdx
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/service.cpp b/libs/vr/libpdx/service.cpp
index fab4770..1d3b62a 100644
--- a/libs/vr/libpdx/service.cpp
+++ b/libs/vr/libpdx/service.cpp
@@ -1,4 +1,3 @@
-#define LOG_TAG "ServiceFramework"
 #include "pdx/service.h"
 
 #include <fcntl.h>
@@ -10,8 +9,6 @@
 
 #include <pdx/trace.h>
 
-#define TRACE 0
-
 namespace android {
 namespace pdx {
 
diff --git a/libs/vr/libpdx_uds/service_dispatcher.cpp b/libs/vr/libpdx/service_dispatcher.cpp
similarity index 83%
rename from libs/vr/libpdx_uds/service_dispatcher.cpp
rename to libs/vr/libpdx/service_dispatcher.cpp
index 2c52578..b112fa3 100644
--- a/libs/vr/libpdx_uds/service_dispatcher.cpp
+++ b/libs/vr/libpdx/service_dispatcher.cpp
@@ -1,26 +1,25 @@
-#include "uds/service_dispatcher.h"
+#include <pdx/service_dispatcher.h>
 
 #include <errno.h>
 #include <log/log.h>
 #include <sys/epoll.h>
 #include <sys/eventfd.h>
 
-#include "pdx/service.h"
-#include "uds/service_endpoint.h"
+#include <pdx/service.h>
+#include <pdx/service_endpoint.h>
 
 static const int kMaxEventsPerLoop = 128;
 
 namespace android {
 namespace pdx {
-namespace uds {
 
-std::unique_ptr<pdx::ServiceDispatcher> ServiceDispatcher::Create() {
+std::unique_ptr<ServiceDispatcher> ServiceDispatcher::Create() {
   std::unique_ptr<ServiceDispatcher> dispatcher{new ServiceDispatcher()};
   if (!dispatcher->epoll_fd_ || !dispatcher->event_fd_) {
     dispatcher.reset();
   }
 
-  return std::move(dispatcher);
+  return dispatcher;
 }
 
 ServiceDispatcher::ServiceDispatcher() {
@@ -70,18 +69,14 @@
 }
 
 int ServiceDispatcher::AddService(const std::shared_ptr<Service>& service) {
-  if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag)
-    return -EINVAL;
-
   std::lock_guard<std::mutex> autolock(mutex_);
 
-  auto* endpoint = static_cast<Endpoint*>(service->endpoint());
   epoll_event event;
   event.events = EPOLLIN;
   event.data.ptr = service.get();
 
-  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, endpoint->epoll_fd(), &event) <
-      0) {
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, service->endpoint()->epoll_fd(),
+                &event) < 0) {
     ALOGE("Failed to add service to dispatcher because: %s\n", strerror(errno));
     return -errno;
   }
@@ -91,9 +86,6 @@
 }
 
 int ServiceDispatcher::RemoveService(const std::shared_ptr<Service>& service) {
-  if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag)
-    return -EINVAL;
-
   std::lock_guard<std::mutex> autolock(mutex_);
 
   // It's dangerous to remove a service while other threads may be using it.
@@ -101,16 +93,15 @@
     return -EBUSY;
 
   epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
-
-  auto* endpoint = static_cast<Endpoint*>(service->endpoint());
-  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, endpoint->epoll_fd(), &dummy) <
-      0) {
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, service->endpoint()->epoll_fd(),
+                &dummy) < 0) {
     ALOGE("Failed to remove service from dispatcher because: %s\n",
           strerror(errno));
     return -errno;
   }
 
-  services_.remove(service);
+  services_.erase(std::remove(services_.begin(), services_.end(), service),
+                  services_.end());
   return 0;
 }
 
@@ -139,7 +130,7 @@
       Service* service = static_cast<Service*>(events[i].data.ptr);
 
       ALOGI_IF(TRACE, "Dispatching message: fd=%d\n",
-               static_cast<Endpoint*>(service->endpoint())->epoll_fd());
+               service->endpoint()->epoll_fd());
       service->ReceiveAndDispatch();
     }
   }
@@ -171,7 +162,7 @@
         Service* service = static_cast<Service*>(events[i].data.ptr);
 
         ALOGI_IF(TRACE, "Dispatching message: fd=%d\n",
-                 static_cast<Endpoint*>(service->endpoint())->epoll_fd());
+                 service->endpoint()->epoll_fd());
         service->ReceiveAndDispatch();
       }
     }
@@ -197,6 +188,5 @@
 
 bool ServiceDispatcher::IsCanceled() const { return canceled_; }
 
-}  // namespace uds
 }  // namespace pdx
 }  // namespace android
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index 8cfa86f..f891c59 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -36,12 +36,13 @@
 }
 
 cc_binary {
-    name: "servicetool",
+    name: "pdx_tool",
     defaults: ["pdx_default_transport_compiler_defaults"],
     srcs: [
-        "servicetool.cpp",
+        "pdx_tool.cpp",
     ],
     shared_libs: [
+        "libcutils",
         "liblog",
     ],
     static_libs: [
diff --git a/libs/vr/libpdx_default_transport/servicetool.cpp b/libs/vr/libpdx_default_transport/pdx_tool.cpp
similarity index 100%
rename from libs/vr/libpdx_default_transport/servicetool.cpp
rename to libs/vr/libpdx_default_transport/pdx_tool.cpp
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h
deleted file mode 100644
index 158871c..0000000
--- a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_
-#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_
-
-#include <servicefs/service_dispatcher.h>
-
-namespace android {
-namespace pdx {
-namespace default_transport {
-
-using ServiceDispatcher = ::android::pdx::servicefs::ServiceDispatcher;
-
-}  // namespace default_transport
-}  // namespace pdx
-}  // namespace android
-
-
-#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h
deleted file mode 100644
index 7cb7a80..0000000
--- a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_
-#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_
-
-#include <uds/service_dispatcher.h>
-
-namespace android {
-namespace pdx {
-namespace default_transport {
-
-using ServiceDispatcher = ::android::pdx::uds::ServiceDispatcher;
-
-}  // namespace default_transport
-}  // namespace pdx
-}  // namespace android
-
-
-#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
index 82a5ea7..d0b7cab 100644
--- a/libs/vr/libpdx_uds/Android.bp
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -16,7 +16,6 @@
         "client_channel_factory.cpp",
         "client_channel.cpp",
         "ipc_helper.cpp",
-        "service_dispatcher.cpp",
         "service_endpoint.cpp",
     ],
     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/client_channel_tests.cpp b/libs/vr/libpdx_uds/client_channel_tests.cpp
index 7c3c68a..1560030 100644
--- a/libs/vr/libpdx_uds/client_channel_tests.cpp
+++ b/libs/vr/libpdx_uds/client_channel_tests.cpp
@@ -13,6 +13,7 @@
 #include <pdx/client.h>
 #include <pdx/rpc/remote_method.h>
 #include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
 
 #include <uds/client_channel_factory.h>
 #include <uds/service_endpoint.h>
@@ -81,7 +82,7 @@
     auto endpoint = Endpoint::CreateFromSocketFd(LocalHandle{});
     endpoint->RegisterNewChannelForTests(std::move(channel_socket));
     service_ = TestService::Create(std::move(endpoint));
-    dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+    dispatcher_ = ServiceDispatcher::Create();
     dispatcher_->AddService(service_);
     dispatch_thread_ = std::thread(
         std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get()));
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_dispatcher.h b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h
deleted file mode 100644
index 23af4f4..0000000
--- a/libs/vr/libpdx_uds/private/uds/service_dispatcher.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_
-#define ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_
-
-#include <list>
-#include <memory>
-#include <mutex>
-#include <unordered_map>
-
-#include <pdx/file_handle.h>
-#include <pdx/service_dispatcher.h>
-
-namespace android {
-namespace pdx {
-namespace uds {
-
-class ServiceDispatcher : public pdx::ServiceDispatcher {
- public:
-  // Get a new instance of ServiceDispatcher, or return nullptr if init failed.
-  static std::unique_ptr<pdx::ServiceDispatcher> Create();
-
-  ~ServiceDispatcher() override;
-  int AddService(const std::shared_ptr<Service>& service) override;
-  int RemoveService(const std::shared_ptr<Service>& service) override;
-  int ReceiveAndDispatch() override;
-  int ReceiveAndDispatch(int timeout) override;
-  int EnterDispatchLoop() override;
-  void SetCanceled(bool cancel) override;
-  bool IsCanceled() const override;
-
- private:
-  ServiceDispatcher();
-
-  // Internal thread accounting.
-  int ThreadEnter();
-  void ThreadExit();
-
-  std::mutex mutex_;
-  std::condition_variable condition_;
-  std::atomic<bool> canceled_{false};
-
-  std::list<std::shared_ptr<Service>> services_;
-
-  int thread_count_ = 0;
-  LocalHandle event_fd_;
-  LocalHandle epoll_fd_;
-
-  ServiceDispatcher(const ServiceDispatcher&) = delete;
-  void operator=(const ServiceDispatcher&) = delete;
-};
-
-}  // namespace uds
-}  // namespace pdx
-}  // namespace android
-
-#endif  // ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx_uds/private/uds/service_endpoint.h b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
index 368891c..01ebf65 100644
--- a/libs/vr/libpdx_uds/private/uds/service_endpoint.h
+++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
@@ -7,12 +7,12 @@
 #include <mutex>
 #include <string>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include <pdx/service.h>
 #include <pdx/service_endpoint.h>
 #include <uds/channel_event_set.h>
-#include <uds/service_dispatcher.h>
 
 namespace android {
 namespace pdx {
@@ -105,7 +105,7 @@
   // socket file descriptor.
   Status<void> RegisterNewChannelForTests(LocalHandle channel_fd);
 
-  int epoll_fd() const { return epoll_fd_.Get(); }
+  int epoll_fd() const override { return epoll_fd_.Get(); }
 
  private:
   struct ChannelData {
@@ -140,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/remote_method_tests.cpp b/libs/vr/libpdx_uds/remote_method_tests.cpp
index 3109753..3f25776 100644
--- a/libs/vr/libpdx_uds/remote_method_tests.cpp
+++ b/libs/vr/libpdx_uds/remote_method_tests.cpp
@@ -15,9 +15,9 @@
 #include <pdx/rpc/remote_method.h>
 #include <pdx/rpc/serializable.h>
 #include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
 #include <uds/client_channel.h>
 #include <uds/client_channel_factory.h>
-#include <uds/service_dispatcher.h>
 #include <uds/service_endpoint.h>
 
 using android::pdx::BorrowedHandle;
@@ -561,7 +561,7 @@
 
   void SetUp() override {
     // Create a dispatcher to handle messages to services.
-    dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+    dispatcher_ = android::pdx::ServiceDispatcher::Create();
     ASSERT_NE(nullptr, dispatcher_);
 
     // Start the message dispatch loop in a separate thread.
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 2943239..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>
@@ -16,10 +17,10 @@
 #include <pdx/client.h>
 #include <pdx/file_handle.h>
 #include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
 #include <private/android_filesystem_config.h>
 #include <uds/client_channel.h>
 #include <uds/client_channel_factory.h>
-#include <uds/service_dispatcher.h>
 #include <uds/service_endpoint.h>
 
 using android::pdx::BorrowedChannelHandle;
@@ -425,7 +426,7 @@
 
   void SetUp() override {
     // Create a dispatcher to handle messages to services.
-    dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+    dispatcher_ = android::pdx::ServiceDispatcher::Create();
     ASSERT_NE(nullptr, dispatcher_);
 
     // Start the message dispatch loop in a separate thread.
@@ -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 733edc6..ac68a5e 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -40,10 +40,8 @@
 DisplayService::DisplayService(Hwc2::Composer* hidl,
                                RequestDisplayCallback request_display_callback)
     : BASE("DisplayService",
-           Endpoint::Create(display::DisplayProtocol::kClientPath)),
-      hardware_composer_(hidl, request_display_callback),
-      request_display_callback_(request_display_callback) {
-  hardware_composer_.Initialize();
+           Endpoint::Create(display::DisplayProtocol::kClientPath)) {
+  hardware_composer_.Initialize(hidl, request_display_callback);
 }
 
 bool DisplayService::IsInitialized() const {
@@ -126,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>(
@@ -245,18 +245,16 @@
           surface_status.GetErrorMessage().c_str());
     return ErrorStatus(surface_status.error());
   }
+  auto surface = surface_status.take();
+  message.SetChannel(surface);
 
-  SurfaceType surface_type = surface_status.get()->surface_type();
-  display::SurfaceUpdateFlags update_flags =
-      surface_status.get()->update_flags();
-  display::SurfaceInfo surface_info{surface_status.get()->surface_id(),
-                                    surface_status.get()->visible(),
-                                    surface_status.get()->z_order()};
+  // Update the surface with the attributes supplied with the create call. For
+  // application surfaces this has the side effect of notifying the display
+  // manager of the new surface. For direct surfaces, this may trigger a mode
+  // change, depending on the value of the visible attribute.
+  surface->OnSetAttributes(message, attributes);
 
-  message.SetChannel(surface_status.take());
-
-  SurfaceUpdated(surface_type, update_flags);
-  return {surface_info};
+  return {{surface->surface_id(), surface->visible(), surface->z_order()}};
 }
 
 void DisplayService::SurfaceUpdated(SurfaceType surface_type,
@@ -353,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));
 }
 
@@ -398,10 +388,6 @@
   return {0};
 }
 
-void DisplayService::OnHardwareComposerRefresh() {
-  hardware_composer_.OnHardwareComposerRefresh();
-}
-
 void DisplayService::SetDisplayConfigurationUpdateNotifier(
     DisplayConfigurationUpdateNotifier update_notifier) {
   update_notifier_ = update_notifier;
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index 6efe264..55e33ab 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -72,8 +72,6 @@
   void GrantDisplayOwnership() { hardware_composer_.Enable(); }
   void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
 
-  void OnHardwareComposerRefresh();
-
  private:
   friend BASE;
   friend DisplaySurface;
@@ -119,7 +117,6 @@
   pdx::Status<void> HandleSurfaceMessage(pdx::Message& message);
 
   HardwareComposer hardware_composer_;
-  RequestDisplayCallback request_display_callback_;
   EpollEventDispatcher dispatcher_;
   DisplayConfigurationUpdateNotifier update_notifier_;
 
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
index 0d6a732..87c823e 100644
--- a/libs/vr/libvrflinger/display_surface.cpp
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -26,14 +26,12 @@
 
 DisplaySurface::DisplaySurface(DisplayService* service,
                                SurfaceType surface_type, int surface_id,
-                               int process_id, int user_id,
-                               const display::SurfaceAttributes& attributes)
+                               int process_id, int user_id)
     : service_(service),
       surface_type_(surface_type),
       surface_id_(surface_id),
       process_id_(process_id),
       user_id_(user_id),
-      attributes_(attributes),
       update_flags_(display::SurfaceUpdateFlags::NewSurface) {}
 
 DisplaySurface::~DisplaySurface() {
@@ -215,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{});
@@ -282,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_) {
@@ -301,6 +299,9 @@
     }
 
     direct_queue_ = producer->CreateConsumerQueue();
+    if (direct_queue_->metadata_size() > 0) {
+      metadata_.reset(new uint8_t[direct_queue_->metadata_size()]);
+    }
     auto status = RegisterQueue(direct_queue_);
     if (!status) {
       ALOGE(
@@ -345,7 +346,12 @@
   while (true) {
     LocalHandle acquire_fence;
     size_t slot;
-    auto buffer_status = direct_queue_->Dequeue(0, &slot, &acquire_fence);
+    auto buffer_status = direct_queue_->Dequeue(
+        0, &slot, metadata_.get(),
+        direct_queue_->metadata_size(), &acquire_fence);
+    ALOGD_IF(TRACE,
+             "DirectDisplaySurface::DequeueBuffersLocked: Dequeue with metadata_size: %zu",
+             direct_queue_->metadata_size());
     if (!buffer_status) {
       ALOGD_IF(
           TRACE > 1 && buffer_status.error() == ETIMEDOUT,
@@ -376,7 +382,7 @@
     }
 
     acquired_buffers_.Append(
-        AcquiredBuffer(buffer_consumer, std::move(acquire_fence)));
+        AcquiredBuffer(buffer_consumer, std::move(acquire_fence), slot));
   }
 }
 
@@ -463,8 +469,8 @@
   if (direct) {
     const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
     if (trusted) {
-      return {std::shared_ptr<DisplaySurface>{new DirectDisplaySurface(
-          service, surface_id, process_id, user_id, attributes)}};
+      return {std::shared_ptr<DisplaySurface>{
+          new DirectDisplaySurface(service, surface_id, process_id, user_id)}};
     } else {
       ALOGE(
           "DisplaySurface::Create: Direct surfaces may only be created by "
@@ -474,7 +480,7 @@
     }
   } else {
     return {std::shared_ptr<DisplaySurface>{new ApplicationDisplaySurface(
-        service, surface_id, process_id, user_id, attributes)}};
+        service, surface_id, process_id, user_id)}};
   }
 }
 
diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h
index 5cbee57..c8b1a07 100644
--- a/libs/vr/libvrflinger/display_surface.h
+++ b/libs/vr/libvrflinger/display_surface.h
@@ -53,8 +53,7 @@
 
  protected:
   DisplaySurface(DisplayService* service, SurfaceType surface_type,
-                 int surface_id, int process_id, int user_id,
-                 const display::SurfaceAttributes& attributes);
+                 int surface_id, int process_id, int user_id);
 
   // Utility to retrieve a shared pointer to this channel as the desired derived
   // type.
@@ -119,10 +118,9 @@
 class ApplicationDisplaySurface : public DisplaySurface {
  public:
   ApplicationDisplaySurface(DisplayService* service, int surface_id,
-                            int process_id, int user_id,
-                            const display::SurfaceAttributes& attributes)
+                            int process_id, int user_id)
       : DisplaySurface(service, SurfaceType::Application, surface_id,
-                       process_id, user_id, attributes) {}
+                       process_id, user_id) {}
 
   std::shared_ptr<ConsumerQueue> GetQueue(int32_t queue_id);
   std::vector<int32_t> GetQueueIds() const override;
@@ -140,11 +138,11 @@
 class DirectDisplaySurface : public DisplaySurface {
  public:
   DirectDisplaySurface(DisplayService* service, int surface_id, int process_id,
-                       int user_id,
-                       const display::SurfaceAttributes& attributes)
+                       int user_id)
       : DisplaySurface(service, SurfaceType::Direct, surface_id, process_id,
-                       user_id, attributes),
-        acquired_buffers_(kMaxPostedBuffers) {}
+                       user_id),
+        acquired_buffers_(kMaxPostedBuffers),
+        metadata_(nullptr) {}
   std::vector<int32_t> GetQueueIds() const override;
   bool IsBufferAvailable();
   bool IsBufferPosted();
@@ -179,6 +177,9 @@
   RingBuffer<AcquiredBuffer> acquired_buffers_;
 
   std::shared_ptr<ConsumerQueue> direct_queue_;
+
+  // Stores metadata when it dequeue buffers from consumer queue.
+  std::unique_ptr<uint8_t[]> metadata_;
 };
 
 }  // namespace dvr
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index def9b7d..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>
@@ -28,7 +30,11 @@
 #include <private/dvr/clock_ns.h>
 #include <private/dvr/ion_buffer.h>
 
+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;
 
@@ -42,12 +48,8 @@
 const char kBacklightBrightnessSysFile[] =
     "/sys/class/leds/lcd-backlight/brightness";
 
-const char kPrimaryDisplayVSyncEventFile[] =
-    "/sys/class/graphics/fb0/vsync_event";
-
-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";
 
@@ -84,24 +86,33 @@
   return true;
 }
 
+// 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());
+  }
+
+  ~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
 
-// Layer static data.
-Hwc2::Composer* Layer::hwc2_hidl_;
-const HWCDisplayMetrics* Layer::display_metrics_;
-
-// HardwareComposer static data;
-constexpr size_t HardwareComposer::kMaxHardwareLayers;
-
 HardwareComposer::HardwareComposer()
-    : HardwareComposer(nullptr, RequestDisplayCallback()) {}
-
-HardwareComposer::HardwareComposer(
-    Hwc2::Composer* hwc2_hidl, RequestDisplayCallback request_display_callback)
-    : initialized_(false),
-      hwc2_hidl_(hwc2_hidl),
-      request_display_callback_(request_display_callback),
-      callbacks_(new ComposerCallback) {}
+    : initialized_(false), request_display_callback_(nullptr) {}
 
 HardwareComposer::~HardwareComposer(void) {
   UpdatePostThreadState(PostThreadState::Quit, true);
@@ -109,16 +120,21 @@
     post_thread_.join();
 }
 
-bool HardwareComposer::Initialize() {
+bool HardwareComposer::Initialize(
+    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 = hwc2_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",
@@ -126,8 +142,8 @@
     return false;
   }
 
-  error =
-      GetDisplayMetrics(HWC_DISPLAY_PRIMARY, config, &native_display_metrics_);
+  error = GetDisplayMetrics(composer, HWC_DISPLAY_PRIMARY, config,
+                            &native_display_metrics_);
 
   if (error != HWC::Error::None) {
     ALOGE(
@@ -149,8 +165,8 @@
   display_transform_ = HWC_TRANSFORM_NONE;
   display_metrics_ = native_display_metrics_;
 
-  // Pass hwc instance and metrics to setup globals for Layer.
-  Layer::InitializeGlobals(hwc2_hidl_, &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(
@@ -210,15 +226,19 @@
 }
 
 void HardwareComposer::OnPostThreadResumed() {
-  hwc2_hidl_->resetCommands();
+  // 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);
+  }
 
-  // HIDL HWC seems to have an internal race condition. If we submit a frame too
-  // soon after turning on VSync we don't get any VSync signals. Give poor HWC
-  // implementations a chance to enable VSync before we continue.
-  EnableVsync(false);
-  std::this_thread::sleep_for(100ms);
   EnableVsync(true);
-  std::this_thread::sleep_for(100ms);
 
   // TODO(skiazyk): We need to do something about accessing this directly,
   // supposedly there is a backlight service on the way.
@@ -233,16 +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();
+  if (composer_) {
+    EnableVsync(false);
   }
-  active_layer_count_ = 0;
 
-  EnableVsync(false);
-
-  hwc2_hidl_->resetCommands();
+  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");
@@ -252,29 +275,35 @@
   uint32_t num_types;
   uint32_t num_requests;
   HWC::Error error =
-      hwc2_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 = hwc2_hidl_->acceptDisplayChanges(display);
+    error = composer_->acceptDisplayChanges(display);
   }
 
   return error;
 }
 
-int32_t HardwareComposer::EnableVsync(bool enabled) {
-  return (int32_t)hwc2_hidl_->setVsyncEnabled(
+HWC::Error HardwareComposer::EnableVsync(bool enabled) {
+  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 = hwc2_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.
@@ -288,20 +317,21 @@
   return error;
 }
 
-HWC::Error HardwareComposer::GetDisplayAttribute(hwc2_display_t display,
+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 hwc2_hidl_->getDisplayAttribute(
+  return composer->getDisplayAttribute(
       display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value);
 }
 
 HWC::Error HardwareComposer::GetDisplayMetrics(
-    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(display, config, HWC2_ATTRIBUTE_WIDTH,
+  error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_WIDTH,
                               &out_metrics->width);
   if (error != HWC::Error::None) {
     ALOGE(
@@ -310,7 +340,7 @@
     return error;
   }
 
-  error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_HEIGHT,
+  error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_HEIGHT,
                               &out_metrics->height);
   if (error != HWC::Error::None) {
     ALOGE(
@@ -319,7 +349,8 @@
     return error;
   }
 
-  error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_VSYNC_PERIOD,
+  error = GetDisplayAttribute(composer, display, config,
+                              HWC2_ATTRIBUTE_VSYNC_PERIOD,
                               &out_metrics->vsync_period_ns);
   if (error != HWC::Error::None) {
     ALOGE(
@@ -328,7 +359,7 @@
     return error;
   }
 
-  error = GetDisplayAttribute(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(
@@ -337,7 +368,7 @@
     return error;
   }
 
-  error = GetDisplayAttribute(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(
@@ -360,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();
@@ -374,7 +405,7 @@
 
   if (post_thread_resumed_) {
     stream << "Hardware Composer Debug Info:" << std::endl;
-    stream << hwc2_hidl_->dumpDebugInfo();
+    stream << composer_->dumpDebugInfo();
   }
 
   return stream.str();
@@ -384,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);
@@ -407,20 +438,18 @@
     retire_fence_fds_.erase(retire_fence_fds_.begin());
   }
 
-  const bool is_frame_pending = IsFramePendingInDriver();
-  const bool is_fence_pending = retire_fence_fds_.size() >
+  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 {
@@ -430,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());
@@ -446,18 +475,18 @@
 
   std::vector<Hwc2::Layer> out_layers;
   std::vector<int> out_fences;
-  error = hwc2_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]);
       }
     }
   }
@@ -473,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.
@@ -546,7 +575,7 @@
 }
 
 int HardwareComposer::PostThreadPollInterruptible(
-    const pdx::LocalHandle& event_fd, int requested_events) {
+    const pdx::LocalHandle& event_fd, int requested_events, int timeout_ms) {
   pollfd pfd[2] = {
       {
           .fd = event_fd.Get(),
@@ -561,7 +590,7 @@
   };
   int ret, error;
   do {
-    ret = poll(pfd, 2, -1);
+    ret = poll(pfd, 2, timeout_ms);
     error = errno;
     ALOGW_IF(ret < 0,
              "HardwareComposer::PostThreadPollInterruptible: Error during "
@@ -571,167 +600,40 @@
 
   if (ret < 0) {
     return -error;
+  } else if (ret == 0) {
+    return -ETIMEDOUT;
   } 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;
-  }
-}
-
-// Reads the timestamp of the last vsync from the display driver.
-// TODO(eieio): This is pretty driver specific, this should be moved to a
-// separate class eventually.
-int HardwareComposer::ReadVSyncTimestamp(int64_t* timestamp) {
-  const int event_fd = primary_display_vsync_event_fd_.Get();
-  int ret, error;
-
-  // 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.
-  ret = lseek(event_fd, 0, SEEK_SET);
-  if (ret < 0) {
-    error = errno;
-    ALOGE(
-        "HardwareComposer::ReadVSyncTimestamp: Failed to seek vsync event fd: "
-        "%s",
-        strerror(error));
-    return -error;
-  }
-
-  // Read the vsync event timestamp.
-  ret = read(event_fd, data.data(), data.size());
-  if (ret < 0) {
-    error = errno;
-    ALOGE_IF(
-        error != EAGAIN,
-        "HardwareComposer::ReadVSyncTimestamp: Error while reading timestamp: "
-        "%s",
-        strerror(error));
-    return -error;
-  }
-
-  ret = sscanf(data.data(), "VSYNC=%" PRIu64,
-               reinterpret_cast<uint64_t*>(timestamp));
-  if (ret < 0) {
-    error = errno;
-    ALOGE(
-        "HardwareComposer::ReadVSyncTimestamp: Error while parsing timestamp: "
-        "%s",
-        strerror(error));
-    return -error;
-  }
-
-  return 0;
-}
-
-// Blocks until the next vsync event is signaled by the display driver.
-// TODO(eieio): This is pretty driver specific, this should be moved to a
-// separate class eventually.
-int HardwareComposer::BlockUntilVSync() {
-  // Vsync is signaled by POLLPRI on the fb vsync node.
-  return PostThreadPollInterruptible(primary_display_vsync_event_fd_, POLLPRI);
+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. This method updates the last_vsync_timeout_ in the
-// process.
-//
-// TODO(eieio): This is pretty driver specific, this should be moved to a
-// separate class eventually.
-int HardwareComposer::WaitForVSync(int64_t* timestamp) {
-  int error;
-
-  // Get the current timestamp and decide what to do.
-  while (true) {
-    int64_t current_vsync_timestamp;
-    error = ReadVSyncTimestamp(&current_vsync_timestamp);
-    if (error < 0 && error != -EAGAIN)
-      return error;
-
-    if (error == -EAGAIN) {
-      // Vsync was turned off, wait for the next vsync event.
-      error = BlockUntilVSync();
-      if (error < 0 || error == kPostThreadInterrupted)
-        return error;
-
-      // Try again to get the timestamp for this new vsync interval.
-      continue;
-    }
-
-    // Check that we advanced to a later vsync interval.
-    if (TimestampGT(current_vsync_timestamp, last_vsync_timestamp_)) {
-      *timestamp = last_vsync_timestamp_ = current_vsync_timestamp;
-      return 0;
-    }
-
-    // See how close we are to the next expected vsync. If we're within 1ms,
-    // sleep for 1ms and try again.
-    const int64_t ns_per_frame = display_metrics_.vsync_period_ns;
-    const int64_t threshold_ns = 1000000;  // 1ms
-
-    const int64_t next_vsync_est = last_vsync_timestamp_ + ns_per_frame;
-    const int64_t distance_to_vsync_est = next_vsync_est - GetSystemClockNs();
-
-    if (distance_to_vsync_est > threshold_ns) {
-      // Wait for vsync event notification.
-      error = BlockUntilVSync();
-      if (error < 0 || error == kPostThreadInterrupted)
-        return error;
-    } else {
-      // Sleep for a short time (1 millisecond) before retrying.
-      error = SleepUntil(GetSystemClockNs() + threshold_ns);
-      if (error < 0 || error == kPostThreadInterrupted)
-        return error;
-    }
+// instead of blocking.
+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;
   }
+  return {predicted_vsync_time};
 }
 
 int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) {
@@ -749,7 +651,8 @@
     return -error;
   }
 
-  return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN);
+  return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN,
+                                     /*timeout_ms*/ -1);
 }
 
 void HardwareComposer::PostThread() {
@@ -772,24 +675,6 @@
            strerror(errno));
 #endif  // ENABLE_BACKLIGHT_BRIGHTNESS
 
-  // Open the vsync event node for the primary display.
-  // TODO(eieio): Move this into a platform-specific class.
-  primary_display_vsync_event_fd_ =
-      LocalHandle(kPrimaryDisplayVSyncEventFile, O_RDONLY);
-  ALOGE_IF(!primary_display_vsync_event_fd_,
-           "HardwareComposer: Failed to open vsync event node for primary "
-           "display: %s",
-           strerror(errno));
-
-  // 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(
@@ -854,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();
 
@@ -923,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();
   }
 }
@@ -941,37 +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], 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;
 }
 
@@ -979,30 +934,6 @@
   vsync_callback_ = callback;
 }
 
-void HardwareComposer::HwcRefresh(hwc2_callback_data_t /*data*/,
-                                  hwc2_display_t /*display*/) {
-  // TODO(eieio): implement invalidate callbacks.
-}
-
-void HardwareComposer::HwcVSync(hwc2_callback_data_t /*data*/,
-                                hwc2_display_t /*display*/,
-                                int64_t /*timestamp*/) {
-  ATRACE_NAME(__PRETTY_FUNCTION__);
-  // Intentionally empty. HWC may require a callback to be set to enable vsync
-  // signals. We bypass this callback thread by monitoring the vsync event
-  // directly, but signals still need to be enabled.
-}
-
-void HardwareComposer::HwcHotplug(hwc2_callback_data_t /*callbackData*/,
-                                  hwc2_display_t /*display*/,
-                                  hwc2_connection_t /*connected*/) {
-  // TODO(eieio): implement display hotplug callbacks.
-}
-
-void HardwareComposer::OnHardwareComposerRefresh() {
-  // TODO(steventhomas): Handle refresh.
-}
-
 void HardwareComposer::SetBacklightBrightness(int brightness) {
   if (backlight_brightness_fd_) {
     std::array<char, 32> text;
@@ -1011,15 +942,121 @@
   }
 }
 
-void Layer::InitializeGlobals(Hwc2::Composer* hwc2_hidl,
-                              const HWCDisplayMetrics* metrics) {
-  hwc2_hidl_ = hwc2_hidl;
-  display_metrics_ = metrics;
+Return<void> HardwareComposer::ComposerCallback::onHotplug(
+    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();
 }
 
+Return<void> HardwareComposer::ComposerCallback::onRefresh(
+    Hwc2::Display /*display*/) {
+  return hardware::Void();
+}
+
+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();
+}
+
+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};
+}
+
+Hwc2::Composer* Layer::composer_{nullptr};
+HWCDisplayMetrics Layer::display_metrics_{0, 0, {0, 0}, 0};
+
 void Layer::Reset() {
-  if (hwc2_hidl_ != nullptr && hardware_composer_layer_) {
-    hwc2_hidl_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
+  if (hardware_composer_layer_) {
+    composer_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
     hardware_composer_layer_ = 0;
   }
 
@@ -1031,41 +1068,74 @@
   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,
-                  HWC::BlendMode blending, HWC::Transform transform,
-                  HWC::Composition composition_type, size_t z_order) {
-  Reset();
-  z_order_ = z_order;
-  blending_ = blending;
-  transform_ = transform;
-  composition_type_ = HWC::Composition::Invalid;
-  target_composition_type_ = composition_type;
-  source_ = SourceSurface{surface};
+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,
-                  HWC::BlendMode blending, HWC::Transform transform,
-                  HWC::Composition composition_type, size_t z_order) {
-  Reset();
-  z_order_ = z_order;
-  blending_ = blending;
-  transform_ = transform;
-  composition_type_ = HWC::Composition::Invalid;
-  target_composition_type_ = composition_type;
-  source_ = SourceBuffer{buffer};
+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) {
   if (source_.is<SourceBuffer>())
     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 {
@@ -1076,93 +1146,108 @@
   return source_.Visit(Visitor{});
 }
 
-void Layer::UpdateLayerSettings() {
-  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 = hwc2_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 = hwc2_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 = hwc2_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 = hwc2_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 =
-      hwc2_hidl_->setLayerPlaneAlpha(display, hardware_composer_layer_, 1.0f);
+      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 =
-      hwc2_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() {
   HWC::Error error =
-      hwc2_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());
+      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;
-      hwc2_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};
-      hwc2_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.
@@ -1170,15 +1255,20 @@
   } else {
     if (composition_type_ != target_composition_type_) {
       composition_type_ = target_composition_type_;
-      hwc2_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 = hwc2_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",
@@ -1187,9 +1277,9 @@
     if (!surface_rect_functions_applied_) {
       const float float_right = right;
       const float float_bottom = bottom;
-      error = hwc2_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 a0c50e1..7010db9 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -52,15 +52,7 @@
 // source supplying buffers for the layer's contents.
 class Layer {
  public:
-  Layer() {}
-
-  // Sets up the global state used by all Layer instances. This must be called
-  // before using any Layer methods.
-  static void InitializeGlobals(Hwc2::Composer* hwc2_hidl,
-                                const HWCDisplayMetrics* metrics);
-
-  // 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
@@ -71,9 +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,
-             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
@@ -83,9 +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, 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.
@@ -120,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();
-
   int GetSurfaceId() const {
     int surface_id = -1;
     pdx::rpc::IfAnyOf<SourceSurface>::Call(
@@ -141,11 +138,47 @@
     return buffer_id;
   }
 
+  // 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; }
+
+  // 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();
 
-  static Hwc2::Composer* hwc2_hidl_;
-  static const HWCDisplayMetrics* display_metrics_;
+  // 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;
@@ -173,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);
       }
     }
 
@@ -217,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*/) {}
@@ -239,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;
@@ -258,16 +301,11 @@
   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(Hwc2::Composer* hidl,
-                   RequestDisplayCallback request_display_callback);
   ~HardwareComposer();
 
-  bool Initialize();
+  bool Initialize(Hwc2::Composer* composer,
+                  RequestDisplayCallback request_display_callback);
 
   bool IsInitialized() const { return initialized_; }
 
@@ -281,11 +319,6 @@
   // Get the HMD display metrics for the current display.
   display::Metrics GetHmdDisplayMetrics() const;
 
-  HWC::Error GetDisplayAttribute(hwc2_display_t display, hwc2_config_t config,
-                                 hwc2_attribute_t attributes,
-                                 int32_t* out_value) const;
-  HWC::Error GetDisplayMetrics(hwc2_display_t display, hwc2_config_t config,
-                               HWCDisplayMetrics* out_metrics) const;
   std::string Dump();
 
   void SetVSyncCallback(VSyncCallback callback);
@@ -308,34 +341,37 @@
   int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer);
   void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
 
-  void OnHardwareComposerRefresh();
-
  private:
-  int32_t EnableVsync(bool enabled);
+  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* 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;
 
-    hardware::Return<void> onHotplug(Hwc2::Display /*display*/,
-                                     Connection /*connected*/) override {
-      // TODO(skiazyk): depending on how the server is implemented, we might
-      // have to set it up to synchronize with receiving this event, as it can
-      // potentially be a critical event for setting up state within the
-      // hwc2 module. That is, we (technically) should not call any other hwc
-      // methods until this method has been called after registering the
-      // callbacks.
-      return hardware::Void();
-    }
+    pdx::Status<int64_t> GetVsyncTime(Hwc2::Display display);
 
-    hardware::Return<void> onRefresh(Hwc2::Display /*display*/) override {
-      return hardware::Void();
-    }
+   private:
+    std::mutex vsync_mutex_;
 
-    hardware::Return<void> onVsync(Hwc2::Display /*display*/,
-                                   int64_t /*timestamp*/) override {
-      return hardware::Void();
-    }
+    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);
@@ -364,22 +400,21 @@
   void UpdatePostThreadState(uint32_t state, bool suspend);
 
   // Blocks until either event_fd becomes readable, or we're interrupted by a
-  // control thread. Any errors are returned as negative errno values. If we're
-  // interrupted, kPostThreadInterrupted will be returned.
+  // control thread, or timeout_ms is reached before any events occur. Any
+  // errors are returned as negative errno values, with -ETIMEDOUT returned in
+  // the case of a timeout. If we're interrupted, kPostThreadInterrupted will be
+  // returned.
   int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd,
-                                  int requested_events);
+                                  int requested_events, int timeout_ms);
 
-  // BlockUntilVSync, WaitForVSync, and SleepUntil are all blocking calls made
-  // on the post thread that can be interrupted by a control thread. If
-  // interrupted, these calls return kPostThreadInterrupted.
+  // 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 BlockUntilVSync();
-  int ReadVSyncTimestamp(int64_t* timestamp);
-  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();
@@ -397,12 +432,11 @@
   void UpdateConfigBuffer();
 
   bool initialized_;
+  bool is_standalone_device_;
 
-  // Hardware composer HAL device from SurfaceFlinger. VrFlinger does not own
-  // this pointer.
-  Hwc2::Composer* hwc2_hidl_;
+  std::unique_ptr<Hwc2::Composer> composer_;
+  sp<ComposerCallback> composer_callback_;
   RequestDisplayCallback request_display_callback_;
-  sp<ComposerCallback> callbacks_;
 
   // Display metrics of the physical display.
   HWCDisplayMetrics native_display_metrics_;
@@ -417,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_;
@@ -433,7 +463,8 @@
   std::thread post_thread_;
 
   // Post thread state machine and synchronization primitives.
-  PostThreadStateType post_thread_state_{PostThreadState::Idle};
+  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_;
@@ -444,18 +475,15 @@
   // Backlight LED brightness sysfs node.
   pdx::LocalHandle backlight_brightness_fd_;
 
-  // Primary display vsync event sysfs node.
-  pdx::LocalHandle primary_display_vsync_event_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;
 
@@ -478,12 +506,6 @@
 
   static constexpr int kPostThreadInterrupted = 1;
 
-  static void HwcRefresh(hwc2_callback_data_t data, hwc2_display_t display);
-  static void HwcVSync(hwc2_callback_data_t data, hwc2_display_t display,
-                       int64_t timestamp);
-  static void HwcHotplug(hwc2_callback_data_t callbackData,
-                         hwc2_display_t display, hwc2_connection_t connected);
-
   HardwareComposer(const HardwareComposer&) = delete;
   void operator=(const HardwareComposer&) = delete;
 };
diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
index f41da87..33cbc84 100644
--- a/libs/vr/libvrflinger/include/dvr/vr_flinger.h
+++ b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
@@ -4,7 +4,7 @@
 #include <thread>
 #include <memory>
 
-#include <pdx/default_transport/service_dispatcher.h>
+#include <pdx/service_dispatcher.h>
 #include <vr/vr_manager/vr_manager.h>
 
 namespace android {
@@ -29,9 +29,6 @@
   void GrantDisplayOwnership();
   void SeizeDisplayOwnership();
 
-  // Called on a binder thread.
-  void OnHardwareComposerRefresh();
-
   // dump all vr flinger state.
   std::string Dump();
 
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
index 3a0ca4a..85dc586 100644
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -18,8 +18,6 @@
 #include <sys/prctl.h>
 #include <sys/resource.h>
 
-#include <pdx/default_transport/service_dispatcher.h>
-
 #include <functional>
 
 #include "DisplayHardware/ComposerHal.h"
@@ -66,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);
 
@@ -76,7 +71,7 @@
 
   request_display_callback_ = request_display_callback;
 
-  dispatcher_ = android::pdx::default_transport::ServiceDispatcher::Create();
+  dispatcher_ = android::pdx::ServiceDispatcher::Create();
   CHECK_ERROR(!dispatcher_, error, "Failed to create service dispatcher.");
 
   display_service_ =
@@ -102,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));
@@ -135,10 +133,6 @@
   display_service_->SeizeDisplayOwnership();
 }
 
-void VrFlinger::OnHardwareComposerRefresh() {
-  display_service_->OnHardwareComposerRefresh();
-}
-
 std::string VrFlinger::Dump() {
   // TODO(karthikrs): Add more state information here.
   return display_service_->DumpState(0/*unused*/);
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 d684ddc..bb25f1d 100644
--- a/libs/vr/libvrsensor/include/dvr/pose_client.h
+++ b/libs/vr/libvrsensor/include/dvr/pose_client.h
@@ -157,6 +157,18 @@
 // @return Zero on success
 int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled);
 
+// Requests a burst of data samples from pose service. The data samples are
+// passed through a shared memory buffer obtained by calling
+// dvrPoseClientGetDataReader().
+//
+// @param DvrPoseDataCaptureRequest Parameters on how to capture data.
+// @return Zero on success.
+int dvrPoseClientDataCapture(DvrPoseClient* client,
+                             const DvrPoseDataCaptureRequest* request);
+
+// Destroys the write buffer queue for the given |data_type|.
+int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
index e4455f1..7bf1cd4 100644
--- a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
+++ b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
@@ -17,6 +17,9 @@
   DVR_POSE_GET_CONTROLLER_RING_BUFFER,
   DVR_POSE_LOG_CONTROLLER,
   DVR_POSE_SENSORS_ENABLE,
+  DVR_POSE_GET_TANGO_READER,
+  DVR_POSE_DATA_CAPTURE,
+  DVR_POSE_TANGO_READER_DESTROY,
 };
 
 #ifdef __cplusplus
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
new file mode 100644
index 0000000..39592bb
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+
+#include <private/dvr/buffer_hub_queue_client.h>
+
+using android::dvr::ConsumerQueue;
+
+typedef struct DvrPoseClient DvrPoseClient;
+
+namespace android {
+namespace dvr {
+
+int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type,
+                                     ConsumerQueue **queue_out);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
index 4ddf1f3..4acc085 100644
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -9,23 +9,24 @@
 #include <pdx/default_transport/client_channel_factory.h>
 #include <pdx/file_handle.h>
 #include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
 #include <private/dvr/display_client.h>
 #include <private/dvr/pose-ipc.h>
 #include <private/dvr/shared_buffer_helpers.h>
 
+using android::dvr::ConsumerQueue;
 using android::pdx::LocalHandle;
 using android::pdx::LocalChannelHandle;
 using android::pdx::Status;
 using android::pdx::Transaction;
 
-#define arraysize(x) (static_cast<int32_t>(std::extent<decltype(x)>::value))
-
 namespace android {
 namespace dvr {
 namespace {
 
 typedef CPUMappedBroadcastRing<DvrPoseRing> SensorPoseRing;
 
+constexpr static int32_t MAX_CONTROLLERS = 2;
 }  // namespace
 
 // PoseClient is a remote interface to the pose service in sensord.
@@ -81,7 +82,7 @@
 
   int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
                         DvrPoseAsync* out_pose) {
-    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+    if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
       return -EINVAL;
     }
     if (!controllers_[controller_id].mapped_pose_buffer) {
@@ -140,6 +141,44 @@
     return ReturnStatusOrError(status);
   }
 
+  int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) {
+    // Get buffer.
+    Transaction trans{*this};
+    Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
+        DVR_POSE_GET_TANGO_READER, &data_type, sizeof(data_type), nullptr, 0);
+
+    if (!status) {
+      ALOGE("PoseClient GetTangoReaderHandle() failed because: %s",
+            status.GetErrorMessage().c_str());
+      *queue_out = nullptr;
+      return -status.error();
+    }
+
+    std::unique_ptr<ConsumerQueue> consumer_queue =
+        ConsumerQueue::Import(status.take());
+    *queue_out = consumer_queue.release();
+    return 0;
+  }
+
+  int DataCapture(const DvrPoseDataCaptureRequest* request) {
+    Transaction trans{*this};
+    Status<int> status = trans.Send<int>(DVR_POSE_DATA_CAPTURE, request,
+                                         sizeof(*request), nullptr, 0);
+    ALOGE_IF(!status, "PoseClient DataCapture() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  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,
+                                         0);
+    ALOGE_IF(!status, "PoseClient DataReaderDestroy() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
   // Enables or disables all pose processing from sensors
   int EnableSensors(bool enabled) {
     Transaction trans{*this};
@@ -166,7 +205,7 @@
   }
 
   int GetControllerRingBuffer(int32_t controller_id) {
-    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+    if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
       return -EINVAL;
     }
     ControllerClientState& client_state = controllers_[controller_id];
@@ -254,9 +293,14 @@
     std::unique_ptr<BufferConsumer> pose_buffer;
     const DvrPoseAsync* mapped_pose_buffer = nullptr;
   };
-  ControllerClientState controllers_[2];
+  ControllerClientState controllers_[MAX_CONTROLLERS];
 };
 
+int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type,
+                                     ConsumerQueue** queue_out) {
+  return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out);
+}
+
 }  // namespace dvr
 }  // namespace android
 
@@ -308,9 +352,17 @@
   return PoseClient::FromC(client)->GetMode(mode);
 }
 
-
 int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled) {
   return PoseClient::FromC(client)->EnableSensors(enabled);
 }
 
+int dvrPoseClientDataCapture(DvrPoseClient* client,
+                             const DvrPoseDataCaptureRequest* request) {
+  return PoseClient::FromC(client)->DataCapture(request);
+}
+
+int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) {
+  return PoseClient::FromC(client)->DataReaderDestroy(data_type);
+}
+
 }  // extern "C"
diff --git a/opengl/Android.bp b/opengl/Android.bp
index aec5a95..9ca8b0b 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -52,6 +52,30 @@
     license: "include/KHR/NOTICE",
 }
 
+llndk_library {
+    name: "libEGL",
+    symbol_file: "libs/libEGL.map.txt",
+    export_include_dirs: ["include"],
+}
+
+llndk_library {
+    name: "libGLESv1_CM",
+    symbol_file: "libs/libGLESv1_CM.map.txt",
+    export_include_dirs: ["include"],
+}
+
+llndk_library {
+    name: "libGLESv2",
+    symbol_file: "libs/libGLESv2.map.txt",
+    export_include_dirs: ["include"],
+}
+
+llndk_library {
+    name: "libGLESv3",
+    symbol_file: "libs/libGLESv3.map.txt",
+    export_include_dirs: ["include"],
+}
+
 cc_library_headers {
     name: "gl_headers",
     vendor_available: true,
diff --git a/opengl/include/EGL/NOTICE b/opengl/include/EGL/NOTICE
index 55f5efa..93d0f2e 100644
--- a/opengl/include/EGL/NOTICE
+++ b/opengl/include/EGL/NOTICE
@@ -1,4 +1,4 @@
-Copyright (c) 2007-2009 The Khronos Group Inc.
+Copyright (c) 2007-2017 The Khronos Group Inc.
 
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and/or associated documentation files (the
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index ccb54ea..93a3965 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -1,11 +1,12 @@
-/* -*- mode: c; tab-width: 8; -*- */
-/* vi: set sw=4 ts=8: */
-/* Reference version of egl.h for EGL 1.4.
- * $Revision: 9356 $ on $Date: 2009-10-21 02:52:25 -0700 (Wed, 21 Oct 2009) $
- */
+#ifndef __egl_h_
+#define __egl_h_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 /*
-** Copyright (c) 2007-2009 The Khronos Group Inc.
+** Copyright (c) 2013-2017 The Khronos Group Inc.
 **
 ** Permission is hereby granted, free of charge, to any person obtaining a
 ** copy of this software and/or associated documentation files (the
@@ -26,304 +27,221 @@
 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */
+/*
+** This header is generated from the Khronos OpenGL / OpenGL ES XML
+** API Registry. The current version of the Registry, generator scripts
+** used to make the header, and the header can be found at
+**   http://www.khronos.org/registry/egl
+**
+** Khronos $Git commit SHA1: a732b061e7 $ on $Git commit date: 2017-06-17 23:27:53 +0100 $
+*/
 
-#ifndef __egl_h_
-#define __egl_h_
-
-/* All platform-dependent types and macro boilerplate (such as EGLAPI
- * and EGLAPIENTRY) should go in eglplatform.h.
- */
 #include <EGL/eglplatform.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+/* Generated on date 20170627 */
 
-/* EGL Types */
-/* EGLint is defined in eglplatform.h */
+/* Generated C header for:
+ * API: egl
+ * Versions considered: .*
+ * Versions emitted: .*
+ * Default extensions included: None
+ * Additional extensions included: _nomatch_^
+ * Extensions removed: _nomatch_^
+ */
+
+#ifndef EGL_VERSION_1_0
+#define EGL_VERSION_1_0 1
 typedef unsigned int EGLBoolean;
-typedef unsigned int EGLenum;
-typedef void *EGLConfig;
-typedef void *EGLContext;
 typedef void *EGLDisplay;
+#include <KHR/khrplatform.h>
+#include <EGL/eglplatform.h>
+typedef void *EGLConfig;
 typedef void *EGLSurface;
-typedef void *EGLClientBuffer;
-
-/* EGL Versioning */
-#define EGL_VERSION_1_0			1
-#define EGL_VERSION_1_1			1
-#define EGL_VERSION_1_2			1
-#define EGL_VERSION_1_3			1
-#define EGL_VERSION_1_4			1
-
-/* EGL Enumerants. Bitmasks and other exceptional cases aside, most
- * enums are assigned unique values starting at 0x3000.
- */
-
-/* EGL aliases */
-#define EGL_FALSE			0
-#define EGL_TRUE			1
-
-/* Out-of-band handle values */
-#define EGL_DEFAULT_DISPLAY		EGL_CAST(EGLNativeDisplayType, 0)
-#define EGL_NO_CONTEXT			EGL_CAST(EGLContext, 0)
-#define EGL_NO_DISPLAY			EGL_CAST(EGLDisplay, 0)
-#define EGL_NO_SURFACE			EGL_CAST(EGLSurface, 0)
-
-/* Out-of-band attribute value */
-#define EGL_DONT_CARE			EGL_CAST(EGLint, -1)
-
-/* Errors / GetError return values */
-#define EGL_SUCCESS			0x3000
-#define EGL_NOT_INITIALIZED		0x3001
-#define EGL_BAD_ACCESS			0x3002
-#define EGL_BAD_ALLOC			0x3003
-#define EGL_BAD_ATTRIBUTE		0x3004
-#define EGL_BAD_CONFIG			0x3005
-#define EGL_BAD_CONTEXT			0x3006
-#define EGL_BAD_CURRENT_SURFACE		0x3007
-#define EGL_BAD_DISPLAY			0x3008
-#define EGL_BAD_MATCH			0x3009
-#define EGL_BAD_NATIVE_PIXMAP		0x300A
-#define EGL_BAD_NATIVE_WINDOW		0x300B
-#define EGL_BAD_PARAMETER		0x300C
-#define EGL_BAD_SURFACE			0x300D
-#define EGL_CONTEXT_LOST		0x300E	/* EGL 1.1 - IMG_power_management */
-
-/* Reserved 0x300F-0x301F for additional errors */
-
-/* Config attributes */
-#define EGL_BUFFER_SIZE			0x3020
-#define EGL_ALPHA_SIZE			0x3021
-#define EGL_BLUE_SIZE			0x3022
-#define EGL_GREEN_SIZE			0x3023
-#define EGL_RED_SIZE			0x3024
-#define EGL_DEPTH_SIZE			0x3025
-#define EGL_STENCIL_SIZE		0x3026
-#define EGL_CONFIG_CAVEAT		0x3027
-#define EGL_CONFIG_ID			0x3028
-#define EGL_LEVEL			0x3029
-#define EGL_MAX_PBUFFER_HEIGHT		0x302A
-#define EGL_MAX_PBUFFER_PIXELS		0x302B
-#define EGL_MAX_PBUFFER_WIDTH		0x302C
-#define EGL_NATIVE_RENDERABLE		0x302D
-#define EGL_NATIVE_VISUAL_ID		0x302E
-#define EGL_NATIVE_VISUAL_TYPE		0x302F
-#define EGL_SAMPLES			0x3031
-#define EGL_SAMPLE_BUFFERS		0x3032
-#define EGL_SURFACE_TYPE		0x3033
-#define EGL_TRANSPARENT_TYPE		0x3034
-#define EGL_TRANSPARENT_BLUE_VALUE	0x3035
-#define EGL_TRANSPARENT_GREEN_VALUE	0x3036
-#define EGL_TRANSPARENT_RED_VALUE	0x3037
-#define EGL_NONE			0x3038	/* Attrib list terminator */
-#define EGL_BIND_TO_TEXTURE_RGB		0x3039
-#define EGL_BIND_TO_TEXTURE_RGBA	0x303A
-#define EGL_MIN_SWAP_INTERVAL		0x303B
-#define EGL_MAX_SWAP_INTERVAL		0x303C
-#define EGL_LUMINANCE_SIZE		0x303D
-#define EGL_ALPHA_MASK_SIZE		0x303E
-#define EGL_COLOR_BUFFER_TYPE		0x303F
-#define EGL_RENDERABLE_TYPE		0x3040
-#define EGL_MATCH_NATIVE_PIXMAP		0x3041	/* Pseudo-attribute (not queryable) */
-#define EGL_CONFORMANT			0x3042
-
-/* Reserved 0x3041-0x304F for additional config attributes */
-
-/* Config attribute values */
-#define EGL_SLOW_CONFIG			0x3050	/* EGL_CONFIG_CAVEAT value */
-#define EGL_NON_CONFORMANT_CONFIG	0x3051	/* EGL_CONFIG_CAVEAT value */
-#define EGL_TRANSPARENT_RGB		0x3052	/* EGL_TRANSPARENT_TYPE value */
-#define EGL_RGB_BUFFER			0x308E	/* EGL_COLOR_BUFFER_TYPE value */
-#define EGL_LUMINANCE_BUFFER		0x308F	/* EGL_COLOR_BUFFER_TYPE value */
-
-/* More config attribute values, for EGL_TEXTURE_FORMAT */
-#define EGL_NO_TEXTURE			0x305C
-#define EGL_TEXTURE_RGB			0x305D
-#define EGL_TEXTURE_RGBA		0x305E
-#define EGL_TEXTURE_2D			0x305F
-
-/* Config attribute mask bits */
-#define EGL_PBUFFER_BIT			0x0001	/* EGL_SURFACE_TYPE mask bits */
-#define EGL_PIXMAP_BIT			0x0002	/* EGL_SURFACE_TYPE mask bits */
-#define EGL_WINDOW_BIT			0x0004	/* EGL_SURFACE_TYPE mask bits */
-#define EGL_VG_COLORSPACE_LINEAR_BIT	0x0020	/* EGL_SURFACE_TYPE mask bits */
-#define EGL_VG_ALPHA_FORMAT_PRE_BIT	0x0040	/* EGL_SURFACE_TYPE mask bits */
-#define EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200	/* EGL_SURFACE_TYPE mask bits */
-#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400	/* EGL_SURFACE_TYPE mask bits */
-
-#define EGL_OPENGL_ES_BIT		0x0001	/* EGL_RENDERABLE_TYPE mask bits */
-#define EGL_OPENVG_BIT			0x0002	/* EGL_RENDERABLE_TYPE mask bits */
-#define EGL_OPENGL_ES2_BIT		0x0004	/* EGL_RENDERABLE_TYPE mask bits */
-#define EGL_OPENGL_BIT			0x0008	/* EGL_RENDERABLE_TYPE mask bits */
-
-/* QueryString targets */
-#define EGL_VENDOR			0x3053
-#define EGL_VERSION			0x3054
-#define EGL_EXTENSIONS			0x3055
-#define EGL_CLIENT_APIS			0x308D
-
-/* QuerySurface / SurfaceAttrib / CreatePbufferSurface targets */
-#define EGL_HEIGHT			0x3056
-#define EGL_WIDTH			0x3057
-#define EGL_LARGEST_PBUFFER		0x3058
-#define EGL_TEXTURE_FORMAT		0x3080
-#define EGL_TEXTURE_TARGET		0x3081
-#define EGL_MIPMAP_TEXTURE		0x3082
-#define EGL_MIPMAP_LEVEL		0x3083
-#define EGL_RENDER_BUFFER		0x3086
-#define EGL_VG_COLORSPACE		0x3087
-#define EGL_VG_ALPHA_FORMAT		0x3088
-#define EGL_HORIZONTAL_RESOLUTION	0x3090
-#define EGL_VERTICAL_RESOLUTION		0x3091
-#define EGL_PIXEL_ASPECT_RATIO		0x3092
-#define EGL_SWAP_BEHAVIOR		0x3093
-#define EGL_MULTISAMPLE_RESOLVE		0x3099
-
-/* EGL_RENDER_BUFFER values / BindTexImage / ReleaseTexImage buffer targets */
-#define EGL_BACK_BUFFER			0x3084
-#define EGL_SINGLE_BUFFER		0x3085
-
-/* OpenVG color spaces */
-#define EGL_VG_COLORSPACE_sRGB		0x3089	/* EGL_VG_COLORSPACE value */
-#define EGL_VG_COLORSPACE_LINEAR	0x308A	/* EGL_VG_COLORSPACE value */
-
-/* O